passenger 4.0.2 → 4.0.3

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 (79) hide show
  1. data.tar.gz.asc +7 -7
  2. data/NEWS +27 -0
  3. data/bin/passenger-config +6 -3
  4. data/bin/passenger-install-apache2-module +2 -2
  5. data/bin/passenger-install-nginx-module +16 -2
  6. data/build/agents.rb +4 -0
  7. data/build/apache2.rb +1 -1
  8. data/build/cplusplus_support.rb +1 -1
  9. data/build/cxx_tests.rb +3 -0
  10. data/build/packaging.rb +51 -8
  11. data/build/ruby_extension.rb +1 -1
  12. data/doc/Packaging.txt.md +20 -7
  13. data/doc/Users guide Apache.html +1 -1
  14. data/doc/Users guide Apache.txt +1 -1
  15. data/doc/Users guide Nginx.html +5 -4
  16. data/doc/Users guide Nginx.txt +1 -1
  17. data/doc/users_guide_snippets/installation.txt +5 -3
  18. data/ext/apache2/Configuration.cpp +12 -0
  19. data/ext/apache2/Configuration.hpp +7 -4
  20. data/ext/apache2/Hooks.cpp +29 -19
  21. data/ext/common/AgentsStarter.cpp +85 -57
  22. data/ext/common/AgentsStarter.h +570 -42
  23. data/ext/common/ApplicationPool2/DirectSpawner.h +5 -2
  24. data/ext/common/ApplicationPool2/Implementation.cpp +7 -1
  25. data/ext/common/ApplicationPool2/Pool.h +6 -3
  26. data/ext/common/ApplicationPool2/Process.h +12 -3
  27. data/ext/common/ApplicationPool2/SmartSpawner.h +2 -1
  28. data/ext/common/Constants.h +4 -1
  29. data/ext/common/EventedBufferedInput.h +139 -16
  30. data/ext/common/MultiLibeio.cpp +4 -2
  31. data/ext/common/SafeLibev.h +15 -62
  32. data/ext/common/ServerInstanceDir.h +10 -26
  33. data/ext/common/Utils.cpp +1 -3
  34. data/ext/common/Utils.h +1 -1
  35. data/ext/common/Utils/StrIntUtils.cpp +9 -0
  36. data/ext/common/Utils/StrIntUtils.h +5 -0
  37. data/ext/common/Utils/VariantMap.h +63 -14
  38. data/ext/common/agents/Base.cpp +50 -15
  39. data/ext/common/agents/HelperAgent/AgentOptions.h +20 -12
  40. data/ext/common/agents/HelperAgent/FileBackedPipe.h +1 -1
  41. data/ext/common/agents/HelperAgent/Main.cpp +5 -4
  42. data/ext/common/agents/HelperAgent/RequestHandler.h +1 -1
  43. data/ext/common/agents/LoggingAgent/Main.cpp +0 -1
  44. data/ext/common/agents/LoggingAgent/RemoteSender.h +2 -2
  45. data/ext/common/agents/SpawnPreparer.cpp +23 -5
  46. data/ext/common/agents/Watchdog/AgentWatcher.cpp +508 -0
  47. data/ext/common/agents/Watchdog/HelperAgentWatcher.cpp +93 -0
  48. data/ext/common/agents/Watchdog/LoggingAgentWatcher.cpp +68 -0
  49. data/ext/common/agents/Watchdog/Main.cpp +180 -802
  50. data/ext/common/agents/Watchdog/ServerInstanceDirToucher.cpp +111 -0
  51. data/ext/nginx/Configuration.c +107 -92
  52. data/ext/nginx/Configuration.h +1 -0
  53. data/ext/nginx/ContentHandler.c +6 -6
  54. data/ext/nginx/ContentHandler.h +1 -1
  55. data/ext/nginx/config +8 -2
  56. data/ext/nginx/ngx_http_passenger_module.c +54 -60
  57. data/ext/nginx/ngx_http_passenger_module.h +6 -6
  58. data/lib/phusion_passenger.rb +17 -10
  59. data/lib/phusion_passenger/admin_tools/server_instance.rb +2 -2
  60. data/lib/phusion_passenger/common_library.rb +0 -1
  61. data/lib/phusion_passenger/platform_info.rb +10 -1
  62. data/lib/phusion_passenger/platform_info/depcheck.rb +4 -4
  63. data/lib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb +2 -2
  64. data/lib/phusion_passenger/platform_info/ruby.rb +7 -0
  65. data/lib/phusion_passenger/request_handler.rb +119 -42
  66. data/lib/phusion_passenger/request_handler/thread_handler.rb +25 -22
  67. data/lib/phusion_passenger/standalone/command.rb +2 -0
  68. data/lib/phusion_passenger/standalone/runtime_installer.rb +4 -3
  69. data/lib/phusion_passenger/standalone/start_command.rb +49 -37
  70. data/resources/templates/nginx/pcre_checksum_could_not_be_verified.txt.erb +11 -0
  71. data/test/cxx/CxxTestMain.cpp +2 -0
  72. data/test/cxx/EventedBufferedInputTest.cpp +758 -0
  73. data/test/cxx/ServerInstanceDirTest.cpp +16 -31
  74. data/test/cxx/TestSupport.cpp +2 -1
  75. data/test/cxx/VariantMapTest.cpp +23 -11
  76. metadata +8 -4
  77. metadata.gz.asc +7 -7
  78. data/ext/common/AgentsStarter.hpp +0 -655
  79. data/lib/phusion_passenger/utils/robust_interruption.rb +0 -173
