1 # -*- encoding: binary -*-
4 # This class and its members can be considered a stable interface
5 # and will not change in a backwards-incompatible fashion between
6 # releases of unicorn. Knowledge of this class is generally not
7 # not needed for most users of unicorn.
9 # Some users may want to access it in the before_fork/after_fork hooks.
10 # See the Unicorn::Configurator RDoc for examples.
13 attr_accessor :nr, :switched
14 attr_reader :to_io # IO.select-compatible
16 PER_DROP = Raindrops::PAGE_SIZE / Raindrops::SIZE
20 drop_index = nr / PER_DROP
21 @raindrop = DROPS[drop_index] ||= Raindrops.new(PER_DROP)
22 @offset = nr % PER_DROP
23 @raindrop[@offset] = 0
26 @to_io, @master = Unicorn.pipe
29 def atfork_child # :nodoc:
30 # we _must_ close in child, parent just holds this open to signal
31 @master = @master.close
34 # master fakes SIGQUIT using this
36 @master = @master.close if @master
39 # parent does not read
40 def atfork_parent # :nodoc:
44 # call a signal handler immediately without triggering EINTR
45 # We do not use the more obvious Process.kill(sig, $$) here since
46 # that signal delivery may be deferred. We want to avoid signal delivery
47 # while the Rack app.call is running because some database drivers
48 # (e.g. ruby-pg) may cancel pending requests.
49 def fake_sig(sig) # :nodoc:
50 old_cb = trap(sig, "IGNORE")
56 # master sends fake signals to children
57 def soft_kill(sig) # :nodoc:
62 signum = Signal.list[sig.to_s] or
63 raise ArgumentError, "BUG: bad signal: #{sig.inspect}"
65 # writing and reading 4 bytes on a pipe is atomic on all POSIX platforms
66 # Do not care in the odd case the buffer is full, here.
67 @master.kgio_trywrite([signum].pack('l'))
69 # worker will be reaped soon
72 # this only runs when the Rack app.call is not running
74 def kgio_tryaccept # :nodoc:
75 case buf = @to_io.kgio_tryread(4)
77 # unpack the buffer and trigger the signal handler
78 signum = buf.unpack('l')
80 # keep looping, more signals may be queued
81 when nil # EOF: master died, but we are at a safe place to exit
83 when :wait_readable # keep waiting
85 end while true # loop, as multiple signals may be sent
88 # worker objects may be compared to just plain Integers
89 def ==(other_nr) # :nodoc:
93 # called in the worker process
94 def tick=(value) # :nodoc:
95 @raindrop[@offset] = value
98 # called in the master process
103 # called in both the master (reaping worker) and worker (SIGQUIT handler)
105 @master.close if @master
106 @to_io.close if @to_io
111 # In most cases, you should be using the Unicorn::Configurator#user
112 # directive instead. This method should only be used if you need
113 # fine-grained control of exactly when you want to change permissions
114 # in your after_fork hooks.
116 # Changes the worker process to the specified +user+ and +group+
117 # This is only intended to be called from within the worker
118 # process from the +after_fork+ hook. This should be called in
119 # the +after_fork+ hook after any privileged functions need to be
120 # run (e.g. to set per-worker CPU affinity, niceness, etc)
122 # Any and all errors raised within this method will be propagated
123 # directly back to the caller (usually the +after_fork+ hook.
124 # These errors commonly include ArgumentError for specifying an
125 # invalid user/group and Errno::EPERM for insufficient privileges
126 def user(user, group = nil)
127 # we do not protect the caller, checking Process.euid == 0 is
128 # insufficient because modern systems have fine-grained
129 # capabilities. Let the caller handle any and all errors.
130 uid = Etc.getpwnam(user).uid
131 gid = Etc.getgrnam(group).gid if group
132 Unicorn::Util.chown_logs(uid, gid)
133 if gid && Process.egid != gid
134 Process.initgroups(user, gid)
135 Process::GID.change_privilege(gid)
137 Process.euid != uid and Process::UID.change_privilege(uid)