passenger 3.0.21 → 3.9.1.beta

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 (835) hide show
  1. data/DEVELOPERS.TXT +4 -10
  2. data/NEWS +19 -27
  3. data/Rakefile +20 -19
  4. data/bin/passenger +3 -2
  5. data/bin/passenger-config +35 -5
  6. data/bin/passenger-install-apache2-module +12 -12
  7. data/bin/passenger-install-nginx-module +55 -38
  8. data/bin/passenger-memory-stats +3 -1
  9. data/bin/passenger-status +7 -35
  10. data/build/agents.rb +107 -21
  11. data/build/apache2.rb +11 -46
  12. data/build/basics.rb +61 -9
  13. data/build/common_library.rb +59 -142
  14. data/build/cxx_tests.rb +111 -110
  15. data/build/documentation.rb +33 -0
  16. data/build/misc.rb +30 -12
  17. data/build/nginx.rb +10 -39
  18. data/build/oxt_tests.rb +1 -0
  19. data/build/ruby_extension.rb +1 -5
  20. data/build/test_basics.rb +3 -2
  21. data/dev/copy_boost_headers.rb +2 -1
  22. data/doc/Architectural overview.html +49 -90
  23. data/doc/DebuggingAndStressTesting.txt.md +49 -0
  24. data/doc/Packaging.txt.md +254 -0
  25. data/doc/Security of user switching support.html +35 -66
  26. data/doc/Users guide Apache.html +588 -758
  27. data/doc/Users guide Apache.idmap.txt +253 -136
  28. data/doc/Users guide Apache.txt +154 -109
  29. data/doc/Users guide Nginx.html +544 -660
  30. data/doc/Users guide Nginx.idmap.txt +179 -91
  31. data/doc/Users guide Nginx.txt +192 -118
  32. data/doc/Users guide Standalone.html +65 -48
  33. data/doc/Users guide Standalone.idmap.txt +10 -2
  34. data/doc/Users guide Standalone.txt +4 -0
  35. data/doc/images/glyphicons-halflings-white.png +0 -0
  36. data/doc/images/glyphicons-halflings.png +0 -0
  37. data/doc/images/phusion_banner_small.png +0 -0
  38. data/doc/images/{smart-lv2.png → smart.png} +0 -0
  39. data/doc/images/{smart-lv2.svg → smart.svg} +0 -0
  40. data/doc/templates/bootstrap.min.css +397 -0
  41. data/doc/templates/markdown.html.erb +117 -0
  42. data/doc/users_guide_snippets/analysis_and_system_maintenance.txt +2 -1
  43. data/doc/users_guide_snippets/appendix_c_spawning_methods.txt +26 -48
  44. data/doc/users_guide_snippets/passenger_spawn_method.txt +18 -30
  45. data/doc/users_guide_snippets/support_information.txt +30 -0
  46. data/ext/apache2/Bucket.cpp +9 -26
  47. data/ext/apache2/Bucket.h +13 -10
  48. data/ext/apache2/Configuration.cpp +70 -58
  49. data/ext/apache2/Configuration.hpp +19 -47
  50. data/ext/apache2/DirectoryMapper.h +7 -7
  51. data/ext/apache2/Hooks.cpp +150 -313
  52. data/ext/boost/algorithm/string/detail/case_conv.hpp +4 -2
  53. data/ext/boost/algorithm/string/detail/find_format.hpp +20 -20
  54. data/ext/boost/algorithm/string/detail/find_format_all.hpp +23 -23
  55. data/ext/boost/algorithm/string/detail/find_format_store.hpp +2 -2
  56. data/ext/boost/algorithm/string/detail/formatter.hpp +25 -0
  57. data/ext/boost/algorithm/string/formatter.hpp +20 -3
  58. data/ext/boost/assert.hpp +85 -4
  59. data/ext/boost/bind/bind.hpp +1 -1
  60. data/ext/boost/concept/detail/backward_compatibility.hpp +1 -1
  61. data/ext/boost/concept_check.hpp +140 -64
  62. data/ext/boost/config.hpp +1 -1
  63. data/ext/boost/config/auto_link.hpp +8 -6
  64. data/ext/boost/config/compiler/borland.hpp +12 -2
  65. data/ext/boost/config/compiler/clang.hpp +89 -30
  66. data/ext/boost/config/compiler/codegear.hpp +3 -2
  67. data/ext/boost/config/compiler/common_edg.hpp +7 -5
  68. data/ext/boost/config/compiler/cray.hpp +61 -0
  69. data/ext/boost/config/compiler/digitalmars.hpp +9 -1
  70. data/ext/boost/config/compiler/gcc.hpp +33 -24
  71. data/ext/boost/config/compiler/gcc_xml.hpp +4 -0
  72. data/ext/boost/config/compiler/hp_acc.hpp +12 -1
  73. data/ext/boost/config/compiler/intel.hpp +78 -4
  74. data/ext/boost/config/compiler/metrowerks.hpp +4 -1
  75. data/ext/boost/config/compiler/mpw.hpp +4 -1
  76. data/ext/boost/config/compiler/nvcc.hpp +8 -66
  77. data/ext/boost/config/compiler/pathscale.hpp +80 -0
  78. data/ext/boost/config/compiler/pgi.hpp +5 -5
  79. data/ext/boost/config/compiler/sunpro_cc.hpp +4 -1
  80. data/ext/boost/config/compiler/vacpp.hpp +37 -13
  81. data/ext/boost/config/compiler/visualc.hpp +24 -11
  82. data/ext/boost/config/platform/bsd.hpp +1 -1
  83. data/ext/boost/config/platform/cray.hpp +18 -0
  84. data/ext/boost/config/platform/cygwin.hpp +10 -0
  85. data/ext/boost/config/platform/linux.hpp +5 -0
  86. data/ext/boost/config/platform/macos.hpp +5 -4
  87. data/ext/boost/config/platform/symbian.hpp +5 -2
  88. data/ext/boost/config/platform/vms.hpp +25 -0
  89. data/ext/boost/config/platform/win32.hpp +7 -1
  90. data/ext/boost/config/select_compiler_config.hpp +8 -25
  91. data/ext/boost/config/select_platform_config.hpp +8 -1
  92. data/ext/boost/config/select_stdlib_config.hpp +9 -1
  93. data/ext/boost/config/stdlib/dinkumware.hpp +6 -9
  94. data/ext/boost/config/stdlib/libcomo.hpp +1 -4
  95. data/ext/boost/config/stdlib/libcpp.hpp +36 -0
  96. data/ext/boost/config/stdlib/libstdcpp3.hpp +37 -11
  97. data/ext/boost/config/stdlib/modena.hpp +1 -4
  98. data/ext/boost/config/stdlib/msl.hpp +1 -4
  99. data/ext/boost/config/stdlib/roguewave.hpp +9 -6
  100. data/ext/boost/config/stdlib/sgi.hpp +12 -4
  101. data/ext/boost/config/stdlib/stlport.hpp +11 -4
  102. data/ext/boost/config/stdlib/vacpp.hpp +11 -4
  103. data/ext/boost/config/suffix.hpp +71 -6
  104. data/ext/boost/config/warning_disable.hpp +1 -1
  105. data/ext/boost/container/container_fwd.hpp +177 -0
  106. data/ext/boost/cstdint.hpp +17 -12
  107. data/ext/boost/current_function.hpp +2 -1
  108. data/ext/boost/date_time/c_time.hpp +17 -1
  109. data/ext/boost/date_time/compiler_config.hpp +13 -15
  110. data/ext/boost/date_time/date_formatting.hpp +7 -1
  111. data/ext/boost/date_time/filetime_functions.hpp +4 -4
  112. data/ext/boost/date_time/gregorian_calendar.ipp +2 -2
  113. data/ext/boost/date_time/strings_from_facet.hpp +3 -3
  114. data/ext/boost/date_time/time_facet.hpp +101 -101
  115. data/ext/boost/detail/endian.hpp +4 -2
  116. data/ext/boost/detail/fenv.hpp +74 -0
  117. data/ext/boost/detail/sp_typeinfo.hpp +6 -0
  118. data/ext/boost/exception/detail/clone_current_exception.hpp +47 -0
  119. data/ext/boost/exception/detail/exception_ptr.hpp +194 -122
  120. data/ext/boost/exception/detail/type_info.hpp +3 -3
  121. data/ext/boost/exception/diagnostic_information.hpp +37 -21
  122. data/ext/boost/exception/exception.hpp +21 -1
  123. data/ext/boost/exception/info.hpp +0 -1
  124. data/ext/boost/function.hpp +2 -2
  125. data/ext/boost/function/function_base.hpp +15 -9
  126. data/ext/boost/function/function_template.hpp +26 -48
  127. data/ext/boost/integer_fwd.hpp +0 -16
  128. data/ext/boost/integer_traits.hpp +2 -2
  129. data/ext/boost/iterator.hpp +1 -1
  130. data/ext/boost/iterator/iterator_adaptor.hpp +1 -7
  131. data/ext/boost/iterator/iterator_facade.hpp +13 -13
  132. data/ext/boost/iterator/transform_iterator.hpp +5 -20
  133. data/ext/boost/lexical_cast.hpp +1655 -673
  134. data/ext/boost/math/policies/policy.hpp +982 -0
  135. data/ext/boost/math/special_functions/detail/fp_traits.hpp +570 -0
  136. data/ext/boost/math/special_functions/detail/round_fwd.hpp +80 -0
  137. data/ext/boost/math/special_functions/fpclassify.hpp +533 -0
  138. data/ext/boost/math/special_functions/math_fwd.hpp +1070 -0
  139. data/ext/boost/math/special_functions/sign.hpp +145 -0
  140. data/ext/boost/math/tools/config.hpp +321 -0
  141. data/ext/boost/math/tools/promotion.hpp +150 -0
  142. data/ext/boost/math/tools/real_cast.hpp +29 -0
  143. data/ext/boost/math/tools/user.hpp +97 -0
  144. data/ext/boost/move/move.hpp +1222 -0
  145. data/ext/boost/mpl/O1_size.hpp +40 -0
  146. data/ext/boost/mpl/O1_size_fwd.hpp +24 -0
  147. data/ext/boost/mpl/advance.hpp +76 -0
  148. data/ext/boost/mpl/advance_fwd.hpp +28 -0
  149. data/ext/boost/mpl/at.hpp +52 -0
  150. data/ext/boost/mpl/at_fwd.hpp +24 -0
  151. data/ext/boost/mpl/aux_/O1_size_impl.hpp +87 -0
  152. data/ext/boost/mpl/aux_/advance_backward.hpp +128 -0
  153. data/ext/boost/mpl/aux_/advance_forward.hpp +127 -0
  154. data/ext/boost/mpl/aux_/arithmetic_op.hpp +92 -0
  155. data/ext/boost/mpl/aux_/at_impl.hpp +45 -0
  156. data/ext/boost/mpl/aux_/begin_end_impl.hpp +101 -0
  157. data/ext/boost/mpl/aux_/clear_impl.hpp +35 -0
  158. data/ext/boost/mpl/aux_/comparison_op.hpp +83 -0
  159. data/ext/boost/mpl/aux_/config/forwarding.hpp +27 -0
  160. data/ext/boost/mpl/aux_/config/typeof.hpp +38 -0
  161. data/ext/boost/mpl/aux_/contains_impl.hpp +61 -0
  162. data/ext/boost/mpl/aux_/find_if_pred.hpp +31 -0
  163. data/ext/boost/mpl/aux_/fold_impl.hpp +43 -0
  164. data/ext/boost/mpl/aux_/has_begin.hpp +23 -0
  165. data/ext/boost/mpl/aux_/has_size.hpp +23 -0
  166. data/ext/boost/mpl/aux_/has_tag.hpp +23 -0
  167. data/ext/boost/mpl/aux_/inserter_algorithm.hpp +159 -0
  168. data/ext/boost/mpl/aux_/is_msvc_eti_arg.hpp +64 -0
  169. data/ext/boost/mpl/aux_/iter_apply.hpp +47 -0
  170. data/ext/boost/mpl/aux_/iter_fold_if_impl.hpp +210 -0
  171. data/ext/boost/mpl/aux_/iter_fold_impl.hpp +42 -0
  172. data/ext/boost/mpl/aux_/lambda_spec.hpp +49 -0
  173. data/ext/boost/mpl/aux_/largest_int.hpp +63 -0
  174. data/ext/boost/mpl/aux_/msvc_eti_base.hpp +77 -0
  175. data/ext/boost/mpl/aux_/msvc_type.hpp +62 -0
  176. data/ext/boost/mpl/aux_/numeric_cast_utils.hpp +77 -0
  177. data/ext/boost/mpl/aux_/numeric_op.hpp +315 -0
  178. data/ext/boost/mpl/aux_/preprocessed/gcc/advance_backward.hpp +97 -0
  179. data/ext/boost/mpl/aux_/preprocessed/gcc/advance_forward.hpp +97 -0
  180. data/ext/boost/mpl/aux_/preprocessed/gcc/equal_to.hpp +94 -0
  181. data/ext/boost/mpl/aux_/preprocessed/gcc/fold_impl.hpp +180 -0
  182. data/ext/boost/mpl/aux_/preprocessed/gcc/greater.hpp +94 -0
  183. data/ext/boost/mpl/aux_/preprocessed/gcc/greater_equal.hpp +94 -0
  184. data/ext/boost/mpl/aux_/preprocessed/gcc/iter_fold_if_impl.hpp +133 -0
  185. data/ext/boost/mpl/aux_/preprocessed/gcc/iter_fold_impl.hpp +180 -0
  186. data/ext/boost/mpl/aux_/preprocessed/gcc/less.hpp +94 -0
  187. data/ext/boost/mpl/aux_/preprocessed/gcc/less_equal.hpp +94 -0
  188. data/ext/boost/mpl/aux_/preprocessed/gcc/list.hpp +323 -0
  189. data/ext/boost/mpl/aux_/preprocessed/gcc/minus.hpp +146 -0
  190. data/ext/boost/mpl/aux_/preprocessed/gcc/not_equal_to.hpp +94 -0
  191. data/ext/boost/mpl/aux_/preprocessed/gcc/plus.hpp +146 -0
  192. data/ext/boost/mpl/aux_/preprocessed/gcc/reverse_fold_impl.hpp +231 -0
  193. data/ext/boost/mpl/aux_/preprocessed/gcc/times.hpp +146 -0
  194. data/ext/boost/mpl/aux_/preprocessed/gcc/vector.hpp +323 -0
  195. data/ext/boost/mpl/aux_/preprocessor/default_params.hpp +67 -0
  196. data/ext/boost/mpl/aux_/push_back_impl.hpp +70 -0
  197. data/ext/boost/mpl/aux_/push_front_impl.hpp +71 -0
  198. data/ext/boost/mpl/aux_/reverse_fold_impl.hpp +44 -0
  199. data/ext/boost/mpl/aux_/size_impl.hpp +52 -0
  200. data/ext/boost/mpl/aux_/traits_lambda_spec.hpp +63 -0
  201. data/ext/boost/mpl/back_fwd.hpp +24 -0
  202. data/ext/boost/mpl/back_inserter.hpp +34 -0
  203. data/ext/boost/mpl/begin_end.hpp +57 -0
  204. data/ext/boost/mpl/begin_end_fwd.hpp +27 -0
  205. data/ext/boost/mpl/clear.hpp +39 -0
  206. data/ext/boost/mpl/clear_fwd.hpp +24 -0
  207. data/ext/boost/mpl/comparison.hpp +24 -0
  208. data/ext/boost/mpl/contains.hpp +41 -0
  209. data/ext/boost/mpl/contains_fwd.hpp +25 -0
  210. data/ext/boost/mpl/deref.hpp +41 -0
  211. data/ext/boost/mpl/distance.hpp +78 -0
  212. data/ext/boost/mpl/distance_fwd.hpp +28 -0
  213. data/ext/boost/mpl/empty_fwd.hpp +24 -0
  214. data/ext/boost/mpl/equal_to.hpp +21 -0
  215. data/ext/boost/mpl/find.hpp +38 -0
  216. data/ext/boost/mpl/find_if.hpp +50 -0
  217. data/ext/boost/mpl/fold.hpp +48 -0
  218. data/ext/boost/mpl/front_fwd.hpp +24 -0
  219. data/ext/boost/mpl/front_inserter.hpp +33 -0
  220. data/ext/boost/mpl/greater.hpp +21 -0
  221. data/ext/boost/mpl/greater_equal.hpp +21 -0
  222. data/ext/boost/mpl/inserter.hpp +32 -0
  223. data/ext/boost/mpl/iter_fold.hpp +49 -0
  224. data/ext/boost/mpl/iter_fold_if.hpp +117 -0
  225. data/ext/boost/mpl/iterator_range.hpp +42 -0
  226. data/ext/boost/mpl/iterator_tags.hpp +27 -0
  227. data/ext/boost/mpl/less.hpp +21 -0
  228. data/ext/boost/mpl/less_equal.hpp +21 -0
  229. data/ext/boost/mpl/limits/list.hpp +21 -0
  230. data/ext/boost/mpl/limits/vector.hpp +21 -0
  231. data/ext/boost/mpl/list.hpp +57 -0
  232. data/ext/boost/mpl/list/aux_/O1_size.hpp +33 -0
  233. data/ext/boost/mpl/list/aux_/begin_end.hpp +44 -0
  234. data/ext/boost/mpl/list/aux_/clear.hpp +34 -0
  235. data/ext/boost/mpl/list/aux_/empty.hpp +34 -0
  236. data/ext/boost/mpl/list/aux_/front.hpp +33 -0
  237. data/ext/boost/mpl/list/aux_/include_preprocessed.hpp +35 -0
  238. data/ext/boost/mpl/list/aux_/item.hpp +55 -0
  239. data/ext/boost/mpl/list/aux_/iterator.hpp +76 -0
  240. data/ext/boost/mpl/list/aux_/pop_front.hpp +34 -0
  241. data/ext/boost/mpl/list/aux_/preprocessed/plain/list10.hpp +149 -0
  242. data/ext/boost/mpl/list/aux_/preprocessed/plain/list20.hpp +169 -0
  243. data/ext/boost/mpl/list/aux_/push_back.hpp +36 -0
  244. data/ext/boost/mpl/list/aux_/push_front.hpp +39 -0
  245. data/ext/boost/mpl/list/aux_/size.hpp +33 -0
  246. data/ext/boost/mpl/list/aux_/tag.hpp +24 -0
  247. data/ext/boost/mpl/list/list0.hpp +42 -0
  248. data/ext/boost/mpl/list/list10.hpp +43 -0
  249. data/ext/boost/mpl/list/list20.hpp +43 -0
  250. data/ext/boost/mpl/long.hpp +22 -0
  251. data/ext/boost/mpl/long_fwd.hpp +27 -0
  252. data/ext/boost/mpl/minus.hpp +21 -0
  253. data/ext/boost/mpl/multiplies.hpp +53 -0
  254. data/ext/boost/mpl/negate.hpp +81 -0
  255. data/ext/boost/mpl/not_equal_to.hpp +21 -0
  256. data/ext/boost/mpl/numeric_cast.hpp +41 -0
  257. data/ext/boost/mpl/pair.hpp +70 -0
  258. data/ext/boost/mpl/plus.hpp +21 -0
  259. data/ext/boost/mpl/pop_back_fwd.hpp +24 -0
  260. data/ext/boost/mpl/pop_front_fwd.hpp +24 -0
  261. data/ext/boost/mpl/prior.hpp +19 -0
  262. data/ext/boost/mpl/push_back.hpp +53 -0
  263. data/ext/boost/mpl/push_back_fwd.hpp +24 -0
  264. data/ext/boost/mpl/push_front.hpp +52 -0
  265. data/ext/boost/mpl/push_front_fwd.hpp +24 -0
  266. data/ext/boost/mpl/remove_if.hpp +83 -0
  267. data/ext/boost/mpl/reverse_fold.hpp +50 -0
  268. data/ext/boost/mpl/same_as.hpp +55 -0
  269. data/ext/boost/mpl/sequence_tag.hpp +124 -0
  270. data/ext/boost/mpl/sequence_tag_fwd.hpp +26 -0
  271. data/ext/boost/mpl/size.hpp +42 -0
  272. data/ext/boost/mpl/size_fwd.hpp +24 -0
  273. data/ext/boost/mpl/tag.hpp +52 -0
  274. data/ext/boost/mpl/times.hpp +21 -0
  275. data/ext/boost/mpl/vector.hpp +57 -0
  276. data/ext/boost/mpl/vector/aux_/O1_size.hpp +56 -0
  277. data/ext/boost/mpl/vector/aux_/at.hpp +116 -0
  278. data/ext/boost/mpl/vector/aux_/back.hpp +59 -0
  279. data/ext/boost/mpl/vector/aux_/begin_end.hpp +49 -0
  280. data/ext/boost/mpl/vector/aux_/clear.hpp +55 -0
  281. data/ext/boost/mpl/vector/aux_/empty.hpp +68 -0
  282. data/ext/boost/mpl/vector/aux_/front.hpp +56 -0
  283. data/ext/boost/mpl/vector/aux_/include_preprocessed.hpp +55 -0
  284. data/ext/boost/mpl/vector/aux_/item.hpp +103 -0
  285. data/ext/boost/mpl/vector/aux_/iterator.hpp +130 -0
  286. data/ext/boost/mpl/vector/aux_/pop_back.hpp +40 -0
  287. data/ext/boost/mpl/vector/aux_/pop_front.hpp +40 -0
  288. data/ext/boost/mpl/vector/aux_/preprocessed/plain/vector10.hpp +829 -0
  289. data/ext/boost/mpl/vector/aux_/preprocessed/plain/vector20.hpp +1144 -0
  290. data/ext/boost/mpl/vector/aux_/preprocessed/typeof_based/vector10.hpp +139 -0
  291. data/ext/boost/mpl/vector/aux_/preprocessed/typeof_based/vector20.hpp +159 -0
  292. data/ext/boost/mpl/vector/aux_/push_back.hpp +40 -0
  293. data/ext/boost/mpl/vector/aux_/push_front.hpp +40 -0
  294. data/ext/boost/mpl/vector/aux_/size.hpp +49 -0
  295. data/ext/boost/mpl/vector/aux_/tag.hpp +32 -0
  296. data/ext/boost/mpl/vector/aux_/vector0.hpp +52 -0
  297. data/ext/boost/mpl/vector/vector0.hpp +34 -0
  298. data/ext/boost/mpl/vector/vector10.hpp +45 -0
  299. data/ext/boost/mpl/vector/vector20.hpp +45 -0
  300. data/ext/boost/none.hpp +1 -1
  301. data/ext/boost/numeric/conversion/bounds.hpp +24 -0
  302. data/ext/boost/numeric/conversion/cast.hpp +61 -0
  303. data/ext/boost/numeric/conversion/conversion_traits.hpp +39 -0
  304. data/ext/boost/numeric/conversion/converter.hpp +68 -0
  305. data/ext/boost/numeric/conversion/converter_policies.hpp +186 -0
  306. data/ext/boost/numeric/conversion/detail/bounds.hpp +58 -0
  307. data/ext/boost/numeric/conversion/detail/conversion_traits.hpp +97 -0
  308. data/ext/boost/numeric/conversion/detail/converter.hpp +602 -0
  309. data/ext/boost/numeric/conversion/detail/int_float_mixture.hpp +72 -0
  310. data/ext/boost/numeric/conversion/detail/is_subranged.hpp +234 -0
  311. data/ext/boost/numeric/conversion/detail/meta.hpp +120 -0
  312. data/ext/boost/numeric/conversion/detail/numeric_cast_traits.hpp +138 -0
  313. data/ext/boost/numeric/conversion/detail/preprocessed/numeric_cast_traits_common.hpp +1741 -0
  314. data/ext/boost/numeric/conversion/detail/preprocessed/numeric_cast_traits_long_long.hpp +347 -0
  315. data/ext/boost/numeric/conversion/detail/sign_mixture.hpp +72 -0
  316. data/ext/boost/numeric/conversion/detail/udt_builtin_mixture.hpp +69 -0
  317. data/ext/boost/numeric/conversion/int_float_mixture_enum.hpp +29 -0
  318. data/ext/boost/numeric/conversion/numeric_cast_traits.hpp +31 -0
  319. data/ext/boost/numeric/conversion/sign_mixture_enum.hpp +29 -0
  320. data/ext/boost/numeric/conversion/udt_builtin_mixture_enum.hpp +26 -0
  321. data/ext/boost/operators.hpp +3 -1
  322. data/ext/boost/optional/optional.hpp +146 -79
  323. data/ext/boost/optional/optional_fwd.hpp +8 -1
  324. data/ext/boost/preprocessor/cat.hpp +2 -2
  325. data/ext/boost/preprocessor/config/config.hpp +39 -4
  326. data/ext/boost/preprocessor/facilities/intercept.hpp +277 -0
  327. data/ext/boost/preprocessor/facilities/overload.hpp +25 -0
  328. data/ext/boost/preprocessor/iteration/detail/iter/forward1.hpp +3 -3
  329. data/ext/boost/preprocessor/iteration/iterate.hpp +3 -3
  330. data/ext/boost/preprocessor/punctuation/paren.hpp +23 -0
  331. data/ext/boost/preprocessor/repetition/enum_shifted_params.hpp +44 -0
  332. data/ext/boost/preprocessor/seq/cat.hpp +5 -4
  333. data/ext/boost/preprocessor/seq/size.hpp +0 -1
  334. data/ext/boost/preprocessor/tuple/eat.hpp +83 -34
  335. data/ext/boost/preprocessor/tuple/elem.hpp +161 -355
  336. data/ext/boost/preprocessor/tuple/rem.hpp +110 -48
  337. data/ext/boost/preprocessor/tuple/to_list.hpp +90 -36
  338. data/ext/boost/preprocessor/variadic/elem.hpp +94 -0
  339. data/ext/boost/preprocessor/variadic/size.hpp +30 -0
  340. data/ext/boost/range/begin.hpp +17 -6
  341. data/ext/boost/range/concepts.hpp +37 -2
  342. data/ext/boost/range/detail/safe_bool.hpp +72 -0
  343. data/ext/boost/range/end.hpp +14 -9
  344. data/ext/boost/range/iterator_range_core.hpp +120 -12
  345. data/ext/boost/range/size.hpp +21 -5
  346. data/ext/boost/smart_ptr/detail/shared_count.hpp +88 -0
  347. data/ext/boost/smart_ptr/detail/sp_counted_base.hpp +3 -0
  348. data/ext/boost/smart_ptr/detail/sp_counted_base_aix.hpp +142 -0
  349. data/ext/boost/smart_ptr/detail/sp_counted_base_gcc_mips.hpp +9 -0
  350. data/ext/boost/smart_ptr/detail/sp_counted_impl.hpp +10 -2
  351. data/ext/boost/smart_ptr/detail/sp_has_sync.hpp +5 -1
  352. data/ext/boost/smart_ptr/detail/spinlock.hpp +4 -1
  353. data/ext/boost/smart_ptr/detail/spinlock_gcc_arm.hpp +20 -3
  354. data/ext/boost/smart_ptr/detail/spinlock_pool.hpp +4 -0
  355. data/ext/boost/smart_ptr/make_shared.hpp +591 -22
  356. data/ext/boost/smart_ptr/shared_array.hpp +29 -1
  357. data/ext/boost/smart_ptr/shared_ptr.hpp +29 -13
  358. data/ext/boost/smart_ptr/weak_ptr.hpp +24 -12
  359. data/ext/boost/src/pthread/once.cpp +9 -7
  360. data/ext/boost/src/pthread/thread.cpp +32 -28
  361. data/ext/boost/src/pthread/timeconv.inl +4 -5
  362. data/ext/boost/src/tss_null.cpp +5 -1
  363. data/ext/boost/static_assert.hpp +8 -2
  364. data/ext/boost/thread/detail/config.hpp +19 -4
  365. data/ext/boost/thread/detail/move.hpp +11 -5
  366. data/ext/boost/thread/detail/thread.hpp +59 -43
  367. data/ext/boost/thread/exceptions.hpp +9 -9
  368. data/ext/boost/thread/future.hpp +150 -82
  369. data/ext/boost/thread/locks.hpp +101 -60
  370. data/ext/boost/thread/pthread/condition_variable.hpp +79 -32
  371. data/ext/boost/thread/pthread/condition_variable_fwd.hpp +12 -3
  372. data/ext/boost/thread/pthread/mutex.hpp +17 -14
  373. data/ext/boost/thread/pthread/once.hpp +3 -4
  374. data/ext/boost/thread/pthread/pthread_mutex_scoped_lock.hpp +12 -2
  375. data/ext/boost/thread/pthread/recursive_mutex.hpp +19 -19
  376. data/ext/boost/thread/pthread/shared_mutex.hpp +13 -13
  377. data/ext/boost/thread/pthread/thread_data.hpp +40 -12
  378. data/ext/boost/thread/thread_time.hpp +5 -0
  379. data/ext/boost/throw_exception.hpp +1 -1
  380. data/ext/boost/token_functions.hpp +34 -10
  381. data/ext/boost/type_traits/add_rvalue_reference.hpp +66 -0
  382. data/ext/boost/type_traits/alignment_of.hpp +1 -1
  383. data/ext/boost/type_traits/detail/bool_trait_def.hpp +26 -3
  384. data/ext/boost/type_traits/detail/bool_trait_undef.hpp +3 -2
  385. data/ext/boost/type_traits/detail/cv_traits_impl.hpp +1 -1
  386. data/ext/boost/type_traits/detail/size_t_trait_def.hpp +6 -4
  387. data/ext/boost/type_traits/detail/type_trait_def.hpp +8 -2
  388. data/ext/boost/type_traits/function_traits.hpp +1 -1
  389. data/ext/boost/type_traits/has_nothrow_constructor.hpp +53 -0
  390. data/ext/boost/type_traits/has_nothrow_copy.hpp +19 -5
  391. data/ext/boost/type_traits/has_trivial_constructor.hpp +51 -0
  392. data/ext/boost/type_traits/has_trivial_copy.hpp +20 -5
  393. data/ext/boost/type_traits/has_trivial_destructor.hpp +12 -5
  394. data/ext/boost/type_traits/intrinsics.hpp +119 -71
  395. data/ext/boost/type_traits/is_const.hpp +5 -5
  396. data/ext/boost/type_traits/is_convertible.hpp +14 -13
  397. data/ext/boost/type_traits/is_enum.hpp +1 -1
  398. data/ext/boost/type_traits/is_floating_point.hpp +27 -0
  399. data/ext/boost/type_traits/is_function.hpp +3 -3
  400. data/ext/boost/type_traits/is_fundamental.hpp +1 -1
  401. data/ext/boost/type_traits/is_member_function_pointer.hpp +2 -2
  402. data/ext/boost/type_traits/is_member_pointer.hpp +2 -2
  403. data/ext/boost/type_traits/is_pod.hpp +11 -3
  404. data/ext/boost/type_traits/is_pointer.hpp +2 -2
  405. data/ext/boost/type_traits/is_signed.hpp +8 -3
  406. data/ext/boost/type_traits/is_union.hpp +8 -0
  407. data/ext/boost/type_traits/is_unsigned.hpp +9 -4
  408. data/ext/boost/type_traits/is_volatile.hpp +5 -5
  409. data/ext/boost/type_traits/remove_cv.hpp +4 -3
  410. data/ext/boost/type_traits/remove_pointer.hpp +51 -2
  411. data/ext/boost/type_traits/remove_reference.hpp +2 -2
  412. data/ext/boost/type_traits/type_with_alignment.hpp +8 -2
  413. data/ext/boost/utility/declval.hpp +44 -0
  414. data/ext/boost/utility/detail/in_place_factory_prefix.hpp +36 -0
  415. data/ext/boost/utility/detail/in_place_factory_suffix.hpp +23 -0
  416. data/ext/boost/utility/detail/result_of_iterate.hpp +142 -0
  417. data/ext/boost/utility/in_place_factory.hpp +88 -0
  418. data/ext/boost/utility/result_of.hpp +103 -0
  419. data/ext/boost/utility/swap.hpp +55 -0
  420. data/ext/common/AnsiColorConstants.h +36 -0
  421. data/ext/common/ApplicationPool2/Common.h +87 -0
  422. data/ext/common/ApplicationPool2/ComponentInfo.h +53 -0
  423. data/ext/common/ApplicationPool2/Group.h +648 -0
  424. data/ext/common/ApplicationPool2/Implementation.cpp +580 -0
  425. data/ext/common/ApplicationPool2/Options.h +576 -0
  426. data/ext/common/ApplicationPool2/PipeWatcher.h +61 -0
  427. data/ext/common/ApplicationPool2/Pool.h +1181 -0
  428. data/ext/common/ApplicationPool2/Process.h +425 -0
  429. data/ext/common/ApplicationPool2/README.md +96 -0
  430. data/ext/common/ApplicationPool2/Session.h +158 -0
  431. data/ext/common/ApplicationPool2/Socket.h +246 -0
  432. data/ext/common/ApplicationPool2/Spawner.h +2212 -0
  433. data/ext/common/ApplicationPool2/SuperGroup.h +749 -0
  434. data/ext/common/BackgroundEventLoop.cpp +129 -0
  435. data/ext/common/BackgroundEventLoop.h +61 -0
  436. data/ext/common/Constants.h +3 -1
  437. data/ext/common/EventedBufferedInput.h +331 -0
  438. data/ext/common/EventedMessageServer.h +17 -34
  439. data/ext/common/EventedServer.h +2 -2
  440. data/ext/common/Exceptions.h +71 -19
  441. data/ext/common/FileDescriptor.h +8 -6
  442. data/ext/common/HttpConstants.h +167 -0
  443. data/ext/common/IniFile.h +24 -0
  444. data/ext/common/Logging.h +62 -849
  445. data/ext/common/MessageReadersWriters.h +19 -0
  446. data/ext/common/MessageServer.h +11 -14
  447. data/ext/common/MultiLibeio.cpp +198 -0
  448. data/ext/common/MultiLibeio.h +67 -0
  449. data/ext/common/ResourceLocator.h +24 -41
  450. data/ext/common/SafeLibev.h +186 -14
  451. data/ext/common/StaticString.h +23 -3
  452. data/ext/common/UnionStation.h +972 -0
  453. data/ext/common/Utils.cpp +168 -24
  454. data/ext/common/Utils.h +25 -3
  455. data/ext/common/Utils/CachedFileStat.hpp +4 -3
  456. data/ext/common/Utils/FileChangeChecker.h +2 -2
  457. data/ext/common/Utils/HashMap.h +50 -0
  458. data/ext/common/Utils/IOUtils.cpp +229 -68
  459. data/ext/common/Utils/IOUtils.h +134 -3
  460. data/ext/common/Utils/Lock.h +28 -0
  461. data/ext/common/Utils/MemoryBarrier.h +52 -0
  462. data/ext/common/Utils/PriorityQueue.h +54 -0
  463. data/ext/common/Utils/ProcessMetricsCollector.h +9 -11
  464. data/ext/common/Utils/ScopeGuard.h +50 -1
  465. data/ext/common/Utils/SmallVector.h +653 -0
  466. data/ext/common/Utils/StrIntUtils.cpp +26 -2
  467. data/ext/common/Utils/StrIntUtils.h +18 -2
  468. data/ext/common/Utils/StringMap.h +125 -8
  469. data/ext/common/Utils/Template.h +212 -0
  470. data/ext/common/Utils/fib.c +699 -0
  471. data/ext/common/Utils/fib.h +101 -0
  472. data/ext/common/Utils/fibpriv.h +67 -0
  473. data/ext/common/Utils/json-forwards.h +249 -0
  474. data/ext/common/Utils/json.h +1855 -0
  475. data/ext/common/Utils/jsoncpp.cpp +4230 -0
  476. data/ext/common/agents/Base.cpp +1126 -0
  477. data/ext/common/{AgentBase.h → agents/Base.h} +5 -1
  478. data/ext/common/agents/EnvPrinter.c +16 -0
  479. data/ext/common/agents/HelperAgent/AgentOptions.h +81 -0
  480. data/ext/common/{HelperAgent → agents/HelperAgent}/BacktracesServer.h +3 -2
  481. data/ext/common/agents/HelperAgent/FileBackedPipe.h +732 -0
  482. data/ext/common/agents/HelperAgent/Main.cpp +497 -0
  483. data/ext/common/agents/HelperAgent/RequestHandler.cpp +283 -0
  484. data/ext/common/agents/HelperAgent/RequestHandler.h +2139 -0
  485. data/ext/common/agents/HelperAgent/ScgiRequestParser.h +451 -0
  486. data/ext/common/{LoggingAgent → agents/LoggingAgent}/DataStoreId.h +1 -1
  487. data/ext/common/{LoggingAgent → agents/LoggingAgent}/FilterSupport.cpp +1 -1
  488. data/ext/common/{LoggingAgent → agents/LoggingAgent}/FilterSupport.h +0 -0
  489. data/ext/common/{LoggingAgent → agents/LoggingAgent}/LoggingServer.h +18 -16
  490. data/ext/common/{LoggingAgent → agents/LoggingAgent}/Main.cpp +15 -13
  491. data/ext/common/{LoggingAgent → agents/LoggingAgent}/RemoteSender.h +6 -6
  492. data/ext/common/agents/SpawnPreparer.cpp +127 -0
  493. data/ext/common/{Watchdog.cpp → agents/Watchdog/Main.cpp} +63 -25
  494. data/ext/libeio/Changes +72 -0
  495. data/ext/{google/COPYING → libeio/LICENSE} +17 -9
  496. data/ext/libeio/Makefile.am +15 -0
  497. data/ext/libeio/Makefile.in +694 -0
  498. data/ext/libeio/aclocal.m4 +9418 -0
  499. data/ext/libeio/autogen.sh +3 -0
  500. data/ext/libeio/config.guess +1501 -0
  501. data/ext/libeio/config.h.in +136 -0
  502. data/ext/libeio/config.sub +1705 -0
  503. data/ext/libeio/configure +14822 -0
  504. data/ext/libeio/configure.ac +22 -0
  505. data/ext/libeio/demo.c +194 -0
  506. data/ext/libeio/ecb.h +457 -0
  507. data/ext/libeio/eio.c +2816 -0
  508. data/ext/libeio/eio.h +411 -0
  509. data/ext/libeio/install-sh +520 -0
  510. data/ext/libeio/libeio.m4 +211 -0
  511. data/ext/libeio/ltmain.sh +9636 -0
  512. data/ext/libeio/missing +376 -0
  513. data/ext/libeio/xthread.h +166 -0
  514. data/ext/libev/Changes +125 -7
  515. data/ext/libev/Makefile.am +5 -3
  516. data/ext/libev/Makefile.in +209 -120
  517. data/ext/libev/aclocal.m4 +6027 -4619
  518. data/ext/libev/autogen.sh +1 -4
  519. data/ext/libev/config.h.in +11 -7
  520. data/ext/libev/configure +7312 -14993
  521. data/ext/libev/configure.ac +12 -5
  522. data/ext/libev/depcomp +630 -0
  523. data/ext/libev/ev++.h +48 -32
  524. data/ext/libev/ev.c +1173 -391
  525. data/ext/libev/ev.h +315 -181
  526. data/ext/libev/ev_epoll.c +66 -15
  527. data/ext/libev/ev_kqueue.c +20 -18
  528. data/ext/libev/ev_poll.c +27 -23
  529. data/ext/libev/ev_port.c +39 -19
  530. data/ext/libev/ev_select.c +23 -17
  531. data/ext/libev/ev_vars.h +25 -8
  532. data/ext/libev/ev_win32.c +6 -6
  533. data/ext/libev/ev_wrap.h +22 -2
  534. data/ext/libev/event.c +18 -17
  535. data/ext/libev/event.h +16 -4
  536. data/ext/libev/libev.m4 +10 -6
  537. data/ext/libev/ltmain.sh +7353 -5811
  538. data/ext/nginx/Configuration.c +74 -42
  539. data/ext/nginx/Configuration.h +3 -5
  540. data/ext/nginx/ContentHandler.c +26 -83
  541. data/ext/nginx/ContentHandler.h +1 -1
  542. data/ext/nginx/config +13 -9
  543. data/ext/nginx/ngx_http_passenger_module.c +3 -7
  544. data/ext/oxt/detail/backtrace_enabled.hpp +5 -102
  545. data/ext/oxt/detail/context.hpp +90 -0
  546. data/ext/oxt/detail/spin_lock_darwin.hpp +4 -0
  547. data/ext/oxt/detail/spin_lock_gcc_x86.hpp +4 -0
  548. data/ext/oxt/detail/spin_lock_pthreads.hpp +14 -0
  549. data/ext/oxt/detail/tracable_exception_enabled.hpp +2 -2
  550. data/ext/oxt/dynamic_thread_group.hpp +27 -1
  551. data/ext/oxt/implementation.cpp +415 -0
  552. data/ext/oxt/{thread.cpp → initialize.hpp} +13 -6
  553. data/ext/oxt/macros.hpp +32 -1
  554. data/ext/oxt/spin_lock.hpp +6 -11
  555. data/ext/oxt/system_calls.cpp +204 -16
  556. data/ext/oxt/system_calls.hpp +85 -45
  557. data/ext/oxt/thread.hpp +13 -117
  558. data/ext/ruby/passenger_native_support.c +82 -237
  559. data/helper-scripts/backtrace-sanitizer.rb +114 -0
  560. data/helper-scripts/classic-rails-loader.rb +135 -0
  561. data/helper-scripts/classic-rails-preloader.rb +161 -0
  562. data/helper-scripts/node-loader.js +314 -0
  563. data/helper-scripts/rack-loader.rb +104 -0
  564. data/helper-scripts/rack-preloader.rb +132 -0
  565. data/helper-scripts/wsgi-loader.py +231 -0
  566. data/helper-scripts/wsgi-preloader.py +1 -0
  567. data/lib/phusion_passenger.rb +159 -61
  568. data/lib/phusion_passenger/abstract_installer.rb +182 -87
  569. data/lib/phusion_passenger/admin_tools/server_instance.rb +25 -19
  570. data/lib/phusion_passenger/analytics_logger.rb +5 -4
  571. data/lib/phusion_passenger/classic_rails/{request_handler.rb → thread_handler_extension.rb} +4 -40
  572. data/lib/phusion_passenger/classic_rails_extensions/init.rb +5 -3
  573. data/lib/phusion_passenger/common_library.rb +441 -0
  574. data/lib/phusion_passenger/console_text_template.rb +4 -16
  575. data/lib/phusion_passenger/constants.rb +1 -8
  576. data/lib/phusion_passenger/debug_logging.rb +5 -2
  577. data/lib/phusion_passenger/dependencies.rb +51 -13
  578. data/lib/phusion_passenger/loader_shared_helpers.rb +318 -0
  579. data/lib/phusion_passenger/message_channel.rb +3 -47
  580. data/lib/phusion_passenger/message_client.rb +2 -2
  581. data/lib/phusion_passenger/native_support.rb +36 -15
  582. data/lib/phusion_passenger/packaging.rb +8 -11
  583. data/lib/phusion_passenger/platform_info.rb +25 -17
  584. data/lib/phusion_passenger/platform_info/apache.rb +10 -7
  585. data/lib/phusion_passenger/platform_info/binary_compatibility.rb +10 -30
  586. data/lib/phusion_passenger/platform_info/compiler.rb +93 -34
  587. data/lib/phusion_passenger/platform_info/ruby.rb +37 -97
  588. data/lib/phusion_passenger/preloader_shared_helpers.rb +121 -0
  589. data/lib/phusion_passenger/public_api.rb +1 -4
  590. data/lib/phusion_passenger/rack/{request_handler.rb → thread_handler_extension.rb} +14 -63
  591. data/lib/phusion_passenger/rails3_extensions/init.rb +9 -8
  592. data/lib/phusion_passenger/request_handler.rb +500 -0
  593. data/lib/phusion_passenger/request_handler/thread_handler.rb +360 -0
  594. data/lib/phusion_passenger/ruby_core_enhancements.rb +142 -0
  595. data/lib/phusion_passenger/standalone/command.rb +36 -15
  596. data/lib/phusion_passenger/standalone/package_runtime_command.rb +16 -8
  597. data/lib/phusion_passenger/standalone/runtime_installer.rb +169 -72
  598. data/lib/phusion_passenger/standalone/start_command.rb +44 -39
  599. data/lib/phusion_passenger/standalone/utils.rb +5 -5
  600. data/lib/phusion_passenger/utils.rb +35 -914
  601. data/lib/phusion_passenger/utils/ansi_colors.rb +59 -0
  602. data/lib/phusion_passenger/utils/file_system_watcher.rb +1 -1
  603. data/lib/phusion_passenger/utils/robust_interruption.rb +134 -0
  604. data/lib/phusion_passenger/utils/tee_input.rb +174 -0
  605. data/lib/phusion_passenger/utils/tmpio.rb +33 -0
  606. data/lib/phusion_passenger/utils/unseekable_socket.rb +6 -0
  607. data/resources/mime.types +5 -1
  608. data/{lib/phusion_passenger/templates → resources}/standalone_default_root/index.html +0 -0
  609. data/{lib/phusion_passenger → resources}/templates/apache2/apache_must_be_compiled_with_compatible_mpm.txt.erb +0 -0
  610. data/{lib/phusion_passenger → resources}/templates/apache2/config_snippets.txt.erb +0 -0
  611. data/{lib/phusion_passenger → resources}/templates/apache2/deployment_example.txt.erb +0 -0
  612. data/{lib/phusion_passenger → resources}/templates/apache2/no_write_permission_to_passenger_root.txt.erb +0 -0
  613. data/{lib/phusion_passenger → resources}/templates/apache2/possible_solutions_for_compilation_and_installation_problems.txt.erb +0 -0
  614. data/{lib/phusion_passenger → resources}/templates/apache2/run_installer_as_root.txt.erb +0 -0
  615. data/{lib/phusion_passenger → resources}/templates/apache2/welcome.txt.erb +0 -0
  616. data/{lib/phusion_passenger → resources}/templates/error_layout.css +6 -0
  617. data/resources/templates/error_layout.html.template +89 -0
  618. data/resources/templates/general_error.html.template +1 -0
  619. data/resources/templates/general_error_with_html.html.template +1 -0
  620. data/{lib/phusion_passenger → resources}/templates/nginx/ask_for_extra_configure_flags.txt.erb +0 -0
  621. data/{lib/phusion_passenger → resources}/templates/nginx/cannot_write_to_dir.txt.erb +0 -0
  622. data/{lib/phusion_passenger → resources}/templates/nginx/config_snippets.txt.erb +0 -0
  623. data/{lib/phusion_passenger → resources}/templates/nginx/config_snippets_inserted.txt.erb +0 -0
  624. data/{lib/phusion_passenger → resources}/templates/nginx/confirm_extra_configure_flags.txt.erb +0 -0
  625. data/{lib/phusion_passenger → resources}/templates/nginx/deployment_example.txt.erb +0 -0
  626. data/resources/templates/nginx/not_available_when_natively_packaged.txt.erb +8 -0
  627. data/{lib/phusion_passenger → resources}/templates/nginx/pcre_could_not_be_downloaded.txt.erb +0 -0
  628. data/{lib/phusion_passenger → resources}/templates/nginx/pcre_could_not_be_extracted.txt.erb +0 -0
  629. data/{lib/phusion_passenger → resources}/templates/nginx/possible_solutions_for_compilation_and_installation_problems.txt.erb +0 -0
  630. data/{lib/phusion_passenger → resources}/templates/nginx/possible_solutions_for_download_and_extraction_problems.txt.erb +0 -0
  631. data/{lib/phusion_passenger → resources}/templates/nginx/query_download_and_install.txt.erb +0 -0
  632. data/{lib/phusion_passenger → resources}/templates/nginx/run_installer_as_root.txt.erb +0 -0
  633. data/{lib/phusion_passenger → resources}/templates/nginx/welcome.txt.erb +0 -0
  634. data/{lib/phusion_passenger → resources}/templates/standalone/cannot_write_to_dir.txt.erb +0 -0
  635. data/{lib/phusion_passenger → resources}/templates/standalone/config.erb +26 -5
  636. data/{lib/phusion_passenger → resources}/templates/standalone/possible_solutions_for_download_and_extraction_problems.txt.erb +0 -0
  637. data/{lib/phusion_passenger → resources}/templates/standalone/run_installer_as_root.txt.erb +0 -0
  638. data/{lib/phusion_passenger → resources}/templates/standalone/welcome.txt.erb +0 -0
  639. data/resources/templates/undisclosed_error.html.template +25 -0
  640. data/test/config.json.example +42 -0
  641. data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +86 -0
  642. data/test/cxx/ApplicationPool2/OptionsTest.cpp +44 -0
  643. data/test/cxx/ApplicationPool2/PoolTest.cpp +1234 -0
  644. data/test/cxx/ApplicationPool2/ProcessTest.cpp +131 -0
  645. data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +229 -0
  646. data/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +744 -0
  647. data/test/cxx/BufferedIOTest.cpp +7 -7
  648. data/test/cxx/CxxTestMain.cpp +65 -2
  649. data/test/cxx/FileBackedPipeTest.cpp +626 -0
  650. data/test/cxx/FileChangeCheckerTest.cpp +20 -18
  651. data/test/cxx/FilterSupportTest.cpp +5 -5
  652. data/test/cxx/IOUtilsTest.cpp +11 -4
  653. data/test/cxx/MessageReadersWritersTest.cpp +1 -1
  654. data/test/cxx/MessageServerTest.cpp +31 -30
  655. data/test/cxx/RequestHandlerTest.cpp +777 -0
  656. data/test/cxx/ScgiRequestParserTest.cpp +36 -16
  657. data/test/cxx/ServerInstanceDirTest.cpp +1 -1
  658. data/test/cxx/StringMapTest.cpp +61 -0
  659. data/test/cxx/TemplateTest.cpp +118 -0
  660. data/test/cxx/TestSupport.cpp +25 -68
  661. data/test/cxx/TestSupport.h +81 -41
  662. data/test/cxx/{LoggingTest.cpp → UnionStationTest.cpp} +79 -74
  663. data/test/cxx/UtilsTest.cpp +59 -5
  664. data/test/integration_tests/apache2_tests.rb +2 -2
  665. data/test/integration_tests/nginx_tests.rb +1 -1
  666. data/test/integration_tests/spec_helper.rb +7 -5
  667. data/test/oxt/oxt_test_main.cpp +2 -0
  668. data/test/oxt/syscall_interruption_test.cpp +1 -0
  669. data/test/ruby/classic_rails/loader_spec.rb +48 -0
  670. data/test/ruby/classic_rails/preloader_spec.rb +54 -0
  671. data/test/ruby/rack/loader_spec.rb +62 -0
  672. data/test/ruby/rack/preloader_spec.rb +74 -0
  673. data/test/ruby/{abstract_request_handler_spec.rb → request_handler_spec.rb} +31 -68
  674. data/test/ruby/shared/loader_spec.rb +241 -0
  675. data/test/ruby/shared/rails/analytics_logging_extensions_spec.rb +141 -182
  676. data/test/ruby/shared/ruby_loader_spec.rb +55 -0
  677. data/test/ruby/spec_helper.rb +8 -53
  678. data/test/ruby/utils/file_system_watcher_spec.rb +9 -1
  679. data/test/ruby/utils_spec.rb +10 -683
  680. data/test/stub/rack/config.ru +28 -3
  681. data/test/stub/rack/start.rb +47 -0
  682. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/Rakefile +0 -0
  683. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/controllers/application_controller.rb +0 -2
  684. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/controllers/bar_controller_1.rb +0 -0
  685. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/controllers/bar_controller_2.rb +0 -0
  686. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/controllers/foo_controller.rb +0 -0
  687. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/app/helpers/application_helper.rb +0 -0
  688. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/boot.rb +0 -0
  689. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/database.yml +3 -3
  690. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/environment.rb +5 -2
  691. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/environments/development.rb +0 -0
  692. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/environments/production.rb +0 -0
  693. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/environments/staging.rb +0 -0
  694. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/initializers/inflections.rb +0 -0
  695. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/initializers/mime_types.rb +0 -0
  696. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/config/routes.rb +1 -0
  697. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/about +0 -0
  698. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/console +0 -0
  699. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/dbconsole +0 -0
  700. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/destroy +0 -0
  701. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/generate +0 -0
  702. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/performance/benchmarker +0 -0
  703. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/performance/profiler +0 -0
  704. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/performance/request +0 -0
  705. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/plugin +0 -0
  706. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/process/inspector +0 -0
  707. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/process/reaper +0 -0
  708. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/process/spawner +0 -0
  709. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/runner +0 -0
  710. data/test/stub/{rails_apps/2.3/foobar → rails2.3}/script/server +0 -0
  711. data/test/stub/{rails_apps/3.0/empty → rails3.0}/Gemfile +0 -0
  712. data/test/stub/rails3.0/Gemfile.lock +80 -0
  713. data/test/stub/{rails_apps/3.0/empty → rails3.0}/Rakefile +0 -0
  714. data/test/stub/{rails_apps/3.0/empty → rails3.0}/app/controllers/application_controller.rb +0 -0
  715. data/test/stub/{rails_apps/3.0/empty → rails3.0}/app/helpers/application_helper.rb +0 -0
  716. data/test/stub/{rails_apps/3.0/empty → rails3.0}/app/views/layouts/application.html.erb +0 -0
  717. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config.ru +0 -0
  718. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/application.rb +0 -0
  719. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/boot.rb +0 -0
  720. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/database.yml +0 -0
  721. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/environment.rb +0 -0
  722. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/environments/development.rb +0 -0
  723. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/environments/production.rb +0 -0
  724. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/environments/test.rb +0 -0
  725. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/backtrace_silencers.rb +0 -0
  726. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/inflections.rb +0 -0
  727. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/mime_types.rb +0 -0
  728. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/passenger.rb +0 -0
  729. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/secret_token.rb +0 -0
  730. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/initializers/session_store.rb +0 -0
  731. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/locales/en.yml +0 -0
  732. data/test/stub/{rails_apps/3.0/empty → rails3.0}/config/routes.rb +0 -0
  733. data/test/stub/{rails_apps/3.0/empty → rails3.0}/db/seeds.rb +0 -0
  734. data/test/stub/{rails_apps/3.0/empty → rails3.0}/doc/README_FOR_APP +0 -0
  735. data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/404.html +0 -0
  736. data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/422.html +0 -0
  737. data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/500.html +0 -0
  738. data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/favicon.ico +0 -0
  739. data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/index.html +0 -0
  740. data/test/stub/{rails_apps/3.0/empty → rails3.0}/public/robots.txt +0 -0
  741. data/test/stub/{rails_apps/3.0/empty → rails3.0}/script/rails +0 -0
  742. data/test/stub/{rails_apps/3.0/empty → rails3.0}/test/performance/browsing_test.rb +0 -0
  743. data/test/stub/{rails_apps/3.0/empty → rails3.0}/test/test_helper.rb +0 -0
  744. data/test/stub/start_error.pl +24 -0
  745. data/test/stub/wsgi/passenger_wsgi.py +71 -3
  746. data/test/support/apache2_controller.rb +2 -2
  747. data/test/support/placebo-preloader.rb +88 -0
  748. data/test/support/test_helper.rb +1 -14
  749. data/test/tut/tut.h +11 -4
  750. metadata +590 -326
  751. data.tar.gz.asc +0 -12
  752. data/PACKAGING.TXT +0 -25
  753. data/build/config.rb +0 -46
  754. data/ext/apache2/HelperAgent.cpp +0 -364
  755. data/ext/boost/call_traits.hpp +0 -24
  756. data/ext/boost/detail/call_traits.hpp +0 -164
  757. data/ext/common/AbstractSpawnManager.h +0 -110
  758. data/ext/common/AgentBase.cpp +0 -432
  759. data/ext/common/ApplicationPool/Client.h +0 -788
  760. data/ext/common/ApplicationPool/Interface.h +0 -295
  761. data/ext/common/ApplicationPool/Pool.h +0 -1327
  762. data/ext/common/ApplicationPool/Server.h +0 -479
  763. data/ext/common/MessageChannel.h +0 -494
  764. data/ext/common/PoolOptions.h +0 -518
  765. data/ext/common/Process.h +0 -253
  766. data/ext/common/Session.h +0 -436
  767. data/ext/common/SpawnManager.h +0 -611
  768. data/ext/google/ChangeLog +0 -167
  769. data/ext/google/dense_hash_map +0 -310
  770. data/ext/google/dense_hash_set +0 -287
  771. data/ext/google/sparse_hash_map +0 -294
  772. data/ext/google/sparse_hash_set +0 -275
  773. data/ext/google/sparsehash/densehashtable.h +0 -1062
  774. data/ext/google/sparsehash/sparseconfig.h +0 -55
  775. data/ext/google/sparsehash/sparsehashtable.h +0 -1015
  776. data/ext/google/sparsetable +0 -1468
  777. data/ext/google/type_traits.h +0 -250
  778. data/ext/nginx/HelperAgent.cpp +0 -1355
  779. data/ext/nginx/ScgiRequestParser.h +0 -375
  780. data/ext/oxt/backtrace.cpp +0 -185
  781. data/ext/oxt/tracable_exception.cpp +0 -89
  782. data/helper-scripts/passenger-spawn-server +0 -106
  783. data/lib/phusion_passenger/abstract_request_handler.rb +0 -766
  784. data/lib/phusion_passenger/abstract_server.rb +0 -372
  785. data/lib/phusion_passenger/abstract_server_collection.rb +0 -335
  786. data/lib/phusion_passenger/app_process.rb +0 -174
  787. data/lib/phusion_passenger/classic_rails/application_spawner.rb +0 -344
  788. data/lib/phusion_passenger/classic_rails/framework_spawner.rb +0 -311
  789. data/lib/phusion_passenger/exceptions.rb +0 -103
  790. data/lib/phusion_passenger/html_template.rb +0 -107
  791. data/lib/phusion_passenger/rack/application_spawner.rb +0 -231
  792. data/lib/phusion_passenger/spawn_manager.rb +0 -359
  793. data/lib/phusion_passenger/templates/app_exited_during_initialization.html.erb +0 -38
  794. data/lib/phusion_passenger/templates/app_init_error.html.erb +0 -64
  795. data/lib/phusion_passenger/templates/database_error.html.erb +0 -66
  796. data/lib/phusion_passenger/templates/error_layout.html.erb +0 -39
  797. data/lib/phusion_passenger/templates/framework_init_error.html.erb +0 -39
  798. data/lib/phusion_passenger/templates/general_error.html.erb +0 -22
  799. data/lib/phusion_passenger/templates/load_error.html.erb +0 -46
  800. data/lib/phusion_passenger/templates/version_not_found.html.erb +0 -34
  801. data/lib/phusion_passenger/utils/rewindable_input.rb +0 -125
  802. data/lib/phusion_passenger/wsgi/application_spawner.rb +0 -108
  803. data/test/config.yml.example +0 -41
  804. data/test/cxx/ApplicationPool_PoolTest.cpp +0 -33
  805. data/test/cxx/ApplicationPool_PoolTestCases.cpp +0 -1029
  806. data/test/cxx/ApplicationPool_ServerTest.cpp +0 -308
  807. data/test/cxx/ApplicationPool_Server_PoolTest.cpp +0 -80
  808. data/test/cxx/MessageChannelTest.cpp +0 -557
  809. data/test/cxx/PoolOptionsTest.cpp +0 -116
  810. data/test/cxx/SpawnManagerTest.cpp +0 -161
  811. data/test/ruby/abstract_server_collection_spec.rb +0 -247
  812. data/test/ruby/abstract_server_spec.rb +0 -61
  813. data/test/ruby/app_process_spec.rb +0 -43
  814. data/test/ruby/classic_rails/application_spawner_spec.rb +0 -89
  815. data/test/ruby/classic_rails/framework_spawner_spec.rb +0 -92
  816. data/test/ruby/rack/application_spawner_spec.rb +0 -116
  817. data/test/ruby/shared/abstract_server_spec.rb +0 -23
  818. data/test/ruby/shared/spawners/classic_rails/framework_spawner_spec.rb +0 -38
  819. data/test/ruby/shared/spawners/classic_rails/lack_of_rails_gem_version_spec.rb +0 -19
  820. data/test/ruby/shared/spawners/classic_rails/spawner_spec.rb +0 -15
  821. data/test/ruby/shared/spawners/non_preloading_spawner_spec.rb +0 -27
  822. data/test/ruby/shared/spawners/preloading_spawner_spec.rb +0 -29
  823. data/test/ruby/shared/spawners/reload_all_spec.rb +0 -36
  824. data/test/ruby/shared/spawners/reload_single_spec.rb +0 -52
  825. data/test/ruby/shared/spawners/spawn_server_spec.rb +0 -28
  826. data/test/ruby/shared/spawners/spawner_spec.rb +0 -273
  827. data/test/ruby/shared/utils/pseudo_io_spec.rb +0 -60
  828. data/test/ruby/spawn_manager_spec.rb +0 -134
  829. data/test/ruby/wsgi/application_spawner_spec.rb +0 -50
  830. data/test/stub/message_channel.rb +0 -11
  831. data/test/stub/message_channel_2.rb +0 -12
  832. data/test/stub/message_channel_3.rb +0 -19
  833. data/test/stub/rails_apps/3.0/empty/Gemfile.lock +0 -73
  834. data/test/stub/spawn_server.rb +0 -22
  835. metadata.gz.asc +0 -12
