passenger 2.0.6 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of passenger might be problematic. Click here for more details.

Files changed (497) hide show
  1. data/DEVELOPERS.TXT +10 -4
  2. data/LICENSE +1 -1
  3. data/NEWS +0 -0
  4. data/Rakefile +183 -117
  5. data/benchmark/dispatcher.rb +5 -9
  6. data/bin/passenger-install-apache2-module +52 -18
  7. data/bin/passenger-memory-stats +67 -13
  8. data/bin/passenger-spawn-server +8 -4
  9. data/bin/passenger-status +21 -46
  10. data/bin/passenger-stress-test +5 -5
  11. data/debian/postinst +1 -1
  12. data/doc/ApplicationPool algorithm.txt +180 -128
  13. data/doc/Architectural overview.html +1 -778
  14. data/doc/Security of user switching support.html +1 -643
  15. data/doc/Users guide Apache.html +3127 -0
  16. data/doc/Users guide Nginx.html +1458 -0
  17. data/doc/Users guide.html +1404 -467
  18. data/doc/Users guide.txt +843 -105
  19. data/doc/cxxapi/ApplicationPoolServer_8h-source.html +751 -641
  20. data/doc/cxxapi/ApplicationPool_8h-source.html +168 -171
  21. data/doc/cxxapi/Application_8h-source.html +494 -394
  22. data/doc/cxxapi/Bucket_8h-source.html +21 -15
  23. data/doc/cxxapi/CachedFileStat_8h-source.html +191 -0
  24. data/doc/cxxapi/Configuration_8h-source.html +311 -149
  25. data/doc/cxxapi/DirectoryMapper_8h-source.html +309 -0
  26. data/doc/cxxapi/DummySpawnManager_8h-source.html +3 -4
  27. data/doc/cxxapi/Exceptions_8h-source.html +182 -165
  28. data/doc/cxxapi/FileChecker_8h-source.html +130 -0
  29. data/doc/cxxapi/Hooks_8h-source.html +2 -3
  30. data/doc/cxxapi/Logging_8h-source.html +92 -89
  31. data/doc/cxxapi/MessageChannel_8h-source.html +585 -477
  32. data/doc/cxxapi/PoolOptions_8h-source.html +305 -0
  33. data/doc/cxxapi/SpawnManager_8h-source.html +515 -540
  34. data/doc/cxxapi/StandardApplicationPool_8h-source.html +779 -679
  35. data/doc/cxxapi/SystemTime_8h-source.html +104 -0
  36. data/doc/cxxapi/Utils_8h-source.html +331 -227
  37. data/doc/cxxapi/annotated.html +6 -7
  38. data/doc/cxxapi/classClient-members.html +1 -2
  39. data/doc/cxxapi/classClient.html +1 -2
  40. data/doc/cxxapi/classHooks-members.html +5 -2
  41. data/doc/cxxapi/classHooks.html +112 -2
  42. data/doc/cxxapi/classPassenger_1_1Application-members.html +2 -3
  43. data/doc/cxxapi/classPassenger_1_1Application.html +8 -9
  44. data/doc/cxxapi/classPassenger_1_1ApplicationPool-members.html +4 -4
  45. data/doc/cxxapi/classPassenger_1_1ApplicationPool.html +42 -81
  46. data/doc/cxxapi/classPassenger_1_1ApplicationPoolServer-members.html +1 -2
  47. data/doc/cxxapi/classPassenger_1_1ApplicationPoolServer.html +3 -4
  48. data/doc/cxxapi/classPassenger_1_1ApplicationPool__inherit__graph.png +0 -0
  49. data/doc/cxxapi/classPassenger_1_1Application_1_1Session-members.html +3 -2
  50. data/doc/cxxapi/classPassenger_1_1Application_1_1Session.html +74 -3
  51. data/doc/cxxapi/classPassenger_1_1BusyException-members.html +1 -2
  52. data/doc/cxxapi/classPassenger_1_1BusyException.html +2 -3
  53. data/doc/cxxapi/classPassenger_1_1ConfigurationException-members.html +1 -2
  54. data/doc/cxxapi/classPassenger_1_1ConfigurationException.html +2 -3
  55. data/doc/cxxapi/classPassenger_1_1DirectoryMapper-members.html +38 -0
  56. data/doc/cxxapi/classPassenger_1_1DirectoryMapper.html +256 -0
  57. data/doc/cxxapi/classPassenger_1_1DummySpawnManager-members.html +1 -2
  58. data/doc/cxxapi/classPassenger_1_1DummySpawnManager.html +2 -3
  59. data/doc/cxxapi/{classPassenger_1_1Thread-members.html → classPassenger_1_1FileChecker-members.html} +4 -5
  60. data/doc/cxxapi/classPassenger_1_1FileChecker.html +121 -0
  61. data/doc/cxxapi/classPassenger_1_1FileNotFoundException-members.html +1 -2
  62. data/doc/cxxapi/classPassenger_1_1FileNotFoundException.html +2 -3
  63. data/doc/cxxapi/classPassenger_1_1FileNotFoundException__inherit__graph.png +0 -0
  64. data/doc/cxxapi/classPassenger_1_1FileSystemException-members.html +2 -3
  65. data/doc/cxxapi/classPassenger_1_1FileSystemException.html +2 -3
  66. data/doc/cxxapi/classPassenger_1_1FileSystemException__inherit__graph.png +0 -0
  67. data/doc/cxxapi/classPassenger_1_1IOException-members.html +1 -2
  68. data/doc/cxxapi/classPassenger_1_1IOException.html +2 -3
  69. data/doc/cxxapi/classPassenger_1_1IOException__inherit__graph.png +0 -0
  70. data/doc/cxxapi/classPassenger_1_1MessageChannel-members.html +5 -2
  71. data/doc/cxxapi/classPassenger_1_1MessageChannel.html +155 -5
  72. data/doc/cxxapi/{classboost_1_1this__thread_1_1enable__syscall__interruption-members.html → classPassenger_1_1RuntimeException-members.html} +2 -3
  73. data/doc/cxxapi/{classboost_1_1this__thread_1_1enable__syscall__interruption.html → classPassenger_1_1RuntimeException.html} +10 -8
  74. data/doc/cxxapi/classPassenger_1_1SpawnException-members.html +1 -2
  75. data/doc/cxxapi/classPassenger_1_1SpawnException.html +2 -3
  76. data/doc/cxxapi/classPassenger_1_1SpawnManager-members.html +2 -3
  77. data/doc/cxxapi/classPassenger_1_1SpawnManager.html +15 -56
  78. data/doc/cxxapi/classPassenger_1_1StandardApplicationPool-members.html +5 -4
  79. data/doc/cxxapi/classPassenger_1_1StandardApplicationPool.html +20 -81
  80. data/doc/cxxapi/classPassenger_1_1StandardApplicationPool__inherit__graph.png +0 -0
  81. data/doc/cxxapi/classPassenger_1_1SystemException-members.html +2 -3
  82. data/doc/cxxapi/classPassenger_1_1SystemException.html +9 -10
  83. data/doc/cxxapi/classPassenger_1_1SystemException__inherit__graph.png +0 -0
  84. data/doc/cxxapi/{classboost_1_1this__thread_1_1disable__syscall__interruption-members.html → classPassenger_1_1SystemTime-members.html} +5 -3
  85. data/doc/cxxapi/classPassenger_1_1SystemTime.html +86 -0
  86. data/doc/cxxapi/classPassenger_1_1TempFile-members.html +2 -3
  87. data/doc/cxxapi/classPassenger_1_1TempFile.html +17 -9
  88. data/doc/cxxapi/definitions_8h-source.html +1 -2
  89. data/doc/cxxapi/files.html +6 -3
  90. data/doc/cxxapi/functions.html +98 -35
  91. data/doc/cxxapi/functions_func.html +60 -32
  92. data/doc/cxxapi/functions_type.html +1 -2
  93. data/doc/cxxapi/functions_vars.html +27 -2
  94. data/doc/cxxapi/graph_legend.html +1 -2
  95. data/doc/cxxapi/graph_legend.png +0 -0
  96. data/doc/cxxapi/group__Configuration.html +3 -4
  97. data/doc/cxxapi/group__Configuration.png +0 -0
  98. data/doc/cxxapi/group__Core.html +3 -4
  99. data/doc/cxxapi/group__Core.png +0 -0
  100. data/doc/cxxapi/group__Exceptions.html +4 -2
  101. data/doc/cxxapi/group__Hooks.html +1 -6
  102. data/doc/cxxapi/group__Hooks.png +0 -0
  103. data/doc/cxxapi/group__Support.html +259 -36
  104. data/doc/cxxapi/hierarchy.html +6 -7
  105. data/doc/cxxapi/inherit__graph__0.map +1 -1
  106. data/doc/cxxapi/inherit__graph__0.md5 +1 -1
  107. data/doc/cxxapi/inherit__graph__0.png +0 -0
  108. data/doc/cxxapi/inherit__graph__1.map +1 -1
  109. data/doc/cxxapi/inherit__graph__1.md5 +1 -1
  110. data/doc/cxxapi/inherit__graph__1.png +0 -0
  111. data/doc/cxxapi/inherit__graph__10.map +1 -1
  112. data/doc/cxxapi/inherit__graph__10.md5 +1 -1
  113. data/doc/cxxapi/inherit__graph__10.png +0 -0
  114. data/doc/cxxapi/inherit__graph__11.map +1 -1
  115. data/doc/cxxapi/inherit__graph__11.md5 +1 -1
  116. data/doc/cxxapi/inherit__graph__11.png +0 -0
  117. data/doc/cxxapi/inherit__graph__12.map +2 -1
  118. data/doc/cxxapi/inherit__graph__12.md5 +1 -1
  119. data/doc/cxxapi/inherit__graph__12.png +0 -0
  120. data/doc/cxxapi/inherit__graph__13.map +1 -1
  121. data/doc/cxxapi/inherit__graph__13.md5 +1 -1
  122. data/doc/cxxapi/inherit__graph__13.png +0 -0
  123. data/doc/cxxapi/inherit__graph__14.map +1 -2
  124. data/doc/cxxapi/inherit__graph__14.md5 +1 -1
  125. data/doc/cxxapi/inherit__graph__14.png +0 -0
  126. data/doc/cxxapi/inherit__graph__15.map +1 -1
  127. data/doc/cxxapi/inherit__graph__15.md5 +1 -1
  128. data/doc/cxxapi/inherit__graph__15.png +0 -0
  129. data/doc/cxxapi/inherit__graph__16.map +1 -1
  130. data/doc/cxxapi/inherit__graph__16.md5 +1 -1
  131. data/doc/cxxapi/inherit__graph__16.png +0 -0
  132. data/doc/cxxapi/inherit__graph__17.png +0 -0
  133. data/doc/cxxapi/inherit__graph__18.png +0 -0
  134. data/doc/cxxapi/inherit__graph__19.png +0 -0
  135. data/doc/cxxapi/inherit__graph__2.map +1 -1
  136. data/doc/cxxapi/inherit__graph__2.md5 +1 -1
  137. data/doc/cxxapi/inherit__graph__2.png +0 -0
  138. data/doc/cxxapi/inherit__graph__20.map +1 -1
  139. data/doc/cxxapi/inherit__graph__20.md5 +1 -1
  140. data/doc/cxxapi/inherit__graph__20.png +0 -0
  141. data/doc/cxxapi/inherit__graph__21.map +1 -1
  142. data/doc/cxxapi/inherit__graph__21.md5 +1 -1
  143. data/doc/cxxapi/inherit__graph__21.png +0 -0
  144. data/doc/cxxapi/inherit__graph__3.map +1 -1
  145. data/doc/cxxapi/inherit__graph__3.md5 +1 -1
  146. data/doc/cxxapi/inherit__graph__3.png +0 -0
  147. data/doc/cxxapi/inherit__graph__4.map +1 -1
  148. data/doc/cxxapi/inherit__graph__4.md5 +1 -1
  149. data/doc/cxxapi/inherit__graph__4.png +0 -0
  150. data/doc/cxxapi/inherit__graph__5.map +2 -1
  151. data/doc/cxxapi/inherit__graph__5.md5 +1 -1
  152. data/doc/cxxapi/inherit__graph__5.png +0 -0
  153. data/doc/cxxapi/inherit__graph__6.map +1 -1
  154. data/doc/cxxapi/inherit__graph__6.md5 +1 -1
  155. data/doc/cxxapi/inherit__graph__6.png +0 -0
  156. data/doc/cxxapi/inherit__graph__7.map +1 -1
  157. data/doc/cxxapi/inherit__graph__7.md5 +1 -1
  158. data/doc/cxxapi/inherit__graph__7.png +0 -0
  159. data/doc/cxxapi/inherit__graph__8.map +1 -1
  160. data/doc/cxxapi/inherit__graph__8.md5 +1 -1
  161. data/doc/cxxapi/inherit__graph__8.png +0 -0
  162. data/doc/cxxapi/inherit__graph__9.map +1 -2
  163. data/doc/cxxapi/inherit__graph__9.md5 +1 -1
  164. data/doc/cxxapi/inherit__graph__9.png +0 -0
  165. data/doc/cxxapi/inherits.html +30 -31
  166. data/doc/cxxapi/main.html +1 -2
  167. data/doc/cxxapi/modules.html +1 -2
  168. data/doc/cxxapi/structPassenger_1_1AnythingToString-members.html +1 -2
  169. data/doc/cxxapi/structPassenger_1_1AnythingToString.html +2 -3
  170. data/doc/cxxapi/structPassenger_1_1AnythingToString_3_01vector_3_01string_01_4_01_4-members.html +1 -2
  171. data/doc/cxxapi/structPassenger_1_1AnythingToString_3_01vector_3_01string_01_4_01_4.html +2 -3
  172. data/doc/cxxapi/structPassenger_1_1PoolOptions-members.html +49 -0
  173. data/doc/cxxapi/structPassenger_1_1PoolOptions.html +404 -0
  174. data/doc/cxxapi/tree.html +18 -20
  175. data/doc/images/conservative_spawning.png +0 -0
  176. data/doc/images/conservative_spawning.svg +248 -0
  177. data/doc/images/smart-lv2.png +0 -0
  178. data/doc/images/smart-lv2.svg +320 -0
  179. data/doc/rdoc/classes/ConditionVariable.html +68 -34
  180. data/doc/rdoc/classes/Exception.html +16 -16
  181. data/doc/rdoc/classes/GC.html +9 -9
  182. data/doc/rdoc/classes/IO.html +36 -17
  183. data/doc/rdoc/classes/PhusionPassenger.html +183 -0
  184. data/doc/rdoc/classes/PhusionPassenger/AbstractRequestHandler.html +511 -0
  185. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/AbstractServer.html +285 -242
  186. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/AbstractServer/ServerAlreadyStarted.html +3 -3
  187. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/AbstractServer/ServerError.html +3 -3
  188. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/AbstractServer/ServerNotStarted.html +3 -3
  189. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/AbstractServer/UnknownMessage.html +3 -3
  190. data/doc/rdoc/classes/PhusionPassenger/AbstractServerCollection.html +598 -0
  191. data/doc/rdoc/classes/PhusionPassenger/AdminTools.html +140 -0
  192. data/doc/rdoc/classes/PhusionPassenger/AdminTools/ControlProcess.html +247 -0
  193. data/doc/rdoc/classes/PhusionPassenger/AdminTools/ControlProcess/Instance.html +138 -0
  194. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/AppInitError.html +36 -19
  195. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/Application.html +81 -96
  196. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/ConsoleTextTemplate.html +18 -18
  197. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/FrameworkInitError.html +20 -18
  198. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/HTMLTemplate.html +18 -18
  199. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/InitializationError.html +9 -9
  200. data/doc/rdoc/classes/PhusionPassenger/InvalidPath.html +92 -0
  201. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/MessageChannel.html +93 -92
  202. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/NativeSupport.html +55 -25
  203. data/doc/rdoc/classes/PhusionPassenger/Rack.html +91 -0
  204. data/doc/rdoc/classes/PhusionPassenger/Rack/ApplicationSpawner.html +185 -0
  205. data/doc/rdoc/classes/PhusionPassenger/Rack/RequestHandler.html +182 -0
  206. data/doc/rdoc/classes/PhusionPassenger/Railz.html +95 -0
  207. data/doc/rdoc/classes/PhusionPassenger/Railz/ApplicationSpawner.html +424 -0
  208. data/doc/rdoc/classes/PhusionPassenger/Railz/ApplicationSpawner/Error.html +98 -0
  209. data/doc/rdoc/classes/PhusionPassenger/Railz/CGIFixed.html +200 -0
  210. data/doc/rdoc/classes/PhusionPassenger/Railz/FrameworkSpawner.html +444 -0
  211. data/doc/rdoc/classes/PhusionPassenger/Railz/FrameworkSpawner/Error.html +98 -0
  212. data/doc/rdoc/classes/PhusionPassenger/Railz/RequestHandler.html +154 -0
  213. data/doc/rdoc/classes/PhusionPassenger/SpawnManager.html +408 -0
  214. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/UnknownError.html +13 -13
  215. data/doc/rdoc/classes/PhusionPassenger/Utils.html +687 -0
  216. data/doc/rdoc/classes/{Passenger → PhusionPassenger}/VersionNotFound.html +8 -8
  217. data/doc/rdoc/classes/PhusionPassenger/WSGI.html +89 -0
  218. data/doc/rdoc/classes/PhusionPassenger/WSGI/ApplicationSpawner.html +188 -0
  219. data/doc/rdoc/classes/PlatformInfo.html +663 -159
  220. data/doc/rdoc/classes/RakeExtensions.html +4 -4
  221. data/doc/rdoc/classes/Signal.html +134 -0
  222. data/doc/rdoc/created.rid +1 -1
  223. data/doc/rdoc/files/DEVELOPERS_TXT.html +15 -10
  224. data/doc/rdoc/files/README.html +5 -7
  225. data/doc/rdoc/files/ext/{passenger → phusion_passenger}/native_support_c.html +2 -2
  226. data/doc/rdoc/files/lib/{passenger → phusion_passenger}/abstract_request_handler_rb.html +7 -9
  227. data/doc/rdoc/files/lib/phusion_passenger/abstract_server_collection_rb.html +120 -0
  228. data/doc/rdoc/files/lib/{passenger → phusion_passenger}/abstract_server_rb.html +7 -10
  229. data/doc/rdoc/files/lib/phusion_passenger/admin_tools/control_process_rb.html +99 -0
  230. data/doc/rdoc/files/lib/phusion_passenger/admin_tools_rb.html +92 -0
  231. data/doc/rdoc/files/lib/{passenger → phusion_passenger}/application_rb.html +6 -8
  232. data/doc/rdoc/files/lib/{passenger → phusion_passenger}/console_text_template_rb.html +5 -7
  233. data/doc/rdoc/files/lib/{passenger → phusion_passenger}/constants_rb.html +4 -5
  234. data/doc/rdoc/files/lib/{passenger → phusion_passenger}/dependencies_rb.html +6 -8
  235. data/doc/rdoc/files/lib/phusion_passenger/events_rb.html +116 -0
  236. data/doc/rdoc/files/lib/{passenger → phusion_passenger}/exceptions_rb.html +5 -7
  237. data/doc/rdoc/files/lib/{passenger → phusion_passenger}/html_template_rb.html +5 -7
  238. data/doc/rdoc/files/lib/{passenger → phusion_passenger}/message_channel_rb.html +5 -7
  239. data/doc/rdoc/files/lib/{passenger → phusion_passenger}/platform_info_rb.html +6 -7
  240. data/doc/rdoc/files/lib/phusion_passenger/rack/application_spawner_rb.html +123 -0
  241. data/doc/rdoc/files/lib/phusion_passenger/rack/request_handler_rb.html +117 -0
  242. data/doc/rdoc/files/lib/{passenger/utils_rb.html → phusion_passenger/railz/application_spawner_rb.html} +24 -17
  243. data/doc/rdoc/files/lib/phusion_passenger/railz/cgi_fixed_rb.html +126 -0
  244. data/doc/rdoc/files/lib/phusion_passenger/railz/framework_spawner_rb.html +139 -0
  245. data/doc/rdoc/files/lib/phusion_passenger/railz/request_handler_rb.html +118 -0
  246. data/doc/rdoc/files/lib/{passenger → phusion_passenger}/simple_benchmarking_rb.html +5 -7
  247. data/doc/rdoc/files/lib/{passenger → phusion_passenger}/spawn_manager_rb.html +40 -24
  248. data/doc/rdoc/files/lib/phusion_passenger/utils_rb.html +169 -0
  249. data/doc/rdoc/files/lib/phusion_passenger/wsgi/application_spawner_rb.html +120 -0
  250. data/doc/rdoc/files/lib/rake/extensions_rb.html +3 -4
  251. data/doc/rdoc/fr_class_index.html +37 -19
  252. data/doc/rdoc/fr_file_index.html +25 -14
  253. data/doc/rdoc/fr_method_index.html +145 -74
  254. data/ext/apache2/Application.h +145 -44
  255. data/ext/apache2/ApplicationPool.h +27 -29
  256. data/ext/apache2/ApplicationPoolServer.h +183 -72
  257. data/ext/apache2/ApplicationPoolServerExecutable.cpp +249 -42
  258. data/ext/apache2/Bucket.cpp +61 -9
  259. data/ext/apache2/Bucket.h +15 -8
  260. data/ext/apache2/CachedFileStat.cpp +114 -0
  261. data/ext/apache2/CachedFileStat.h +169 -0
  262. data/ext/apache2/Configuration.cpp +213 -22
  263. data/ext/apache2/Configuration.h +176 -13
  264. data/ext/apache2/DirectoryMapper.h +287 -0
  265. data/ext/apache2/Exceptions.h +30 -12
  266. data/ext/apache2/FileChecker.h +108 -0
  267. data/ext/apache2/Hooks.cpp +709 -493
  268. data/ext/apache2/LICENSE-CNRI.TXT +15 -0
  269. data/ext/apache2/Logging.h +26 -22
  270. data/ext/apache2/MessageChannel.h +124 -15
  271. data/ext/apache2/PoolOptions.h +283 -0
  272. data/ext/apache2/SpawnManager.h +75 -99
  273. data/ext/apache2/StandardApplicationPool.h +296 -195
  274. data/ext/apache2/SystemTime.cpp +28 -0
  275. data/ext/apache2/SystemTime.h +82 -0
  276. data/ext/apache2/Utils.cpp +172 -18
  277. data/ext/apache2/Utils.h +124 -19
  278. data/ext/boost/cstdint.hpp +4 -2
  279. data/ext/boost/current_function.hpp +67 -0
  280. data/ext/boost/detail/sp_counted_base.hpp +4 -4
  281. data/ext/boost/thread/exceptions.hpp +2 -1
  282. data/ext/boost/thread/pthread/thread.hpp +11 -3
  283. data/ext/boost/thread/pthread/thread_data.hpp +2 -1
  284. data/ext/oxt/backtrace.cpp +172 -0
  285. data/ext/oxt/backtrace.hpp +135 -0
  286. data/ext/oxt/detail/backtrace_disabled.hpp +39 -0
  287. data/ext/oxt/detail/backtrace_enabled.hpp +155 -0
  288. data/ext/oxt/detail/spin_lock_gcc_x86.hpp +82 -0
  289. data/ext/oxt/detail/spin_lock_portable.hpp +38 -0
  290. data/ext/oxt/detail/spin_lock_pthreads.hpp +97 -0
  291. data/ext/oxt/detail/tracable_exception_disabled.hpp +46 -0
  292. data/ext/oxt/detail/tracable_exception_enabled.hpp +48 -0
  293. data/ext/oxt/macros.hpp +58 -0
  294. data/ext/oxt/spin_lock.hpp +55 -0
  295. data/ext/{apache2/System.cpp → oxt/system_calls.cpp} +87 -52
  296. data/ext/oxt/system_calls.hpp +234 -0
  297. data/ext/oxt/thread.cpp +32 -0
  298. data/ext/oxt/thread.hpp +223 -0
  299. data/ext/oxt/tracable_exception.cpp +87 -0
  300. data/ext/oxt/tracable_exception.hpp +35 -0
  301. data/{lib/passenger/constants.rb → ext/phusion_passenger/extconf.rb} +14 -9
  302. data/ext/{passenger → phusion_passenger}/native_support.c +33 -6
  303. data/lib/{passenger → phusion_passenger}/abstract_request_handler.rb +209 -93
  304. data/lib/{passenger → phusion_passenger}/abstract_server.rb +23 -8
  305. data/lib/phusion_passenger/abstract_server_collection.rb +301 -0
  306. data/lib/phusion_passenger/admin_tools.rb +25 -0
  307. data/lib/phusion_passenger/admin_tools/control_process.rb +107 -0
  308. data/lib/{passenger → phusion_passenger}/application.rb +13 -16
  309. data/lib/{passenger → phusion_passenger}/console_text_template.rb +2 -2
  310. data/{ext/passenger/extconf.rb → lib/phusion_passenger/constants.rb} +5 -5
  311. data/lib/{passenger → phusion_passenger}/dependencies.rb +38 -32
  312. data/lib/phusion_passenger/events.rb +45 -0
  313. data/lib/{passenger → phusion_passenger}/exceptions.rb +12 -5
  314. data/lib/{passenger → phusion_passenger}/html_template.rb +2 -2
  315. data/lib/{passenger → phusion_passenger}/message_channel.rb +3 -2
  316. data/lib/phusion_passenger/platform_info.rb +500 -0
  317. data/lib/{passenger → phusion_passenger}/rack/application_spawner.rb +29 -22
  318. data/lib/{passenger → phusion_passenger}/rack/request_handler.rb +14 -9
  319. data/lib/{passenger → phusion_passenger}/railz/application_spawner.rb +94 -74
  320. data/lib/{passenger → phusion_passenger}/railz/cgi_fixed.rb +2 -2
  321. data/lib/{passenger → phusion_passenger}/railz/framework_spawner.rb +86 -98
  322. data/lib/{passenger → phusion_passenger}/railz/request_handler.rb +6 -6
  323. data/lib/{passenger → phusion_passenger}/simple_benchmarking.rb +0 -0
  324. data/lib/{passenger → phusion_passenger}/spawn_manager.rb +136 -128
  325. data/lib/{passenger → phusion_passenger}/templates/apache2_config_snippets.txt.erb +0 -0
  326. data/lib/{passenger → phusion_passenger}/templates/apache_must_be_compiled_with_compatible_mpm.txt.erb +0 -0
  327. data/lib/phusion_passenger/templates/app_exited_during_initialization.html.erb +38 -0
  328. data/lib/{passenger → phusion_passenger}/templates/app_init_error.html.erb +0 -0
  329. data/lib/{passenger → phusion_passenger}/templates/database_error.html.erb +0 -0
  330. data/lib/{passenger → phusion_passenger}/templates/deployment_example.txt.erb +1 -1
  331. data/lib/{passenger → phusion_passenger}/templates/error_layout.css +0 -0
  332. data/lib/{passenger → phusion_passenger}/templates/error_layout.html.erb +0 -0
  333. data/lib/{passenger → phusion_passenger}/templates/framework_init_error.html.erb +0 -0
  334. data/lib/{passenger → phusion_passenger}/templates/general_error.html.erb +0 -0
  335. data/lib/{passenger → phusion_passenger}/templates/invalid_app_root.html.erb +1 -1
  336. data/lib/{passenger → phusion_passenger}/templates/load_error.html.erb +0 -0
  337. data/lib/{passenger → phusion_passenger}/templates/no_write_permission_to_passenger_root.txt.erb +0 -0
  338. data/lib/{passenger → phusion_passenger}/templates/possible_solutions_for_compilation_and_installation_problems.txt.erb +0 -0
  339. data/lib/{passenger → phusion_passenger}/templates/run_installer_as_root.txt.erb +0 -0
  340. data/lib/{passenger → phusion_passenger}/templates/version_not_found.html.erb +0 -0
  341. data/lib/{passenger → phusion_passenger}/templates/welcome.txt.erb +0 -0
  342. data/lib/{passenger → phusion_passenger}/utils.rb +210 -44
  343. data/lib/{passenger → phusion_passenger}/wsgi/application_spawner.rb +18 -15
  344. data/lib/{passenger → phusion_passenger}/wsgi/request_handler.py +7 -1
  345. data/man/passenger-memory-stats.8 +1 -1
  346. data/misc/render_error_pages.rb +1 -1
  347. data/test/ApplicationPoolServerTest.cpp +0 -28
  348. data/test/ApplicationPoolServer_ApplicationPoolTest.cpp +4 -0
  349. data/test/ApplicationPoolTest.cpp +307 -69
  350. data/test/CachedFileStatTest.cpp +262 -0
  351. data/test/FileCheckerTest.cpp +79 -0
  352. data/test/MessageChannelTest.cpp +3 -3
  353. data/test/PoolOptionsTest.cpp +37 -0
  354. data/test/SpawnManagerTest.cpp +4 -4
  355. data/test/StandardApplicationPoolTest.cpp +4 -0
  356. data/test/SystemTimeTest.cpp +37 -0
  357. data/test/UtilsTest.cpp +137 -0
  358. data/test/integration_tests.rb +270 -23
  359. data/test/oxt/backtrace_test.cpp +128 -0
  360. data/test/oxt/oxt_test_main.cpp +25 -0
  361. data/test/oxt/syscall_interruption_test.cpp +50 -0
  362. data/test/ruby/abstract_request_handler_spec.rb +83 -0
  363. data/test/ruby/abstract_server_collection_spec.rb +246 -0
  364. data/test/ruby/application_spec.rb +3 -3
  365. data/test/ruby/message_channel_spec.rb +2 -2
  366. data/test/ruby/rack/application_spawner_spec.rb +3 -5
  367. data/test/ruby/rails/application_spawner_spec.rb +54 -15
  368. data/test/ruby/rails/framework_spawner_spec.rb +6 -8
  369. data/test/ruby/rails/minimal_spawner_spec.rb +29 -0
  370. data/test/ruby/rails/spawner_error_handling_spec.rb +1 -1
  371. data/test/ruby/rails/spawner_privilege_lowering_spec.rb +3 -3
  372. data/test/ruby/spawn_manager_spec.rb +23 -12
  373. data/test/ruby/utils_spec.rb +36 -2
  374. data/test/ruby/wsgi/application_spawner_spec.rb +47 -0
  375. data/test/stub/apache2/httpd.conf.erb +3 -5
  376. data/test/stub/message_channel.rb +2 -2
  377. data/test/stub/message_channel_2.rb +2 -2
  378. data/test/stub/message_channel_3.rb +3 -3
  379. data/test/stub/minimal-railsapp/README +0 -0
  380. data/test/stub/minimal-railsapp/config/application.rb +0 -0
  381. data/test/stub/minimal-railsapp/config/environment.rb +0 -0
  382. data/test/stub/minimal-railsapp/vendor/rails/actionmailer/lib/action_mailer.rb +0 -0
  383. data/test/stub/minimal-railsapp/vendor/rails/actionpack/lib/action_controller.rb +0 -0
  384. data/test/stub/minimal-railsapp/vendor/rails/actionpack/lib/action_pack.rb +0 -0
  385. data/test/stub/minimal-railsapp/vendor/rails/actionpack/lib/action_view.rb +0 -0
  386. data/test/stub/minimal-railsapp/vendor/rails/activerecord/lib/active_record.rb +0 -0
  387. data/test/stub/minimal-railsapp/vendor/rails/activeresource/lib/active_resource.rb +0 -0
  388. data/test/stub/minimal-railsapp/vendor/rails/activesupport/lib/active_support.rb +0 -0
  389. data/test/stub/minimal-railsapp/vendor/rails/activesupport/lib/active_support/whiny_nil.rb +0 -0
  390. data/test/stub/minimal-railsapp/vendor/rails/railties/lib/dispatcher.rb +0 -0
  391. data/test/stub/minimal-railsapp/vendor/rails/railties/lib/initializer.rb +0 -0
  392. data/test/stub/minimal-railsapp/vendor/rails/railties/lib/ruby_version_check.rb +0 -0
  393. data/test/stub/rails_apps/foobar/app/controllers/foo_controller.rb +8 -0
  394. data/test/stub/rails_apps/foobar/config/environments/development.rb +1 -2
  395. data/test/stub/rails_apps/mycook/app/controllers/welcome_controller.rb +21 -1
  396. data/test/stub/rails_apps/mycook/sites/some.site/public/uploads.html +26 -0
  397. data/test/stub/rails_apps/mycook/sites/some.site/public/welcome/cached.html +26 -0
  398. data/test/stub/railsapp/app/controllers/application.rb +0 -0
  399. data/test/stub/railsapp/app/controllers/bar_controller_1.rb +0 -0
  400. data/test/stub/railsapp/app/controllers/bar_controller_2.rb +1 -1
  401. data/test/stub/railsapp/app/controllers/foo_controller.rb +4 -0
  402. data/test/stub/railsapp/app/helpers/application_helper.rb +0 -0
  403. data/test/stub/railsapp/config/boot.rb +0 -0
  404. data/test/stub/railsapp/config/database.yml +0 -0
  405. data/test/stub/railsapp/config/environment.rb +0 -0
  406. data/test/stub/railsapp/config/environments/development.rb +0 -0
  407. data/test/stub/railsapp/config/environments/production.rb +0 -0
  408. data/test/stub/railsapp/config/initializers/inflections.rb +0 -0
  409. data/test/stub/railsapp/config/initializers/mime_types.rb +0 -0
  410. data/test/stub/railsapp/config/routes.rb +0 -0
  411. data/test/stub/railsapp/public/useless.txt +0 -0
  412. data/test/stub/spawn_server.rb +3 -4
  413. data/test/stub/wsgi/passenger_wsgi.pyc +0 -0
  414. data/test/support/apache2_controller.rb +57 -7
  415. data/test/support/tut.h +15 -0
  416. data/vendor/README +12 -0
  417. data/vendor/README_FOR_PACKAGERS +1 -0
  418. data/vendor/rack-0.9.1/AUTHORS +8 -0
  419. data/vendor/rack-0.9.1/COPYING +18 -0
  420. data/vendor/rack-0.9.1/ChangeLog +1423 -0
  421. data/vendor/rack-0.9.1/KNOWN-ISSUES +18 -0
  422. data/vendor/rack-0.9.1/README +306 -0
  423. data/vendor/rack-0.9.1/Rakefile +188 -0
  424. data/vendor/rack-0.9.1/SPEC +129 -0
  425. data/vendor/rack-0.9.1/lib/rack.rb +86 -0
  426. data/vendor/rack-0.9.1/lib/rack/adapter/camping.rb +22 -0
  427. data/vendor/rack-0.9.1/lib/rack/auth/abstract/handler.rb +28 -0
  428. data/vendor/rack-0.9.1/lib/rack/auth/abstract/request.rb +37 -0
  429. data/vendor/rack-0.9.1/lib/rack/auth/basic.rb +58 -0
  430. data/vendor/rack-0.9.1/lib/rack/auth/digest/md5.rb +124 -0
  431. data/vendor/rack-0.9.1/lib/rack/auth/digest/nonce.rb +51 -0
  432. data/vendor/rack-0.9.1/lib/rack/auth/digest/params.rb +55 -0
  433. data/vendor/rack-0.9.1/lib/rack/auth/digest/request.rb +40 -0
  434. data/vendor/rack-0.9.1/lib/rack/auth/openid.rb +438 -0
  435. data/vendor/rack-0.9.1/lib/rack/builder.rb +67 -0
  436. data/vendor/rack-0.9.1/lib/rack/cascade.rb +36 -0
  437. data/vendor/rack-0.9.1/lib/rack/commonlogger.rb +61 -0
  438. data/vendor/rack-0.9.1/lib/rack/conditionalget.rb +43 -0
  439. data/vendor/rack-0.9.1/lib/rack/content_length.rb +25 -0
  440. data/vendor/rack-0.9.1/lib/rack/deflater.rb +87 -0
  441. data/vendor/rack-0.9.1/lib/rack/directory.rb +150 -0
  442. data/vendor/rack-0.9.1/lib/rack/file.rb +85 -0
  443. data/vendor/rack-0.9.1/lib/rack/handler.rb +48 -0
  444. data/vendor/rack-0.9.1/lib/rack/handler/cgi.rb +57 -0
  445. data/vendor/rack-0.9.1/lib/rack/handler/evented_mongrel.rb +8 -0
  446. data/vendor/rack-0.9.1/lib/rack/handler/fastcgi.rb +86 -0
  447. data/vendor/rack-0.9.1/lib/rack/handler/lsws.rb +52 -0
  448. data/vendor/rack-0.9.1/lib/rack/handler/mongrel.rb +82 -0
  449. data/vendor/rack-0.9.1/lib/rack/handler/scgi.rb +57 -0
  450. data/vendor/rack-0.9.1/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  451. data/vendor/rack-0.9.1/lib/rack/handler/thin.rb +15 -0
  452. data/vendor/rack-0.9.1/lib/rack/handler/webrick.rb +61 -0
  453. data/vendor/rack-0.9.1/lib/rack/head.rb +19 -0
  454. data/vendor/rack-0.9.1/lib/rack/lint.rb +465 -0
  455. data/vendor/rack-0.9.1/lib/rack/lobster.rb +65 -0
  456. data/vendor/rack-0.9.1/lib/rack/methodoverride.rb +27 -0
  457. data/vendor/rack-0.9.1/lib/rack/mime.rb +204 -0
  458. data/vendor/rack-0.9.1/lib/rack/mock.rb +160 -0
  459. data/vendor/rack-0.9.1/lib/rack/recursive.rb +57 -0
  460. data/vendor/rack-0.9.1/lib/rack/reloader.rb +64 -0
  461. data/vendor/rack-0.9.1/lib/rack/request.rb +218 -0
  462. data/vendor/rack-0.9.1/lib/rack/response.rb +171 -0
  463. data/vendor/rack-0.9.1/lib/rack/session/abstract/id.rb +153 -0
  464. data/vendor/rack-0.9.1/lib/rack/session/cookie.rb +89 -0
  465. data/vendor/rack-0.9.1/lib/rack/session/memcache.rb +97 -0
  466. data/vendor/rack-0.9.1/lib/rack/session/pool.rb +73 -0
  467. data/vendor/rack-0.9.1/lib/rack/showexceptions.rb +348 -0
  468. data/vendor/rack-0.9.1/lib/rack/showstatus.rb +106 -0
  469. data/vendor/rack-0.9.1/lib/rack/static.rb +38 -0
  470. data/vendor/rack-0.9.1/lib/rack/urlmap.rb +48 -0
  471. data/vendor/rack-0.9.1/lib/rack/utils.rb +347 -0
  472. metadata +1197 -1055
  473. data/doc/cxxapi/System_8h-source.html +0 -251
  474. data/doc/cxxapi/classDirectoryMapper-members.html +0 -38
  475. data/doc/cxxapi/classDirectoryMapper.html +0 -203
  476. data/doc/cxxapi/classPassenger_1_1Thread.html +0 -100
  477. data/doc/cxxapi/classboost_1_1this__thread_1_1disable__syscall__interruption.html +0 -46
  478. data/doc/cxxapi/classboost_1_1this__thread_1_1restore__syscall__interruption-members.html +0 -33
  479. data/doc/cxxapi/classboost_1_1this__thread_1_1restore__syscall__interruption.html +0 -44
  480. data/doc/cxxapi/namespacePassenger.html +0 -208
  481. data/doc/cxxapi/namespacePassenger_1_1InterruptableCalls.html +0 -43
  482. data/doc/cxxapi/namespacemembers.html +0 -70
  483. data/doc/cxxapi/namespacemembers_func.html +0 -66
  484. data/doc/cxxapi/namespacemembers_type.html +0 -46
  485. data/doc/cxxapi/namespaces.html +0 -35
  486. data/doc/rdoc/classes/Passenger.html +0 -136
  487. data/doc/rdoc/classes/Passenger/AbstractRequestHandler.html +0 -402
  488. data/doc/rdoc/classes/Passenger/SpawnManager.html +0 -379
  489. data/doc/rdoc/classes/Passenger/Utils.html +0 -578
  490. data/ext/apache2/System.h +0 -228
  491. data/lib/passenger/platform_info.rb +0 -302
  492. data/lib/passenger/templates/app_exited_during_initialization.html.erb +0 -19
  493. data/test/stub/apache2/httpd.conf +0 -75
  494. data/test/stub/rails_apps/foobar/config/environments/test.rb +0 -22
  495. data/test/stub/rails_apps/mycook/config/environments/test.rb +0 -22
  496. data/test/stub/railsapp/config/environments/test.rb +0 -22
  497. data/test/stub/railsapp2/config/environments/test.rb +0 -22