@@ -23,26 +23,11 @@ namespace tut {
23
23
 
24
24
  DEFINE_TEST_GROUP(ServerInstanceDirTest);
25
25
 
26
- TEST_METHOD(1) {
27
- // The (pid_t, string) constructor creates a server instance directory
28
- // in the given parent directory, and this server instance directory
29
- // name contains the major and minor structure versions and the given PID.
30
- ServerInstanceDir dir(1234, parentDir);
31
- vector<string> contents = listDir(parentDir);
32
- ensure_equals(contents.size(), 1u);
33
- ensure_equals(contents[0],
34
- "passenger." +
35
- toString(ServerInstanceDir::DIR_STRUCTURE_MAJOR_VERSION) +
36
- "." +
37
- toString(ServerInstanceDir::DIR_STRUCTURE_MINOR_VERSION) +
38
- ".1234");
39
- }
40
-
41
26
  TEST_METHOD(2) {
42
27
  // The (string) constructor creates a ServerInstanceDir object that's
43
28
  // associated with the given directory, and creates the directory
44
29
  // if it doesn't exist.
45
- ServerInstanceDir dir(1234, parentDir);
30
+ ServerInstanceDir dir(parentDir + "/passenger-test.1234");
46
31
  ServerInstanceDir dir2(dir.getPath());
47
32
  ServerInstanceDir dir3(parentDir + "/foo");
48
33
  ensure_equals(dir2.getPath(), dir.getPath());
@@ -54,12 +39,12 @@ namespace tut {
54
39
  // A ServerInstanceDir object removes the server instance directory
55
40
  // upon destruction, but only if there are no more generations in it.
56
41
  {
57
- ServerInstanceDir dir(1234, parentDir);
42
+ ServerInstanceDir dir(parentDir + "/passenger-test.1234");
58
43
  }
59
44
  ensure_equals(listDir(parentDir).size(), 0u);
60
45
 
61
46
  {
62
- ServerInstanceDir dir(1234, parentDir);
47
+ ServerInstanceDir dir(parentDir + "/passenger-test.1234");
63
48
  createGenerationDir(dir.getPath(), 1);
64
49
  }
65
50
  ensure_equals(listDir(parentDir).size(), 1u);
@@ -68,7 +53,7 @@ namespace tut {
68
53
  TEST_METHOD(4) {
69
54
  // The destructor does not throw any exceptions if the server instance
70
55
  // directory doesn't exist anymore.
71
- ServerInstanceDir dir(1234, parentDir);
56
+ ServerInstanceDir dir(parentDir + "/passenger-test.1234");
72
57
  removeDirTree(dir.getPath());
73
58
  }
74
59
 
@@ -77,8 +62,8 @@ namespace tut {
77
62
  // wasn't created with the ownership flag or if it's been detached.
78
63
  string path, path2;
79
64
  {
80
- ServerInstanceDir dir(1234, parentDir, false);
81
- ServerInstanceDir dir2(5678, parentDir);
65
+ ServerInstanceDir dir(parentDir + "/passenger-test.1234", false);
66
+ ServerInstanceDir dir2(parentDir + "/passenger-test.5678", false);
82
67
  dir2.detach();
83
68
  path = dir.getPath();
84
69
  path2 = dir2.getPath();
@@ -90,7 +75,7 @@ namespace tut {
90
75
  TEST_METHOD(6) {
91
76
  // If there are no existing generations, newGeneration() creates a new
92
77
  // generation directory with number 0.
93
- ServerInstanceDir dir(1234, parentDir);
78
+ ServerInstanceDir dir(parentDir + "/passenger-test.1234");
94
79
  unsigned int ncontents = listDir(dir.getPath()).size();
95
80
  ServerInstanceDir::GenerationPtr generation = dir.newGeneration(true,
96
81
  "nobody", nobodyGroup, 0, 0);
@@ -103,7 +88,7 @@ namespace tut {
103
88
  TEST_METHOD(7) {
104
89
  // A Generation object returned by newGeneration() deletes the associated
105
90
  // generation directory upon destruction.
106
- ServerInstanceDir dir(1234, parentDir);
91
+ ServerInstanceDir dir(parentDir + "/passenger-test.1234");
107
92
  ServerInstanceDir::GenerationPtr generation = dir.newGeneration(true,
108
93
  "nobody", nobodyGroup, 0, 0);
109
94
  string path = generation->getPath();
@@ -113,7 +98,7 @@ namespace tut {
113
98
 
114
99
  TEST_METHOD(8) {
115
100
  // getNewestGeneration() returns the newest generation.
116
- ServerInstanceDir dir(1234, parentDir);
101
+ ServerInstanceDir dir(parentDir + "/passenger-test.1234");
117
102
  ServerInstanceDir::GenerationPtr generation0 = dir.newGeneration(true, "nobody", nobodyGroup, 0, 0);
118
103
  ServerInstanceDir::GenerationPtr generation1 = dir.newGeneration(true, "nobody", nobodyGroup, 0, 0);
119
104
  ServerInstanceDir::GenerationPtr generation2 = dir.newGeneration(true, "nobody", nobodyGroup, 0, 0);
@@ -127,14 +112,14 @@ namespace tut {
127
112
 
128
113
  TEST_METHOD(9) {
129
114
  // getNewestGeneration returns null if there are no generations.
130
- ServerInstanceDir dir(1234, parentDir);
115
+ ServerInstanceDir dir(parentDir + "/passenger-test.1234");
131
116
  ensure(dir.getNewestGeneration() == NULL);
132
117
  }
133
118
 
134
119
  TEST_METHOD(10) {
135
120
  // A Generation object returned by getNewestGeneration() doesn't delete
136
121
  // the associated generation directory upon destruction.
137
- ServerInstanceDir dir(1234, parentDir);
122
+ ServerInstanceDir dir(parentDir + "/passenger-test.1234");
138
123
  ServerInstanceDir::GenerationPtr generation = dir.newGeneration(true, "nobody", nobodyGroup, 0, 0);
139
124
  ServerInstanceDir::GenerationPtr newestGeneration = dir.getNewestGeneration();
140
125
  newestGeneration.reset();
@@ -143,7 +128,7 @@ namespace tut {
143
128
 
144
129
  TEST_METHOD(11) {
145
130
  // getGeneration() returns the given generation.
146
- ServerInstanceDir dir(1234, parentDir);
131
+ ServerInstanceDir dir(parentDir + "/passenger-test.1234");
147
132
  ServerInstanceDir::GenerationPtr generation0 = dir.newGeneration(true, "nobody", nobodyGroup, 0, 0);
148
133
  ServerInstanceDir::GenerationPtr generation1 = dir.newGeneration(true, "nobody", nobodyGroup, 0, 0);
149
134
  ServerInstanceDir::GenerationPtr generation2 = dir.newGeneration(true, "nobody", nobodyGroup, 0, 0);
@@ -156,7 +141,7 @@ namespace tut {
156
141
  TEST_METHOD(12) {
157
142
  // A Generation object returned by getGeneration() doesn't delete the
158
143
  // associated generation directory upon destruction.
159
- ServerInstanceDir dir(1234, parentDir);
144
+ ServerInstanceDir dir(parentDir + "/passenger-test.1234");
160
145
  ServerInstanceDir::GenerationPtr generation0 = dir.newGeneration(true, "nobody", nobodyGroup, 0, 0);
161
146
  ServerInstanceDir::GenerationPtr generation1 = dir.newGeneration(true, "nobody", nobodyGroup, 0, 0);
162
147
 
@@ -169,7 +154,7 @@ namespace tut {
169
154
  TEST_METHOD(13) {
170
155
  // A detached Generation doesn't delete the associated generation
171
156
  // directory upon destruction.
172
- ServerInstanceDir dir(1234, parentDir);
157
+ ServerInstanceDir dir(parentDir + "/passenger-test.1234");
173
158
  ServerInstanceDir::GenerationPtr generation = dir.newGeneration(true, "nobody", nobodyGroup, 0, 0);
174
159
  string path = generation->getPath();
175
160
  generation->detach();
@@ -180,7 +165,7 @@ namespace tut {
180
165
  TEST_METHOD(14) {
181
166
  // It's possible to have two ServerInstanceDir objects constructed
182
167
  // with the same (pid_t, string) constructor arguments.
183
- ServerInstanceDir dir1(1234, parentDir);
184
- ServerInstanceDir dir2(1234, parentDir);
168
+ ServerInstanceDir dir(parentDir + "/passenger-test.1234");
169
+ ServerInstanceDir dir2(parentDir + "/passenger-test.1234");
185
170
  }
186
171
  }
@@ -19,7 +19,8 @@ Json::Value testConfig;
19
19
  void createServerInstanceDirAndGeneration(ServerInstanceDirPtr &serverInstanceDir,
20
20
  ServerInstanceDir::GenerationPtr &generation)
21
21
  {
22
- serverInstanceDir.reset(new ServerInstanceDir(getpid()));
22
+ string path = "/tmp/passenger-test." + toString(getpid());
23
+ serverInstanceDir.reset(new ServerInstanceDir(path));
23
24
  generation = serverInstanceDir->newGeneration(geteuid() == 0,
24
25
  "nobody", getPrimaryGroupName("nobody"),
25
26
  geteuid(), getegid());
@@ -21,18 +21,15 @@ namespace tut {
21
21
  // Test setting and getting string values.
22
22
  map.set("hello", "world");
23
23
  map.set("abcd", "efgh");
24
- map.set("foo", "");
25
24
  map.set("", "bar");
26
- ensure_equals(map.get("hello"), "world");
27
- ensure_equals(map.get("abcd"), "efgh");
28
- ensure_equals(map.get("foo"), "");
29
- ensure_equals(map.get(""), "bar");
30
- ensure_equals(map.size(), 4u);
31
- ensure(map.has("hello"));
32
- ensure(map.has("abcd"));
33
- ensure(map.has("foo"));
34
- ensure(map.has(""));
35
- ensure(!map.has("xyz"));
25
+ ensure_equals("(1)", map.get("hello"), "world");
26
+ ensure_equals("(2)", map.get("abcd"), "efgh");
27
+ ensure_equals("(3)", map.get(""), "bar");
28
+ ensure_equals("(4)", map.size(), 3u);
29
+ ensure("(5)", map.has("hello"));
30
+ ensure("(6)", map.has("abcd"));
31
+ ensure("(7)", map.has(""));
32
+ ensure("(8)", !map.has("xyz"));
36
33
  }
37
34
 
38
35
  TEST_METHOD(3) {
@@ -176,4 +173,19 @@ namespace tut {
176
173
  ensure_equals(map.get("foo"), "1234");
177
174
  ensure_equals(map.get("bar"), "5678");
178
175
  }
176
+
177
+ TEST_METHOD(7) {
178
+ // Setting an empty value result in the deletion of the key.
179
+ map.set("a", "a");
180
+ map.set("b", "b");
181
+ map.set("b", "");
182
+ try {
183
+ map.get("b");
184
+ fail("MissingKeyException expected");
185
+ } catch (const VariantMap::MissingKeyException &e) {
186
+ // Pass.
187
+ }
188
+ ensure(!map.has("foo"));
189
+ ensure_equals(map.size(), 1u);
190
+ }
179
191
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passenger
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.2
4
+ version: 4.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-07 00:00:00.000000000 Z
12
+ date: 2013-05-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -156,7 +156,6 @@ files:
156
156
  - lib/phusion_passenger/utils/ansi_colors.rb
157
157
  - lib/phusion_passenger/utils/file_system_watcher.rb
158
158
  - lib/phusion_passenger/utils/hosts_file_parser.rb
159
- - lib/phusion_passenger/utils/robust_interruption.rb
160
159
  - lib/phusion_passenger/utils/tee_input.rb
161
160
  - lib/phusion_passenger/utils/tmpdir.rb
162
161
  - lib/phusion_passenger/utils/tmpio.rb
@@ -281,7 +280,11 @@ files:
281
280
  - ext/common/agents/LoggingAgent/FilterSupport.cpp
282
281
  - ext/common/agents/LoggingAgent/Main.cpp
283
282
  - ext/common/agents/SpawnPreparer.cpp
283
+ - ext/common/agents/Watchdog/AgentWatcher.cpp
284
+ - ext/common/agents/Watchdog/HelperAgentWatcher.cpp
285
+ - ext/common/agents/Watchdog/LoggingAgentWatcher.cpp
284
286
  - ext/common/agents/Watchdog/Main.cpp
287
+ - ext/common/agents/Watchdog/ServerInstanceDirToucher.cpp
285
288
  - ext/common/AgentsStarter.cpp
286
289
  - ext/common/ApplicationPool2/AppTypes.cpp
287
290
  - ext/common/ApplicationPool2/Implementation.cpp
@@ -391,7 +394,6 @@ files:
391
394
  - ext/common/Utils/utf8.h
392
395
  - ext/common/Utils/VariantMap.h
393
396
  - ext/common/Utils.h
394
- - ext/common/AgentsStarter.hpp
395
397
  - ext/common/Utils/CachedFileStat.hpp
396
398
  - ext/common/ApplicationPool2/README.md
397
399
  - ext/apache2/Bucket.cpp
@@ -2223,6 +2225,7 @@ files:
2223
2225
  - resources/templates/nginx/confirm_extra_configure_flags.txt.erb
2224
2226
  - resources/templates/nginx/deployment_example.txt.erb
2225
2227
  - resources/templates/nginx/not_available_when_natively_packaged.txt.erb
2228
+ - resources/templates/nginx/pcre_checksum_could_not_be_verified.txt.erb
2226
2229
  - resources/templates/nginx/pcre_could_not_be_downloaded.txt.erb
2227
2230
  - resources/templates/nginx/pcre_could_not_be_extracted.txt.erb
2228
2231
  - resources/templates/nginx/possible_solutions_for_compilation_and_installation_problems.txt.erb
@@ -2261,6 +2264,7 @@ files:
2261
2264
  - test/cxx/CachedFileStatTest.cpp
2262
2265
  - test/cxx/CxxTestMain.cpp
2263
2266
  - test/cxx/DechunkerTest.cpp
2267
+ - test/cxx/EventedBufferedInputTest.cpp
2264
2268
  - test/cxx/EventedClientTest.cpp
2265
2269
  - test/cxx/FileBackedPipeTest.cpp
2266
2270
  - test/cxx/FileChangeCheckerTest.cpp
metadata.gz.asc CHANGED
@@ -2,11 +2,11 @@
2
2
  Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
3
3
  Comment: GPGTools - http://gpgtools.org
4
4
 
5
- iQEcBAABAgAGBQJRiUovAAoJECrHRaUKISqM+WoIAIOplVMq6lTNkFzoNJ28RM8i
6
- OdBHR1+p7veJJBWyTQhNRGyi416rayEUZL8pRlaeak7688fdiJZ+6spYkZmAWsvp
7
- LSu1UGS70jeZamGFsdnrMk9d2zY0r8+kOsjDksZ2t4awTmASYuNRlfvglxF1HIan
8
- l0lffOJNXyBddrN315x5Lo0i8fWXjSahqXRgP+7ZU3+van8r5vK4UBeXGVTk8w2h
9
- MiOmf54mXFCq/Bg85FX7mS6Odxlu25+UkSm88nq5zTDS8C7OAlP295rE9bFBGFJ5
10
- 63zXJEH/lcpFp3OisX4Sixz9wGiza7pmtQd9veQMwmlX/PtL3nME3NbXLlCkp5g=
11
- =gRq7
5
+ iQEcBAABAgAGBQJRn9OxAAoJECrHRaUKISqM/UAH/282HhIyLkr616LFhFf7KWTQ
6
+ OQnu9P8NlHQaUw/yJDZonQDMnMjlPuUYdgJ3xgqFEpLpcAspmJdhlO1G4z4jdhtV
7
+ G5kTA9LCCuBAbpml0d17grFOEO/23VhJaNjU2uOQKnxS2Q6zVazffCj8AQwRugNc
8
+ PXCshWCE909zln9HgpC3Rs+W0kJHGsrkX/lFHQPWQvVuTQYxYYBuHsafj+TcAzjg
9
+ wB7TRZIsBV4SzGuaQDlmDhwDo38LEOHhg3d7wFej0sGaUaqmrBgcgBt8UKPI2nr5
10
+ U43eiJEitSfaNxCtLtfIQDGxvSVcY2Qh2yJ/+L4UguStJbxlQ5dgNswD3P1yc5w=
11
+ =zaV0
12
12
  -----END PGP SIGNATURE-----
@@ -1,655 +0,0 @@
1
- /*
2
- * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2010-2013 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_AGENTS_STARTER_HPP_
26
- #define _PASSENGER_AGENTS_STARTER_HPP_
27
-
28
- #include <boost/function.hpp>
29
- #include <oxt/system_calls.hpp>
30
- #include <oxt/backtrace.hpp>
31
- #include <string>
32
- #include <vector>
33
- #include <set>
34
-
35
- #include <sys/types.h>
36
- #include <unistd.h>
37
- #include <signal.h>
38
-
39
- #include <Constants.h>
40
- #include <FileDescriptor.h>
41
- #include <MessageClient.h>
42
- #include <ServerInstanceDir.h>
43
- #include <Exceptions.h>
44
- #include <ResourceLocator.h>
45
- #include <Utils.h>
46
- #include <Utils/IOUtils.h>
47
- #include <Utils/MessageIO.h>
48
- #include <Utils/Base64.h>
49
- #include <Utils/Timer.h>
50
- #include <Utils/ScopeGuard.h>
51
- #include <Utils/VariantMap.h>
52
-
53
- namespace Passenger {
54
-
55
- using namespace std;
56
- using namespace boost;
57
- using namespace oxt;
58
-
59
- /**
60
- * Utility class for starting various Phusion Passenger agents through the watchdog.
61
- */
62
- class AgentsStarter {
63
- public:
64
- enum Type {
65
- APACHE,
66
- NGINX
67
- };
68
-
69
- private:
70
- /** The watchdog's PID. Equals 0 if the watchdog hasn't been started yet
71
- * or if detach() is called. */
72
- pid_t pid;
73
-
74
- Type type;
75
-
76
- /** The watchdog's feedback file descriptor. Only valid if pid != 0. */
77
- FileDescriptor feedbackFd;
78
-
79
- /**
80
- * The helper agent's request socket filename. This socket only exists
81
- * for the Nginx helper agent, and it's for serving SCGI requests.
82
- *
83
- * Only valid if pid != 0.
84
- */
85
- string requestSocketFilename;
86
-
87
- /**
88
- * A password for connecting to the request socket. Only valid if pid != 0.
89
- */
90
- string requestSocketPassword;
91
-
92
- /**
93
- * The helper agent's message server socket filename, on which e.g. the
94
- * application pool server is listening. Only valid if pid != 0.
95
- *
96
- * The application pool server is available through the account "_web_server".
97
- */
98
- string messageSocketFilename;
99
-
100
- /**
101
- * A password for the message server socket. The associated username is "_web_server".
102
- *
103
- * Only valid if pid != 0.
104
- */
105
- string messageSocketPassword;
106
-
107
- bool loggingAgentRunningLocally;
108
- string loggingSocketAddress;
109
- string loggingSocketPassword;
110
-
111
- /**
112
- * The server instance dir of the agents. Only valid if pid != 0.
113
- */
114
- ServerInstanceDirPtr serverInstanceDir;
115
-
116
- /**
117
- * The generation dir of the agents. Only valid if pid != 0.
118
- */
119
- ServerInstanceDir::GenerationPtr generation;
120
-
121
- /**
122
- * Safely dup2() the given file descriptor to 3 (FEEDBACK_FD).
123
- */
124
- void installFeedbackFd(const FileDescriptor &fd) {
125
- if (fd != FEEDBACK_FD && syscalls::dup2(fd, FEEDBACK_FD) == -1) {
126
- int e = errno;
127
- try {
128
- writeArrayMessage(fd,
129
- "system error",
130
- "dup2() failed",
131
- toString(e).c_str(),
132
- NULL);
133
- _exit(1);
134
- } catch (...) {
135
- fprintf(stderr, "Passenger AgentsStarter: dup2() failed: %s (%d)\n",
136
- strerror(e), e);
137
- fflush(stderr);
138
- _exit(1);
139
- }
140
- }
141
- }
142
-
143
- /**
144
- * Call this if the watchdog seems to have crashed. This function will try
145
- * to determine whether the watchdog is still running, whether it crashed
146
- * with a signal, etc. If it has detected that the watchdog is no longer running
147
- * then it will set <em>pid</em> to -1.
148
- */
149
- void inspectWatchdogCrashReason(pid_t &pid) {
150
- this_thread::disable_interruption di;
151
- this_thread::disable_syscall_interruption dsi;
152
- int ret, status;
153
-
154
- /* Upon noticing that something went wrong, the watchdog
155
- * or its subprocesses might still be writing out an error
156
- * report, so we wait a while before killing the watchdog.
157
- */
158
- ret = timedWaitPid(pid, &status, 5000);
159
- if (ret == 0) {
160
- /* Looks like the watchdog didn't crash and is still running. */
161
- throw RuntimeException(
162
- "Unable to start the Phusion Passenger watchdog: "
163
- "it froze during startup and reported an unknown error");
164
- } else if (ret != -1 && WIFSIGNALED(status)) {
165
- /* Looks like a crash which caused a signal. */
166
- pid = -1;
167
- throw RuntimeException(
168
- "Unable to start the Phusion Passenger watchdog: "
169
- "it seems to have been killed with signal " +
170
- getSignalName(WTERMSIG(status)) + " during startup");
171
- } else if (ret == -1) {
172
- /* Looks like it exited for a different reason and has no exit code. */
173
- pid = -1;
174
- throw RuntimeException(
175
- "Unable to start the Phusion Passenger watchdog: "
176
- "it seems to have crashed during startup for an unknown reason");
177
- } else {
178
- /* Looks like it exited for a different reason, but has an exit code. */
179
- pid = -1;
180
- throw RuntimeException(
181
- "Unable to start the Phusion Passenger watchdog: "
182
- "it seems to have crashed during startup for an unknown reason, "
183
- "with exit code " + toString(WEXITSTATUS(status)));
184
- }
185
- }
186
-
187
- static void killProcessGroupAndWait(pid_t *pid, unsigned long long timeout = 0) {
188
- if (*pid != -1 && (timeout == 0 || timedWaitPid(*pid, NULL, timeout) <= 0)) {
189
- this_thread::disable_syscall_interruption dsi;
190
- syscalls::killpg(*pid, SIGKILL);
191
- syscalls::waitpid(*pid, NULL, 0);
192
- *pid = -1;
193
- }
194
- }
195
-
196
- /**
197
- * Behaves like <tt>waitpid(pid, status, WNOHANG)</tt>, but waits at most
198
- * <em>timeout</em> miliseconds for the process to exit.
199
- */
200
- static int timedWaitPid(pid_t pid, int *status, unsigned long long timeout) {
201
- Timer timer;
202
- int ret;
203
-
204
- do {
205
- ret = syscalls::waitpid(pid, status, WNOHANG);
206
- if (ret > 0 || ret == -1) {
207
- return ret;
208
- } else {
209
- syscalls::usleep(10000);
210
- }
211
- } while (timer.elapsed() < timeout);
212
- return 0; // timed out
213
- }
214
-
215
- /**
216
- * Gracefully shutdown an agent process by sending an exit command to its socket.
217
- * Returns whether the agent has successfully processed the exit command.
218
- * Any exceptions are caught and will cause false to be returned.
219
- */
220
- bool gracefullyShutdownAgent(const string &socketFilename, const string &username,
221
- const string &password
222
- ) {
223
- try {
224
- MessageClient client;
225
- vector<string> args;
226
-
227
- client.connect("unix:" + socketFilename, username, password);
228
- client.write("exit", NULL);
229
- return client.read(args) && args[0] == "Passed security" &&
230
- client.read(args) && args[0] == "exit command received";
231
- } catch (const SystemException &) {
232
- } catch (const IOException &) {
233
- } catch (const SecurityException &) {
234
- }
235
- return false;
236
- }
237
-
238
- string serializePrestartURLs(const set<string> &prestartURLs) const {
239
- set<string>::const_iterator it;
240
- string result;
241
-
242
- for (it = prestartURLs.begin(); it != prestartURLs.end(); it++) {
243
- result.append(*it);
244
- result.append(1, '\0');
245
- }
246
- return Base64::encode(result);
247
- }
248
-
249
- public:
250
- /**
251
- * Construct a AgentsStarter object. The watchdog and the agents
252
- * aren't started yet until you call start().
253
- *
254
- * @param type Whether one wants to start the Apache or the Nginx helper agent.
255
- */
256
- AgentsStarter(Type type) {
257
- pid = 0;
258
- loggingAgentRunningLocally = false;
259
- this->type = type;
260
- }
261
-
262
- ~AgentsStarter() {
263
- if (pid != 0) {
264
- this_thread::disable_syscall_interruption dsi;
265
- bool cleanShutdown = gracefullyShutdownAgent(messageSocketFilename,
266
- "_web_server", messageSocketPassword);
267
- if (loggingAgentRunningLocally) {
268
- string filename = parseUnixSocketAddress(loggingSocketAddress);
269
- cleanShutdown = cleanShutdown &&
270
- gracefullyShutdownAgent(filename,
271
- "logging", loggingSocketPassword);
272
- }
273
-
274
- /* Send a message down the feedback fd to tell the watchdog
275
- * Whether this is a clean shutdown. Closing the fd without
276
- * sending anything also indicates an unclean shutdown,
277
- * but we send a byte anyway in case there are other processes
278
- * who have the fd open.
279
- */
280
- if (cleanShutdown) {
281
- syscalls::write(feedbackFd, "c", 1);
282
- } else {
283
- syscalls::write(feedbackFd, "u", 1);
284
- }
285
-
286
- /* If we failed to send an exit command to one of the agents then we have
287
- * to forcefully kill all agents now because otherwise one of them might
288
- * never exit. We do this by closing the feedback fd without sending a
289
- * random byte, to indicate that this is an abnormal shutdown. The watchdog
290
- * will then kill all agents.
291
- */
292
-
293
- feedbackFd.close();
294
- syscalls::waitpid(pid, NULL, 0);
295
- }
296
- }
297
-
298
- /**
299
- * Returns the type as was passed to the constructor.
300
- */
301
- Type getType() const {
302
- return type;
303
- }
304
-
305
- /**
306
- * Returns the watchdog's PID. Equals 0 if the watchdog hasn't been started yet
307
- * or if detach() is called.
308
- */
309
- pid_t getPid() const {
310
- return pid;
311
- }
312
-
313
- /**
314
- * The helper agent's request socket filename, on which it's listening
315
- * for SCGI requests.
316
- *
317
- * @pre getPid() != 0 && getType() == NGINX
318
- */
319
- string getRequestSocketFilename() const {
320
- return requestSocketFilename;
321
- }
322
-
323
- /**
324
- * Returns the password for connecting to the request socket.
325
- *
326
- * @pre getPid() != 0 && getType() == NGINX
327
- */
328
- string getRequestSocketPassword() const {
329
- return requestSocketPassword;
330
- }
331
-
332
- string getMessageSocketFilename() const {
333
- return messageSocketFilename;
334
- }
335
-
336
- string getMessageSocketPassword() const {
337
- return messageSocketPassword;
338
- }
339
-
340
- string getLoggingSocketAddress() const {
341
- return loggingSocketAddress;
342
- }
343
-
344
- string getLoggingSocketPassword() const {
345
- return loggingSocketPassword;
346
- }
347
-
348
- /**
349
- * Returns the server instance dir of the agents.
350
- *
351
- * @pre getPid() != 0
352
- */
353
- ServerInstanceDirPtr getServerInstanceDir() const {
354
- return serverInstanceDir;
355
- }
356
-
357
- /**
358
- * Returns the generation dir of the agents.
359
- *
360
- * @pre getPid() != 0
361
- */
362
- ServerInstanceDir::GenerationPtr getGeneration() const {
363
- return generation;
364
- }
365
-
366
- /**
367
- * Start the agents through the watchdog, with the given parameters.
368
- *
369
- * @throws SystemException Something went wrong.
370
- * @throws IOException Something went wrong while communicating with one
371
- * of the agents during its initialization phase.
372
- * @throws RuntimeException Something went wrong.
373
- */
374
- void start(int logLevel, const string &debugLogFile,
375
- pid_t webServerPid, const string &tempDir,
376
- bool userSwitching, const string &defaultUser, const string &defaultGroup,
377
- uid_t webServerWorkerUid, gid_t webServerWorkerGid,
378
- const string &passengerRoot, const string &defaultRubyCommand,
379
- unsigned int maxPoolSize, unsigned int maxInstancesPerApp,
380
- unsigned int poolIdleTime,
381
- const string &analyticsServer,
382
- const string &analyticsLogUser,
383
- const string &analyticsLogGroup,
384
- const string &unionStationGatewayAddress,
385
- unsigned short unionStationGatewayPort,
386
- const string &unionStationGatewayCert,
387
- const string &unionStationProxyAddress,
388
- const set<string> &prestartURLs,
389
- const function<void ()> &afterFork = function<void ()>())
390
- {
391
- TRACE_POINT();
392
- this_thread::disable_interruption di;
393
- this_thread::disable_syscall_interruption dsi;
394
- ResourceLocator locator(passengerRoot);
395
-
396
- string realUnionStationGatewayCert;
397
- if (unionStationGatewayCert.empty()) {
398
- realUnionStationGatewayCert = locator.getResourcesDir() + "/union_station_gateway.crt";
399
- } else if (unionStationGatewayCert != "-") {
400
- realUnionStationGatewayCert = unionStationGatewayCert;
401
- }
402
- string watchdogFilename = locator.getAgentsDir() + "/PassengerWatchdog";
403
-
404
- VariantMap watchdogArgs;
405
- watchdogArgs
406
- .set ("web_server_type", type == APACHE ? "apache" : "nginx")
407
- .setInt ("log_level", logLevel)
408
- .set ("debug_log_file", debugLogFile)
409
- .setPid ("web_server_pid", webServerPid)
410
- .set ("temp_dir", tempDir.empty() ? getSystemTempDir() : tempDir)
411
- .setBool("user_switching", userSwitching)
412
- .set ("default_user", defaultUser)
413
- .set ("default_group", defaultGroup)
414
- .setUid ("web_server_worker_uid", webServerWorkerUid)
415
- .setGid ("web_server_worker_gid", webServerWorkerGid)
416
- .set ("passenger_root", passengerRoot)
417
- .set ("default_ruby", defaultRubyCommand)
418
- .setInt ("max_pool_size", maxPoolSize)
419
- .setInt ("max_instances_per_app", maxInstancesPerApp)
420
- .setInt ("pool_idle_time", poolIdleTime)
421
- .set ("analytics_server", analyticsServer)
422
- .set ("analytics_log_user", analyticsLogUser)
423
- .set ("analytics_log_group", analyticsLogGroup)
424
- .set ("union_station_gateway_address", unionStationGatewayAddress)
425
- .setInt ("union_station_gateway_port", unionStationGatewayPort)
426
- .set ("union_station_gateway_cert", realUnionStationGatewayCert)
427
- .set ("union_station_proxy_address", unionStationProxyAddress)
428
- .set ("prestart_urls", serializePrestartURLs(prestartURLs));
429
-
430
- SocketPair fds;
431
- int e;
432
- pid_t pid;
433
-
434
- fds = createUnixSocketPair();
435
- pid = syscalls::fork();
436
- if (pid == 0) {
437
- // Child
438
-
439
- /* Become the session leader so that Apache can't kill the
440
- * watchdog with killpg() during shutdown, so that a
441
- * Ctrl-C only affects the web server, and so that
442
- * we can kill all of our subprocesses in a single killpg().
443
- */
444
- setsid();
445
-
446
- // Make sure the feedback fd is 3 and close all file descriptors
447
- // except stdin, stdout, stderr and 3.
448
- syscalls::close(fds[0]);
449
- installFeedbackFd(fds[1]);
450
- closeAllFileDescriptors(FEEDBACK_FD);
451
-
452
- /* We don't know how the web server or the environment affect
453
- * signal handlers and the signal mask, so reset this stuff
454
- * just in case.
455
- */
456
- resetSignalHandlersAndMask();
457
-
458
- if (afterFork) {
459
- afterFork();
460
- }
461
-
462
- execl(watchdogFilename.c_str(), "PassengerWatchdog", (char *) 0);
463
- e = errno;
464
- try {
465
- writeArrayMessage(FEEDBACK_FD,
466
- "exec error",
467
- toString(e).c_str(),
468
- NULL);
469
- _exit(1);
470
- } catch (...) {
471
- fprintf(stderr, "Passenger AgentsStarter: could not execute %s: %s (%d)\n",
472
- watchdogFilename.c_str(), strerror(e), e);
473
- fflush(stderr);
474
- _exit(1);
475
- }
476
- } else if (pid == -1) {
477
- // Error
478
- e = errno;
479
- throw SystemException("Cannot fork a new process", e);
480
- } else {
481
- // Parent
482
- UPDATE_TRACE_POINT();
483
- FileDescriptor feedbackFd = fds[0];
484
- vector<string> args;
485
- bool result, allAgentsStarted;
486
-
487
- ServerInstanceDirPtr serverInstanceDir;
488
- ServerInstanceDir::GenerationPtr generation;
489
- ScopeGuard guard(boost::bind(&AgentsStarter::killProcessGroupAndWait, &pid, 0));
490
- fds[1].close();
491
-
492
-
493
- /****** Send arguments to watchdog through the feedback channel ******/
494
-
495
- UPDATE_TRACE_POINT();
496
- /* Here we don't care about EPIPE and ECONNRESET errors. The watchdog
497
- * could have sent an error message over the feedback fd without
498
- * reading the arguments. We'll notice that later.
499
- */
500
- try {
501
- watchdogArgs.writeToFd(feedbackFd);
502
- } catch (const SystemException &e) {
503
- if (e.code() != EPIPE && e.code() != ECONNRESET) {
504
- inspectWatchdogCrashReason(pid);
505
- }
506
- }
507
-
508
-
509
- /****** Read basic startup information ******/
510
-
511
- this_thread::restore_interruption ri(di);
512
- this_thread::restore_syscall_interruption rsi(dsi);
513
- UPDATE_TRACE_POINT();
514
-
515
- try {
516
- result = readArrayMessage(feedbackFd, args);
517
- } catch (const SystemException &ex) {
518
- if (ex.code() == ECONNRESET) {
519
- inspectWatchdogCrashReason(pid);
520
- } else {
521
- killProcessGroupAndWait(&pid, 5000);
522
- guard.clear();
523
- throw SystemException("Unable to start the Phusion Passenger watchdog: "
524
- "unable to read its startup information",
525
- ex.code());
526
- }
527
- }
528
- if (!result) {
529
- UPDATE_TRACE_POINT();
530
- inspectWatchdogCrashReason(pid);
531
- }
532
-
533
- UPDATE_TRACE_POINT();
534
- if (args[0] == "Basic startup info") {
535
- if (args.size() == 3) {
536
- serverInstanceDir.reset(new ServerInstanceDir(args[1], false));
537
- generation = serverInstanceDir->getGeneration(atoi(args[2]));
538
- } else {
539
- throw IOException("Unable to start the Phusion Passenger watchdog: "
540
- "it returned an invalid basic startup information message");
541
- }
542
- } else if (args[0] == "Watchdog startup error") {
543
- killProcessGroupAndWait(&pid, 5000);
544
- guard.clear();
545
- throw RuntimeException("Unable to start the Phusion Passenger watchdog "
546
- "because it encountered the following error during startup: " +
547
- args[1]);
548
- } else if (args[0] == "system error") {
549
- killProcessGroupAndWait(&pid, 5000);
550
- guard.clear();
551
- throw SystemException(args[1], atoi(args[2]));
552
- } else if (args[0] == "exec error") {
553
- e = atoi(args[1]);
554
- killProcessGroupAndWait(&pid, 5000);
555
- guard.clear();
556
- if (e == ENOENT) {
557
- string passengerRootConfig;
558
- if (type == APACHE) {
559
- passengerRootConfig = "PassengerRoot";
560
- } else {
561
- passengerRootConfig = "passenger_root";
562
- }
563
- throw RuntimeException("Unable to start the Phusion Passenger watchdog "
564
- "because its executable (" + watchdogFilename + ") does "
565
- "not exist. This probably means that your Phusion Passenger "
566
- "installation is broken or incomplete, or that your '" +
567
- passengerRootConfig + "' directive is set to the wrong value. "
568
- "Please reinstall Phusion Passenger or fix your '" +
569
- passengerRootConfig + "' directive, whichever is applicable.");
570
- } else {
571
- throw SystemException("Unable to start the Phusion Passenger watchdog (" +
572
- watchdogFilename + ")", e);
573
- }
574
- }
575
-
576
-
577
- /****** Read agents startup information ******/
578
-
579
- UPDATE_TRACE_POINT();
580
- allAgentsStarted = false;
581
-
582
- while (!allAgentsStarted) {
583
- try {
584
- UPDATE_TRACE_POINT();
585
- result = readArrayMessage(feedbackFd, args);
586
- } catch (const SystemException &ex) {
587
- killProcessGroupAndWait(&pid, 5000);
588
- guard.clear();
589
- throw SystemException("Unable to start the Phusion Passenger watchdog: "
590
- "unable to read all agent startup information",
591
- ex.code());
592
- }
593
- if (!result) {
594
- UPDATE_TRACE_POINT();
595
- inspectWatchdogCrashReason(pid);
596
- }
597
-
598
- if (args[0] == "HelperAgent info") {
599
- UPDATE_TRACE_POINT();
600
- if (args.size() == 5) {
601
- this->pid = pid;
602
- this->feedbackFd = feedbackFd;
603
- requestSocketFilename = args[1];
604
- requestSocketPassword = Base64::decode(args[2]);
605
- messageSocketFilename = args[3];
606
- messageSocketPassword = Base64::decode(args[4]);
607
- this->serverInstanceDir = serverInstanceDir;
608
- this->generation = generation;
609
- } else {
610
- killProcessGroupAndWait(&pid, 5000);
611
- guard.clear();
612
- throw IOException("Unable to start the Phusion Passenger watchdog: "
613
- "it returned an invalid initialization feedback message");
614
- }
615
- } else if (args[0] == "LoggingServer info") {
616
- UPDATE_TRACE_POINT();
617
- if (args.size() == 3) {
618
- loggingAgentRunningLocally = true;
619
- loggingSocketAddress = args[1];
620
- loggingSocketPassword = args[2];
621
- } else {
622
- killProcessGroupAndWait(&pid, 5000);
623
- guard.clear();
624
- throw IOException("Unable to start the Phusion Passenger watchdog: "
625
- "it returned an invalid initialization feedback message");
626
- }
627
- } else if (args[0] == "All agents started") {
628
- allAgentsStarted = true;
629
- } else {
630
- UPDATE_TRACE_POINT();
631
- killProcessGroupAndWait(&pid, 5000);
632
- guard.clear();
633
- throw RuntimeException("One of the Passenger agents sent an unknown feedback message '" + args[0] + "'");
634
- }
635
- }
636
-
637
- guard.clear();
638
- }
639
- }
640
-
641
- /**
642
- * Close any file descriptors that this object has, and make it so that the destructor
643
- * doesn't try to shut down the agents.
644
- *
645
- * @post getPid() == 0
646
- */
647
- void detach() {
648
- feedbackFd.close();
649
- pid = 0;
650
- }
651
- };
652
-
653
- } // namespace Passenger
654
-
655
- #endif /* _PASSENGER_AGENTS_STARTER_HPP_ */