doc: remove references to old servers
[unicorn.git] / lib / unicorn / worker.rb
blob6748a2f04c9e99cc8f5bb699b10a59711cab3931
1 # -*- encoding: binary -*-
2 require "raindrops"
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.
11 class Unicorn::Worker
12   # :stopdoc:
13   attr_accessor :nr, :switched
14   attr_reader :to_io # IO.select-compatible
16   PER_DROP = Raindrops::PAGE_SIZE / Raindrops::SIZE
17   DROPS = []
19   def initialize(nr)
20     drop_index = nr / PER_DROP
21     @raindrop = DROPS[drop_index] ||= Raindrops.new(PER_DROP)
22     @offset = nr % PER_DROP
23     @raindrop[@offset] = 0
24     @nr = nr
25     @switched = false
26     @to_io, @master = Unicorn.pipe
27   end
29   def atfork_child # :nodoc:
30     # we _must_ close in child, parent just holds this open to signal
31     @master = @master.close
32   end
34   # master fakes SIGQUIT using this
35   def quit # :nodoc:
36     @master = @master.close if @master
37   end
39   # parent does not read
40   def atfork_parent # :nodoc:
41     @to_io = @to_io.close
42   end
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")
51     old_cb.call
52   ensure
53     trap(sig, old_cb)
54   end
56   # master sends fake signals to children
57   def soft_kill(sig) # :nodoc:
58     case sig
59     when Integer
60       signum = sig
61     else
62       signum = Signal.list[sig.to_s] or
63           raise ArgumentError, "BUG: bad signal: #{sig.inspect}"
64     end
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'))
68   rescue Errno::EPIPE
69     # worker will be reaped soon
70   end
72   # this only runs when the Rack app.call is not running
73   # act like a listener
74   def kgio_tryaccept # :nodoc:
75     case buf = @to_io.kgio_tryread(4)
76     when String
77       # unpack the buffer and trigger the signal handler
78       signum = buf.unpack('l')
79       fake_sig(signum[0])
80       # keep looping, more signals may be queued
81     when nil # EOF: master died, but we are at a safe place to exit
82       fake_sig(:QUIT)
83     when :wait_readable # keep waiting
84       return false
85     end while true # loop, as multiple signals may be sent
86   end
88   # worker objects may be compared to just plain Integers
89   def ==(other_nr) # :nodoc:
90     @nr == other_nr
91   end
93   # called in the worker process
94   def tick=(value) # :nodoc:
95     @raindrop[@offset] = value
96   end
98   # called in the master process
99   def tick # :nodoc:
100     @raindrop[@offset]
101   end
103   # called in both the master (reaping worker) and worker (SIGQUIT handler)
104   def close # :nodoc:
105     @master.close if @master
106     @to_io.close if @to_io
107   end
109   # :startdoc:
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.
115   #
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)
121   #
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)
136     end
137     Process.euid != uid and Process::UID.change_privilege(uid)
138     @switched = true
139   end