unicorn-fotopedia 0.99.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (163) hide show
  1. data/.CHANGELOG.old +25 -0
  2. data/.document +19 -0
  3. data/.gitignore +21 -0
  4. data/.mailmap +26 -0
  5. data/CONTRIBUTORS +32 -0
  6. data/COPYING +339 -0
  7. data/DESIGN +105 -0
  8. data/Documentation/.gitignore +5 -0
  9. data/Documentation/GNUmakefile +30 -0
  10. data/Documentation/unicorn.1.txt +171 -0
  11. data/Documentation/unicorn_rails.1.txt +172 -0
  12. data/FAQ +52 -0
  13. data/GIT-VERSION-GEN +40 -0
  14. data/GNUmakefile +292 -0
  15. data/HACKING +116 -0
  16. data/ISSUES +36 -0
  17. data/KNOWN_ISSUES +50 -0
  18. data/LICENSE +55 -0
  19. data/PHILOSOPHY +145 -0
  20. data/README +149 -0
  21. data/Rakefile +191 -0
  22. data/SIGNALS +109 -0
  23. data/Sandbox +78 -0
  24. data/TODO +5 -0
  25. data/TUNING +70 -0
  26. data/bin/unicorn +126 -0
  27. data/bin/unicorn_rails +203 -0
  28. data/examples/big_app_gc.rb +33 -0
  29. data/examples/echo.ru +27 -0
  30. data/examples/git.ru +13 -0
  31. data/examples/init.sh +58 -0
  32. data/examples/logger_mp_safe.rb +25 -0
  33. data/examples/nginx.conf +139 -0
  34. data/examples/unicorn.conf.rb +78 -0
  35. data/ext/unicorn_http/CFLAGS +13 -0
  36. data/ext/unicorn_http/c_util.h +124 -0
  37. data/ext/unicorn_http/common_field_optimization.h +111 -0
  38. data/ext/unicorn_http/ext_help.h +77 -0
  39. data/ext/unicorn_http/extconf.rb +14 -0
  40. data/ext/unicorn_http/global_variables.h +89 -0
  41. data/ext/unicorn_http/unicorn_http.rl +714 -0
  42. data/ext/unicorn_http/unicorn_http_common.rl +75 -0
  43. data/lib/unicorn.rb +847 -0
  44. data/lib/unicorn/app/exec_cgi.rb +150 -0
  45. data/lib/unicorn/app/inetd.rb +109 -0
  46. data/lib/unicorn/app/old_rails.rb +33 -0
  47. data/lib/unicorn/app/old_rails/static.rb +58 -0
  48. data/lib/unicorn/cgi_wrapper.rb +145 -0
  49. data/lib/unicorn/configurator.rb +421 -0
  50. data/lib/unicorn/const.rb +34 -0
  51. data/lib/unicorn/http_request.rb +72 -0
  52. data/lib/unicorn/http_response.rb +75 -0
  53. data/lib/unicorn/launcher.rb +65 -0
  54. data/lib/unicorn/oob_gc.rb +58 -0
  55. data/lib/unicorn/socket_helper.rb +152 -0
  56. data/lib/unicorn/tee_input.rb +217 -0
  57. data/lib/unicorn/util.rb +90 -0
  58. data/local.mk.sample +62 -0
  59. data/setup.rb +1586 -0
  60. data/t/.gitignore +2 -0
  61. data/t/GNUmakefile +67 -0
  62. data/t/README +42 -0
  63. data/t/bin/content-md5-put +36 -0
  64. data/t/bin/sha1sum.rb +23 -0
  65. data/t/bin/unused_listen +40 -0
  66. data/t/bin/utee +12 -0
  67. data/t/env.ru +3 -0
  68. data/t/my-tap-lib.sh +200 -0
  69. data/t/t0000-http-basic.sh +50 -0
  70. data/t/t0001-reload-bad-config.sh +52 -0
  71. data/t/t0002-config-conflict.sh +49 -0
  72. data/t/test-lib.sh +100 -0
  73. data/test/aggregate.rb +15 -0
  74. data/test/benchmark/README +50 -0
  75. data/test/benchmark/dd.ru +18 -0
  76. data/test/exec/README +5 -0
  77. data/test/exec/test_exec.rb +1038 -0
  78. data/test/rails/app-1.2.3/.gitignore +2 -0
  79. data/test/rails/app-1.2.3/Rakefile +7 -0
  80. data/test/rails/app-1.2.3/app/controllers/application.rb +6 -0
  81. data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +36 -0
  82. data/test/rails/app-1.2.3/app/helpers/application_helper.rb +4 -0
  83. data/test/rails/app-1.2.3/config/boot.rb +11 -0
  84. data/test/rails/app-1.2.3/config/database.yml +12 -0
  85. data/test/rails/app-1.2.3/config/environment.rb +13 -0
  86. data/test/rails/app-1.2.3/config/environments/development.rb +9 -0
  87. data/test/rails/app-1.2.3/config/environments/production.rb +5 -0
  88. data/test/rails/app-1.2.3/config/routes.rb +6 -0
  89. data/test/rails/app-1.2.3/db/.gitignore +0 -0
  90. data/test/rails/app-1.2.3/public/404.html +1 -0
  91. data/test/rails/app-1.2.3/public/500.html +1 -0
  92. data/test/rails/app-2.0.2/.gitignore +2 -0
  93. data/test/rails/app-2.0.2/Rakefile +7 -0
  94. data/test/rails/app-2.0.2/app/controllers/application.rb +4 -0
  95. data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +36 -0
  96. data/test/rails/app-2.0.2/app/helpers/application_helper.rb +4 -0
  97. data/test/rails/app-2.0.2/config/boot.rb +11 -0
  98. data/test/rails/app-2.0.2/config/database.yml +12 -0
  99. data/test/rails/app-2.0.2/config/environment.rb +17 -0
  100. data/test/rails/app-2.0.2/config/environments/development.rb +8 -0
  101. data/test/rails/app-2.0.2/config/environments/production.rb +5 -0
  102. data/test/rails/app-2.0.2/config/routes.rb +6 -0
  103. data/test/rails/app-2.0.2/db/.gitignore +0 -0
  104. data/test/rails/app-2.0.2/public/404.html +1 -0
  105. data/test/rails/app-2.0.2/public/500.html +1 -0
  106. data/test/rails/app-2.1.2/.gitignore +2 -0
  107. data/test/rails/app-2.1.2/Rakefile +7 -0
  108. data/test/rails/app-2.1.2/app/controllers/application.rb +4 -0
  109. data/test/rails/app-2.1.2/app/controllers/foo_controller.rb +36 -0
  110. data/test/rails/app-2.1.2/app/helpers/application_helper.rb +4 -0
  111. data/test/rails/app-2.1.2/config/boot.rb +111 -0
  112. data/test/rails/app-2.1.2/config/database.yml +12 -0
  113. data/test/rails/app-2.1.2/config/environment.rb +17 -0
  114. data/test/rails/app-2.1.2/config/environments/development.rb +7 -0
  115. data/test/rails/app-2.1.2/config/environments/production.rb +5 -0
  116. data/test/rails/app-2.1.2/config/routes.rb +6 -0
  117. data/test/rails/app-2.1.2/db/.gitignore +0 -0
  118. data/test/rails/app-2.1.2/public/404.html +1 -0
  119. data/test/rails/app-2.1.2/public/500.html +1 -0
  120. data/test/rails/app-2.2.2/.gitignore +2 -0
  121. data/test/rails/app-2.2.2/Rakefile +7 -0
  122. data/test/rails/app-2.2.2/app/controllers/application.rb +4 -0
  123. data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +36 -0
  124. data/test/rails/app-2.2.2/app/helpers/application_helper.rb +4 -0
  125. data/test/rails/app-2.2.2/config/boot.rb +111 -0
  126. data/test/rails/app-2.2.2/config/database.yml +12 -0
  127. data/test/rails/app-2.2.2/config/environment.rb +17 -0
  128. data/test/rails/app-2.2.2/config/environments/development.rb +7 -0
  129. data/test/rails/app-2.2.2/config/environments/production.rb +5 -0
  130. data/test/rails/app-2.2.2/config/routes.rb +6 -0
  131. data/test/rails/app-2.2.2/db/.gitignore +0 -0
  132. data/test/rails/app-2.2.2/public/404.html +1 -0
  133. data/test/rails/app-2.2.2/public/500.html +1 -0
  134. data/test/rails/app-2.3.5/.gitignore +2 -0
  135. data/test/rails/app-2.3.5/Rakefile +7 -0
  136. data/test/rails/app-2.3.5/app/controllers/application_controller.rb +5 -0
  137. data/test/rails/app-2.3.5/app/controllers/foo_controller.rb +36 -0
  138. data/test/rails/app-2.3.5/app/helpers/application_helper.rb +4 -0
  139. data/test/rails/app-2.3.5/config/boot.rb +109 -0
  140. data/test/rails/app-2.3.5/config/database.yml +12 -0
  141. data/test/rails/app-2.3.5/config/environment.rb +17 -0
  142. data/test/rails/app-2.3.5/config/environments/development.rb +7 -0
  143. data/test/rails/app-2.3.5/config/environments/production.rb +6 -0
  144. data/test/rails/app-2.3.5/config/routes.rb +6 -0
  145. data/test/rails/app-2.3.5/db/.gitignore +0 -0
  146. data/test/rails/app-2.3.5/public/404.html +1 -0
  147. data/test/rails/app-2.3.5/public/500.html +1 -0
  148. data/test/rails/app-2.3.5/public/x.txt +1 -0
  149. data/test/rails/test_rails.rb +280 -0
  150. data/test/test_helper.rb +301 -0
  151. data/test/unit/test_configurator.rb +150 -0
  152. data/test/unit/test_http_parser.rb +555 -0
  153. data/test/unit/test_http_parser_ng.rb +443 -0
  154. data/test/unit/test_request.rb +184 -0
  155. data/test/unit/test_response.rb +110 -0
  156. data/test/unit/test_server.rb +291 -0
  157. data/test/unit/test_signals.rb +206 -0
  158. data/test/unit/test_socket_helper.rb +147 -0
  159. data/test/unit/test_tee_input.rb +257 -0
  160. data/test/unit/test_upload.rb +298 -0
  161. data/test/unit/test_util.rb +96 -0
  162. data/unicorn.gemspec +52 -0
  163. metadata +283 -0