@@ -0,0 +1,158 @@
1
+ /*
2
+ * Phusion Passenger - http://www.modrails.com/
3
+ * Copyright (c) 2011, 2012 Phusion
4
+ *
5
+ * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ */
25
+ #ifndef _PASSENGER_APPLICATION_POOL_SESSION_H_
26
+ #define _PASSENGER_APPLICATION_POOL_SESSION_H_
27
+
28
+ #include <sys/types.h>
29
+ #include <boost/shared_ptr.hpp>
30
+ #include <oxt/macros.hpp>
31
+ #include <oxt/system_calls.hpp>
32
+ #include <oxt/backtrace.hpp>
33
+ #include <ApplicationPool2/Common.h>
34
+ #include <ApplicationPool2/Socket.h>
35
+ #include <FileDescriptor.h>
36
+ #include <Utils/ScopeGuard.h>
37
+
38
+ namespace Passenger {
39
+ namespace ApplicationPool2 {
40
+
41
+ using namespace oxt;
42
+
43
+
44
+ /**
45
+ * Represents a communication session with a process. A communication session
46
+ * within Phusion Passenger is usually a single request + response but the API
47
+ * allows arbitrary I/O. See Process's class overview for normal usage of Session.
48
+ *
49
+ * Not thread-safe, but Pool's and Process's API encourage that
50
+ * a Session is only used by 1 thread and then thrown away.
51
+ */
52
+ class Session {
53
+ public:
54
+ typedef void (*Callback)(Session *session);
55
+
56
+ private:
57
+ /** For keeping the OS process alive until all of a Process's sessions are closed. */
58
+ ProcessPtr process;
59
+ /** Socket to use for this session. Guaranteed to be alive thanks to the 'process' reference. */
60
+ Socket *socket;
61
+
62
+ Connection connection;
63
+ FileDescriptor theFd;
64
+ bool closed;
65
+
66
+ void deinitiate(bool success) {
67
+ connection.fail = !success;
68
+ socket->checkinConnection(connection);
69
+ connection.fd = -1;
70
+ theFd = FileDescriptor();
71
+ }
72
+
73
+ void callOnInitiateFailure() {
74
+ if (OXT_LIKELY(onInitiateFailure != NULL)) {
75
+ onInitiateFailure(this);
76
+ }
77
+ }
78
+
79
+ void callOnClose() {
80
+ if (OXT_LIKELY(onClose != NULL)) {
81
+ onClose(this);
82
+ }
83
+ closed = true;
84
+ }
85
+
86
+ public:
87
+ Callback onInitiateFailure;
88
+ Callback onClose;
89
+
90
+ Session(const ProcessPtr &process, Socket *socket) {
91
+ this->process = process;
92
+ this->socket = socket;
93
+ closed = false;
94
+ onInitiateFailure = NULL;
95
+ onClose = NULL;
96
+ }
97
+
98
+ ~Session() {
99
+ TRACE_POINT();
100
+ // If user doesn't close() explicitly, we penalize performance.
101
+ if (OXT_LIKELY(initiated())) {
102
+ deinitiate(false);
103
+ }
104
+ if (OXT_LIKELY(!closed)) {
105
+ callOnClose();
106
+ }
107
+ }
108
+
109
+ const string &getConnectPassword() const;
110
+ pid_t getPid() const;
111
+ const string &getGupid() const;
112
+
113
+ ProcessPtr getProcess() const {
114
+ return process;
115
+ }
116
+
117
+ Socket *getSocket() const {
118
+ return socket;
119
+ }
120
+
121
+ const string &getProtocol() const {
122
+ return socket->protocol;
123
+ }
124
+
125
+ void initiate() {
126
+ assert(!closed);
127
+ ScopeGuard g(boost::bind(&Session::callOnInitiateFailure, this));
128
+ connection = socket->checkoutConnection();
129
+ connection.fail = true;
130
+ theFd = FileDescriptor(connection.fd, false);
131
+ g.clear();
132
+ }
133
+
134
+ bool initiated() const {
135
+ return connection.fd != -1;
136
+ }
137
+
138
+ const FileDescriptor &fd() const {
139
+ return theFd;
140
+ }
141
+
142
+ void close(bool success) {
143
+ if (OXT_LIKELY(initiated())) {
144
+ deinitiate(success);
145
+ }
146
+ if (OXT_LIKELY(!closed)) {
147
+ callOnClose();
148
+ }
149
+ }
150
+ };
151
+
152
+ typedef shared_ptr<Session> SessionPtr;
153
+
154
+
155
+ } // namespace ApplicationPool2
156
+ } // namespace Passenger
157
+
158
+ #endif /* _PASSENGER_APPLICATION_POOL2_SESSION_H_ */
@@ -0,0 +1,246 @@
1
+ /*
2
+ * Phusion Passenger - http://www.modrails.com/
3
+ * Copyright (c) 2011 Phusion
4
+ *
5
+ * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ */
25
+ #ifndef _PASSENGER_APPLICATION_POOL_SOCKET_H_
26
+ #define _PASSENGER_APPLICATION_POOL_SOCKET_H_
27
+
28
+ #include <string>
29
+ #include <vector>
30
+ #include <boost/thread.hpp>
31
+ #include <boost/shared_ptr.hpp>
32
+ #include <boost/weak_ptr.hpp>
33
+ #include <climits>
34
+ #include <cassert>
35
+ #include <Logging.h>
36
+ #include <Utils/PriorityQueue.h>
37
+ #include <Utils/IOUtils.h>
38
+
39
+ namespace Passenger {
40
+ namespace ApplicationPool2 {
41
+
42
+ using namespace std;
43
+ using namespace boost;
44
+
45
+
46
+ class Process;
47
+
48
+ struct Connection {
49
+ int fd;
50
+ bool persistent: 1;
51
+ bool fail: 1;
52
+
53
+ Connection() {
54
+ fd = -1;
55
+ persistent = false;
56
+ fail = false;
57
+ }
58
+
59
+ void close() {
60
+ if (fd != -1) {
61
+ int fd2 = fd;
62
+ fd = -1;
63
+ persistent = false;
64
+ safelyClose(fd2);
65
+ }
66
+ }
67
+ };
68
+
69
+ /**
70
+ * Not thread-safe except for the connection pooling methods, so only use
71
+ * within the ApplicationPool lock.
72
+ */
73
+ class Socket {
74
+ private:
75
+ boost::mutex connectionPoolLock;
76
+ int totalConnections;
77
+ vector<Connection> idleConnections;
78
+
79
+ int connectionPoolLimit() const {
80
+ return concurrency;
81
+ }
82
+
83
+ Connection connect() const {
84
+ Connection connection;
85
+ P_TRACE(3, "Connecting to " << address);
86
+ connection.fd = connectToServer(address);
87
+ return connection;
88
+ }
89
+
90
+ public:
91
+ // Read-only.
92
+ string name;
93
+ string address;
94
+ string protocol;
95
+ int concurrency;
96
+
97
+ /** The handle inside the associated Process's 'sessionSockets' priority queue.
98
+ * Guaranteed to be valid as long as the Process is alive.
99
+ */
100
+ PriorityQueue<Socket>::Handle pqHandle;
101
+
102
+ /** Invariant: sessions >= 0 */
103
+ int sessions;
104
+
105
+ Socket()
106
+ : concurrency(0)
107
+ { }
108
+
109
+ Socket(const string &_name, const string &_address, const string &_protocol, int _concurrency)
110
+ : totalConnections(0),
111
+ name(_name),
112
+ address(_address),
113
+ protocol(_protocol),
114
+ concurrency(_concurrency),
115
+ sessions(0)
116
+ { }
117
+
118
+ Socket(const Socket &other)
119
+ : totalConnections(other.totalConnections),
120
+ idleConnections(other.idleConnections),
121
+ name(other.name),
122
+ address(other.address),
123
+ protocol(other.protocol),
124
+ concurrency(other.concurrency),
125
+ pqHandle(other.pqHandle),
126
+ sessions(other.sessions)
127
+ { }
128
+
129
+ Socket &operator=(const Socket &other) {
130
+ totalConnections = other.totalConnections;
131
+ idleConnections = other.idleConnections;
132
+ name = other.name;
133
+ address = other.address;
134
+ protocol = other.protocol;
135
+ concurrency = other.concurrency;
136
+ pqHandle = other.pqHandle;
137
+ sessions = other.sessions;
138
+ return *this;
139
+ }
140
+
141
+ /**
142
+ * Connect to this socket or reuse an existing connection.
143
+ *
144
+ * One MUST call checkinConnection() when one's done using the Connection.
145
+ * Failure to do so will result in a resource leak.
146
+ */
147
+ Connection checkoutConnection() {
148
+ lock_guard<boost::mutex> l(connectionPoolLock);
149
+
150
+ if (!idleConnections.empty()) {
151
+ Connection connection = idleConnections.back();
152
+ idleConnections.pop_back();
153
+ return connection;
154
+ } else if (totalConnections < connectionPoolLimit()) {
155
+ Connection connection = connect();
156
+ connection.persistent = true;
157
+ totalConnections++;
158
+ return connection;
159
+ } else {
160
+ return connect();
161
+ }
162
+ }
163
+
164
+ void checkinConnection(Connection connection) {
165
+ unique_lock<boost::mutex> l(connectionPoolLock);
166
+
167
+ if (connection.persistent) {
168
+ if (connection.fail) {
169
+ totalConnections--;
170
+ l.unlock();
171
+ connection.close();
172
+ } else {
173
+ idleConnections.push_back(connection);
174
+ }
175
+ } else {
176
+ l.unlock();
177
+ connection.close();
178
+ }
179
+ }
180
+
181
+
182
+ bool idle() const {
183
+ return sessions == 0;
184
+ }
185
+
186
+ int utilization() const {
187
+ /* Different sockets within a Process may have different
188
+ * 'concurrency' values. We want:
189
+ * - Process.sessionSockets to sort the sockets from least used to most used.
190
+ * - to give sockets with concurrency == 0 more priority over sockets
191
+ * with concurrency > 0.
192
+ * Therefore, we describe our utilization as a percentage of 'concurrency', with
193
+ * the percentage value in [0..INT_MAX] instead of [0..1].
194
+ */
195
+ if (concurrency == 0) {
196
+ // Allows Process.sessionSockets to give
197
+ // idle sockets more priority.
198
+ if (sessions == 0) {
199
+ return 0;
200
+ } else {
201
+ return 1;
202
+ }
203
+ } else {
204
+ return (int) (((long long) sessions * INT_MAX) / (double) concurrency);
205
+ }
206
+ }
207
+
208
+ bool atFullCapacity() const {
209
+ return concurrency != 0 && sessions >= concurrency;
210
+ }
211
+ };
212
+
213
+ class SocketList: public vector<Socket> {
214
+ public:
215
+ void add(const string &name, const string &address, const string &protocol, int concurrency) {
216
+ push_back(Socket(name, address, protocol, concurrency));
217
+ }
218
+
219
+ const Socket *findSocketWithName(const StaticString &name) const {
220
+ const_iterator it, end = this->end();
221
+ for (it = begin(); it != end; it++) {
222
+ if (it->name == name) {
223
+ return &(*it);
224
+ }
225
+ }
226
+ return NULL;
227
+ }
228
+
229
+ bool hasSessionSockets() const {
230
+ const_iterator it;
231
+ for (it = begin(); it != end(); it++) {
232
+ if (it->protocol == "session" || it->protocol == "http_session") {
233
+ return true;
234
+ }
235
+ }
236
+ return false;
237
+ }
238
+ };
239
+
240
+ typedef shared_ptr<SocketList> SocketListPtr;
241
+
242
+
243
+ } // namespace ApplicationPool2
244
+ } // namespace Pasenger
245
+
246
+ #endif /* _PASSENGER_APPLICATION_POOL2_SOCKET_H_ */
@@ -0,0 +1,2212 @@
1
+ /*
2
+ * Phusion Passenger - http://www.modrails.com/
3
+ * Copyright (c) 2011, 2012 Phusion
4
+ *
5
+ * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ */
25
+ #ifndef _PASSENGER_APPLICATION_POOL_SPAWNER_H_
26
+ #define _PASSENGER_APPLICATION_POOL_SPAWNER_H_
27
+
28
+ /*
29
+ * This file implements application spawning support. Several classes
30
+ * are provided which all implement the Spawner interface. The spawn()
31
+ * method spawns an application process based on the given options
32
+ * and returns a Process object which contains information about the
33
+ * spawned process.
34
+ *
35
+ * The DirectSpawner class spawns application processes directly.
36
+ *
37
+ * The SmartSpawner class spawns application processes through a
38
+ * preloader process. The preloader process loads the application
39
+ * code into its address space and then listens on a socket for spawn
40
+ * commands. Upon receiving a spawn command, it will fork() itself.
41
+ * This makes spawning multiple application processes much faster.
42
+ * Note that a single SmartSpawner instance is only usable for a
43
+ * single application.
44
+ *
45
+ * DummySpawner doesn't do anything. It returns dummy Process objects.
46
+ *
47
+ * DirectSpawner, SmartSpawner and DummySpawner all implement the Spawner interface.
48
+ *
49
+ * SpawnerFactory is a convenience class which takes an Options objects
50
+ * and figures out, based on options.spawnMethod, whether to create
51
+ * a DirectSpawner or a SmartSpawner. In case of the smart spawning
52
+ * method, SpawnerFactory also automatically figures out which preloader
53
+ * to use based on options.appType.
54
+ */
55
+
56
+ #include <string>
57
+ #include <map>
58
+ #include <vector>
59
+ #include <utility>
60
+ #include <boost/make_shared.hpp>
61
+ #include <boost/shared_array.hpp>
62
+ #include <boost/bind.hpp>
63
+ #include <oxt/system_calls.hpp>
64
+ #include <oxt/backtrace.hpp>
65
+ #include <sys/types.h>
66
+ #include <cstdio>
67
+ #include <cstdlib>
68
+ #include <cstring>
69
+ #include <cerrno>
70
+ #include <cassert>
71
+ #include <unistd.h>
72
+ #include <pthread.h>
73
+ #include <limits.h> // for PTHREAD_STACK_MIN
74
+ #include <pwd.h>
75
+ #include <grp.h>
76
+ #include <dirent.h>
77
+ #include <ApplicationPool2/Process.h>
78
+ #include <ApplicationPool2/Options.h>
79
+ #include <ApplicationPool2/PipeWatcher.h>
80
+ #include <FileDescriptor.h>
81
+ #include <SafeLibev.h>
82
+ #include <Exceptions.h>
83
+ #include <ResourceLocator.h>
84
+ #include <StaticString.h>
85
+ #include <ServerInstanceDir.h>
86
+ #include <Utils/BufferedIO.h>
87
+ #include <Utils/ScopeGuard.h>
88
+ #include <Utils/Timer.h>
89
+ #include <Utils/IOUtils.h>
90
+ #include <Utils/StrIntUtils.h>
91
+ #include <Utils/Base64.h>
92
+
93
+ namespace tut {
94
+ struct ApplicationPool2_DirectSpawnerTest;
95
+ struct ApplicationPool2_SmartSpawnerTest;
96
+ }
97
+
98
+ namespace Passenger {
99
+ namespace ApplicationPool2 {
100
+
101
+ using namespace std;
102
+ using namespace boost;
103
+ using namespace oxt;
104
+
105
+
106
+ class Spawner {
107
+ protected:
108
+ friend struct tut::ApplicationPool2_DirectSpawnerTest;
109
+ friend struct tut::ApplicationPool2_SmartSpawnerTest;
110
+
111
+ /**
112
+ * Given a file descriptor, captures its output in a background thread
113
+ * and also forwards it immediately to a target file descriptor.
114
+ * Call stop() to stop the background thread and to obtain the captured
115
+ * output so far.
116
+ */
117
+ class BackgroundIOCapturer {
118
+ private:
119
+ FileDescriptor fd;
120
+ int target;
121
+ boost::mutex dataSyncher;
122
+ string data;
123
+ oxt::thread *thr;
124
+
125
+ void capture() {
126
+ TRACE_POINT();
127
+ while (!this_thread::interruption_requested()) {
128
+ char buf[1024 * 8];
129
+ ssize_t ret;
130
+
131
+ UPDATE_TRACE_POINT();
132
+ ret = syscalls::read(fd, buf, sizeof(buf));
133
+ int e = errno;
134
+ this_thread::disable_syscall_interruption dsi;
135
+ if (ret == 0) {
136
+ break;
137
+ } else if (ret == -1) {
138
+ P_WARN("Background I/O capturer error: " <<
139
+ strerror(e) << " (errno=" << e << ")");
140
+ break;
141
+ } else {
142
+ {
143
+ lock_guard<boost::mutex> l(dataSyncher);
144
+ data.append(buf, ret);
145
+ }
146
+ if (target != -1) {
147
+ UPDATE_TRACE_POINT();
148
+ writeExact(target, buf, ret);
149
+ }
150
+ }
151
+ }
152
+ }
153
+
154
+ public:
155
+ BackgroundIOCapturer(const FileDescriptor &_fd, int _target)
156
+ : fd(_fd),
157
+ target(_target),
158
+ thr(NULL)
159
+ { }
160
+
161
+ ~BackgroundIOCapturer() {
162
+ TRACE_POINT();
163
+ if (thr != NULL) {
164
+ this_thread::disable_interruption di;
165
+ this_thread::disable_syscall_interruption dsi;
166
+ thr->interrupt_and_join();
167
+ delete thr;
168
+ thr = NULL;
169
+ }
170
+ }
171
+
172
+ const FileDescriptor &getFd() const {
173
+ return fd;
174
+ }
175
+
176
+ void start() {
177
+ assert(thr == NULL);
178
+ thr = new oxt::thread(boost::bind(&BackgroundIOCapturer::capture, this),
179
+ "Background I/O capturer", 64 * 1024);
180
+ }
181
+
182
+ string stop() {
183
+ TRACE_POINT();
184
+ assert(thr != NULL);
185
+ this_thread::disable_interruption di;
186
+ this_thread::disable_syscall_interruption dsi;
187
+ thr->interrupt_and_join();
188
+ delete thr;
189
+ thr = NULL;
190
+ lock_guard<boost::mutex> l(dataSyncher);
191
+ return data;
192
+ }
193
+
194
+ void appendToBuffer(const StaticString &dataToAdd) {
195
+ TRACE_POINT();
196
+ lock_guard<boost::mutex> l(dataSyncher);
197
+ data.append(dataToAdd.data(), dataToAdd.size());
198
+ }
199
+ };
200
+
201
+ typedef shared_ptr<BackgroundIOCapturer> BackgroundIOCapturerPtr;
202
+
203
+ /**
204
+ * A temporary directory for spawned child processes to write
205
+ * debugging information to. It is removed after spawning has
206
+ * determined to be successful or failed.
207
+ */
208
+ struct DebugDir {
209
+ string path;
210
+
211
+ DebugDir(uid_t uid, gid_t gid) {
212
+ path = "/tmp/passenger.spawn-debug.";
213
+ path.append(toString(getpid()));
214
+ path.append("-");
215
+ path.append(pointerToIntString(this));
216
+
217
+ if (syscalls::mkdir(path.c_str(), 0700) == -1) {
218
+ int e = errno;
219
+ throw FileSystemException("Cannot create directory '" +
220
+ path + "'", e, path);
221
+ }
222
+ this_thread::disable_interruption di;
223
+ this_thread::disable_syscall_interruption dsi;
224
+ syscalls::chown(path.c_str(), uid, gid);
225
+ }
226
+
227
+ ~DebugDir() {
228
+ removeDirTree(path);
229
+ }
230
+
231
+ const string &getPath() const {
232
+ return path;
233
+ }
234
+
235
+ map<string, string> readAll() {
236
+ map<string, string> result;
237
+ DIR *dir = opendir(path.c_str());
238
+ ScopeGuard guard(boost::bind(closedir, dir));
239
+ struct dirent *ent;
240
+
241
+ while ((ent = readdir(dir)) != NULL) {
242
+ if (ent->d_name[0] != '.') {
243
+ try {
244
+ result.insert(make_pair<string, string>(
245
+ ent->d_name,
246
+ Passenger::readAll(path + "/" + ent->d_name)));
247
+ } catch (const SystemException &) {
248
+ // Do nothing.
249
+ }
250
+ }
251
+ }
252
+ return result;
253
+ }
254
+ };
255
+
256
+ typedef shared_ptr<DebugDir> DebugDirPtr;
257
+
258
+ struct SpawnPreparationInfo {
259
+ // General
260
+
261
+ /** Absolute application root path. */
262
+ string appRoot;
263
+ /** Absolute pre-exec chroot path. If no chroot is configured, then this is "/". */
264
+ string chrootDir;
265
+ /** Absolute application root path inside the chroot. If no chroot is
266
+ * configured then this is is equal to appRoot. */
267
+ string appRootInsideChroot;
268
+ /** A list of all parent directories of the appRoot, as well as appRoot itself.
269
+ * The pre-exec chroot directory is included, and this list goes no futher than that.
270
+ * For example if appRoot is /var/jail/foo/bar/baz and the chroot is /var/jail,
271
+ * then this list contains:
272
+ * /var/jail/foo
273
+ * /var/jail/foo/bar
274
+ * /var/jail/foo/bar/baz
275
+ */
276
+ vector<string> appRootPaths;
277
+ /** Same as appRootPaths, but without the chroot component. For example if
278
+ * appRoot is /var/jail/foo/bar/baz and the chroot is /var/jail, then this list
279
+ * contains:
280
+ * /foo
281
+ * /foo/bar
282
+ * /foo/bar/baz
283
+ */
284
+ vector<string> appRootPathsInsideChroot;
285
+
286
+ // User switching
287
+ bool switchUser;
288
+ string username;
289
+ string groupname;
290
+ string home;
291
+ string shell;
292
+ uid_t uid;
293
+ gid_t gid;
294
+ int ngroups;
295
+ shared_array<gid_t> gidset;
296
+ };
297
+
298
+ /**
299
+ * Structure containing arguments and working state for negotiating
300
+ * the spawning protocol.
301
+ */
302
+ struct NegotiationDetails {
303
+ // Arguments.
304
+ SafeLibevPtr libev;
305
+ BackgroundIOCapturerPtr stderrCapturer;
306
+ pid_t pid;
307
+ FileDescriptor adminSocket;
308
+ FileDescriptor errorPipe;
309
+ const Options *options;
310
+ bool forwardStderr;
311
+ DebugDirPtr debugDir;
312
+
313
+ // Working state.
314
+ BufferedIO io;
315
+ string gupid;
316
+ string connectPassword;
317
+ unsigned long long spawnStartTime;
318
+ unsigned long long timeout;
319
+
320
+ NegotiationDetails() {
321
+ pid = 0;
322
+ options = NULL;
323
+ forwardStderr = false;
324
+ spawnStartTime = 0;
325
+ timeout = 0;
326
+ }
327
+ };
328
+
329
+ /**
330
+ * Structure containing arguments and working state for negotiating
331
+ * the preloader startup protocol.
332
+ */
333
+ struct StartupDetails {
334
+ // Arguments.
335
+ FileDescriptor adminSocket;
336
+ BufferedIO io;
337
+ BackgroundIOCapturerPtr stderrCapturer;
338
+ DebugDirPtr debugDir;
339
+ const Options *options;
340
+ bool forwardStderr;
341
+
342
+ // Working state.
343
+ unsigned long long timeout;
344
+
345
+ StartupDetails() {
346
+ options = NULL;
347
+ forwardStderr = false;
348
+ timeout = 0;
349
+ }
350
+ };
351
+
352
+
353
+ private:
354
+ /**
355
+ * Appends key + "\0" + value + "\0" to 'output'.
356
+ */
357
+ static void appendNullTerminatedKeyValue(string &output, const StaticString &key,
358
+ const StaticString &value)
359
+ {
360
+ unsigned int minCapacity = key.size() + value.size() + 2;
361
+ if (output.capacity() < minCapacity) {
362
+ output.reserve(minCapacity + 1024);
363
+ }
364
+ output.append(key.data(), key.size());
365
+ output.append(1, '\0');
366
+ output.append(value.data(), value.size());
367
+ output.append(1, '\0');
368
+ }
369
+
370
+ void sendSpawnRequest(NegotiationDetails &details) {
371
+ TRACE_POINT();
372
+ try {
373
+ string data = "You have control 1.0\n"
374
+ "passenger_root: " + resourceLocator.getRoot() + "\n"
375
+ "passenger_version: " PASSENGER_VERSION "\n"
376
+ "ruby_libdir: " + resourceLocator.getRubyLibDir() + "\n"
377
+ "generation_dir: " + generation->getPath() + "\n"
378
+ "gupid: " + details.gupid + "\n"
379
+ "connect_password: " + details.connectPassword + "\n";
380
+
381
+ vector<string> args;
382
+ vector<string>::const_iterator it, end;
383
+ details.options->toVector(args, resourceLocator);
384
+ for (it = args.begin(); it != args.end(); it++) {
385
+ const string &key = *it;
386
+ it++;
387
+ const string &value = *it;
388
+ data.append(key + ": " + value + "\n");
389
+ }
390
+
391
+ writeExact(details.adminSocket, data, &details.timeout);
392
+ P_TRACE(2, "Spawn request for " << details.options->appRoot << ":\n" << data);
393
+ writeExact(details.adminSocket, "\n", &details.timeout);
394
+ } catch (const SystemException &e) {
395
+ if (e.code() == EPIPE) {
396
+ /* Ignore this. Process might have written an
397
+ * error response before reading the arguments,
398
+ * in which case we'll want to show that instead.
399
+ */
400
+ } else {
401
+ throw;
402
+ }
403
+ }
404
+ }
405
+
406
+ ProcessPtr handleSpawnResponse(NegotiationDetails &details) {
407
+ TRACE_POINT();
408
+ SocketListPtr sockets = make_shared<SocketList>();
409
+ while (true) {
410
+ string line;
411
+
412
+ try {
413
+ line = readMessageLine(details);
414
+ } catch (const SystemException &e) {
415
+ throwAppSpawnException("An error occurred while starting the "
416
+ "web application. There was an I/O error while reading its "
417
+ "startup response: " + e.sys(),
418
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
419
+ details);
420
+ } catch (const TimeoutException &) {
421
+ throwAppSpawnException("An error occurred while starting the "
422
+ "web application: it did not write a startup response in time.",
423
+ SpawnException::APP_STARTUP_TIMEOUT,
424
+ details);
425
+ }
426
+
427
+ if (line.empty()) {
428
+ throwAppSpawnException("An error occurred while starting the "
429
+ "web application. It unexpected closed the connection while "
430
+ "sending its startup response.",
431
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
432
+ details);
433
+ } else if (line[line.size() - 1] != '\n') {
434
+ throwAppSpawnException("An error occurred while starting the "
435
+ "web application. It sent a line without a newline character "
436
+ "in its startup response.",
437
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
438
+ details);
439
+ } else if (line == "\n") {
440
+ break;
441
+ }
442
+
443
+ string::size_type pos = line.find(": ");
444
+ if (pos == string::npos) {
445
+ throwAppSpawnException("An error occurred while starting the "
446
+ "web application. It sent a startup response line without "
447
+ "separator.",
448
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
449
+ details);
450
+ }
451
+
452
+ string key = line.substr(0, pos);
453
+ string value = line.substr(pos + 2, line.size() - pos - 3);
454
+ if (key == "socket") {
455
+ // socket: <name>;<address>;<protocol>;<concurrency>
456
+ // TODO: in case of TCP sockets, check whether it points to localhost
457
+ // TODO: in case of unix sockets, check whether filename is absolute
458
+ // and whether owner is correct
459
+ vector<string> args;
460
+ split(value, ';', args);
461
+ if (args.size() == 4) {
462
+ sockets->add(args[0],
463
+ fixupSocketAddress(*details.options, args[1]),
464
+ args[2],
465
+ atoi(args[3]));
466
+ } else {
467
+ throwAppSpawnException("An error occurred while starting the "
468
+ "web application. It reported a wrongly formatted 'socket'"
469
+ "response value: '" + value + "'",
470
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
471
+ details);
472
+ }
473
+ } else {
474
+ throwAppSpawnException("An error occurred while starting the "
475
+ "web application. It sent an unknown startup response line "
476
+ "called '" + key + "'.",
477
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
478
+ details);
479
+ }
480
+ }
481
+
482
+ if (sockets->hasSessionSockets() == 0) {
483
+ throwAppSpawnException("An error occured while starting the web "
484
+ "application. It did not advertise any session sockets.",
485
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
486
+ details);
487
+ }
488
+
489
+ return make_shared<Process>(details.libev, details.pid,
490
+ details.gupid, details.connectPassword,
491
+ details.adminSocket, details.errorPipe,
492
+ sockets, creationTime, details.spawnStartTime,
493
+ details.forwardStderr);
494
+ }
495
+
496
+ protected:
497
+ ResourceLocator resourceLocator;
498
+ RandomGeneratorPtr randomGenerator;
499
+ ServerInstanceDir::GenerationPtr generation;
500
+
501
+ static void nonInterruptableKillAndWaitpid(pid_t pid) {
502
+ this_thread::disable_syscall_interruption dsi;
503
+ syscalls::kill(pid, SIGKILL);
504
+ syscalls::waitpid(pid, NULL, 0);
505
+ }
506
+
507
+ /**
508
+ * Behaves like <tt>waitpid(pid, status, WNOHANG)</tt>, but waits at most
509
+ * <em>timeout</em> miliseconds for the process to exit.
510
+ */
511
+ static int timedWaitpid(pid_t pid, int *status, unsigned long long timeout) {
512
+ Timer timer;
513
+ int ret;
514
+
515
+ do {
516
+ ret = syscalls::waitpid(pid, status, WNOHANG);
517
+ if (ret > 0 || ret == -1) {
518
+ return ret;
519
+ } else {
520
+ syscalls::usleep(10000);
521
+ }
522
+ } while (timer.elapsed() < timeout);
523
+ return 0; // timed out
524
+ }
525
+
526
+ static string fixupSocketAddress(const Options &options, const string &address) {
527
+ TRACE_POINT();
528
+ if (!options.preexecChroot.empty() && !options.postexecChroot.empty()) {
529
+ ServerAddressType type = getSocketAddressType(address);
530
+ if (type == SAT_UNIX) {
531
+ string filename = parseUnixSocketAddress(address);
532
+ string fixedAddress = "unix:";
533
+ if (!options.preexecChroot.empty()) {
534
+ fixedAddress.append(options.preexecChroot.data(),
535
+ options.preexecChroot.size());
536
+ }
537
+ if (!options.postexecChroot.empty()) {
538
+ fixedAddress.append(options.postexecChroot.data(),
539
+ options.postexecChroot.size());
540
+ }
541
+ fixedAddress.append(filename);
542
+ return fixedAddress;
543
+ } else {
544
+ return address;
545
+ }
546
+ } else {
547
+ return address;
548
+ }
549
+ }
550
+
551
+ static void checkChrootDirectories(const Options &options) {
552
+ if (!options.preexecChroot.empty()) {
553
+ // TODO: check whether appRoot is a child directory of preexecChroot
554
+ // and whether postexecChroot is a child directory of appRoot.
555
+ }
556
+ }
557
+
558
+ static void createCommandArgs(const vector<string> &command,
559
+ shared_array<const char *> &args)
560
+ {
561
+ args.reset(new const char *[command.size()]);
562
+ for (unsigned int i = 1; i < command.size(); i++) {
563
+ args[i - 1] = command[i].c_str();
564
+ }
565
+ args[command.size() - 1] = NULL;
566
+ }
567
+
568
+ void possiblyRaiseInternalError(const Options &options) {
569
+ if (options.raiseInternalError) {
570
+ throw RuntimeException("An internal error!");
571
+ }
572
+ }
573
+
574
+ void throwAppSpawnException(const string &msg,
575
+ SpawnException::ErrorKind errorKind,
576
+ NegotiationDetails &details)
577
+ {
578
+ TRACE_POINT();
579
+ // Stop the stderr capturing thread and get the captured stderr
580
+ // output so far.
581
+ string stderrOutput;
582
+ if (details.stderrCapturer != NULL) {
583
+ stderrOutput = details.stderrCapturer->stop();
584
+ }
585
+
586
+ // If the exception wasn't due to a timeout, try to capture the
587
+ // remaining stderr output for at most 2 seconds.
588
+ if (errorKind != SpawnException::PRELOADER_STARTUP_TIMEOUT
589
+ && errorKind != SpawnException::APP_STARTUP_TIMEOUT
590
+ && details.stderrCapturer != NULL) {
591
+ bool done = false;
592
+ unsigned long long timeout = 2000;
593
+ while (!done) {
594
+ char buf[1024 * 32];
595
+ unsigned int ret;
596
+
597
+ try {
598
+ ret = readExact(details.stderrCapturer->getFd(), buf,
599
+ sizeof(buf), &timeout);
600
+ if (ret == 0) {
601
+ done = true;
602
+ } else {
603
+ stderrOutput.append(buf, ret);
604
+ }
605
+ } catch (const SystemException &e) {
606
+ P_WARN("Stderr I/O capture error: " << e.what());
607
+ done = true;
608
+ } catch (const TimeoutException &) {
609
+ done = true;
610
+ }
611
+ }
612
+ }
613
+ details.stderrCapturer.reset();
614
+
615
+ // Now throw SpawnException with the captured stderr output
616
+ // as error response.
617
+ SpawnException e(msg, stderrOutput, false, errorKind);
618
+ annotateAppSpawnException(e, details);
619
+ throw e;
620
+ }
621
+
622
+ virtual void annotateAppSpawnException(SpawnException &e, NegotiationDetails &details) {
623
+ if (details.debugDir != NULL) {
624
+ e.addAnnotations(details.debugDir->readAll());
625
+ }
626
+ }
627
+
628
+ template<typename Details>
629
+ static string readMessageLine(Details &details) {
630
+ TRACE_POINT();
631
+ while (true) {
632
+ string result = details.io.readLine(1024 * 4, &details.timeout);
633
+ if (result.empty()) {
634
+ return result;
635
+ } else if (startsWith(result, "!> ")) {
636
+ result.erase(0, sizeof("!> ") - 1);
637
+ return result;
638
+ } else {
639
+ if (details.stderrCapturer != NULL) {
640
+ details.stderrCapturer->appendToBuffer(result);
641
+ }
642
+ if (details.forwardStderr) {
643
+ write(STDOUT_FILENO, result.data(), result.size());
644
+ }
645
+ }
646
+ }
647
+ }
648
+
649
+ SpawnPreparationInfo prepareSpawn(const Options &options) const {
650
+ TRACE_POINT();
651
+ SpawnPreparationInfo info;
652
+ prepareChroot(info, options);
653
+ prepareUserSwitching(info, options);
654
+ prepareSwitchingWorkingDirectory(info, options);
655
+ return info;
656
+ }
657
+
658
+ void prepareChroot(SpawnPreparationInfo &info, const Options &options) const {
659
+ TRACE_POINT();
660
+ info.appRoot = absolutizePath(options.appRoot);
661
+ if (options.preexecChroot.empty()) {
662
+ info.chrootDir = "/";
663
+ } else {
664
+ info.chrootDir = absolutizePath(options.preexecChroot);
665
+ }
666
+ if (info.appRoot != info.chrootDir && startsWith(info.appRoot, info.chrootDir + "/")) {
667
+ throw SpawnException("Invalid configuration: '" + info.chrootDir +
668
+ "' has been configured as the chroot jail, but the application " +
669
+ "root directory '" + info.appRoot + "' is not a subdirectory of the " +
670
+ "chroot directory, which it must be.");
671
+ }
672
+ if (info.appRoot == info.chrootDir) {
673
+ info.appRootInsideChroot = "/";
674
+ } else if (info.chrootDir == "/") {
675
+ info.appRootInsideChroot = info.appRoot;
676
+ } else {
677
+ info.appRootInsideChroot = info.appRoot.substr(info.chrootDir.size());
678
+ }
679
+ }
680
+
681
+ void prepareUserSwitching(SpawnPreparationInfo &info, const Options &options) const {
682
+ TRACE_POINT();
683
+ if (geteuid() != 0) {
684
+ struct passwd *userInfo = getpwuid(geteuid());
685
+ if (userInfo == NULL) {
686
+ throw RuntimeException("Cannot get user database entry for user " +
687
+ getProcessUsername() + "; it looks like your system's " +
688
+ "user database is broken, please fix it.");
689
+ }
690
+ struct group *groupInfo = getgrgid(userInfo->pw_gid);
691
+ if (groupInfo == NULL) {
692
+ throw RuntimeException(string("Cannot get group database entry for ") +
693
+ "the default group belonging to username '" +
694
+ getProcessUsername() + "'; it looks like your system's " +
695
+ "user database is broken, please fix it.");
696
+ }
697
+
698
+ info.switchUser = false;
699
+ info.username = userInfo->pw_name;
700
+ info.groupname = groupInfo->gr_name;
701
+ info.home = userInfo->pw_dir;
702
+ info.shell = userInfo->pw_shell;
703
+ info.uid = geteuid();
704
+ info.gid = getegid();
705
+ info.ngroups = 0;
706
+ return;
707
+ }
708
+
709
+ string defaultGroup;
710
+ string startupFile = absolutizePath(options.getStartupFile(), info.appRoot);
711
+ struct passwd *userInfo = NULL;
712
+ struct group *groupInfo = NULL;
713
+
714
+ if (options.defaultGroup.empty()) {
715
+ struct passwd *info = getpwnam(options.defaultUser.c_str());
716
+ if (info == NULL) {
717
+ throw RuntimeException("Cannot get user database entry for username '" +
718
+ options.defaultUser + "'");
719
+ }
720
+ struct group *group = getgrgid(info->pw_gid);
721
+ if (group == NULL) {
722
+ throw RuntimeException(string("Cannot get group database entry for ") +
723
+ "the default group belonging to username '" +
724
+ options.defaultUser + "'");
725
+ }
726
+ defaultGroup = group->gr_name;
727
+ } else {
728
+ defaultGroup = options.defaultGroup;
729
+ }
730
+
731
+ if (!options.user.empty()) {
732
+ userInfo = getpwnam(options.user.c_str());
733
+ } else {
734
+ struct stat buf;
735
+ if (syscalls::lstat(startupFile.c_str(), &buf) == -1) {
736
+ int e = errno;
737
+ throw SystemException("Cannot lstat(\"" + startupFile +
738
+ "\")", e);
739
+ }
740
+ userInfo = getpwuid(buf.st_uid);
741
+ }
742
+ if (userInfo == NULL || userInfo->pw_uid == 0) {
743
+ userInfo = getpwnam(options.defaultUser.c_str());
744
+ }
745
+
746
+ if (!options.group.empty()) {
747
+ if (options.group == "!STARTUP_FILE!") {
748
+ struct stat buf;
749
+ if (syscalls::lstat(startupFile.c_str(), &buf) == -1) {
750
+ int e = errno;
751
+ throw SystemException("Cannot lstat(\"" +
752
+ startupFile + "\")", e);
753
+ }
754
+ groupInfo = getgrgid(buf.st_gid);
755
+ } else {
756
+ groupInfo = getgrnam(options.group.c_str());
757
+ }
758
+ } else if (userInfo != NULL) {
759
+ groupInfo = getgrgid(userInfo->pw_gid);
760
+ }
761
+ if (groupInfo == NULL || groupInfo->gr_gid == 0) {
762
+ groupInfo = getgrnam(defaultGroup.c_str());
763
+ }
764
+
765
+ if (userInfo == NULL) {
766
+ throw RuntimeException("Cannot determine a user to lower privilege to");
767
+ }
768
+ if (groupInfo == NULL) {
769
+ throw RuntimeException("Cannot determine a group to lower privilege to");
770
+ }
771
+
772
+ #ifdef __APPLE__
773
+ int groups[1024];
774
+ info.ngroups = sizeof(groups) / sizeof(int);
775
+ #else
776
+ gid_t groups[1024];
777
+ info.ngroups = sizeof(groups) / sizeof(gid_t);
778
+ #endif
779
+ int ret;
780
+ info.switchUser = true;
781
+ info.username = userInfo->pw_name;
782
+ info.groupname = groupInfo->gr_name;
783
+ info.home = userInfo->pw_dir;
784
+ info.shell = userInfo->pw_shell;
785
+ info.uid = userInfo->pw_uid;
786
+ info.gid = groupInfo->gr_gid;
787
+ ret = getgrouplist(userInfo->pw_name, groupInfo->gr_gid,
788
+ groups, &info.ngroups);
789
+ if (ret == -1) {
790
+ int e = errno;
791
+ throw SystemException("getgrouplist() failed", e);
792
+ }
793
+ info.gidset = shared_array<gid_t>(new gid_t[info.ngroups]);
794
+ for (int i = 0; i < info.ngroups; i++) {
795
+ info.gidset[i] = groups[i];
796
+ }
797
+ }
798
+
799
+ void prepareSwitchingWorkingDirectory(SpawnPreparationInfo &info, const Options &options) const {
800
+ vector<string> components;
801
+ split(info.appRootInsideChroot, '/', components);
802
+ assert(components.front() == "");
803
+ components.erase(components.begin());
804
+
805
+ for (unsigned int i = 0; i < components.size(); i++) {
806
+ string path;
807
+ for (unsigned int j = 0; j <= i; j++) {
808
+ path.append("/");
809
+ path.append(components[j]);
810
+ }
811
+ if (path.empty()) {
812
+ path = "/";
813
+ }
814
+ if (info.chrootDir == "/") {
815
+ info.appRootPaths.push_back(path);
816
+ } else {
817
+ info.appRootPaths.push_back(info.chrootDir + path);
818
+ }
819
+ info.appRootPathsInsideChroot.push_back(path);
820
+ }
821
+
822
+ assert(info.appRootPathsInsideChroot.back() == info.appRootInsideChroot);
823
+ }
824
+
825
+ string serializeEnvvarsFromPoolOptions(const Options &options) const {
826
+ vector< pair<StaticString, StaticString> >::const_iterator it, end;
827
+ string result;
828
+
829
+ appendNullTerminatedKeyValue(result, "IN_PASSENGER", "1");
830
+ appendNullTerminatedKeyValue(result, "PYTHONUNBUFFERED", "1");
831
+ appendNullTerminatedKeyValue(result, "RAILS_ENV", options.environment);
832
+ appendNullTerminatedKeyValue(result, "RACK_ENV", options.environment);
833
+ appendNullTerminatedKeyValue(result, "WSGI_ENV", options.environment);
834
+ appendNullTerminatedKeyValue(result, "PASSENGER_ENV", options.environment);
835
+ if (!options.baseURI.empty() && options.baseURI != "/") {
836
+ appendNullTerminatedKeyValue(result,
837
+ "RAILS_RELATIVE_URL_ROOT",
838
+ options.environment);
839
+ appendNullTerminatedKeyValue(result,
840
+ "RACK_BASE_URI",
841
+ options.environment);
842
+ appendNullTerminatedKeyValue(result,
843
+ "PASSENGER_BASE_URI",
844
+ options.environment);
845
+ }
846
+
847
+ it = options.environmentVariables.begin();
848
+ end = options.environmentVariables.end();
849
+ while (it != end) {
850
+ appendNullTerminatedKeyValue(result, it->first, it->second);
851
+ it++;
852
+ }
853
+
854
+ return Base64::encode(result);
855
+ }
856
+
857
+ void switchUser(const SpawnPreparationInfo &info) {
858
+ if (info.switchUser) {
859
+ if (setgroups(info.ngroups, info.gidset.get()) == -1) {
860
+ int e = errno;
861
+ printf("!> Error\n");
862
+ printf("!> \n");
863
+ printf("setgroups() failed: %s (errno=%d)\n",
864
+ strerror(e), e);
865
+ fflush(stdout);
866
+ _exit(1);
867
+ }
868
+ if (setgid(info.gid) == -1) {
869
+ int e = errno;
870
+ printf("!> Error\n");
871
+ printf("!> \n");
872
+ printf("setgid() failed: %s (errno=%d)\n",
873
+ strerror(e), e);
874
+ fflush(stdout);
875
+ _exit(1);
876
+ }
877
+ if (setuid(info.uid) == -1) {
878
+ int e = errno;
879
+ printf("!> Error\n");
880
+ printf("!> \n");
881
+ printf("setuid() failed: %s (errno=%d)\n",
882
+ strerror(e), e);
883
+ fflush(stdout);
884
+ _exit(1);
885
+ }
886
+
887
+ // We set these environment variables here instead of
888
+ // in the SpawnPreparer because SpawnPreparer might
889
+ // be executed by bash, but these environment variables
890
+ // must be set before bash.
891
+ setenv("USER", info.username.c_str(), 1);
892
+ setenv("LOGNAME", info.username.c_str(), 1);
893
+ setenv("SHELL", info.shell.c_str(), 1);
894
+ setenv("HOME", info.home.c_str(), 1);
895
+ }
896
+ }
897
+
898
+ void setChroot(const SpawnPreparationInfo &info) {
899
+ if (info.chrootDir != "/") {
900
+ int ret = chroot(info.chrootDir.c_str());
901
+ if (ret == -1) {
902
+ int e = errno;
903
+ fprintf(stderr, "Cannot chroot() to '%s': %s (errno=%d)\n",
904
+ info.chrootDir.c_str(),
905
+ strerror(e),
906
+ e);
907
+ fflush(stderr);
908
+ _exit(1);
909
+ }
910
+ }
911
+ }
912
+
913
+ void setWorkingDirectory(const SpawnPreparationInfo &info) {
914
+ vector<string>::const_iterator it, end = info.appRootPathsInsideChroot.end();
915
+ int ret;
916
+
917
+ for (it = info.appRootPathsInsideChroot.begin(); it != end; it++) {
918
+ struct stat buf;
919
+ ret = stat(it->c_str(), &buf);
920
+ if (ret == -1 && errno == EACCES) {
921
+ char parent[PATH_MAX];
922
+ const char *end = strrchr(it->c_str(), '/');
923
+ memcpy(parent, it->c_str(), end - it->c_str());
924
+ parent[end - it->c_str()] = '\0';
925
+
926
+ printf("!> Error\n");
927
+ printf("!> \n");
928
+ printf("This web application process is being run as user '%s' and group '%s' "
929
+ "and must be able to access its application root directory '%s'. "
930
+ "However, the parent directory '%s' has wrong permissions, thereby "
931
+ "preventing this process from accessing its application root directory. "
932
+ "Please fix the permissions of the directory '%s' first.\n",
933
+ info.username.c_str(),
934
+ info.groupname.c_str(),
935
+ info.appRootPaths.back().c_str(),
936
+ parent,
937
+ parent);
938
+ fflush(stdout);
939
+ _exit(1);
940
+ } else if (ret == -1) {
941
+ int e = errno;
942
+ printf("!> Error\n");
943
+ printf("!> \n");
944
+ printf("Unable to stat() directory '%s': %s (errno=%d)\n",
945
+ it->c_str(), strerror(e), e);
946
+ fflush(stdout);
947
+ _exit(1);
948
+ }
949
+ }
950
+
951
+ ret = chdir(info.appRootPathsInsideChroot.back().c_str());
952
+ if (ret == 0) {
953
+ setenv("PWD", info.appRootPathsInsideChroot.back().c_str(), 1);
954
+ } else if (ret == -1 && errno == EACCES) {
955
+ printf("!> Error\n");
956
+ printf("!> \n");
957
+ printf("This web application process is being run as user '%s' and group '%s' "
958
+ "and must be able to access its application root directory '%s'. "
959
+ "However this directory is not accessible because it has wrong permissions. "
960
+ "Please fix these permissions first.\n",
961
+ info.username.c_str(),
962
+ info.groupname.c_str(),
963
+ info.appRootPaths.back().c_str());
964
+ fflush(stdout);
965
+ _exit(1);
966
+ } else {
967
+ int e = errno;
968
+ printf("!> Error\n");
969
+ printf("!> \n");
970
+ printf("Unable to change working directory to '%s': %s (errno=%d)\n",
971
+ info.appRootPathsInsideChroot.back().c_str(), strerror(e), e);
972
+ fflush(stdout);
973
+ _exit(1);
974
+ }
975
+ }
976
+
977
+ ProcessPtr negotiateSpawn(NegotiationDetails &details) {
978
+ TRACE_POINT();
979
+ details.spawnStartTime = SystemTime::getUsec();
980
+ details.gupid = integerToHex(SystemTime::get() / 60) + "-" +
981
+ randomGenerator->generateAsciiString(11);
982
+ details.connectPassword = randomGenerator->generateAsciiString(43);
983
+ details.timeout = details.options->startTimeout * 1000;
984
+
985
+ string result;
986
+ try {
987
+ result = readMessageLine(details);
988
+ } catch (const SystemException &e) {
989
+ throwAppSpawnException("An error occurred while starting the "
990
+ "web application. There was an I/O error while reading its "
991
+ "handshake message: " + e.sys(),
992
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
993
+ details);
994
+ } catch (const TimeoutException &) {
995
+ throwAppSpawnException("An error occurred while starting the "
996
+ "web application: it did not write a handshake message in time.",
997
+ SpawnException::APP_STARTUP_TIMEOUT,
998
+ details);
999
+ }
1000
+
1001
+ if (result == "I have control 1.0\n") {
1002
+ UPDATE_TRACE_POINT();
1003
+ sendSpawnRequest(details);
1004
+ try {
1005
+ result = readMessageLine(details);
1006
+ } catch (const SystemException &e) {
1007
+ throwAppSpawnException("An error occurred while starting the "
1008
+ "web application. There was an I/O error while reading its "
1009
+ "startup response: " + e.sys(),
1010
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
1011
+ details);
1012
+ } catch (const TimeoutException &) {
1013
+ throwAppSpawnException("An error occurred while starting the "
1014
+ "web application: it did not write a startup response in time.",
1015
+ SpawnException::APP_STARTUP_TIMEOUT,
1016
+ details);
1017
+ }
1018
+ if (result == "Ready\n") {
1019
+ return handleSpawnResponse(details);
1020
+ } else if (result == "Error\n") {
1021
+ handleSpawnErrorResponse(details);
1022
+ } else {
1023
+ handleInvalidSpawnResponseType(result, details);
1024
+ }
1025
+ } else {
1026
+ UPDATE_TRACE_POINT();
1027
+ if (result == "Error\n") {
1028
+ handleSpawnErrorResponse(details);
1029
+ } else {
1030
+ handleInvalidSpawnResponseType(result, details);
1031
+ }
1032
+ }
1033
+ return ProcessPtr(); // Never reached.
1034
+ }
1035
+
1036
+ void handleSpawnErrorResponse(NegotiationDetails &details) {
1037
+ TRACE_POINT();
1038
+ map<string, string> attributes;
1039
+
1040
+ while (true) {
1041
+ string line = readMessageLine(details);
1042
+ if (line.empty()) {
1043
+ throwAppSpawnException("An error occurred while starting the "
1044
+ "web application. It unexpected closed the connection while "
1045
+ "sending its startup response.",
1046
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
1047
+ details);
1048
+ } else if (line[line.size() - 1] != '\n') {
1049
+ throwAppSpawnException("An error occurred while starting the "
1050
+ "web application. It sent a line without a newline character "
1051
+ "in its startup response.",
1052
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
1053
+ details);
1054
+ } else if (line == "\n") {
1055
+ break;
1056
+ }
1057
+
1058
+ string::size_type pos = line.find(": ");
1059
+ if (pos == string::npos) {
1060
+ throwAppSpawnException("An error occurred while starting the "
1061
+ "web application. It sent a startup response line without "
1062
+ "separator.",
1063
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
1064
+ details);
1065
+ }
1066
+
1067
+ string key = line.substr(0, pos);
1068
+ string value = line.substr(pos + 2, line.size() - pos - 3);
1069
+ attributes[key] = value;
1070
+ }
1071
+
1072
+ try {
1073
+ string message = details.io.readAll(&details.timeout);
1074
+ SpawnException e("An error occured while starting the web application.",
1075
+ message,
1076
+ attributes["html"] == "true",
1077
+ SpawnException::APP_STARTUP_EXPLAINABLE_ERROR);
1078
+ annotateAppSpawnException(e, details);
1079
+ throw e;
1080
+ } catch (const SystemException &e) {
1081
+ throwAppSpawnException("An error occurred while starting the "
1082
+ "web application. It tried to report an error message, but "
1083
+ "an I/O error occurred while reading this error message: " +
1084
+ e.sys(),
1085
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
1086
+ details);
1087
+ } catch (const TimeoutException &) {
1088
+ throwAppSpawnException("An error occurred while starting the "
1089
+ "web application. It tried to report an error message, but "
1090
+ "it took too much time doing that.",
1091
+ SpawnException::APP_STARTUP_TIMEOUT,
1092
+ details);
1093
+ }
1094
+ }
1095
+
1096
+ void handleInvalidSpawnResponseType(const string &line, NegotiationDetails &details) {
1097
+ throwAppSpawnException("An error occurred while starting "
1098
+ "the web application. It sent an unknown response type \"" +
1099
+ cEscapeString(line) + "\".",
1100
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
1101
+ details);
1102
+ }
1103
+
1104
+ public:
1105
+ /**
1106
+ * Timestamp at which this Spawner was created. Microseconds resolution.
1107
+ */
1108
+ const unsigned long long creationTime;
1109
+
1110
+ Spawner(const ResourceLocator &_resourceLocator)
1111
+ : resourceLocator(_resourceLocator),
1112
+ creationTime(SystemTime::getUsec())
1113
+ { }
1114
+
1115
+ virtual ~Spawner() { }
1116
+ virtual ProcessPtr spawn(const Options &options) = 0;
1117
+
1118
+ /** Does not depend on the event loop. */
1119
+ virtual bool cleanable() const {
1120
+ return false;
1121
+ }
1122
+
1123
+ virtual void cleanup() { }
1124
+
1125
+ /** Does not depend on the event loop. */
1126
+ virtual unsigned long long lastUsed() const {
1127
+ return 0;
1128
+ }
1129
+ };
1130
+ typedef shared_ptr<Spawner> SpawnerPtr;
1131
+
1132
+
1133
+ class SmartSpawner: public Spawner, public enable_shared_from_this<SmartSpawner> {
1134
+ private:
1135
+ struct SpawnResult {
1136
+ pid_t pid;
1137
+ FileDescriptor adminSocket;
1138
+ BufferedIO io;
1139
+ };
1140
+
1141
+ /** The event loop that created Process objects should use, and that I/O forwarding
1142
+ * functions should use. For example data on the error pipe is forwarded using this event loop.
1143
+ */
1144
+ SafeLibevPtr libev;
1145
+ const vector<string> preloaderCommand;
1146
+ map<string, string> preloaderAnnotations;
1147
+ Options options;
1148
+ ev::io preloaderOutputWatcher;
1149
+ shared_ptr<PipeWatcher> preloaderErrorWatcher;
1150
+
1151
+ // Protects m_lastUsed and pid.
1152
+ mutable boost::mutex simpleFieldSyncher;
1153
+ // Protects everything else.
1154
+ mutable boost::mutex syncher;
1155
+
1156
+ pid_t pid;
1157
+ FileDescriptor adminSocket;
1158
+ string socketAddress;
1159
+ unsigned long long m_lastUsed;
1160
+
1161
+ void onPreloaderOutputReadable(ev::io &io, int revents) {
1162
+ char buf[1024 * 8];
1163
+ ssize_t ret;
1164
+
1165
+ ret = syscalls::read(adminSocket, buf, sizeof(buf));
1166
+ if (ret <= 0) {
1167
+ preloaderOutputWatcher.stop();
1168
+ } else if (forwardStdout) {
1169
+ write(STDOUT_FILENO, buf, ret);
1170
+ }
1171
+ }
1172
+
1173
+ string getPreloaderCommandString() const {
1174
+ string result;
1175
+ unsigned int i;
1176
+
1177
+ for (i = 0; i < preloaderCommand.size(); i++) {
1178
+ if (i != 0) {
1179
+ result.append(1, '\0');
1180
+ }
1181
+ result.append(preloaderCommand[i]);
1182
+ }
1183
+ return result;
1184
+ }
1185
+
1186
+ vector<string> createRealPreloaderCommand(const Options &options,
1187
+ shared_array<const char *> &args)
1188
+ {
1189
+ string agentsDir = resourceLocator.getAgentsDir();
1190
+ vector<string> command;
1191
+
1192
+ if (options.loadShellEnvvars) {
1193
+ command.push_back("bash");
1194
+ command.push_back("bash");
1195
+ command.push_back("-lc");
1196
+ command.push_back("exec \"$@\"");
1197
+ command.push_back("SpawnPreparerShell");
1198
+ } else {
1199
+ command.push_back(agentsDir + "/SpawnPreparer");
1200
+ }
1201
+ command.push_back(agentsDir + "/SpawnPreparer");
1202
+ command.push_back(serializeEnvvarsFromPoolOptions(options));
1203
+ command.push_back(preloaderCommand[0]);
1204
+ command.push_back("Passenger AppPreloader: " + options.appRoot);
1205
+ for (unsigned int i = 1; i < preloaderCommand.size(); i++) {
1206
+ command.push_back(preloaderCommand[i]);
1207
+ }
1208
+
1209
+ createCommandArgs(command, args);
1210
+ return command;
1211
+ }
1212
+
1213
+ void throwPreloaderSpawnException(const string &msg,
1214
+ SpawnException::ErrorKind errorKind,
1215
+ StartupDetails &details)
1216
+ {
1217
+ throwPreloaderSpawnException(msg, errorKind, details.stderrCapturer,
1218
+ details.debugDir);
1219
+ }
1220
+
1221
+ void throwPreloaderSpawnException(const string &msg,
1222
+ SpawnException::ErrorKind errorKind,
1223
+ BackgroundIOCapturerPtr &stderrCapturer,
1224
+ const DebugDirPtr &debugDir)
1225
+ {
1226
+ TRACE_POINT();
1227
+ // Stop the stderr capturing thread and get the captured stderr
1228
+ // output so far.
1229
+ string stderrOutput;
1230
+ if (stderrCapturer != NULL) {
1231
+ stderrOutput = stderrCapturer->stop();
1232
+ }
1233
+
1234
+ // If the exception wasn't due to a timeout, try to capture the
1235
+ // remaining stderr output for at most 2 seconds.
1236
+ if (errorKind != SpawnException::PRELOADER_STARTUP_TIMEOUT
1237
+ && errorKind != SpawnException::APP_STARTUP_TIMEOUT
1238
+ && stderrCapturer != NULL) {
1239
+ bool done = false;
1240
+ unsigned long long timeout = 2000;
1241
+ while (!done) {
1242
+ char buf[1024 * 32];
1243
+ unsigned int ret;
1244
+
1245
+ try {
1246
+ ret = readExact(stderrCapturer->getFd(), buf,
1247
+ sizeof(buf), &timeout);
1248
+ if (ret == 0) {
1249
+ done = true;
1250
+ } else {
1251
+ stderrOutput.append(buf, ret);
1252
+ }
1253
+ } catch (const SystemException &e) {
1254
+ P_WARN("Stderr I/O capture error: " << e.what());
1255
+ done = true;
1256
+ } catch (const TimeoutException &) {
1257
+ done = true;
1258
+ }
1259
+ }
1260
+ }
1261
+ stderrCapturer.reset();
1262
+
1263
+ // Now throw SpawnException with the captured stderr output
1264
+ // as error response.
1265
+ SpawnException e(msg, stderrOutput, false, errorKind);
1266
+ e.setPreloaderCommand(getPreloaderCommandString());
1267
+ annotatePreloaderException(e, debugDir);
1268
+ throw e;
1269
+ }
1270
+
1271
+ void annotatePreloaderException(SpawnException &e, const DebugDirPtr &debugDir) {
1272
+ if (debugDir != NULL) {
1273
+ e.addAnnotations(debugDir->readAll());
1274
+ }
1275
+ }
1276
+
1277
+ bool preloaderStarted() const {
1278
+ return pid != -1;
1279
+ }
1280
+
1281
+ void startPreloader() {
1282
+ TRACE_POINT();
1283
+ assert(!preloaderStarted());
1284
+ checkChrootDirectories(options);
1285
+
1286
+ shared_array<const char *> args;
1287
+ vector<string> command = createRealPreloaderCommand(options, args);
1288
+ SpawnPreparationInfo preparation = prepareSpawn(options);
1289
+ SocketPair adminSocket = createUnixSocketPair();
1290
+ Pipe errorPipe = createPipe();
1291
+ DebugDirPtr debugDir = make_shared<DebugDir>(preparation.uid, preparation.gid);
1292
+ pid_t pid;
1293
+
1294
+ pid = syscalls::fork();
1295
+ if (pid == 0) {
1296
+ setenv("PASSENGER_DEBUG_DIR", debugDir->getPath().c_str(), 1);
1297
+ purgeStdio(stdout);
1298
+ purgeStdio(stderr);
1299
+ resetSignalHandlersAndMask();
1300
+ disableMallocDebugging();
1301
+ int adminSocketCopy = dup2(adminSocket.first, 3);
1302
+ int errorPipeCopy = dup2(errorPipe.second, 4);
1303
+ dup2(adminSocketCopy, 0);
1304
+ dup2(adminSocketCopy, 1);
1305
+ dup2(errorPipeCopy, 2);
1306
+ closeAllFileDescriptors(2);
1307
+ setChroot(preparation);
1308
+ switchUser(preparation);
1309
+ setWorkingDirectory(preparation);
1310
+ execvp(command[0].c_str(), (char * const *) args.get());
1311
+
1312
+ int e = errno;
1313
+ printf("!> Error\n");
1314
+ printf("!> \n");
1315
+ printf("Cannot execute \"%s\": %s (errno=%d)\n", command[0].c_str(),
1316
+ strerror(e), e);
1317
+ fprintf(stderr, "Cannot execute \"%s\": %s (errno=%d)\n",
1318
+ command[0].c_str(), strerror(e), e);
1319
+ fflush(stdout);
1320
+ fflush(stderr);
1321
+ _exit(1);
1322
+
1323
+ } else if (pid == -1) {
1324
+ int e = errno;
1325
+ throw SystemException("Cannot fork a new process", e);
1326
+
1327
+ } else {
1328
+ ScopeGuard guard(boost::bind(nonInterruptableKillAndWaitpid, pid));
1329
+ adminSocket.first.close();
1330
+ errorPipe.second.close();
1331
+
1332
+ StartupDetails details;
1333
+ details.adminSocket = adminSocket.second;
1334
+ details.io = BufferedIO(adminSocket.second);
1335
+ details.stderrCapturer =
1336
+ make_shared<BackgroundIOCapturer>(
1337
+ errorPipe.first,
1338
+ forwardStderr ? STDERR_FILENO : -1);
1339
+ details.stderrCapturer->start();
1340
+ details.debugDir = debugDir;
1341
+ details.options = &options;
1342
+ details.timeout = options.startTimeout * 1000;
1343
+ details.forwardStderr = forwardStderr;
1344
+
1345
+ socketAddress = negotiatePreloaderStartup(details);
1346
+ this->adminSocket = adminSocket.second;
1347
+ {
1348
+ lock_guard<boost::mutex> l(simpleFieldSyncher);
1349
+ this->pid = pid;
1350
+ }
1351
+ preloaderOutputWatcher.set(adminSocket.second, ev::READ);
1352
+ libev->start(preloaderOutputWatcher);
1353
+ setNonBlocking(errorPipe.first);
1354
+ preloaderErrorWatcher = make_shared<PipeWatcher>(libev,
1355
+ errorPipe.first, forwardStderr ? STDERR_FILENO : -1);
1356
+ preloaderErrorWatcher->start();
1357
+ preloaderAnnotations = debugDir->readAll();
1358
+ P_DEBUG("Preloader for " << options.appRoot <<
1359
+ " started on PID " << pid <<
1360
+ ", listening on " << socketAddress);
1361
+ guard.clear();
1362
+ }
1363
+ }
1364
+
1365
+ void stopPreloader() {
1366
+ TRACE_POINT();
1367
+ this_thread::disable_interruption di;
1368
+ this_thread::disable_syscall_interruption dsi;
1369
+
1370
+ if (!preloaderStarted()) {
1371
+ return;
1372
+ }
1373
+ adminSocket.close();
1374
+ if (timedWaitpid(pid, NULL, 5000) == 0) {
1375
+ P_TRACE(2, "Spawn server did not exit in time, killing it...");
1376
+ syscalls::kill(pid, SIGKILL);
1377
+ syscalls::waitpid(pid, NULL, 0);
1378
+ }
1379
+ libev->stop(preloaderOutputWatcher);
1380
+ // Detach the error pipe; it will truly be closed after the error
1381
+ // pipe has reached EOF.
1382
+ preloaderErrorWatcher.reset();
1383
+ // Delete socket after the process has exited so that it
1384
+ // doesn't crash upon deleting a nonexistant file.
1385
+ // TODO: in Passenger 4 we must check whether the file really was
1386
+ // owned by the preloader, otherwise this is a potential security flaw.
1387
+ if (getSocketAddressType(socketAddress) == SAT_UNIX) {
1388
+ string filename = parseUnixSocketAddress(socketAddress);
1389
+ syscalls::unlink(filename.c_str());
1390
+ }
1391
+ {
1392
+ lock_guard<boost::mutex> l(simpleFieldSyncher);
1393
+ pid = -1;
1394
+ }
1395
+ socketAddress.clear();
1396
+ }
1397
+
1398
+ void sendStartupRequest(StartupDetails &details) {
1399
+ TRACE_POINT();
1400
+ try {
1401
+ writeExact(details.adminSocket,
1402
+ "You have control 1.0\n"
1403
+ "passenger_root: " + resourceLocator.getRoot() + "\n"
1404
+ "ruby_libdir: " + resourceLocator.getRubyLibDir() + "\n"
1405
+ "passenger_version: " PASSENGER_VERSION "\n"
1406
+ "generation_dir: " + generation->getPath() + "\n",
1407
+ &details.timeout);
1408
+
1409
+ vector<string> args;
1410
+ vector<string>::const_iterator it, end;
1411
+ details.options->toVector(args, resourceLocator);
1412
+ for (it = args.begin(); it != args.end(); it++) {
1413
+ const string &key = *it;
1414
+ it++;
1415
+ const string &value = *it;
1416
+ writeExact(details.adminSocket,
1417
+ key + ": " + value + "\n",
1418
+ &details.timeout);
1419
+ }
1420
+ writeExact(details.adminSocket, "\n", &details.timeout);
1421
+ } catch (const SystemException &e) {
1422
+ if (e.code() == EPIPE) {
1423
+ /* Ignore this. Process might have written an
1424
+ * error response before reading the arguments,
1425
+ * in which case we'll want to show that instead.
1426
+ */
1427
+ } else {
1428
+ throwPreloaderSpawnException("An error occurred while starting up "
1429
+ "the preloader. There was an I/O error while "
1430
+ "sending the startup request message to it: " +
1431
+ e.sys(),
1432
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1433
+ details);
1434
+ }
1435
+ } catch (const TimeoutException &) {
1436
+ throwPreloaderSpawnException("An error occurred while starting up the "
1437
+ "preloader: it did not read the startup request message in time.",
1438
+ SpawnException::PRELOADER_STARTUP_TIMEOUT,
1439
+ details);
1440
+ }
1441
+ }
1442
+
1443
+ string handleStartupResponse(StartupDetails &details) {
1444
+ TRACE_POINT();
1445
+ string socketAddress;
1446
+
1447
+ while (true) {
1448
+ string line;
1449
+
1450
+ try {
1451
+ line = readMessageLine(details);
1452
+ } catch (const SystemException &e) {
1453
+ throwPreloaderSpawnException("An error occurred while starting up "
1454
+ "the preloader. There was an I/O error while reading its "
1455
+ "startup response: " + e.sys(),
1456
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1457
+ details);
1458
+ } catch (const TimeoutException &) {
1459
+ throwPreloaderSpawnException("An error occurred while starting up "
1460
+ "the preloader: it did not write a startup response in time.",
1461
+ SpawnException::PRELOADER_STARTUP_TIMEOUT,
1462
+ details);
1463
+ }
1464
+
1465
+ if (line.empty()) {
1466
+ throwPreloaderSpawnException("An error occurred while starting up "
1467
+ "the preloader. It unexpected closed the connection while "
1468
+ "sending its startup response.",
1469
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1470
+ details);
1471
+ } else if (line[line.size() - 1] != '\n') {
1472
+ throwPreloaderSpawnException("An error occurred while starting up "
1473
+ "the preloader. It sent a line without a newline character "
1474
+ "in its startup response.",
1475
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1476
+ details);
1477
+ } else if (line == "\n") {
1478
+ break;
1479
+ }
1480
+
1481
+ string::size_type pos = line.find(": ");
1482
+ if (pos == string::npos) {
1483
+ throwPreloaderSpawnException("An error occurred while starting up "
1484
+ "the preloader. It sent a startup response line without "
1485
+ "separator.",
1486
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1487
+ details);
1488
+ }
1489
+
1490
+ string key = line.substr(0, pos);
1491
+ string value = line.substr(pos + 2, line.size() - pos - 3);
1492
+ if (key == "socket") {
1493
+ socketAddress = fixupSocketAddress(options, value);
1494
+ } else {
1495
+ throwPreloaderSpawnException("An error occurred while starting up "
1496
+ "the preloader. It sent an unknown startup response line "
1497
+ "called '" + key + "'.",
1498
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1499
+ details);
1500
+ }
1501
+ }
1502
+
1503
+ if (socketAddress.empty()) {
1504
+ throwPreloaderSpawnException("An error occurred while starting up "
1505
+ "the preloader. It did not report a socket address in its "
1506
+ "startup response.",
1507
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1508
+ details);
1509
+ }
1510
+
1511
+ return socketAddress;
1512
+ }
1513
+
1514
+ void handleErrorResponse(StartupDetails &details) {
1515
+ TRACE_POINT();
1516
+ map<string, string> attributes;
1517
+
1518
+ while (true) {
1519
+ string line;
1520
+
1521
+ try {
1522
+ line = readMessageLine(details);
1523
+ } catch (const SystemException &e) {
1524
+ throwPreloaderSpawnException("An error occurred while starting up "
1525
+ "the preloader. There was an I/O error while reading its "
1526
+ "startup response: " + e.sys(),
1527
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1528
+ details);
1529
+ } catch (const TimeoutException &) {
1530
+ throwPreloaderSpawnException("An error occurred while starting up "
1531
+ "the preloader: it did not write a startup response in time.",
1532
+ SpawnException::PRELOADER_STARTUP_TIMEOUT,
1533
+ details);
1534
+ }
1535
+
1536
+ if (line.empty()) {
1537
+ throwPreloaderSpawnException("An error occurred while starting up "
1538
+ "the preloader. It unexpected closed the connection while "
1539
+ "sending its startup response.",
1540
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1541
+ details);
1542
+ } else if (line[line.size() - 1] != '\n') {
1543
+ throwPreloaderSpawnException("An error occurred while starting up "
1544
+ "the preloader. It sent a line without a newline character "
1545
+ "in its startup response.",
1546
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1547
+ details);
1548
+ } else if (line == "\n") {
1549
+ break;
1550
+ }
1551
+
1552
+ string::size_type pos = line.find(": ");
1553
+ if (pos == string::npos) {
1554
+ throwPreloaderSpawnException("An error occurred while starting up "
1555
+ "the preloader. It sent a startup response line without "
1556
+ "separator.",
1557
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1558
+ details);
1559
+ }
1560
+
1561
+ string key = line.substr(0, pos);
1562
+ string value = line.substr(pos + 2, line.size() - pos - 3);
1563
+ attributes[key] = value;
1564
+ }
1565
+
1566
+ try {
1567
+ string message = details.io.readAll(&details.timeout);
1568
+ SpawnException e("An error occured while starting up the preloader.",
1569
+ message,
1570
+ attributes["html"] == "true",
1571
+ SpawnException::PRELOADER_STARTUP_EXPLAINABLE_ERROR);
1572
+ e.setPreloaderCommand(getPreloaderCommandString());
1573
+ annotatePreloaderException(e, details.debugDir);
1574
+ throw e;
1575
+ } catch (const SystemException &e) {
1576
+ throwPreloaderSpawnException("An error occurred while starting up "
1577
+ "the preloader. It tried to report an error message, but "
1578
+ "an I/O error occurred while reading this error message: " +
1579
+ e.sys(),
1580
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1581
+ details);
1582
+ } catch (const TimeoutException &) {
1583
+ throwPreloaderSpawnException("An error occurred while starting up "
1584
+ "the preloader. It tried to report an error message, but "
1585
+ "it took too much time doing that.",
1586
+ SpawnException::PRELOADER_STARTUP_TIMEOUT,
1587
+ details);
1588
+ }
1589
+ }
1590
+
1591
+ void handleInvalidResponseType(StartupDetails &details, const string &line) {
1592
+ throwPreloaderSpawnException("An error occurred while starting up "
1593
+ "the preloader. It sent an unknown response type \"" +
1594
+ cEscapeString(line) + "\".",
1595
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1596
+ details);
1597
+ }
1598
+
1599
+ string negotiatePreloaderStartup(StartupDetails &details) {
1600
+ TRACE_POINT();
1601
+ string result;
1602
+ try {
1603
+ result = readMessageLine(details);
1604
+ } catch (const SystemException &e) {
1605
+ throwPreloaderSpawnException("An error occurred while starting up "
1606
+ "the preloader. There was an I/O error while reading its "
1607
+ "handshake message: " + e.sys(),
1608
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1609
+ details);
1610
+ } catch (const TimeoutException &) {
1611
+ throwPreloaderSpawnException("An error occurred while starting up "
1612
+ "the preloader: it did not write a handshake message in time.",
1613
+ SpawnException::PRELOADER_STARTUP_TIMEOUT,
1614
+ details);
1615
+ }
1616
+
1617
+ if (result == "I have control 1.0\n") {
1618
+ UPDATE_TRACE_POINT();
1619
+ sendStartupRequest(details);
1620
+ try {
1621
+ result = readMessageLine(details);
1622
+ } catch (const SystemException &e) {
1623
+ throwPreloaderSpawnException("An error occurred while starting up "
1624
+ "the preloader. There was an I/O error while reading its "
1625
+ "startup response: " + e.sys(),
1626
+ SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1627
+ details);
1628
+ } catch (const TimeoutException &) {
1629
+ throwPreloaderSpawnException("An error occurred while starting up "
1630
+ "the preloader: it did not write a startup response in time.",
1631
+ SpawnException::PRELOADER_STARTUP_TIMEOUT,
1632
+ details);
1633
+ }
1634
+ if (result == "Ready\n") {
1635
+ return handleStartupResponse(details);
1636
+ } else if (result == "Error\n") {
1637
+ handleErrorResponse(details);
1638
+ } else {
1639
+ handleInvalidResponseType(details, result);
1640
+ }
1641
+ } else {
1642
+ UPDATE_TRACE_POINT();
1643
+ if (result == "Error\n") {
1644
+ handleErrorResponse(details);
1645
+ } else {
1646
+ handleInvalidResponseType(details, result);
1647
+ }
1648
+ }
1649
+
1650
+ // Never reached, shut up compiler warning.
1651
+ abort();
1652
+ return "";
1653
+ }
1654
+
1655
+ SpawnResult sendSpawnCommand(const Options &options) {
1656
+ TRACE_POINT();
1657
+ FileDescriptor fd;
1658
+ try {
1659
+ fd = connectToServer(socketAddress);
1660
+ } catch (const SystemException &e) {
1661
+ BackgroundIOCapturerPtr stderrCapturer;
1662
+ throwPreloaderSpawnException("An error occurred while starting "
1663
+ "the application. Unable to connect to the preloader's "
1664
+ "socket: " + string(e.what()),
1665
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
1666
+ stderrCapturer,
1667
+ DebugDirPtr());
1668
+ }
1669
+
1670
+ UPDATE_TRACE_POINT();
1671
+ BufferedIO io(fd);
1672
+ unsigned long long timeout = options.startTimeout * 1000;
1673
+ string result;
1674
+ vector<string> args;
1675
+ vector<string>::const_iterator it;
1676
+
1677
+ writeExact(fd, "spawn\n", &timeout);
1678
+ options.toVector(args, resourceLocator);
1679
+ for (it = args.begin(); it != args.end(); it++) {
1680
+ const string &key = *it;
1681
+ it++;
1682
+ const string &value = *it;
1683
+ writeExact(fd, key + ": " + value + "\n", &timeout);
1684
+ }
1685
+ writeExact(fd, "\n", &timeout);
1686
+
1687
+ result = io.readLine(1024, &timeout);
1688
+ if (result == "OK\n") {
1689
+ UPDATE_TRACE_POINT();
1690
+ pid_t spawnedPid;
1691
+
1692
+ spawnedPid = atoi(io.readLine(1024, &timeout).c_str());
1693
+ if (spawnedPid <= 0) {
1694
+ BackgroundIOCapturerPtr stderrCapturer;
1695
+ throwPreloaderSpawnException("An error occurred while starting "
1696
+ "the web application. Its preloader responded to the "
1697
+ "'spawn' command with an invalid PID: '" +
1698
+ toString(spawnedPid) + "'",
1699
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
1700
+ stderrCapturer,
1701
+ DebugDirPtr());
1702
+ }
1703
+ // TODO: we really should be checking UID.
1704
+ // FIXME: for Passenger 4 we *must* check the UID otherwise this is a gaping security hole.
1705
+ if (getsid(spawnedPid) != getsid(pid)) {
1706
+ BackgroundIOCapturerPtr stderrCapturer;
1707
+ throwPreloaderSpawnException("An error occurred while starting "
1708
+ "the web application. Its preloader responded to the "
1709
+ "'spawn' command with a PID that doesn't belong to "
1710
+ "the same session: '" + toString(spawnedPid) + "'",
1711
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
1712
+ stderrCapturer,
1713
+ DebugDirPtr());
1714
+ }
1715
+
1716
+ SpawnResult result;
1717
+ result.pid = spawnedPid;
1718
+ result.adminSocket = fd;
1719
+ result.io = io;
1720
+ return result;
1721
+
1722
+ } else if (result == "Error\n") {
1723
+ UPDATE_TRACE_POINT();
1724
+ NegotiationDetails details;
1725
+ details.io = io;
1726
+ details.timeout = timeout;
1727
+ handleSpawnErrorResponse(details);
1728
+
1729
+ } else {
1730
+ UPDATE_TRACE_POINT();
1731
+ NegotiationDetails details;
1732
+ handleInvalidSpawnResponseType(result, details);
1733
+ }
1734
+
1735
+ return SpawnResult(); // Never reached.
1736
+ }
1737
+
1738
+ template<typename Exception>
1739
+ SpawnResult sendSpawnCommandAgain(const Exception &e, const Options &options) {
1740
+ TRACE_POINT();
1741
+ P_WARN("An error occurred while spawning a process: " << e.what());
1742
+ P_WARN("The application preloader seems to have crashed, restarting it and trying again...");
1743
+ stopPreloader();
1744
+ startPreloader();
1745
+ ScopeGuard guard(boost::bind(&SmartSpawner::stopPreloader, this));
1746
+ SpawnResult result = sendSpawnCommand(options);
1747
+ guard.clear();
1748
+ return result;
1749
+ }
1750
+
1751
+ protected:
1752
+ virtual void annotateAppSpawnException(SpawnException &e, NegotiationDetails &details) {
1753
+ Spawner::annotateAppSpawnException(e, details);
1754
+ e.addAnnotations(preloaderAnnotations);
1755
+ }
1756
+
1757
+ public:
1758
+ /** Whether to forward the preloader process's stdout to our stdout. True by default. */
1759
+ bool forwardStdout;
1760
+ /** Whether to forward the preloader process's stderr to our stderr. True by default. */
1761
+ bool forwardStderr;
1762
+
1763
+ SmartSpawner(const SafeLibevPtr &_libev,
1764
+ const ResourceLocator &_resourceLocator,
1765
+ const ServerInstanceDir::GenerationPtr &_generation,
1766
+ const vector<string> &_preloaderCommand,
1767
+ const Options &_options,
1768
+ const RandomGeneratorPtr &_randomGenerator = RandomGeneratorPtr())
1769
+ : Spawner(_resourceLocator),
1770
+ libev(_libev),
1771
+ preloaderCommand(_preloaderCommand)
1772
+ {
1773
+ if (preloaderCommand.size() < 2) {
1774
+ throw ArgumentException("preloaderCommand must have at least 2 elements");
1775
+ }
1776
+
1777
+ forwardStdout = true;
1778
+ forwardStderr = true;
1779
+
1780
+ generation = _generation;
1781
+ options = _options.copyAndPersist();
1782
+ pid = -1;
1783
+ m_lastUsed = SystemTime::getUsec();
1784
+
1785
+ preloaderOutputWatcher.set<SmartSpawner, &SmartSpawner::onPreloaderOutputReadable>(this);
1786
+
1787
+ if (_randomGenerator == NULL) {
1788
+ randomGenerator = make_shared<RandomGenerator>();
1789
+ } else {
1790
+ randomGenerator = _randomGenerator;
1791
+ }
1792
+ }
1793
+
1794
+ virtual ~SmartSpawner() {
1795
+ lock_guard<boost::mutex> l(syncher);
1796
+ stopPreloader();
1797
+ }
1798
+
1799
+ virtual ProcessPtr spawn(const Options &options) {
1800
+ TRACE_POINT();
1801
+ assert(options.appType == this->options.appType);
1802
+ assert(options.appRoot == this->options.appRoot);
1803
+
1804
+ P_DEBUG("Spawning new process: appRoot=" << options.appRoot);
1805
+ possiblyRaiseInternalError(options);
1806
+
1807
+ {
1808
+ lock_guard<boost::mutex> l(simpleFieldSyncher);
1809
+ m_lastUsed = SystemTime::getUsec();
1810
+ }
1811
+ if (!preloaderStarted()) {
1812
+ UPDATE_TRACE_POINT();
1813
+ startPreloader();
1814
+ }
1815
+
1816
+ UPDATE_TRACE_POINT();
1817
+ SpawnResult result;
1818
+ try {
1819
+ result = sendSpawnCommand(options);
1820
+ } catch (const SystemException &e) {
1821
+ result = sendSpawnCommandAgain(e, options);
1822
+ } catch (const IOException &e) {
1823
+ result = sendSpawnCommandAgain(e, options);
1824
+ } catch (const SpawnException &e) {
1825
+ result = sendSpawnCommandAgain(e, options);
1826
+ }
1827
+
1828
+ UPDATE_TRACE_POINT();
1829
+ NegotiationDetails details;
1830
+ details.libev = libev;
1831
+ details.pid = result.pid;
1832
+ details.adminSocket = result.adminSocket;
1833
+ details.io = result.io;
1834
+ details.options = &options;
1835
+ details.forwardStderr = forwardStderr;
1836
+ ProcessPtr process = negotiateSpawn(details);
1837
+ P_DEBUG("Process spawning done: appRoot=" << options.appRoot <<
1838
+ ", pid=" << process->pid);
1839
+ return process;
1840
+ }
1841
+
1842
+ virtual bool cleanable() const {
1843
+ return true;
1844
+ }
1845
+
1846
+ virtual void cleanup() {
1847
+ TRACE_POINT();
1848
+ {
1849
+ lock_guard<boost::mutex> l(simpleFieldSyncher);
1850
+ m_lastUsed = SystemTime::getUsec();
1851
+ }
1852
+ lock_guard<boost::mutex> lock(syncher);
1853
+ stopPreloader();
1854
+ }
1855
+
1856
+ virtual unsigned long long lastUsed() const {
1857
+ lock_guard<boost::mutex> lock(simpleFieldSyncher);
1858
+ return m_lastUsed;
1859
+ }
1860
+
1861
+ pid_t getPreloaderPid() const {
1862
+ lock_guard<boost::mutex> lock(simpleFieldSyncher);
1863
+ return pid;
1864
+ }
1865
+ };
1866
+
1867
+
1868
+ class DirectSpawner: public Spawner {
1869
+ private:
1870
+ SafeLibevPtr libev;
1871
+
1872
+ static int startBackgroundThread(void *(*mainFunction)(void *), void *arg) {
1873
+ // Using raw pthread API because we don't want to register such
1874
+ // trivial threads on the oxt::thread list.
1875
+ pthread_t thr;
1876
+ pthread_attr_t attr;
1877
+ size_t stack_size = 96 * 1024;
1878
+
1879
+ unsigned long min_stack_size;
1880
+ bool stack_min_size_defined;
1881
+ bool round_stack_size;
1882
+ int ret;
1883
+
1884
+ #ifdef PTHREAD_STACK_MIN
1885
+ // PTHREAD_STACK_MIN may not be a constant macro so we need
1886
+ // to evaluate it dynamically.
1887
+ min_stack_size = PTHREAD_STACK_MIN;
1888
+ stack_min_size_defined = true;
1889
+ #else
1890
+ // Assume minimum stack size is 128 KB.
1891
+ min_stack_size = 128 * 1024;
1892
+ stack_min_size_defined = false;
1893
+ #endif
1894
+ if (stack_size != 0 && stack_size < min_stack_size) {
1895
+ stack_size = min_stack_size;
1896
+ round_stack_size = !stack_min_size_defined;
1897
+ } else {
1898
+ round_stack_size = true;
1899
+ }
1900
+
1901
+ if (round_stack_size) {
1902
+ // Round stack size up to page boundary.
1903
+ long page_size;
1904
+ #if defined(_SC_PAGESIZE)
1905
+ page_size = sysconf(_SC_PAGESIZE);
1906
+ #elif defined(_SC_PAGE_SIZE)
1907
+ page_size = sysconf(_SC_PAGE_SIZE);
1908
+ #elif defined(PAGESIZE)
1909
+ page_size = sysconf(PAGESIZE);
1910
+ #elif defined(PAGE_SIZE)
1911
+ page_size = sysconf(PAGE_SIZE);
1912
+ #else
1913
+ page_size = getpagesize();
1914
+ #endif
1915
+ if (stack_size % page_size != 0) {
1916
+ stack_size = stack_size - (stack_size % page_size) + page_size;
1917
+ }
1918
+ }
1919
+
1920
+ pthread_attr_init(&attr);
1921
+ pthread_attr_setdetachstate(&attr, 1);
1922
+ pthread_attr_setstacksize(&attr, stack_size);
1923
+ ret = pthread_create(&thr, &attr, mainFunction, arg);
1924
+ pthread_attr_destroy(&attr);
1925
+ return ret;
1926
+ }
1927
+
1928
+ static void *detachProcessMain(void *arg) {
1929
+ this_thread::disable_syscall_interruption dsi;
1930
+ pid_t pid = (pid_t) (long) arg;
1931
+ syscalls::waitpid(pid, NULL, 0);
1932
+ return NULL;
1933
+ }
1934
+
1935
+ void detachProcess(pid_t pid) {
1936
+ startBackgroundThread(detachProcessMain, (void *) (long) pid);
1937
+ }
1938
+
1939
+ vector<string> createCommand(const Options &options, shared_array<const char *> &args) const {
1940
+ vector<string> startCommandArgs;
1941
+ string processTitle;
1942
+ string agentsDir = resourceLocator.getAgentsDir();
1943
+ vector<string> command;
1944
+
1945
+ split(options.getStartCommand(resourceLocator), '\1', startCommandArgs);
1946
+ if (startCommandArgs.empty()) {
1947
+ throw RuntimeException("No startCommand given");
1948
+ }
1949
+ if (options.getProcessTitle().empty()) {
1950
+ processTitle = startCommandArgs[0];
1951
+ } else {
1952
+ processTitle = options.getProcessTitle() + ": " + options.appRoot;
1953
+ }
1954
+
1955
+ if (options.loadShellEnvvars) {
1956
+ command.push_back("bash");
1957
+ command.push_back("bash");
1958
+ command.push_back("-lc");
1959
+ command.push_back("exec \"$@\"");
1960
+ command.push_back("SpawnPreparerShell");
1961
+ } else {
1962
+ command.push_back(agentsDir + "/SpawnPreparer");
1963
+ }
1964
+ command.push_back(agentsDir + "/SpawnPreparer");
1965
+ command.push_back(serializeEnvvarsFromPoolOptions(options));
1966
+ command.push_back(startCommandArgs[0]);
1967
+ command.push_back(processTitle);
1968
+ for (unsigned int i = 1; i < startCommandArgs.size(); i++) {
1969
+ command.push_back(startCommandArgs[i]);
1970
+ }
1971
+
1972
+ createCommandArgs(command, args);
1973
+ return command;
1974
+ }
1975
+
1976
+ public:
1977
+ /** Whether to forward spawned processes' stderr to our stderr. True by default. */
1978
+ bool forwardStderr;
1979
+
1980
+ DirectSpawner(const SafeLibevPtr &_libev,
1981
+ const ResourceLocator &_resourceLocator,
1982
+ const ServerInstanceDir::GenerationPtr &_generation,
1983
+ const RandomGeneratorPtr &_randomGenerator = RandomGeneratorPtr())
1984
+ : Spawner(_resourceLocator),
1985
+ libev(_libev)
1986
+ {
1987
+ forwardStderr = true;
1988
+ generation = _generation;
1989
+ if (_randomGenerator == NULL) {
1990
+ randomGenerator = make_shared<RandomGenerator>();
1991
+ } else {
1992
+ randomGenerator = _randomGenerator;
1993
+ }
1994
+ }
1995
+
1996
+ virtual ProcessPtr spawn(const Options &options) {
1997
+ TRACE_POINT();
1998
+ P_DEBUG("Spawning new process: appRoot=" << options.appRoot);
1999
+ possiblyRaiseInternalError(options);
2000
+
2001
+ shared_array<const char *> args;
2002
+ vector<string> command = createCommand(options, args);
2003
+ SpawnPreparationInfo preparation = prepareSpawn(options);
2004
+ SocketPair adminSocket = createUnixSocketPair();
2005
+ Pipe errorPipe = createPipe();
2006
+ DebugDirPtr debugDir = make_shared<DebugDir>(preparation.uid, preparation.gid);
2007
+ pid_t pid;
2008
+
2009
+ pid = syscalls::fork();
2010
+ if (pid == 0) {
2011
+ setenv("PASSENGER_DEBUG_DIR", debugDir->getPath().c_str(), 1);
2012
+ purgeStdio(stdout);
2013
+ purgeStdio(stderr);
2014
+ resetSignalHandlersAndMask();
2015
+ disableMallocDebugging();
2016
+ int adminSocketCopy = dup2(adminSocket.first, 3);
2017
+ int errorPipeCopy = dup2(errorPipe.second, 4);
2018
+ dup2(adminSocketCopy, 0);
2019
+ dup2(adminSocketCopy, 1);
2020
+ dup2(errorPipeCopy, 2);
2021
+ closeAllFileDescriptors(2);
2022
+ setChroot(preparation);
2023
+ switchUser(preparation);
2024
+ setWorkingDirectory(preparation);
2025
+ execvp(args[0], (char * const *) args.get());
2026
+
2027
+ int e = errno;
2028
+ printf("!> Error\n");
2029
+ printf("!> \n");
2030
+ printf("Cannot execute \"%s\": %s (errno=%d)\n", command[0].c_str(),
2031
+ strerror(e), e);
2032
+ fprintf(stderr, "Cannot execute \"%s\": %s (errno=%d)\n",
2033
+ command[0].c_str(), strerror(e), e);
2034
+ fflush(stdout);
2035
+ fflush(stderr);
2036
+ _exit(1);
2037
+
2038
+ } else if (pid == -1) {
2039
+ int e = errno;
2040
+ throw SystemException("Cannot fork a new process", e);
2041
+
2042
+ } else {
2043
+ UPDATE_TRACE_POINT();
2044
+ ScopeGuard guard(boost::bind(nonInterruptableKillAndWaitpid, pid));
2045
+ adminSocket.first.close();
2046
+ errorPipe.second.close();
2047
+
2048
+ NegotiationDetails details;
2049
+ details.libev = libev;
2050
+ details.stderrCapturer =
2051
+ make_shared<BackgroundIOCapturer>(
2052
+ errorPipe.first,
2053
+ forwardStderr ? STDERR_FILENO : -1);
2054
+ details.stderrCapturer->start();
2055
+ details.pid = pid;
2056
+ details.adminSocket = adminSocket.second;
2057
+ details.io = BufferedIO(adminSocket.second);
2058
+ details.errorPipe = errorPipe.first;
2059
+ details.options = &options;
2060
+ details.forwardStderr = forwardStderr;
2061
+ details.debugDir = debugDir;
2062
+
2063
+ ProcessPtr process = negotiateSpawn(details);
2064
+ detachProcess(process->pid);
2065
+ guard.clear();
2066
+ P_DEBUG("Process spawning done: appRoot=" << options.appRoot <<
2067
+ ", pid=" << process->pid);
2068
+ return process;
2069
+ }
2070
+ }
2071
+ };
2072
+
2073
+
2074
+ class DummySpawner: public Spawner {
2075
+ private:
2076
+ boost::mutex lock;
2077
+ unsigned int count;
2078
+
2079
+ public:
2080
+ unsigned int concurrency;
2081
+ unsigned int spawnTime;
2082
+ unsigned int cleanCount;
2083
+
2084
+ DummySpawner(const ResourceLocator &resourceLocator)
2085
+ : Spawner(resourceLocator)
2086
+ {
2087
+ count = 0;
2088
+ concurrency = 1;
2089
+ spawnTime = 0;
2090
+ cleanCount = 0;
2091
+ }
2092
+
2093
+ virtual ProcessPtr spawn(const Options &options) {
2094
+ TRACE_POINT();
2095
+ possiblyRaiseInternalError(options);
2096
+
2097
+ SocketPair adminSocket = createUnixSocketPair();
2098
+ SocketListPtr sockets = make_shared<SocketList>();
2099
+ sockets->add("main", "tcp://127.0.0.1:1234", "session", concurrency);
2100
+ syscalls::usleep(spawnTime);
2101
+
2102
+ lock_guard<boost::mutex> l(lock);
2103
+ count++;
2104
+ return make_shared<Process>(SafeLibevPtr(),
2105
+ (pid_t) count, "gupid-" + toString(count),
2106
+ toString(count),
2107
+ adminSocket.second, FileDescriptor(), sockets,
2108
+ SystemTime::getUsec(), SystemTime::getUsec());
2109
+ }
2110
+
2111
+ virtual bool cleanable() const {
2112
+ return true;
2113
+ }
2114
+
2115
+ virtual void cleanup() {
2116
+ cleanCount++;
2117
+ }
2118
+ };
2119
+
2120
+ typedef shared_ptr<DummySpawner> DummySpawnerPtr;
2121
+
2122
+
2123
+ class SpawnerFactory {
2124
+ private:
2125
+ SafeLibevPtr libev;
2126
+ ResourceLocator resourceLocator;
2127
+ ServerInstanceDir::GenerationPtr generation;
2128
+ RandomGeneratorPtr randomGenerator;
2129
+
2130
+ SpawnerPtr tryCreateSmartSpawner(const Options &options) {
2131
+ string dir = resourceLocator.getHelperScriptsDir();
2132
+ vector<string> preloaderCommand;
2133
+ if (options.appType == "classic-rails") {
2134
+ preloaderCommand.push_back(options.ruby);
2135
+ preloaderCommand.push_back(dir + "/classic-rails-preloader.rb");
2136
+ } else if (options.appType == "rack") {
2137
+ preloaderCommand.push_back(options.ruby);
2138
+ preloaderCommand.push_back(dir + "/rack-preloader.rb");
2139
+ } else {
2140
+ return SpawnerPtr();
2141
+ }
2142
+ return make_shared<SmartSpawner>(libev, resourceLocator,
2143
+ generation, preloaderCommand, options,
2144
+ randomGenerator);
2145
+ }
2146
+
2147
+ public:
2148
+ // Properties for DummySpawner
2149
+ unsigned int dummyConcurrency;
2150
+ unsigned int dummySpawnerCreationSleepTime;
2151
+ unsigned int dummySpawnTime;
2152
+
2153
+ // Properties for SmartSpawner and DirectSpawner.
2154
+ bool forwardStderr;
2155
+
2156
+ SpawnerFactory(const SafeLibevPtr &_libev,
2157
+ const ResourceLocator &_resourceLocator,
2158
+ const ServerInstanceDir::GenerationPtr &_generation,
2159
+ const RandomGeneratorPtr &_randomGenerator = RandomGeneratorPtr())
2160
+ : libev(_libev),
2161
+ resourceLocator(_resourceLocator),
2162
+ generation(_generation)
2163
+ {
2164
+ dummyConcurrency = 1;
2165
+ dummySpawnerCreationSleepTime = 0;
2166
+ dummySpawnTime = 0;
2167
+ forwardStderr = true;
2168
+ if (randomGenerator == NULL) {
2169
+ randomGenerator = make_shared<RandomGenerator>();
2170
+ } else {
2171
+ randomGenerator = _randomGenerator;
2172
+ }
2173
+ }
2174
+
2175
+ virtual ~SpawnerFactory() { }
2176
+
2177
+ virtual SpawnerPtr create(const Options &options) {
2178
+ if (options.spawnMethod == "smart" || options.spawnMethod == "smart-lv2") {
2179
+ SpawnerPtr spawner = tryCreateSmartSpawner(options);
2180
+ if (spawner == NULL) {
2181
+ spawner = make_shared<DirectSpawner>(libev,
2182
+ resourceLocator, generation,
2183
+ randomGenerator);
2184
+ static_pointer_cast<DirectSpawner>(spawner)->forwardStderr = forwardStderr;
2185
+ } else {
2186
+ static_pointer_cast<SmartSpawner>(spawner)->forwardStderr = forwardStderr;
2187
+ }
2188
+ return spawner;
2189
+ } else if (options.spawnMethod == "direct" || options.spawnMethod == "conservative") {
2190
+ shared_ptr<DirectSpawner> spawner = make_shared<DirectSpawner>(libev, resourceLocator,
2191
+ generation, randomGenerator);
2192
+ spawner->forwardStderr = forwardStderr;
2193
+ return spawner;
2194
+ } else if (options.spawnMethod == "dummy") {
2195
+ syscalls::usleep(dummySpawnerCreationSleepTime);
2196
+ DummySpawnerPtr spawner = make_shared<DummySpawner>(resourceLocator);
2197
+ spawner->concurrency = dummyConcurrency;
2198
+ spawner->spawnTime = dummySpawnTime;
2199
+ return spawner;
2200
+ } else {
2201
+ throw ArgumentException("Unknown spawn method '" + options.spawnMethod + "'");
2202
+ }
2203
+ }
2204
+ };
2205
+
2206
+ typedef shared_ptr<SpawnerFactory> SpawnerFactoryPtr;
2207
+
2208
+
2209
+ } // namespace ApplicationPool2
2210
+ } // namespace Passenger
2211
+
2212
+ #endif /* _PASSENGER_APPLICATION_POOL2_SPAWNER_H_ */