@@ -24,6 +24,8 @@
24
24
  #include <list>
25
25
  #include <boost/shared_ptr.hpp>
26
26
  #include <boost/thread.hpp>
27
+ #include <oxt/system_calls.hpp>
28
+ #include <oxt/backtrace.hpp>
27
29
 
28
30
  #include <sys/types.h>
29
31
  #include <sys/wait.h>
@@ -33,19 +35,21 @@
33
35
  #include <cstdarg>
34
36
  #include <unistd.h>
35
37
  #include <errno.h>
38
+ #include <grp.h>
36
39
  #include <pwd.h>
37
40
  #include <signal.h>
38
41
 
39
42
  #include "Application.h"
43
+ #include "PoolOptions.h"
40
44
  #include "MessageChannel.h"
41
45
  #include "Exceptions.h"
42
46
  #include "Logging.h"
43
- #include "System.h"
44
47
 
45
48
  namespace Passenger {
46
49
 
47
50
  using namespace std;
48
51
  using namespace boost;
52
+ using namespace oxt;
49
53
 
50
54
  /**
51
55
  * @brief Spawning of Ruby on Rails/Rack application instances.
@@ -89,7 +93,7 @@ private:
89
93
  string rubyCommand;
90
94
  string user;
91
95
 
92
- mutex lock;
96
+ boost::mutex lock;
93
97
 
94
98
  MessageChannel channel;
95
99
  pid_t pid;
@@ -103,30 +107,34 @@ private:
103
107
  * @throws IOException The specified log file could not be opened.
104
108
  */
105
109
  void restartServer() {
110
+ TRACE_POINT();
106
111
  if (pid != 0) {
112
+ UPDATE_TRACE_POINT();
107
113
  channel.close();
108
114
 
109
115
  // Wait at most 5 seconds for the spawn server to exit.
110
116
  // If that doesn't work, kill it, then wait at most 5 seconds
111
117
  // for it to exit.
112
- time_t begin = InterruptableCalls::time(NULL);
118
+ time_t begin = syscalls::time(NULL);
113
119
  bool done = false;
114
- while (!done && InterruptableCalls::time(NULL) - begin < 5) {
115
- if (InterruptableCalls::waitpid(pid, NULL, WNOHANG) > 0) {
120
+ while (!done && syscalls::time(NULL) - begin < 5) {
121
+ if (syscalls::waitpid(pid, NULL, WNOHANG) > 0) {
116
122
  done = true;
117
123
  } else {
118
- InterruptableCalls::usleep(100000);
124
+ syscalls::usleep(100000);
119
125
  }
120
126
  }
127
+ UPDATE_TRACE_POINT();
121
128
  if (!done) {
129
+ UPDATE_TRACE_POINT();
122
130
  P_TRACE(2, "Spawn server did not exit in time, killing it...");
123
- InterruptableCalls::kill(pid, SIGTERM);
124
- begin = InterruptableCalls::time(NULL);
125
- while (InterruptableCalls::time(NULL) - begin < 5) {
126
- if (InterruptableCalls::waitpid(pid, NULL, WNOHANG) > 0) {
131
+ syscalls::kill(pid, SIGTERM);
132
+ begin = syscalls::time(NULL);
133
+ while (syscalls::time(NULL) - begin < 5) {
134
+ if (syscalls::waitpid(pid, NULL, WNOHANG) > 0) {
127
135
  break;
128
136
  } else {
129
- InterruptableCalls::usleep(100000);
137
+ syscalls::usleep(100000);
130
138
  }
131
139
  }
132
140
  P_TRACE(2, "Spawn server has exited.");
@@ -138,11 +146,11 @@ private:
138
146
  FILE *logFileHandle = NULL;
139
147
 
140
148
  serverNeedsRestart = true;
141
- if (InterruptableCalls::socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
149
+ if (syscalls::socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
142
150
  throw SystemException("Cannot create a Unix socket", errno);
143
151
  }
144
152
  if (!logFile.empty()) {
145
- logFileHandle = InterruptableCalls::fopen(logFile.c_str(), "a");
153
+ logFileHandle = syscalls::fopen(logFile.c_str(), "a");
146
154
  if (logFileHandle == NULL) {
147
155
  string message("Cannot open log file '");
148
156
  message.append(logFile);
@@ -151,7 +159,8 @@ private:
151
159
  }
152
160
  }
153
161
 
154
- pid = InterruptableCalls::fork();
162
+ UPDATE_TRACE_POINT();
163
+ pid = syscalls::fork();
155
164
  if (pid == 0) {
156
165
  if (!logFile.empty()) {
157
166
  dup2(fileno(logFileHandle), STDERR_FILENO);
@@ -168,6 +177,14 @@ private:
168
177
  if (!user.empty()) {
169
178
  struct passwd *entry = getpwnam(user.c_str());
170
179
  if (entry != NULL) {
180
+ if (initgroups(user.c_str(), entry->pw_gid) != 0) {
181
+ int e = errno;
182
+ fprintf(stderr, "*** Passenger: cannot set supplementary "
183
+ "groups for user %s: %s (%d)\n",
184
+ user.c_str(),
185
+ strerror(e),
186
+ e);
187
+ }
171
188
  if (setgid(entry->pw_gid) != 0) {
172
189
  int e = errno;
173
190
  fprintf(stderr, "*** Passenger: cannot run spawn "
@@ -202,33 +219,35 @@ private:
202
219
  // This argument is ignored by the spawn server. This works on some
203
220
  // systems, such as Ubuntu Linux.
204
221
  " ",
205
- NULL);
222
+ (char *) NULL);
206
223
  int e = errno;
207
- fprintf(stderr, "*** Passenger ERROR: Could not start the spawn server: %s: %s (%d)\n",
224
+ fprintf(stderr, "*** Passenger ERROR (%s:%d):\n"
225
+ "Could not start the spawn server: %s: %s (%d)\n",
226
+ __FILE__, __LINE__,
208
227
  rubyCommand.c_str(), strerror(e), e);
209
228
  fflush(stderr);
210
229
  _exit(1);
211
230
  } else if (pid == -1) {
212
231
  int e = errno;
213
- InterruptableCalls::close(fds[0]);
214
- InterruptableCalls::close(fds[1]);
232
+ syscalls::close(fds[0]);
233
+ syscalls::close(fds[1]);
215
234
  if (logFileHandle != NULL) {
216
- InterruptableCalls::fclose(logFileHandle);
235
+ syscalls::fclose(logFileHandle);
217
236
  }
218
237
  pid = 0;
219
238
  throw SystemException("Unable to fork a process", e);
220
239
  } else {
221
- InterruptableCalls::close(fds[1]);
240
+ syscalls::close(fds[1]);
222
241
  if (!logFile.empty()) {
223
- InterruptableCalls::fclose(logFileHandle);
242
+ syscalls::fclose(logFileHandle);
224
243
  }
225
244
  channel = MessageChannel(fds[0]);
226
245
  serverNeedsRestart = false;
227
246
 
228
247
  #ifdef TESTING_SPAWN_MANAGER
229
248
  if (nextRestartShouldFail) {
230
- InterruptableCalls::kill(pid, SIGTERM);
231
- InterruptableCalls::usleep(500000);
249
+ syscalls::kill(pid, SIGTERM);
250
+ syscalls::usleep(500000);
232
251
  }
233
252
  #endif
234
253
  }
@@ -237,41 +256,26 @@ private:
237
256
  /**
238
257
  * Send the spawn command to the spawn server.
239
258
  *
240
- * @param appRoot The application root of the application to spawn.
241
- * @param lowerPrivilege Whether to lower the application's privileges.
242
- * @param lowestUser The user to fallback to if lowering privilege fails.
243
- * @param environment The RAILS_ENV/RACK_ENV environment that should be used.
244
- * @param spawnMethod The spawn method to use.
245
- * @param appType The application type.
259
+ * @param PoolOptions The spawn options to use.
246
260
  * @return An Application smart pointer, representing the spawned application.
247
261
  * @throws SpawnException Something went wrong.
248
262
  */
249
- ApplicationPtr sendSpawnCommand(
250
- const string &appRoot,
251
- bool lowerPrivilege,
252
- const string &lowestUser,
253
- const string &environment,
254
- const string &spawnMethod,
255
- const string &appType
256
- ) {
263
+ ApplicationPtr sendSpawnCommand(const PoolOptions &PoolOptions) {
264
+ TRACE_POINT();
257
265
  vector<string> args;
258
266
  int ownerPipe;
259
267
 
260
268
  try {
261
- channel.write("spawn_application",
262
- appRoot.c_str(),
263
- (lowerPrivilege) ? "true" : "false",
264
- lowestUser.c_str(),
265
- environment.c_str(),
266
- spawnMethod.c_str(),
267
- appType.c_str(),
268
- NULL);
269
+ args.push_back("spawn_application");
270
+ PoolOptions.toVector(args);
271
+ channel.write(args);
269
272
  } catch (const SystemException &e) {
270
273
  throw SpawnException(string("Could not write 'spawn_application' "
271
274
  "command to the spawn server: ") + e.sys());
272
275
  }
273
276
 
274
277
  try {
278
+ UPDATE_TRACE_POINT();
275
279
  // Read status.
276
280
  if (!channel.read(args)) {
277
281
  throw SpawnException("The spawn server has exited unexpectedly.");
@@ -299,6 +303,7 @@ private:
299
303
  throw SpawnException(string("Could not read from the spawn server: ") + e.sys());
300
304
  }
301
305
 
306
+ UPDATE_TRACE_POINT();
302
307
  try {
303
308
  ownerPipe = channel.readFileDescriptor();
304
309
  } catch (const SystemException &e) {
@@ -312,14 +317,15 @@ private:
312
317
  }
313
318
 
314
319
  if (args.size() != 3) {
315
- InterruptableCalls::close(ownerPipe);
320
+ UPDATE_TRACE_POINT();
321
+ syscalls::close(ownerPipe);
316
322
  throw SpawnException("The spawn server sent an invalid message.");
317
323
  }
318
324
 
319
325
  pid_t pid = atoi(args[0]);
320
- bool usingAbstractNamespace = args[2] == "true";
321
326
 
322
- if (!usingAbstractNamespace) {
327
+ UPDATE_TRACE_POINT();
328
+ if (args[2] == "unix") {
323
329
  int ret;
324
330
  do {
325
331
  ret = chmod(args[1].c_str(), S_IRUSR | S_IWUSR);
@@ -328,18 +334,16 @@ private:
328
334
  ret = chown(args[1].c_str(), getuid(), getgid());
329
335
  } while (ret == -1 && errno == EINTR);
330
336
  }
331
- return ApplicationPtr(new Application(appRoot, pid, args[1],
332
- usingAbstractNamespace, ownerPipe));
337
+ return ApplicationPtr(new Application(PoolOptions.appRoot,
338
+ pid, args[1], args[2], ownerPipe));
333
339
  }
334
340
 
335
341
  /**
336
342
  * @throws boost::thread_interrupted
337
343
  */
338
344
  ApplicationPtr
339
- handleSpawnException(const SpawnException &e, const string &appRoot,
340
- bool lowerPrivilege, const string &lowestUser,
341
- const string &environment, const string &spawnMethod,
342
- const string &appType) {
345
+ handleSpawnException(const SpawnException &e, const PoolOptions &PoolOptions) {
346
+ TRACE_POINT();
343
347
  bool restarted;
344
348
  try {
345
349
  P_DEBUG("Spawn server died. Attempting to restart it...");
@@ -355,8 +359,7 @@ private:
355
359
  restarted = false;
356
360
  }
357
361
  if (restarted) {
358
- return sendSpawnCommand(appRoot, lowerPrivilege, lowestUser,
359
- environment, spawnMethod, appType);
362
+ return sendSpawnCommand(PoolOptions);
360
363
  } else {
361
364
  throw SpawnException("The spawn server died unexpectedly, and restarting it failed.");
362
365
  }
@@ -369,6 +372,7 @@ private:
369
372
  * @throws SystemException Something went wrong.
370
373
  */
371
374
  void sendReloadCommand(const string &appRoot) {
375
+ TRACE_POINT();
372
376
  try {
373
377
  channel.write("reload", appRoot.c_str(), NULL);
374
378
  } catch (const SystemException &e) {
@@ -378,6 +382,7 @@ private:
378
382
  }
379
383
 
380
384
  void handleReloadException(const SystemException &e, const string &appRoot) {
385
+ TRACE_POINT();
381
386
  bool restarted;
382
387
  try {
383
388
  P_DEBUG("Spawn server died. Attempting to restart it...");
@@ -434,6 +439,7 @@ public:
434
439
  const string &logFile = "",
435
440
  const string &rubyCommand = "ruby",
436
441
  const string &user = "") {
442
+ TRACE_POINT();
437
443
  this->spawnServerCommand = spawnServerCommand;
438
444
  this->logFile = logFile;
439
445
  this->rubyCommand = rubyCommand;
@@ -454,76 +460,45 @@ public:
454
460
  }
455
461
 
456
462
  ~SpawnManager() throw() {
463
+ TRACE_POINT();
457
464
  if (pid != 0) {
465
+ UPDATE_TRACE_POINT();
458
466
  this_thread::disable_interruption di;
459
467
  this_thread::disable_syscall_interruption dsi;
460
468
  P_TRACE(2, "Shutting down spawn manager (PID " << pid << ").");
461
469
  channel.close();
462
- InterruptableCalls::waitpid(pid, NULL, 0);
470
+ syscalls::waitpid(pid, NULL, 0);
463
471
  P_TRACE(2, "Spawn manager exited.");
464
472
  }
465
473
  }
466
474
 
467
475
  /**
468
- * Spawn a new instance of a Ruby on Rails or Rack application.
476
+ * Spawn a new instance of an application. Spawning details are to be passed
477
+ * via the <tt>PoolOptions</tt> parameter.
469
478
  *
470
479
  * If the spawn server died during the spawning process, then the server
471
480
  * will be automatically restarted, and another spawn attempt will be made.
472
481
  * If restarting the server fails, or if the second spawn attempt fails,
473
482
  * then an exception will be thrown.
474
483
  *
475
- * If <tt>lowerPrivilege</tt> is true, then it will be attempt to
476
- * switch the spawned application instance to the user who owns the
477
- * application's <tt>config/environment.rb</tt>, and to the default
478
- * group of that user.
479
- *
480
- * If that user doesn't exist on the system, or if that user is root,
481
- * then it will be attempted to switch to the username given by
482
- * <tt>lowestUser</tt> (and to the default group of that user).
483
- * If <tt>lowestUser</tt> doesn't exist either, or if switching user failed
484
- * (because the spawn server process does not have the privilege to do so),
485
- * then the application will be spawned anyway, without reporting an error.
486
- *
487
- * It goes without saying that lowering privilege is only possible if
488
- * the spawn server is running as root (and thus, by induction, that
489
- * Passenger and Apache's control process are also running as root).
490
- * Note that if Apache is listening on port 80, then its control process must
491
- * be running as root. See "doc/Security of user switching.txt" for
492
- * a detailed explanation.
493
- *
494
- * @param appRoot The application root of a RoR application, i.e. the folder that
495
- * contains 'app/', 'public/', 'config/', etc. This must be a valid directory,
496
- * but the path does not have to be absolute.
497
- * @param lowerPrivilege Whether to lower the application's privileges.
498
- * @param lowestUser The user to fallback to if lowering privilege fails.
499
- * @param environment The RAILS_ENV/RACK_ENV environment that should be used. May not be empty.
500
- * @param spawnMethod The spawn method to use. Either "smart" or "conservative".
501
- * See the Ruby class SpawnManager for details.
502
- * @param appType The application type. Either "rails" or "rack".
484
+ * @param PoolOptions An object containing the details for this spawn operation,
485
+ * such as which application to spawn. See PoolOptions for details.
503
486
  * @return A smart pointer to an Application object, which represents the application
504
487
  * instance that has been spawned. Use this object to communicate with the
505
488
  * spawned application.
506
489
  * @throws SpawnException Something went wrong.
507
490
  * @throws boost::thread_interrupted
508
491
  */
509
- ApplicationPtr spawn(
510
- const string &appRoot,
511
- bool lowerPrivilege = true,
512
- const string &lowestUser = "nobody",
513
- const string &environment = "production",
514
- const string &spawnMethod = "smart",
515
- const string &appType = "rails"
516
- ) {
517
- mutex::scoped_lock l(lock);
492
+ ApplicationPtr spawn(const PoolOptions &PoolOptions) {
493
+ TRACE_POINT();
494
+ boost::mutex::scoped_lock l(lock);
518
495
  try {
519
- return sendSpawnCommand(appRoot, lowerPrivilege, lowestUser,
520
- environment, spawnMethod, appType);
496
+ return sendSpawnCommand(PoolOptions);
521
497
  } catch (const SpawnException &e) {
522
498
  if (e.hasErrorPage()) {
523
499
  throw;
524
500
  } else {
525
- return handleSpawnException(e, appRoot, lowerPrivilege,
526
- lowestUser, environment, spawnMethod, appType);
501
+ return handleSpawnException(e, PoolOptions);
527
502
  }
528
503
  }
529
504
  }
@@ -544,6 +519,7 @@ public:
544
519
  * restart was attempted, but it failed.
545
520
  */
546
521
  void reload(const string &appRoot) {
522
+ TRACE_POINT();
547
523
  this_thread::disable_interruption di;
548
524
  this_thread::disable_syscall_interruption dsi;
549
525
  try {
@@ -27,6 +27,9 @@
27
27
  #include <boost/date_time/microsec_time_clock.hpp>
28
28
  #include <boost/date_time/posix_time/posix_time.hpp>
29
29
 
30
+ #include <oxt/system_calls.hpp>
31
+ #include <oxt/backtrace.hpp>
32
+
30
33
  #include <string>
31
34
  #include <sstream>
32
35
  #include <map>
@@ -44,7 +47,8 @@
44
47
 
45
48
  #include "ApplicationPool.h"
46
49
  #include "Logging.h"
47
- #include "System.h"
50
+ #include "FileChecker.h"
51
+ #include "CachedFileStat.h"
48
52
  #ifdef PASSENGER_USE_DUMMY_SPAWN_MANAGER
49
53
  #include "DummySpawnManager.h"
50
54
  #else
@@ -55,6 +59,7 @@ namespace Passenger {
55
59
 
56
60
  using namespace std;
57
61
  using namespace boost;
62
+ using namespace oxt;
58
63
 
59
64
  class ApplicationPoolServer;
60
65
 
@@ -99,37 +104,100 @@ private:
99
104
  static const unsigned int GET_TIMEOUT = 5000; // In milliseconds.
100
105
 
101
106
  friend class ApplicationPoolServer;
107
+ struct Domain;
102
108
  struct AppContainer;
103
109
 
110
+ typedef shared_ptr<Domain> DomainPtr;
104
111
  typedef shared_ptr<AppContainer> AppContainerPtr;
105
112
  typedef list<AppContainerPtr> AppContainerList;
106
- typedef shared_ptr<AppContainerList> AppContainerListPtr;
107
- typedef map<string, AppContainerListPtr> ApplicationMap;
113
+ typedef map<string, DomainPtr> DomainMap;
114
+
115
+ struct Domain {
116
+ AppContainerList instances;
117
+ unsigned int size;
118
+ unsigned long maxRequests;
119
+ FileChecker restartFileChecker;
120
+ CachedFileStat alwaysRestartFileStatter;
121
+
122
+ Domain(const PoolOptions &options)
123
+ : restartFileChecker(determineRestartDir(options) + "/restart.txt"),
124
+ alwaysRestartFileStatter(determineRestartDir(options) + "/always_restart.txt")
125
+ {
126
+ }
127
+
128
+ private:
129
+ static string determineRestartDir(const PoolOptions &options) {
130
+ if (options.restartDir.empty()) {
131
+ return options.appRoot + "/tmp";
132
+ } else if (options.restartDir[0] == '/') {
133
+ return options.restartDir;
134
+ } else {
135
+ return options.appRoot + "/" + options.restartDir;
136
+ }
137
+ }
138
+ };
108
139
 
109
140
  struct AppContainer {
110
141
  ApplicationPtr app;
142
+ time_t startTime;
111
143
  time_t lastUsed;
112
144
  unsigned int sessions;
145
+ unsigned int processed;
113
146
  AppContainerList::iterator iterator;
114
147
  AppContainerList::iterator ia_iterator;
148
+
149
+ AppContainer() {
150
+ startTime = time(NULL);
151
+ processed = 0;
152
+ }
153
+
154
+ /**
155
+ * Returns the uptime of this AppContainer so far, as a string.
156
+ */
157
+ string uptime() const {
158
+ time_t seconds = time(NULL) - startTime;
159
+ stringstream result;
160
+
161
+ if (seconds >= 60) {
162
+ time_t minutes = seconds / 60;
163
+ if (minutes >= 60) {
164
+ time_t hours = minutes / 60;
165
+ minutes = minutes % 60;
166
+ result << hours << "h ";
167
+ }
168
+
169
+ seconds = seconds % 60;
170
+ result << minutes << "m ";
171
+ }
172
+ result << seconds << "s";
173
+ return result.str();
174
+ }
115
175
  };
116
176
 
177
+ /**
178
+ * A data structure which contains data that's shared between a
179
+ * StandardApplicationPool and a SessionCloseCallback object.
180
+ * This is because the StandardApplicationPool's life time could be
181
+ * different from a SessionCloseCallback's.
182
+ */
117
183
  struct SharedData {
118
- mutex lock;
184
+ boost::mutex lock;
119
185
  condition activeOrMaxChanged;
120
186
 
121
- ApplicationMap apps;
187
+ DomainMap domains;
122
188
  unsigned int max;
123
189
  unsigned int count;
124
190
  unsigned int active;
125
191
  unsigned int maxPerApp;
126
192
  AppContainerList inactiveApps;
127
- map<string, time_t> restartFileTimes;
128
193
  map<string, unsigned int> appInstanceCount;
129
194
  };
130
195
 
131
196
  typedef shared_ptr<SharedData> SharedDataPtr;
132
197
 
198
+ /**
199
+ * Function object which will be called when a session has been closed.
200
+ */
133
201
  struct SessionCloseCallback {
134
202
  SharedDataPtr data;
135
203
  weak_ptr<AppContainer> container;
@@ -141,28 +209,42 @@ private:
141
209
  }
142
210
 
143
211
  void operator()() {
144
- mutex::scoped_lock l(data->lock);
212
+ boost::mutex::scoped_lock l(data->lock);
145
213
  AppContainerPtr container(this->container.lock());
146
214
 
147
215
  if (container == NULL) {
148
216
  return;
149
217
  }
150
218
 
151
- ApplicationMap::iterator it;
152
- it = data->apps.find(container->app->getAppRoot());
153
- if (it != data->apps.end()) {
154
- AppContainerListPtr list(it->second);
155
- container->lastUsed = time(NULL);
156
- container->sessions--;
157
- if (container->sessions == 0) {
158
- list->erase(container->iterator);
159
- list->push_front(container);
160
- container->iterator = list->begin();
161
- data->inactiveApps.push_back(container);
162
- container->ia_iterator = data->inactiveApps.end();
163
- container->ia_iterator--;
219
+ DomainMap::iterator it;
220
+ it = data->domains.find(container->app->getAppRoot());
221
+ if (it != data->domains.end()) {
222
+ Domain *domain = it->second.get();
223
+ AppContainerList *instances = &domain->instances;
224
+
225
+ container->processed++;
226
+ if (domain->maxRequests > 0 && container->processed >= domain->maxRequests) {
227
+ instances->erase(container->iterator);
228
+ domain->size--;
229
+ if (instances->empty()) {
230
+ data->domains.erase(container->app->getAppRoot());
231
+ }
232
+ data->count--;
164
233
  data->active--;
165
234
  data->activeOrMaxChanged.notify_all();
235
+ } else {
236
+ container->lastUsed = time(NULL);
237
+ container->sessions--;
238
+ if (container->sessions == 0) {
239
+ instances->erase(container->iterator);
240
+ instances->push_front(container);
241
+ container->iterator = instances->begin();
242
+ data->inactiveApps.push_back(container);
243
+ container->ia_iterator = data->inactiveApps.end();
244
+ container->ia_iterator--;
245
+ data->active--;
246
+ data->activeOrMaxChanged.notify_all();
247
+ }
166
248
  }
167
249
  }
168
250
  }
@@ -174,24 +256,22 @@ private:
174
256
  SpawnManager spawnManager;
175
257
  #endif
176
258
  SharedDataPtr data;
177
- thread *cleanerThread;
259
+ boost::thread *cleanerThread;
178
260
  bool detached;
179
261
  bool done;
180
- bool useGlobalQueue;
181
262
  unsigned int maxIdleTime;
182
263
  unsigned int waitingOnGlobalQueue;
183
264
  condition cleanerThreadSleeper;
184
265
 
185
266
  // Shortcuts for instance variables in SharedData. Saves typing in get().
186
- mutex &lock;
267
+ boost::mutex &lock;
187
268
  condition &activeOrMaxChanged;
188
- ApplicationMap &apps;
269
+ DomainMap &domains;
189
270
  unsigned int &max;
190
271
  unsigned int &count;
191
272
  unsigned int &active;
192
273
  unsigned int &maxPerApp;
193
274
  AppContainerList &inactiveApps;
194
- map<string, time_t> &restartFileTimes;
195
275
  map<string, unsigned int> &appInstanceCount;
196
276
 
197
277
  /**
@@ -199,25 +279,38 @@ private:
199
279
  */
200
280
  bool inline verifyState() {
201
281
  #if PASSENGER_DEBUG
202
- // Invariant for _apps_.
203
- ApplicationMap::const_iterator it;
204
- for (it = apps.begin(); it != apps.end(); it++) {
205
- AppContainerList *list = it->second.get();
206
- P_ASSERT(!list->empty(), false, "List for '" << it->first << "' is nonempty.");
282
+ // Invariants for _domains_.
283
+ DomainMap::const_iterator it;
284
+ unsigned int totalSize = 0;
285
+ for (it = domains.begin(); it != domains.end(); it++) {
286
+ const string &appRoot = it->first;
287
+ Domain *domain = it->second.get();
288
+ AppContainerList *instances = &domain->instances;
289
+
290
+ P_ASSERT(domain->size <= count, false,
291
+ "domains['" << appRoot << "'].size (" << domain->size <<
292
+ ") <= count (" << count << ")");
293
+ totalSize += domain->size;
294
+
295
+ // Invariants for Domain.
296
+
297
+ P_ASSERT(!instances->empty(), false,
298
+ "domains['" << appRoot << "'].instances is nonempty.");
207
299
 
208
300
  AppContainerList::const_iterator prev_lit;
209
301
  AppContainerList::const_iterator lit;
210
- prev_lit = list->begin();
302
+ prev_lit = instances->begin();
211
303
  lit = prev_lit;
212
304
  lit++;
213
- for (; lit != list->end(); lit++) {
305
+ for (; lit != instances->end(); lit++) {
214
306
  if ((*prev_lit)->sessions > 0) {
215
307
  P_ASSERT((*lit)->sessions > 0, false,
216
- "List for '" << it->first <<
217
- "' is sorted from nonactive to active");
308
+ "domains['" << appRoot << "'].instances "
309
+ "is sorted from nonactive to active");
218
310
  }
219
311
  }
220
312
  }
313
+ P_ASSERT(totalSize == count, false, "(sum of all d.size in domains) == count");
221
314
 
222
315
  P_ASSERT(active <= count, false,
223
316
  "active (" << active << ") < count (" << count << ")");
@@ -227,9 +320,7 @@ private:
227
320
  return true;
228
321
  }
229
322
 
230
- template<typename LockActionType>
231
- string toString(LockActionType lockAction) const {
232
- unique_lock<mutex> l(lock, lockAction);
323
+ string toStringWithoutLock() const {
233
324
  stringstream result;
234
325
 
235
326
  result << "----------- General information -----------" << endl;
@@ -237,23 +328,27 @@ private:
237
328
  result << "count = " << count << endl;
238
329
  result << "active = " << active << endl;
239
330
  result << "inactive = " << inactiveApps.size() << endl;
240
- result << "Using global queue: " << (useGlobalQueue ? "yes" : "no") << endl;
241
331
  result << "Waiting on global queue: " << waitingOnGlobalQueue << endl;
242
332
  result << endl;
243
333
 
244
- result << "----------- Applications -----------" << endl;
245
- ApplicationMap::const_iterator it;
246
- for (it = apps.begin(); it != apps.end(); it++) {
247
- AppContainerList *list = it->second.get();
334
+ result << "----------- Domains -----------" << endl;
335
+ DomainMap::const_iterator it;
336
+ for (it = domains.begin(); it != domains.end(); it++) {
337
+ Domain *domain = it->second.get();
338
+ AppContainerList *instances = &domain->instances;
248
339
  AppContainerList::const_iterator lit;
249
340
 
250
341
  result << it->first << ": " << endl;
251
- for (lit = list->begin(); lit != list->end(); lit++) {
342
+ for (lit = instances->begin(); lit != instances->end(); lit++) {
252
343
  AppContainer *container = lit->get();
253
344
  char buf[128];
254
345
 
255
- snprintf(buf, sizeof(buf), "PID: %-8d Sessions: %d",
256
- container->app->getPid(), container->sessions);
346
+ snprintf(buf, sizeof(buf),
347
+ "PID: %-5lu Sessions: %-2u Processed: %-5u Uptime: %s",
348
+ (unsigned long) container->app->getPid(),
349
+ container->sessions,
350
+ container->processed,
351
+ container->uptime().c_str());
257
352
  result << " " << buf << endl;
258
353
  }
259
354
  result << endl;
@@ -261,45 +356,14 @@ private:
261
356
  return result.str();
262
357
  }
263
358
 
264
- bool needsRestart(const string &appRoot) {
265
- string restartFile(appRoot);
266
- restartFile.append("/tmp/restart.txt");
267
-
268
- struct stat buf;
269
- bool result;
270
- int ret;
271
-
272
- do {
273
- ret = stat(restartFile.c_str(), &buf);
274
- } while (ret == -1 && errno == EINTR);
275
- if (ret == 0) {
276
- do {
277
- ret = unlink(restartFile.c_str());
278
- } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
279
- if (ret == 0 || errno == ENOENT) {
280
- restartFileTimes.erase(appRoot);
281
- result = true;
282
- } else {
283
- map<string, time_t>::const_iterator it;
284
-
285
- it = restartFileTimes.find(appRoot);
286
- if (it == restartFileTimes.end()) {
287
- result = true;
288
- } else {
289
- result = buf.st_mtime != restartFileTimes[appRoot];
290
- }
291
- restartFileTimes[appRoot] = buf.st_mtime;
292
- }
293
- } else {
294
- restartFileTimes.erase(appRoot);
295
- result = false;
296
- }
297
- return result;
359
+ bool needsRestart(const string &appRoot, Domain *domain, const PoolOptions &options) {
360
+ return domain->alwaysRestartFileStatter.refresh(options.statThrottleRate) == 0
361
+ || domain->restartFileChecker.changed(options.statThrottleRate);
298
362
  }
299
363
 
300
364
  void cleanerThreadMainLoop() {
301
365
  this_thread::disable_syscall_interruption dsi;
302
- unique_lock<mutex> l(lock);
366
+ unique_lock<boost::mutex> l(lock);
303
367
  try {
304
368
  while (!done && !this_thread::interruption_requested()) {
305
369
  xtime xt;
@@ -316,31 +380,31 @@ private:
316
380
  }
317
381
  }
318
382
 
319
- time_t now = InterruptableCalls::time(NULL);
383
+ time_t now = syscalls::time(NULL);
320
384
  AppContainerList::iterator it;
321
385
  for (it = inactiveApps.begin(); it != inactiveApps.end(); it++) {
322
386
  AppContainer &container(*it->get());
323
387
  ApplicationPtr app(container.app);
324
- AppContainerListPtr appList(apps[app->getAppRoot()]);
388
+ Domain *domain = domains[app->getAppRoot()].get();
389
+ AppContainerList *instances = &domain->instances;
325
390
 
326
- if (now - container.lastUsed > (time_t) maxIdleTime) {
391
+ if (maxIdleTime > 0 &&
392
+ (now - container.lastUsed > (time_t) maxIdleTime)) {
327
393
  P_DEBUG("Cleaning idle app " << app->getAppRoot() <<
328
394
  " (PID " << app->getPid() << ")");
329
- appList->erase(container.iterator);
395
+ instances->erase(container.iterator);
330
396
 
331
397
  AppContainerList::iterator prev = it;
332
398
  prev--;
333
399
  inactiveApps.erase(it);
334
400
  it = prev;
335
401
 
336
- appInstanceCount[app->getAppRoot()]--;
402
+ domain->size--;
337
403
 
338
404
  count--;
339
405
  }
340
- if (appList->empty()) {
341
- apps.erase(app->getAppRoot());
342
- appInstanceCount.erase(app->getAppRoot());
343
- data->restartFileTimes.erase(app->getAppRoot());
406
+ if (instances->empty()) {
407
+ domains.erase(app->getAppRoot());
344
408
  }
345
409
  }
346
410
  }
@@ -350,34 +414,31 @@ private:
350
414
  }
351
415
 
352
416
  /**
417
+ * Spawn a new application instance, or use an existing one that's in the pool.
418
+ *
353
419
  * @throws boost::thread_interrupted
354
420
  * @throws SpawnException
355
421
  * @throws SystemException
356
422
  */
357
- pair<AppContainerPtr, AppContainerList *>
358
- spawnOrUseExisting(
359
- mutex::scoped_lock &l,
360
- const string &appRoot,
361
- bool lowerPrivilege,
362
- const string &lowestUser,
363
- const string &environment,
364
- const string &spawnMethod,
365
- const string &appType
366
- ) {
423
+ pair<AppContainerPtr, Domain *>
424
+ spawnOrUseExisting(boost::mutex::scoped_lock &l, const PoolOptions &options) {
367
425
  beginning_of_function:
368
426
 
427
+ TRACE_POINT();
369
428
  this_thread::disable_interruption di;
370
429
  this_thread::disable_syscall_interruption dsi;
430
+ const string &appRoot(options.appRoot);
371
431
  AppContainerPtr container;
372
- AppContainerList *list;
432
+ Domain *domain;
433
+ AppContainerList *instances;
373
434
 
374
435
  try {
375
- ApplicationMap::iterator it(apps.find(appRoot));
436
+ DomainMap::iterator it(domains.find(appRoot));
376
437
 
377
- if (it != apps.end() && needsRestart(appRoot)) {
438
+ if (it != domains.end() && needsRestart(appRoot, it->second.get(), options)) {
378
439
  AppContainerList::iterator it2;
379
- list = it->second.get();
380
- for (it2 = list->begin(); it2 != list->end(); it2++) {
440
+ instances = &it->second->instances;
441
+ for (it2 = instances->begin(); it2 != instances->end(); it2++) {
381
442
  container = *it2;
382
443
  if (container->sessions == 0) {
383
444
  inactiveApps.erase(container->ia_iterator);
@@ -385,49 +446,50 @@ private:
385
446
  active--;
386
447
  }
387
448
  it2--;
388
- list->erase(container->iterator);
449
+ instances->erase(container->iterator);
389
450
  count--;
390
451
  }
391
- apps.erase(appRoot);
392
- appInstanceCount.erase(appRoot);
452
+ domains.erase(appRoot);
393
453
  spawnManager.reload(appRoot);
394
- it = apps.end();
454
+ it = domains.end();
395
455
  activeOrMaxChanged.notify_all();
396
456
  }
397
457
 
398
- if (it != apps.end()) {
399
- list = it->second.get();
458
+ if (it != domains.end()) {
459
+ domain = it->second.get();
460
+ instances = &domain->instances;
400
461
 
401
- if (list->front()->sessions == 0) {
402
- container = list->front();
403
- list->pop_front();
404
- list->push_back(container);
405
- container->iterator = list->end();
462
+ if (instances->front()->sessions == 0) {
463
+ container = instances->front();
464
+ instances->pop_front();
465
+ instances->push_back(container);
466
+ container->iterator = instances->end();
406
467
  container->iterator--;
407
468
  inactiveApps.erase(container->ia_iterator);
408
469
  active++;
409
470
  activeOrMaxChanged.notify_all();
410
471
  } else if (count >= max || (
411
- maxPerApp != 0 && appInstanceCount[appRoot] >= maxPerApp )
472
+ maxPerApp != 0 && domain->size >= maxPerApp )
412
473
  ) {
413
- if (useGlobalQueue) {
474
+ if (options.useGlobalQueue) {
475
+ UPDATE_TRACE_POINT();
414
476
  waitingOnGlobalQueue++;
415
477
  activeOrMaxChanged.wait(l);
416
478
  waitingOnGlobalQueue--;
417
479
  goto beginning_of_function;
418
480
  } else {
419
- AppContainerList::iterator it(list->begin());
420
- AppContainerList::iterator smallest(list->begin());
481
+ AppContainerList::iterator it(instances->begin());
482
+ AppContainerList::iterator smallest(instances->begin());
421
483
  it++;
422
- for (; it != list->end(); it++) {
484
+ for (; it != instances->end(); it++) {
423
485
  if ((*it)->sessions < (*smallest)->sessions) {
424
486
  smallest = it;
425
487
  }
426
488
  }
427
489
  container = *smallest;
428
- list->erase(smallest);
429
- list->push_back(container);
430
- container->iterator = list->end();
490
+ instances->erase(smallest);
491
+ instances->push_back(container);
492
+ container->iterator = instances->end();
431
493
  container->iterator--;
432
494
  }
433
495
  } else {
@@ -435,57 +497,57 @@ private:
435
497
  {
436
498
  this_thread::restore_interruption ri(di);
437
499
  this_thread::restore_syscall_interruption rsi(dsi);
438
- container->app = spawnManager.spawn(appRoot,
439
- lowerPrivilege, lowestUser, environment,
440
- spawnMethod, appType);
500
+ container->app = spawnManager.spawn(options);
441
501
  }
442
502
  container->sessions = 0;
443
- list->push_back(container);
444
- container->iterator = list->end();
503
+ instances->push_back(container);
504
+ container->iterator = instances->end();
445
505
  container->iterator--;
446
- appInstanceCount[appRoot]++;
506
+ domain->size++;
447
507
  count++;
448
508
  active++;
449
509
  activeOrMaxChanged.notify_all();
450
510
  }
451
511
  } else {
452
512
  if (active >= max) {
513
+ UPDATE_TRACE_POINT();
453
514
  activeOrMaxChanged.wait(l);
454
515
  goto beginning_of_function;
455
- }
456
- if (count == max) {
516
+ } else if (count == max) {
457
517
  container = inactiveApps.front();
458
518
  inactiveApps.pop_front();
459
- list = apps[container->app->getAppRoot()].get();
460
- list->erase(container->iterator);
461
- if (list->empty()) {
462
- apps.erase(container->app->getAppRoot());
463
- restartFileTimes.erase(container->app->getAppRoot());
464
- appInstanceCount.erase(container->app->getAppRoot());
519
+ domain = domains[container->app->getAppRoot()].get();
520
+ instances = &domain->instances;
521
+ instances->erase(container->iterator);
522
+ if (instances->empty()) {
523
+ domains.erase(container->app->getAppRoot());
465
524
  } else {
466
- appInstanceCount[container->app->getAppRoot()]--;
525
+ domain->size--;
467
526
  }
468
527
  count--;
469
528
  }
529
+
530
+ UPDATE_TRACE_POINT();
470
531
  container = ptr(new AppContainer());
471
532
  {
472
533
  this_thread::restore_interruption ri(di);
473
534
  this_thread::restore_syscall_interruption rsi(dsi);
474
- container->app = spawnManager.spawn(appRoot, lowerPrivilege, lowestUser,
475
- environment, spawnMethod, appType);
535
+ container->app = spawnManager.spawn(options);
476
536
  }
477
537
  container->sessions = 0;
478
- it = apps.find(appRoot);
479
- if (it == apps.end()) {
480
- list = new AppContainerList();
481
- apps[appRoot] = ptr(list);
482
- appInstanceCount[appRoot] = 1;
538
+ it = domains.find(appRoot);
539
+ if (it == domains.end()) {
540
+ domain = new Domain(options);
541
+ domain->size = 1;
542
+ domain->maxRequests = options.maxRequests;
543
+ domains[appRoot] = ptr(domain);
483
544
  } else {
484
- list = it->second.get();
485
- appInstanceCount[appRoot]++;
545
+ domain = it->second.get();
546
+ domain->size++;
486
547
  }
487
- list->push_back(container);
488
- container->iterator = list->end();
548
+ instances = &domain->instances;
549
+ instances->push_back(container);
550
+ container->iterator = instances->end();
489
551
  container->iterator--;
490
552
  count++;
491
553
  active++;
@@ -509,7 +571,7 @@ private:
509
571
  throw SpawnException(message);
510
572
  }
511
573
 
512
- return make_pair(container, list);
574
+ return make_pair(container, domain);
513
575
  }
514
576
 
515
577
  public:
@@ -529,7 +591,6 @@ public:
529
591
  * running as root. If the empty string is given, or if
530
592
  * the <tt>user</tt> is not a valid username, then
531
593
  * the spawn manager will be run as the current user.
532
- * @param rubyCommand The Ruby interpreter's command.
533
594
  * @throws SystemException An error occured while trying to setup the spawn server.
534
595
  * @throws IOException The specified log file could not be opened.
535
596
  */
@@ -544,25 +605,24 @@ public:
544
605
  data(new SharedData()),
545
606
  lock(data->lock),
546
607
  activeOrMaxChanged(data->activeOrMaxChanged),
547
- apps(data->apps),
608
+ domains(data->domains),
548
609
  max(data->max),
549
610
  count(data->count),
550
611
  active(data->active),
551
612
  maxPerApp(data->maxPerApp),
552
613
  inactiveApps(data->inactiveApps),
553
- restartFileTimes(data->restartFileTimes),
554
614
  appInstanceCount(data->appInstanceCount)
555
615
  {
616
+ TRACE_POINT();
556
617
  detached = false;
557
618
  done = false;
558
619
  max = DEFAULT_MAX_POOL_SIZE;
559
620
  count = 0;
560
621
  active = 0;
561
- useGlobalQueue = false;
562
622
  waitingOnGlobalQueue = 0;
563
623
  maxPerApp = DEFAULT_MAX_INSTANCES_PER_APP;
564
624
  maxIdleTime = DEFAULT_MAX_IDLE_TIME;
565
- cleanerThread = new thread(
625
+ cleanerThread = new boost::thread(
566
626
  bind(&StandardApplicationPool::cleanerThreadMainLoop, this),
567
627
  CLEANER_THREAD_STACK_SIZE
568
628
  );
@@ -572,7 +632,7 @@ public:
572
632
  if (!detached) {
573
633
  this_thread::disable_interruption di;
574
634
  {
575
- mutex::scoped_lock l(lock);
635
+ boost::mutex::scoped_lock l(lock);
576
636
  done = true;
577
637
  cleanerThreadSleeper.notify_one();
578
638
  }
@@ -581,28 +641,27 @@ public:
581
641
  delete cleanerThread;
582
642
  }
583
643
 
584
- virtual Application::SessionPtr get(
585
- const string &appRoot,
586
- bool lowerPrivilege = true,
587
- const string &lowestUser = "nobody",
588
- const string &environment = "production",
589
- const string &spawnMethod = "smart",
590
- const string &appType = "rails"
591
- ) {
644
+ virtual Application::SessionPtr get(const string &appRoot) {
645
+ return ApplicationPool::get(appRoot);
646
+ }
647
+
648
+ virtual Application::SessionPtr get(const PoolOptions &options) {
649
+ TRACE_POINT();
592
650
  using namespace boost::posix_time;
593
651
  unsigned int attempt = 0;
594
- ptime timeLimit(get_system_time() + millisec(GET_TIMEOUT));
595
- unique_lock<mutex> l(lock);
652
+ // TODO: We should probably add a timeout to the following
653
+ // lock. This way we can fail gracefully if the server's under
654
+ // rediculous load. Though I'm not sure how much it really helps.
655
+ unique_lock<boost::mutex> l(lock);
596
656
 
597
657
  while (true) {
598
658
  attempt++;
599
659
 
600
- pair<AppContainerPtr, AppContainerList *> p(
601
- spawnOrUseExisting(l, appRoot, lowerPrivilege, lowestUser,
602
- environment, spawnMethod, appType)
660
+ pair<AppContainerPtr, Domain *> p(
661
+ spawnOrUseExisting(l, options)
603
662
  );
604
- AppContainerPtr &container(p.first);
605
- AppContainerList &list(*p.second);
663
+ AppContainerPtr &container = p.first;
664
+ Domain *domain = p.second;
606
665
 
607
666
  container->lastUsed = time(NULL);
608
667
  container->sessions++;
@@ -610,13 +669,26 @@ public:
610
669
  P_ASSERT(verifyState(), Application::SessionPtr(),
611
670
  "State is valid:\n" << toString(false));
612
671
  try {
672
+ UPDATE_TRACE_POINT();
613
673
  return container->app->connect(SessionCloseCallback(data, container));
614
674
  } catch (const exception &e) {
615
675
  container->sessions--;
676
+
677
+ AppContainerList &instances(domain->instances);
678
+ instances.erase(container->iterator);
679
+ domain->size--;
680
+ if (instances.empty()) {
681
+ domains.erase(options.appRoot);
682
+ }
683
+ count--;
684
+ active--;
685
+ activeOrMaxChanged.notify_all();
686
+ P_ASSERT(verifyState(), Application::SessionPtr(),
687
+ "State is valid: " << toString(false));
616
688
  if (attempt == MAX_GET_ATTEMPTS) {
617
689
  string message("Cannot connect to an existing "
618
690
  "application instance for '");
619
- message.append(appRoot);
691
+ message.append(options.appRoot);
620
692
  message.append("': ");
621
693
  try {
622
694
  const SystemException &syse =
@@ -626,17 +698,6 @@ public:
626
698
  message.append(e.what());
627
699
  }
628
700
  throw IOException(message);
629
- } else {
630
- list.erase(container->iterator);
631
- if (list.empty()) {
632
- apps.erase(appRoot);
633
- appInstanceCount.erase(appRoot);
634
- }
635
- count--;
636
- active--;
637
- activeOrMaxChanged.notify_all();
638
- P_ASSERT(verifyState(), Application::SessionPtr(),
639
- "State is valid.");
640
701
  }
641
702
  }
642
703
  }
@@ -645,10 +706,9 @@ public:
645
706
  }
646
707
 
647
708
  virtual void clear() {
648
- mutex::scoped_lock l(lock);
649
- apps.clear();
709
+ boost::mutex::scoped_lock l(lock);
710
+ domains.clear();
650
711
  inactiveApps.clear();
651
- restartFileTimes.clear();
652
712
  appInstanceCount.clear();
653
713
  count = 0;
654
714
  active = 0;
@@ -656,13 +716,13 @@ public:
656
716
  }
657
717
 
658
718
  virtual void setMaxIdleTime(unsigned int seconds) {
659
- mutex::scoped_lock l(lock);
719
+ boost::mutex::scoped_lock l(lock);
660
720
  maxIdleTime = seconds;
661
721
  cleanerThreadSleeper.notify_one();
662
722
  }
663
723
 
664
724
  virtual void setMax(unsigned int max) {
665
- mutex::scoped_lock l(lock);
725
+ boost::mutex::scoped_lock l(lock);
666
726
  this->max = max;
667
727
  activeOrMaxChanged.notify_all();
668
728
  }
@@ -676,15 +736,11 @@ public:
676
736
  }
677
737
 
678
738
  virtual void setMaxPerApp(unsigned int maxPerApp) {
679
- mutex::scoped_lock l(lock);
739
+ boost::mutex::scoped_lock l(lock);
680
740
  this->maxPerApp = maxPerApp;
681
741
  activeOrMaxChanged.notify_all();
682
742
  }
683
743
 
684
- virtual void setUseGlobalQueue(bool value) {
685
- this->useGlobalQueue = value;
686
- }
687
-
688
744
  virtual pid_t getSpawnServerPid() const {
689
745
  return spawnManager.getServerPid();
690
746
  }
@@ -695,13 +751,58 @@ public:
695
751
  */
696
752
  virtual string toString(bool lockMutex = true) const {
697
753
  if (lockMutex) {
698
- return toString(boost::adopt_lock);
754
+ unique_lock<boost::mutex> l(lock);
755
+ return toStringWithoutLock();
699
756
  } else {
700
- return toString(boost::defer_lock);
757
+ return toStringWithoutLock();
701
758
  }
702
759
  }
760
+
761
+ /**
762
+ * Returns an XML description of the internal state of the
763
+ * application pool.
764
+ */
765
+ virtual string toXml() const {
766
+ unique_lock<boost::mutex> l(lock);
767
+ stringstream result;
768
+ DomainMap::const_iterator it;
769
+
770
+ result << "<?xml version=\"1.0\" encoding=\"iso8859-1\" ?>\n";
771
+ result << "<info>";
772
+
773
+ result << "<domains>";
774
+ for (it = domains.begin(); it != domains.end(); it++) {
775
+ Domain *domain = it->second.get();
776
+ AppContainerList *instances = &domain->instances;
777
+ AppContainerList::const_iterator lit;
778
+
779
+ result << "<domain>";
780
+ result << "<name>" << escapeForXml(it->first) << "</name>";
781
+
782
+ result << "<instances>";
783
+ for (lit = instances->begin(); lit != instances->end(); lit++) {
784
+ AppContainer *container = lit->get();
785
+
786
+ result << "<instance>";
787
+ result << "<pid>" << container->app->getPid() << "</pid>";
788
+ result << "<sessions>" << container->sessions << "</sessions>";
789
+ result << "<processed>" << container->processed << "</processed>";
790
+ result << "<uptime>" << container->uptime() << "</uptime>";
791
+ result << "</instance>";
792
+ }
793
+ result << "</instances>";
794
+
795
+ result << "</domain>";
796
+ }
797
+ result << "</domains>";
798
+
799
+ result << "</info>";
800
+ return result.str();
801
+ }
703
802
  };
704
803
 
804
+ typedef shared_ptr<StandardApplicationPool> StandardApplicationPoolPtr;
805
+
705
806
  } // namespace Passenger
706
807
 
707
808
  #endif /* _PASSENGER_STANDARD_APPLICATION_POOL_H_ */