@@ -0,0 +1,75 @@
1
+ %%{
2
+
3
+ machine unicorn_http_common;
4
+
5
+ #### HTTP PROTOCOL GRAMMAR
6
+ # line endings
7
+ CRLF = "\r\n";
8
+
9
+ # character types
10
+ CTL = (cntrl | 127);
11
+ safe = ("$" | "-" | "_" | ".");
12
+ extra = ("!" | "*" | "'" | "(" | ")" | ",");
13
+ reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
14
+ sorta_safe = ("\"" | "<" | ">");
15
+ unsafe = (CTL | " " | "#" | "%" | sorta_safe);
16
+ national = any -- (alpha | digit | reserved | extra | safe | unsafe);
17
+ unreserved = (alpha | digit | safe | extra | national);
18
+ escape = ("%" xdigit xdigit);
19
+ uchar = (unreserved | escape | sorta_safe);
20
+ pchar = (uchar | ":" | "@" | "&" | "=" | "+");
21
+ tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
22
+ lws = (" " | "\t");
23
+
24
+ # elements
25
+ token = (ascii -- (CTL | tspecials));
26
+
27
+ # URI schemes and absolute paths
28
+ scheme = ( "http"i ("s"i)? ) $downcase_char >mark %scheme;
29
+ hostname = (alnum | "-" | "." | "_")+;
30
+ host_with_port = (hostname (":" digit*)?) >mark %host;
31
+ userinfo = ((unreserved | escape | ";" | ":" | "&" | "=" | "+")+ "@")*;
32
+
33
+ path = ( pchar+ ( "/" pchar* )* ) ;
34
+ query = ( uchar | reserved )* %query_string ;
35
+ param = ( pchar | "/" )* ;
36
+ params = ( param ( ";" param )* ) ;
37
+ rel_path = (path? (";" params)? %request_path) ("?" %start_query query)?;
38
+ absolute_path = ( "/"+ rel_path );
39
+ path_uri = absolute_path > mark %request_uri;
40
+ Absolute_URI = (scheme "://" userinfo host_with_port path_uri);
41
+
42
+ Request_URI = ((absolute_path | "*") >mark %request_uri) | Absolute_URI;
43
+ Fragment = ( uchar | reserved )* >mark %fragment;
44
+ Method = (token){1,20} >mark %request_method;
45
+ GetOnly = "GET" >mark %request_method;
46
+
47
+ http_number = ( digit+ "." digit+ ) ;
48
+ HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ;
49
+ Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " " HTTP_Version CRLF ) ;
50
+
51
+ field_name = ( token -- ":" )+ >start_field $snake_upcase_field %write_field;
52
+
53
+ field_value = any* >start_value %write_value;
54
+
55
+ value_cont = lws+ any* >start_value %write_cont_value;
56
+
57
+ message_header = ((field_name ":" lws* field_value)|value_cont) :> CRLF;
58
+ chunk_ext_val = token*;
59
+ chunk_ext_name = token*;
60
+ chunk_extension = ( ";" " "* chunk_ext_name ("=" chunk_ext_val)? )*;
61
+ last_chunk = "0"+ chunk_extension CRLF;
62
+ chunk_size = (xdigit* [1-9a-fA-F] xdigit*) $add_to_chunk_size;
63
+ chunk_end = CRLF;
64
+ chunk_body = any >skip_chunk_data;
65
+ chunk_begin = chunk_size chunk_extension CRLF;
66
+ chunk = chunk_begin chunk_body chunk_end;
67
+ ChunkedBody := chunk* last_chunk @end_chunked_body;
68
+ Trailers := (message_header)* CRLF @end_trailers;
69
+
70
+ FullRequest = Request_Line (message_header)* CRLF @header_done;
71
+ SimpleRequest = GetOnly " " Request_URI ("#"Fragment){0,1} CRLF @header_done;
72
+
73
+ main := FullRequest | SimpleRequest;
74
+
75
+ }%%
@@ -0,0 +1,847 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ require 'fcntl'
4
+ require 'etc'
5
+ require 'rack'
6
+ require 'unicorn/socket_helper'
7
+ require 'unicorn/const'
8
+ require 'unicorn/http_request'
9
+ require 'unicorn/configurator'
10
+ require 'unicorn/util'
11
+ require 'unicorn/tee_input'
12
+ require 'unicorn/http_response'
13
+
14
+ # Unicorn module containing all of the classes (include C extensions) for running
15
+ # a Unicorn web server. It contains a minimalist HTTP server with just enough
16
+ # functionality to service web application requests fast as possible.
17
+ module Unicorn
18
+
19
+ # raised inside TeeInput when a client closes the socket inside the
20
+ # application dispatch. This is always raised with an empty backtrace
21
+ # since there is nothing in the application stack that is responsible
22
+ # for client shutdowns/disconnects.
23
+ class ClientShutdown < EOFError
24
+ end
25
+
26
+ class << self
27
+ def run(app, options = {})
28
+ HttpServer.new(app, options).start.join
29
+ end
30
+
31
+ # This returns a lambda to pass in as the app, this does not "build" the
32
+ # app (which we defer based on the outcome of "preload_app" in the
33
+ # Unicorn config). The returned lambda will be called when it is
34
+ # time to build the app.
35
+ def builder(ru, opts)
36
+ if ru =~ /\.ru\z/
37
+ # parse embedded command-line options in config.ru comments
38
+ /^#\\(.*)/ =~ File.read(ru) and opts.parse!($1.split(/\s+/))
39
+ end
40
+
41
+ lambda do ||
42
+ inner_app = case ru
43
+ when /\.ru$/
44
+ raw = File.read(ru)
45
+ raw.sub!(/^__END__\n.*/, '')
46
+ eval("Rack::Builder.new {(#{raw}\n)}.to_app", TOPLEVEL_BINDING, ru)
47
+ else
48
+ require ru
49
+ Object.const_get(File.basename(ru, '.rb').capitalize)
50
+ end
51
+
52
+ pp({ :inner_app => inner_app }) if $DEBUG
53
+
54
+ # return value, matches rackup defaults based on env
55
+ case ENV["RACK_ENV"]
56
+ when "development"
57
+ Rack::Builder.new do
58
+ use Rack::CommonLogger, $stderr
59
+ use Rack::ShowExceptions
60
+ use Rack::Lint
61
+ run inner_app
62
+ end.to_app
63
+ when "deployment"
64
+ Rack::Builder.new do
65
+ use Rack::CommonLogger, $stderr
66
+ run inner_app
67
+ end.to_app
68
+ else
69
+ inner_app
70
+ end
71
+ end
72
+ end
73
+
74
+ # returns an array of strings representing TCP listen socket addresses
75
+ # and Unix domain socket paths. This is useful for use with
76
+ # Raindrops::Middleware under Linux: http://raindrops.bogomips.org/
77
+ def listener_names
78
+ HttpServer::LISTENERS.map { |io| SocketHelper.sock_name(io) }
79
+ end
80
+ end
81
+
82
+ # This is the process manager of Unicorn. This manages worker
83
+ # processes which in turn handle the I/O and application process.
84
+ # Listener sockets are started in the master process and shared with
85
+ # forked worker children.
86
+
87
+ class HttpServer < Struct.new(:app, :soft_timeout, :timeout, :worker_processes,
88
+ :before_fork, :after_fork, :before_exec,
89
+ :logger, :pid, :listener_opts, :preload_app,
90
+ :reexec_pid, :orig_app, :init_listeners,
91
+ :master_pid, :config, :ready_pipe, :user)
92
+ include ::Unicorn::SocketHelper
93
+
94
+ # prevents IO objects in here from being GC-ed
95
+ IO_PURGATORY = []
96
+
97
+ # all bound listener sockets
98
+ LISTENERS = []
99
+
100
+ # This hash maps PIDs to Workers
101
+ WORKERS = {}
102
+
103
+ # We use SELF_PIPE differently in the master and worker processes:
104
+ #
105
+ # * The master process never closes or reinitializes this once
106
+ # initialized. Signal handlers in the master process will write to
107
+ # it to wake up the master from IO.select in exactly the same manner
108
+ # djb describes in http://cr.yp.to/docs/selfpipe.html
109
+ #
110
+ # * The workers immediately close the pipe they inherit from the
111
+ # master and replace it with a new pipe after forking. This new
112
+ # pipe is also used to wakeup from IO.select from inside (worker)
113
+ # signal handlers. However, workers *close* the pipe descriptors in
114
+ # the signal handlers to raise EBADF in IO.select instead of writing
115
+ # like we do in the master. We cannot easily use the reader set for
116
+ # IO.select because LISTENERS is already that set, and it's extra
117
+ # work (and cycles) to distinguish the pipe FD from the reader set
118
+ # once IO.select returns. So we're lazy and just close the pipe when
119
+ # a (rare) signal arrives in the worker and reinitialize the pipe later.
120
+ SELF_PIPE = []
121
+
122
+ # signal queue used for self-piping
123
+ SIG_QUEUE = []
124
+
125
+ # constant lookups are faster and we're single-threaded/non-reentrant
126
+ REQUEST = HttpRequest.new
127
+
128
+ # We populate this at startup so we can figure out how to reexecute
129
+ # and upgrade the currently running instance of Unicorn
130
+ # This Hash is considered a stable interface and changing its contents
131
+ # will allow you to switch between different installations of Unicorn
132
+ # or even different installations of the same applications without
133
+ # downtime. Keys of this constant Hash are described as follows:
134
+ #
135
+ # * 0 - the path to the unicorn/unicorn_rails executable
136
+ # * :argv - a deep copy of the ARGV array the executable originally saw
137
+ # * :cwd - the working directory of the application, this is where
138
+ # you originally started Unicorn.
139
+ #
140
+ # To change your unicorn executable to a different path without downtime,
141
+ # you can set the following in your Unicorn config file, HUP and then
142
+ # continue with the traditional USR2 + QUIT upgrade steps:
143
+ #
144
+ # Unicorn::HttpServer::START_CTX[0] = "/home/bofh/1.9.2/bin/unicorn"
145
+ START_CTX = {
146
+ :argv => ARGV.map { |arg| arg.dup },
147
+ :cwd => lambda {
148
+ # favor ENV['PWD'] since it is (usually) symlink aware for
149
+ # Capistrano and like systems
150
+ begin
151
+ a = File.stat(pwd = ENV['PWD'])
152
+ b = File.stat(Dir.pwd)
153
+ a.ino == b.ino && a.dev == b.dev ? pwd : Dir.pwd
154
+ rescue
155
+ Dir.pwd
156
+ end
157
+ }.call,
158
+ 0 => $0.dup,
159
+ }
160
+
161
+ # This class and its members can be considered a stable interface
162
+ # and will not change in a backwards-incompatible fashion between
163
+ # releases of Unicorn. You may need to access it in the
164
+ # before_fork/after_fork hooks. See the Unicorn::Configurator RDoc
165
+ # for examples.
166
+ class Worker < Struct.new(:nr, :tmp, :switched)
167
+
168
+ # worker objects may be compared to just plain numbers
169
+ def ==(other_nr)
170
+ self.nr == other_nr
171
+ end
172
+
173
+ # Changes the worker process to the specified +user+ and +group+
174
+ # This is only intended to be called from within the worker
175
+ # process from the +after_fork+ hook. This should be called in
176
+ # the +after_fork+ hook after any priviledged functions need to be
177
+ # run (e.g. to set per-worker CPU affinity, niceness, etc)
178
+ #
179
+ # Any and all errors raised within this method will be propagated
180
+ # directly back to the caller (usually the +after_fork+ hook.
181
+ # These errors commonly include ArgumentError for specifying an
182
+ # invalid user/group and Errno::EPERM for insufficient priviledges
183
+ def user(user, group = nil)
184
+ # we do not protect the caller, checking Process.euid == 0 is
185
+ # insufficient because modern systems have fine-grained
186
+ # capabilities. Let the caller handle any and all errors.
187
+ uid = Etc.getpwnam(user).uid
188
+ gid = Etc.getgrnam(group).gid if group
189
+ Unicorn::Util.chown_logs(uid, gid)
190
+ tmp.chown(uid, gid)
191
+ if gid && Process.egid != gid
192
+ Process.initgroups(user, gid)
193
+ Process::GID.change_privilege(gid)
194
+ end
195
+ Process.euid != uid and Process::UID.change_privilege(uid)
196
+ self.switched = true
197
+ end
198
+
199
+ end
200
+
201
+ # Creates a working server on host:port (strange things happen if
202
+ # port isn't a Number). Use HttpServer::run to start the server and
203
+ # HttpServer.run.join to join the thread that's processing
204
+ # incoming requests on the socket.
205
+ def initialize(app, options = {})
206
+ self.app = app
207
+ self.reexec_pid = 0
208
+ self.ready_pipe = options.delete(:ready_pipe)
209
+ self.init_listeners = options[:listeners] ? options[:listeners].dup : []
210
+ self.config = Configurator.new(options.merge(:use_defaults => true))
211
+ self.listener_opts = {}
212
+
213
+ # we try inheriting listeners first, so we bind them later.
214
+ # we don't write the pid file until we've bound listeners in case
215
+ # unicorn was started twice by mistake. Even though our #pid= method
216
+ # checks for stale/existing pid files, race conditions are still
217
+ # possible (and difficult/non-portable to avoid) and can be likely
218
+ # to clobber the pid if the second start was in quick succession
219
+ # after the first, so we rely on the listener binding to fail in
220
+ # that case. Some tests (in and outside of this source tree) and
221
+ # monitoring tools may also rely on pid files existing before we
222
+ # attempt to connect to the listener(s)
223
+ config.commit!(self, :skip => [:listeners, :pid])
224
+ self.orig_app = app
225
+ end
226
+
227
+ # Runs the thing. Returns self so you can run join on it
228
+ def start
229
+ BasicSocket.do_not_reverse_lookup = true
230
+
231
+ # inherit sockets from parents, they need to be plain Socket objects
232
+ # before they become UNIXServer or TCPServer
233
+ inherited = ENV['UNICORN_FD'].to_s.split(/,/).map do |fd|
234
+ io = Socket.for_fd(fd.to_i)
235
+ set_server_sockopt(io, listener_opts[sock_name(io)])
236
+ IO_PURGATORY << io
237
+ logger.info "inherited addr=#{sock_name(io)} fd=#{fd}"
238
+ server_cast(io)
239
+ end
240
+
241
+ config_listeners = config[:listeners].dup
242
+ LISTENERS.replace(inherited)
243
+
244
+ # we start out with generic Socket objects that get cast to either
245
+ # TCPServer or UNIXServer objects; but since the Socket objects
246
+ # share the same OS-level file descriptor as the higher-level *Server
247
+ # objects; we need to prevent Socket objects from being garbage-collected
248
+ config_listeners -= listener_names
249
+ if config_listeners.empty? && LISTENERS.empty?
250
+ config_listeners << Unicorn::Const::DEFAULT_LISTEN
251
+ init_listeners << Unicorn::Const::DEFAULT_LISTEN
252
+ START_CTX[:argv] << "-l#{Unicorn::Const::DEFAULT_LISTEN}"
253
+ end
254
+ config_listeners.each { |addr| listen(addr) }
255
+ raise ArgumentError, "no listeners" if LISTENERS.empty?
256
+
257
+ # this pipe is used to wake us up from select(2) in #join when signals
258
+ # are trapped. See trap_deferred.
259
+ init_self_pipe!
260
+
261
+ # setup signal handlers before writing pid file in case people get
262
+ # trigger happy and send signals as soon as the pid file exists.
263
+ # Note that signals don't actually get handled until the #join method
264
+ QUEUE_SIGS.each { |sig| trap_deferred(sig) }
265
+ trap(:CHLD) { |_| awaken_master }
266
+ self.pid = config[:pid]
267
+
268
+ self.master_pid = $$
269
+ build_app! if preload_app
270
+ maintain_worker_count
271
+ self
272
+ end
273
+
274
+ # replaces current listener set with +listeners+. This will
275
+ # close the socket if it will not exist in the new listener set
276
+ def listeners=(listeners)
277
+ cur_names, dead_names = [], []
278
+ listener_names.each do |name|
279
+ if ?/ == name[0]
280
+ # mark unlinked sockets as dead so we can rebind them
281
+ (File.socket?(name) ? cur_names : dead_names) << name
282
+ else
283
+ cur_names << name
284
+ end
285
+ end
286
+ set_names = listener_names(listeners)
287
+ dead_names.concat(cur_names - set_names).uniq!
288
+
289
+ LISTENERS.delete_if do |io|
290
+ if dead_names.include?(sock_name(io))
291
+ IO_PURGATORY.delete_if do |pio|
292
+ pio.fileno == io.fileno && (pio.close rescue nil).nil? # true
293
+ end
294
+ (io.close rescue nil).nil? # true
295
+ else
296
+ set_server_sockopt(io, listener_opts[sock_name(io)])
297
+ false
298
+ end
299
+ end
300
+
301
+ (set_names - cur_names).each { |addr| listen(addr) }
302
+ end
303
+
304
+ def stdout_path=(path); redirect_io($stdout, path); end
305
+ def stderr_path=(path); redirect_io($stderr, path); end
306
+
307
+ def logger=(obj)
308
+ HttpRequest::DEFAULTS["rack.logger"] = super
309
+ end
310
+
311
+ # sets the path for the PID file of the master process
312
+ def pid=(path)
313
+ if path
314
+ if x = valid_pid?(path)
315
+ return path if pid && path == pid && x == $$
316
+ raise ArgumentError, "Already running on PID:#{x} " \
317
+ "(or pid=#{path} is stale)"
318
+ end
319
+ end
320
+ unlink_pid_safe(pid) if pid
321
+
322
+ if path
323
+ fp = begin
324
+ tmp = "#{File.dirname(path)}/#{rand}.#$$"
325
+ File.open(tmp, File::RDWR|File::CREAT|File::EXCL, 0644)
326
+ rescue Errno::EEXIST
327
+ retry
328
+ end
329
+ fp.syswrite("#$$\n")
330
+ File.rename(fp.path, path)
331
+ fp.close
332
+ end
333
+ super(path)
334
+ end
335
+
336
+ # add a given address to the +listeners+ set, idempotently
337
+ # Allows workers to add a private, per-process listener via the
338
+ # after_fork hook. Very useful for debugging and testing.
339
+ # +:tries+ may be specified as an option for the number of times
340
+ # to retry, and +:delay+ may be specified as the time in seconds
341
+ # to delay between retries.
342
+ # A negative value for +:tries+ indicates the listen will be
343
+ # retried indefinitely, this is useful when workers belonging to
344
+ # different masters are spawned during a transparent upgrade.
345
+ def listen(address, opt = {}.merge(listener_opts[address] || {}))
346
+ address = config.expand_addr(address)
347
+ return if String === address && listener_names.include?(address)
348
+
349
+ delay = opt[:delay] || 0.5
350
+ tries = opt[:tries] || 5
351
+ begin
352
+ io = bind_listen(address, opt)
353
+ unless TCPServer === io || UNIXServer === io
354
+ IO_PURGATORY << io
355
+ io = server_cast(io)
356
+ end
357
+ logger.info "listening on addr=#{sock_name(io)} fd=#{io.fileno}"
358
+ LISTENERS << io
359
+ io
360
+ rescue Errno::EADDRINUSE => err
361
+ logger.error "adding listener failed addr=#{address} (in use)"
362
+ raise err if tries == 0
363
+ tries -= 1
364
+ logger.error "retrying in #{delay} seconds " \
365
+ "(#{tries < 0 ? 'infinite' : tries} tries left)"
366
+ sleep(delay)
367
+ retry
368
+ rescue => err
369
+ logger.fatal "error adding listener addr=#{address}"
370
+ raise err
371
+ end
372
+ end
373
+
374
+ # monitors children and receives signals forever
375
+ # (or until a termination signal is sent). This handles signals
376
+ # one-at-a-time time and we'll happily drop signals in case somebody
377
+ # is signalling us too often.
378
+ def join
379
+ respawn = true
380
+ last_check = Time.now
381
+
382
+ proc_name 'master'
383
+ logger.info "master process ready" # test_exec.rb relies on this message
384
+ if ready_pipe
385
+ ready_pipe.syswrite($$.to_s)
386
+ ready_pipe.close rescue nil
387
+ self.ready_pipe = nil
388
+ end
389
+ begin
390
+ loop do
391
+ reap_all_workers
392
+ case SIG_QUEUE.shift
393
+ when nil
394
+ # avoid murdering workers after our master process (or the
395
+ # machine) comes out of suspend/hibernation
396
+ if (last_check + soft_timeout) >= (last_check = Time.now)
397
+ murder_lazy_workers
398
+ else
399
+ # wait for workers to wakeup on suspend
400
+ master_sleep(timeout/2.0 + 1)
401
+ end
402
+ maintain_worker_count if respawn
403
+ master_sleep(1)
404
+ when :QUIT # graceful shutdown
405
+ break
406
+ when :TERM, :INT # immediate shutdown
407
+ stop(false)
408
+ break
409
+ when :USR1 # rotate logs
410
+ logger.info "master reopening logs..."
411
+ Unicorn::Util.reopen_logs
412
+ logger.info "master done reopening logs"
413
+ kill_each_worker(:USR1)
414
+ when :USR2 # exec binary, stay alive in case something went wrong
415
+ reexec
416
+ when :WINCH
417
+ if Process.ppid == 1 || Process.getpgrp != $$
418
+ respawn = false
419
+ logger.info "gracefully stopping all workers"
420
+ kill_each_worker(:QUIT)
421
+ else
422
+ logger.info "SIGWINCH ignored because we're not daemonized"
423
+ end
424
+ when :TTIN
425
+ self.worker_processes += 1
426
+ when :TTOU
427
+ self.worker_processes -= 1 if self.worker_processes > 0
428
+ when :HUP
429
+ respawn = true
430
+ if config.config_file
431
+ load_config!
432
+ redo # immediate reaping since we may have QUIT workers
433
+ else # exec binary and exit if there's no config file
434
+ logger.info "config_file not present, reexecuting binary"
435
+ reexec
436
+ break
437
+ end
438
+ end
439
+ end
440
+ rescue Errno::EINTR
441
+ retry
442
+ rescue => e
443
+ logger.error "Unhandled master loop exception #{e.inspect}."
444
+ logger.error e.backtrace.join("\n")
445
+ retry
446
+ end
447
+ stop # gracefully shutdown all workers on our way out
448
+ logger.info "master complete"
449
+ unlink_pid_safe(pid) if pid
450
+ end
451
+
452
+ # Terminates all workers, but does not exit master process
453
+ def stop(graceful = true)
454
+ self.listeners = []
455
+ limit = Time.now + timeout
456
+ until WORKERS.empty? || Time.now > limit
457
+ kill_each_worker(graceful ? :QUIT : :TERM)
458
+ sleep(0.1)
459
+ reap_all_workers
460
+ end
461
+ kill_each_worker(:KILL)
462
+ end
463
+
464
+ private
465
+
466
+ # list of signals we care about and trap in master.
467
+ QUEUE_SIGS = [ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP,
468
+ :TTIN, :TTOU ]
469
+
470
+ # defer a signal for later processing in #join (master process)
471
+ def trap_deferred(signal)
472
+ trap(signal) do |sig_nr|
473
+ if SIG_QUEUE.size < 5
474
+ SIG_QUEUE << signal
475
+ awaken_master
476
+ else
477
+ logger.error "ignoring SIG#{signal}, queue=#{SIG_QUEUE.inspect}"
478
+ end
479
+ end
480
+ end
481
+
482
+ # wait for a signal hander to wake us up and then consume the pipe
483
+ # Wake up every second anyways to run murder_lazy_workers
484
+ def master_sleep(sec)
485
+ begin
486
+ ready = IO.select([SELF_PIPE.first], nil, nil, sec) or return
487
+ ready.first && ready.first.first or return
488
+ loop { SELF_PIPE.first.read_nonblock(Const::CHUNK_SIZE) }
489
+ rescue Errno::EAGAIN, Errno::EINTR
490
+ end
491
+ end
492
+
493
+ def awaken_master
494
+ begin
495
+ SELF_PIPE.last.write_nonblock('.') # wakeup master process from select
496
+ rescue Errno::EAGAIN, Errno::EINTR
497
+ # pipe is full, master should wake up anyways
498
+ retry
499
+ end
500
+ end
501
+
502
+ # reaps all unreaped workers
503
+ def reap_all_workers
504
+ begin
505
+ loop do
506
+ wpid, status = Process.waitpid2(-1, Process::WNOHANG)
507
+ wpid or break
508
+ if reexec_pid == wpid
509
+ logger.error "reaped #{status.inspect} exec()-ed"
510
+ self.reexec_pid = 0
511
+ self.pid = pid.chomp('.oldbin') if pid
512
+ proc_name 'master'
513
+ else
514
+ worker = WORKERS.delete(wpid) and worker.tmp.close rescue nil
515
+ logger.info "reaped #{status.inspect} " \
516
+ "worker=#{worker.nr rescue 'unknown'}"
517
+ end
518
+ end
519
+ rescue Errno::ECHILD
520
+ end
521
+ end
522
+
523
+ # reexecutes the START_CTX with a new binary
524
+ def reexec
525
+ if reexec_pid > 0
526
+ begin
527
+ Process.kill(0, reexec_pid)
528
+ logger.error "reexec-ed child already running PID:#{reexec_pid}"
529
+ return
530
+ rescue Errno::ESRCH
531
+ self.reexec_pid = 0
532
+ end
533
+ end
534
+
535
+ if pid
536
+ old_pid = "#{pid}.oldbin"
537
+ prev_pid = pid.dup
538
+ begin
539
+ self.pid = old_pid # clear the path for a new pid file
540
+ rescue ArgumentError
541
+ logger.error "old PID:#{valid_pid?(old_pid)} running with " \
542
+ "existing pid=#{old_pid}, refusing rexec"
543
+ return
544
+ rescue => e
545
+ logger.error "error writing pid=#{old_pid} #{e.class} #{e.message}"
546
+ return
547
+ end
548
+ end
549
+
550
+ self.reexec_pid = fork do
551
+ listener_fds = LISTENERS.map { |sock| sock.fileno }
552
+ ENV['UNICORN_FD'] = listener_fds.join(',')
553
+ Dir.chdir(START_CTX[:cwd])
554
+ cmd = [ START_CTX[0] ].concat(START_CTX[:argv])
555
+
556
+ # avoid leaking FDs we don't know about, but let before_exec
557
+ # unset FD_CLOEXEC, if anything else in the app eventually
558
+ # relies on FD inheritence.
559
+ (3..1024).each do |io|
560
+ next if listener_fds.include?(io)
561
+ io = IO.for_fd(io) rescue nil
562
+ io or next
563
+ IO_PURGATORY << io
564
+ io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
565
+ end
566
+ logger.info "executing #{cmd.inspect} (in #{Dir.pwd})"
567
+ before_exec.call(self)
568
+ exec(*cmd)
569
+ end
570
+ proc_name 'master (old)'
571
+ end
572
+
573
+ # forcibly terminate all workers that haven't checked in in timeout
574
+ # seconds. The timeout is implemented using an unlinked File
575
+ # shared between the parent process and each worker. The worker
576
+ # runs File#chmod to modify the ctime of the File. If the ctime
577
+ # is stale for >timeout seconds, then we'll kill the corresponding
578
+ # worker.
579
+ def murder_lazy_workers
580
+ WORKERS.dup.each_pair do |wpid, worker|
581
+ stat = worker.tmp.stat
582
+ # skip workers that disable fchmod or have never fchmod-ed
583
+ stat.mode == 0100600 and next
584
+ # FIXME: if the worker has not been working for soft_timeout, it will be
585
+ # killed even if it is not blocking
586
+ (diff = (Time.now - stat.ctime)) <= soft_timeout and
587
+ diff <= timeout and next
588
+ # lazy since less than timeout, attempt soft kill
589
+ if diff < timeout
590
+ logger.error "worker=#{worker.nr} PID:#{wpid} soft timeout " \
591
+ "(#{diff}s > #{soft_timeout}s), killing softly"
592
+ kill_worker(:ABRT, wpid)
593
+ else
594
+ logger.error "worker=#{worker.nr} PID:#{wpid} hard timeout " \
595
+ "(#{diff}s > #{timeout}s), killing"
596
+ kill_worker(:KILL, wpid) # take no prisoners for timeout violations
597
+ end
598
+ end
599
+ end
600
+
601
+ def spawn_missing_workers
602
+ (0...worker_processes).each do |worker_nr|
603
+ WORKERS.values.include?(worker_nr) and next
604
+ worker = Worker.new(worker_nr, Unicorn::Util.tmpio)
605
+ before_fork.call(self, worker)
606
+ WORKERS[fork {
607
+ ready_pipe.close if ready_pipe
608
+ self.ready_pipe = nil
609
+ worker_loop(worker)
610
+ }] = worker
611
+ end
612
+ end
613
+
614
+ def maintain_worker_count
615
+ (off = WORKERS.size - worker_processes) == 0 and return
616
+ off < 0 and return spawn_missing_workers
617
+ WORKERS.dup.each_pair { |wpid,w|
618
+ w.nr >= worker_processes and kill_worker(:QUIT, wpid) rescue nil
619
+ }
620
+ end
621
+
622
+ # if we get any error, try to write something back to the client
623
+ # assuming we haven't closed the socket, but don't get hung up
624
+ # if the socket is already closed or broken. We'll always ensure
625
+ # the socket is closed at the end of this function
626
+ def handle_error(client, e)
627
+ msg = case e
628
+ when EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
629
+ Const::ERROR_500_RESPONSE
630
+ when HttpParserError # try to tell the client they're bad
631
+ Const::ERROR_400_RESPONSE
632
+ else
633
+ logger.error "Read error: #{e.inspect}"
634
+ logger.error e.backtrace.join("\n")
635
+ Const::ERROR_500_RESPONSE
636
+ end
637
+ client.write_nonblock(msg)
638
+ client.close
639
+ rescue
640
+ nil
641
+ end
642
+
643
+ # once a client is accepted, it is processed in its entirety here
644
+ # in 3 easy steps: read request, call app, write app response
645
+ def process_client(client)
646
+ client.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
647
+ response = app.call(env = REQUEST.read(client))
648
+
649
+ if 100 == response.first.to_i
650
+ client.write(Const::EXPECT_100_RESPONSE)
651
+ env.delete(Const::HTTP_EXPECT)
652
+ response = app.call(env)
653
+ end
654
+ HttpResponse.write(client, response, HttpRequest::PARSER.headers?)
655
+ rescue => e
656
+ handle_error(client, e)
657
+ end
658
+
659
+ # gets rid of stuff the worker has no business keeping track of
660
+ # to free some resources and drops all sig handlers.
661
+ # traps for USR1, USR2, and HUP may be set in the after_fork Proc
662
+ # by the user.
663
+ def init_worker_process(worker)
664
+ QUEUE_SIGS.each { |sig| trap(sig, nil) }
665
+ trap(:CHLD, 'DEFAULT')
666
+ SIG_QUEUE.clear
667
+ proc_name "worker[#{worker.nr}]"
668
+ START_CTX.clear
669
+ init_self_pipe!
670
+
671
+ # try to handle SIGABRT correctly
672
+ trap('ABRT') do
673
+ raise SignalException, "SIGABRT"
674
+ end
675
+
676
+ WORKERS.values.each { |other| other.tmp.close rescue nil }
677
+ WORKERS.clear
678
+ LISTENERS.each { |sock| sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
679
+ worker.tmp.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
680
+ after_fork.call(self, worker) # can drop perms
681
+ worker.user(*user) if user.kind_of?(Array) && ! worker.switched
682
+ self.timeout /= 2.0 # halve it for select()
683
+ build_app! unless preload_app
684
+ end
685
+
686
+ def reopen_worker_logs(worker_nr)
687
+ logger.info "worker=#{worker_nr} reopening logs..."
688
+ Unicorn::Util.reopen_logs
689
+ logger.info "worker=#{worker_nr} done reopening logs"
690
+ init_self_pipe!
691
+ end
692
+
693
+ # runs inside each forked worker, this sits around and waits
694
+ # for connections and doesn't die until the parent dies (or is
695
+ # given a INT, QUIT, or TERM signal)
696
+ def worker_loop(worker)
697
+ ppid = master_pid
698
+ init_worker_process(worker)
699
+ nr = 0 # this becomes negative if we need to reopen logs
700
+ alive = worker.tmp # tmp is our lifeline to the master process
701
+ ready = LISTENERS
702
+
703
+ # closing anything we IO.select on will raise EBADF
704
+ trap(:USR1) { nr = -65536; SELF_PIPE.first.close rescue nil }
705
+ trap(:QUIT) { alive = nil; LISTENERS.each { |s| s.close rescue nil } }
706
+ [:TERM, :INT].each { |sig| trap(sig) { exit!(0) } } # instant shutdown
707
+ logger.info "worker=#{worker.nr} ready"
708
+ m = 0
709
+
710
+ begin
711
+ nr < 0 and reopen_worker_logs(worker.nr)
712
+ nr = 0
713
+
714
+ # we're a goner in timeout seconds anyways if alive.chmod
715
+ # breaks, so don't trap the exception. Using fchmod() since
716
+ # futimes() is not available in base Ruby and I very strongly
717
+ # prefer temporary files to be unlinked for security,
718
+ # performance and reliability reasons, so utime is out. No-op
719
+ # changes with chmod doesn't update ctime on all filesystems; so
720
+ # we change our counter each and every time (after process_client
721
+ # and before IO.select).
722
+ alive.chmod(m = 0 == m ? 1 : 0)
723
+
724
+ ready.each do |sock|
725
+ begin
726
+ process_client(sock.accept_nonblock)
727
+ nr += 1
728
+ alive.chmod(m = 0 == m ? 1 : 0)
729
+ rescue Errno::EAGAIN, Errno::ECONNABORTED
730
+ end
731
+ break if nr < 0
732
+ end
733
+
734
+ # make the following bet: if we accepted clients this round,
735
+ # we're probably reasonably busy, so avoid calling select()
736
+ # and do a speculative accept_nonblock on ready listeners
737
+ # before we sleep again in select().
738
+ redo unless nr == 0 # (nr < 0) => reopen logs
739
+
740
+ ppid == Process.ppid or return
741
+ alive.chmod(m = 0 == m ? 1 : 0)
742
+ begin
743
+ # timeout used so we can detect parent death:
744
+ ret = IO.select(LISTENERS, nil, SELF_PIPE, timeout) or redo
745
+ ready = ret.first
746
+ rescue Errno::EINTR
747
+ ready = LISTENERS
748
+ rescue Errno::EBADF
749
+ nr < 0 or return
750
+ end
751
+ rescue => e
752
+ if alive
753
+ logger.error "Unhandled listen loop exception #{e.inspect}."
754
+ logger.error e.backtrace.join("\n")
755
+ end
756
+ end while alive
757
+ end
758
+
759
+ # delivers a signal to a worker and fails gracefully if the worker
760
+ # is no longer running.
761
+ def kill_worker(signal, wpid)
762
+ begin
763
+ Process.kill(signal, wpid)
764
+ rescue Errno::ESRCH
765
+ worker = WORKERS.delete(wpid) and worker.tmp.close rescue nil
766
+ end
767
+ end
768
+
769
+ # delivers a signal to each worker
770
+ def kill_each_worker(signal)
771
+ WORKERS.keys.each { |wpid| kill_worker(signal, wpid) }
772
+ end
773
+
774
+ # unlinks a PID file at given +path+ if it contains the current PID
775
+ # still potentially racy without locking the directory (which is
776
+ # non-portable and may interact badly with other programs), but the
777
+ # window for hitting the race condition is small
778
+ def unlink_pid_safe(path)
779
+ (File.read(path).to_i == $$ and File.unlink(path)) rescue nil
780
+ end
781
+
782
+ # returns a PID if a given path contains a non-stale PID file,
783
+ # nil otherwise.
784
+ def valid_pid?(path)
785
+ wpid = File.read(path).to_i
786
+ wpid <= 0 and return nil
787
+ begin
788
+ Process.kill(0, wpid)
789
+ wpid
790
+ rescue Errno::ESRCH
791
+ # don't unlink stale pid files, racy without non-portable locking...
792
+ end
793
+ rescue Errno::ENOENT
794
+ end
795
+
796
+ def load_config!
797
+ loaded_app = app
798
+ begin
799
+ logger.info "reloading config_file=#{config.config_file}"
800
+ config[:listeners].replace(init_listeners)
801
+ config.reload
802
+ config.commit!(self)
803
+ kill_each_worker(:QUIT)
804
+ Unicorn::Util.reopen_logs
805
+ self.app = orig_app
806
+ build_app! if preload_app
807
+ logger.info "done reloading config_file=#{config.config_file}"
808
+ rescue StandardError, LoadError, SyntaxError => e
809
+ logger.error "error reloading config_file=#{config.config_file}: " \
810
+ "#{e.class} #{e.message}"
811
+ self.app = loaded_app
812
+ end
813
+ end
814
+
815
+ # returns an array of string names for the given listener array
816
+ def listener_names(listeners = LISTENERS)
817
+ listeners.map { |io| sock_name(io) }
818
+ end
819
+
820
+ def build_app!
821
+ if app.respond_to?(:arity) && app.arity == 0
822
+ if defined?(Gem) && Gem.respond_to?(:refresh)
823
+ logger.info "Refreshing Gem list"
824
+ Gem.refresh
825
+ end
826
+ self.app = app.call
827
+ end
828
+ end
829
+
830
+ def proc_name(tag)
831
+ $0 = ([ File.basename(START_CTX[0]), tag
832
+ ]).concat(START_CTX[:argv]).join(' ')
833
+ end
834
+
835
+ def redirect_io(io, path)
836
+ File.open(path, 'ab') { |fp| io.reopen(fp) } if path
837
+ io.sync = true
838
+ end
839
+
840
+ def init_self_pipe!
841
+ SELF_PIPE.each { |io| io.close rescue nil }
842
+ SELF_PIPE.replace(IO.pipe)
843
+ SELF_PIPE.each { |io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
844
+ end
845
+
846
+ end
847
+ end