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
@@ -25,10 +25,11 @@
25
25
  #ifndef _PASSENGER_CONFIGURATION_HPP_
26
26
  #define _PASSENGER_CONFIGURATION_HPP_
27
27
 
28
- #include "Utils.h"
29
- #include "Logging.h"
30
- #include "ServerInstanceDir.h"
31
- #include "Constants.h"
28
+ #include <Logging.h>
29
+ #include <ServerInstanceDir.h>
30
+ #include <Constants.h>
31
+ #include <Utils.h>
32
+ #include <Utils/VariantMap.h>
32
33
 
33
34
  /* The APR headers must come after the Passenger headers. See Hooks.cpp
34
35
  * to learn why.
@@ -334,6 +335,8 @@ struct ServerConfig {
334
335
  /** The Passenger root folder. */
335
336
  const char *root;
336
337
 
338
+ VariantMap ctl;
339
+
337
340
  /** The default Ruby interpreter to use. */
338
341
  const char *defaultRuby;
339
342
 
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2010, 2011, 2012 Phusion
3
+ * Copyright (c) 2010-2013 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -53,7 +53,7 @@
53
53
  #include "Utils/IOUtils.h"
54
54
  #include "Utils/Timer.h"
55
55
  #include "Logging.h"
56
- #include "AgentsStarter.hpp"
56
+ #include "AgentsStarter.h"
57
57
  #include "DirectoryMapper.h"
58
58
  #include "Constants.h"
59
59
 
@@ -1237,7 +1237,7 @@ private:
1237
1237
  public:
1238
1238
  Hooks(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
1239
1239
  : cstat(1024),
1240
- agentsStarter(AgentsStarter::NGINX)
1240
+ agentsStarter(AS_APACHE)
1241
1241
  {
1242
1242
  serverConfig.finalize();
1243
1243
  Passenger::setLogLevel(serverConfig.logLevel);
@@ -1258,23 +1258,33 @@ public:
1258
1258
  "TIP: The correct value for this option was given to you by "
1259
1259
  "'passenger-install-apache2-module'.");
1260
1260
  }
1261
+
1262
+ VariantMap params;
1263
+ params
1264
+ .setPid ("web_server_pid", getpid())
1265
+ .setUid ("web_server_worker_uid", unixd_config.user_id)
1266
+ .setGid ("web_server_worker_gid", unixd_config.group_id)
1267
+ .setInt ("log_level", serverConfig.logLevel)
1268
+ .set ("debug_log_file", (serverConfig.debugLogFile == NULL) ? "" : serverConfig.debugLogFile)
1269
+ .set ("temp_dir", serverConfig.tempDir)
1270
+ .setBool("user_switching", serverConfig.userSwitching)
1271
+ .set ("default_user", serverConfig.defaultUser)
1272
+ .set ("default_group", serverConfig.defaultGroup)
1273
+ .set ("default_ruby", serverConfig.defaultRuby)
1274
+ .setInt ("max_pool_size", serverConfig.maxPoolSize)
1275
+ .setInt ("pool_idle_time", serverConfig.poolIdleTime)
1276
+ .setInt ("max_instances_per_app", serverConfig.maxInstancesPerApp)
1277
+ .set ("analytics_log_user", serverConfig.analyticsLogUser)
1278
+ .set ("analytics_log_group", serverConfig.analyticsLogGroup)
1279
+ .set ("union_station_gateway_address", serverConfig.unionStationGatewayAddress)
1280
+ .setInt ("union_station_gateway_port", serverConfig.unionStationGatewayPort)
1281
+ .set ("union_station_gateway_cert", serverConfig.unionStationGatewayCert)
1282
+ .set ("union_station_proxy_address", serverConfig.unionStationProxyAddress)
1283
+ .setStrSet("prestart_urls", serverConfig.prestartURLs);
1261
1284
 
1262
- agentsStarter.start(serverConfig.logLevel,
1263
- (serverConfig.debugLogFile == NULL) ? "" : serverConfig.debugLogFile,
1264
- getpid(), serverConfig.tempDir,
1265
- serverConfig.userSwitching,
1266
- serverConfig.defaultUser, serverConfig.defaultGroup,
1267
- unixd_config.user_id, unixd_config.group_id,
1268
- serverConfig.root, serverConfig.defaultRuby, serverConfig.maxPoolSize,
1269
- serverConfig.maxInstancesPerApp, serverConfig.poolIdleTime,
1270
- "",
1271
- serverConfig.analyticsLogUser,
1272
- serverConfig.analyticsLogGroup,
1273
- serverConfig.unionStationGatewayAddress,
1274
- serverConfig.unionStationGatewayPort,
1275
- serverConfig.unionStationGatewayCert,
1276
- serverConfig.unionStationProxyAddress,
1277
- serverConfig.prestartURLs);
1285
+ serverConfig.ctl.addTo(params);
1286
+
1287
+ agentsStarter.start(serverConfig.root, params);
1278
1288
 
1279
1289
  // Store some relevant information in the generation directory.
1280
1290
  string generationPath = agentsStarter.getGeneration()->getPath();
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2010-2012 Phusion
3
+ * Copyright (c) 2010-2013 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -23,76 +23,104 @@
23
23
  * THE SOFTWARE.
24
24
  */
25
25
  #include <oxt/thread.hpp>
26
- #include "AgentsStarter.h"
27
- #include "AgentsStarter.hpp"
26
+ #include <set>
28
27
  #include <cerrno>
29
28
  #include <cstring>
29
+ #include <AgentsStarter.h>
30
30
 
31
31
  using namespace std;
32
32
  using namespace boost;
33
33
  using namespace oxt;
34
34
 
35
35
 
36
- AgentsStarter *
37
- agents_starter_new(AgentsStarterType type, char **error_message) {
38
- Passenger::AgentsStarter::Type theType;
39
- if (type == AS_APACHE) {
40
- theType = Passenger::AgentsStarter::APACHE;
41
- } else {
42
- theType = Passenger::AgentsStarter::NGINX;
36
+ PSG_VariantMap *
37
+ psg_variant_map_new() {
38
+ return (PSG_VariantMap *) new Passenger::VariantMap();
39
+ }
40
+
41
+ void
42
+ psg_variant_map_set(PSG_VariantMap *m,
43
+ const char *name,
44
+ const char *value,
45
+ unsigned int value_len)
46
+ {
47
+ Passenger::VariantMap *vm = (Passenger::VariantMap *) m;
48
+ vm->set(name, string(value, value_len));
49
+ }
50
+
51
+ void
52
+ psg_variant_map_set2(PSG_VariantMap *m,
53
+ const char *name,
54
+ unsigned int name_len,
55
+ const char *value,
56
+ unsigned int value_len)
57
+ {
58
+ Passenger::VariantMap *vm = (Passenger::VariantMap *) m;
59
+ vm->set(string(name, name_len), string(value, value_len));
60
+ }
61
+
62
+ void
63
+ psg_variant_map_set_int(PSG_VariantMap *m,
64
+ const char *name,
65
+ int value)
66
+ {
67
+ Passenger::VariantMap *vm = (Passenger::VariantMap *) m;
68
+ vm->setInt(name, value);
69
+ }
70
+
71
+ void
72
+ psg_variant_map_set_bool(PSG_VariantMap *m,
73
+ const char *name,
74
+ int value)
75
+ {
76
+ Passenger::VariantMap *vm = (Passenger::VariantMap *) m;
77
+ vm->setBool(name, value);
78
+ }
79
+
80
+ void
81
+ psg_variant_map_set_strset(PSG_VariantMap *m,
82
+ const char *name,
83
+ const char **strs,
84
+ unsigned int count)
85
+ {
86
+ Passenger::VariantMap *vm = (Passenger::VariantMap *) m;
87
+ std::set<string> the_set;
88
+
89
+ for (unsigned int i = 0; i < count; i++) {
90
+ the_set.insert(strs[i]);
43
91
  }
44
- return (AgentsStarter *) new Passenger::AgentsStarter(theType);
92
+ vm->setStrSet(name, the_set);
93
+ }
94
+
95
+ void
96
+ psg_variant_map_free(PSG_VariantMap *m) {
97
+ delete (Passenger::VariantMap *) m;
98
+ }
99
+
100
+
101
+ PSG_AgentsStarter *
102
+ psg_agents_starter_new(PSG_AgentsStarterType type, char **error_message) {
103
+ return (PSG_AgentsStarter *) new Passenger::AgentsStarter(type);
45
104
  }
46
105
 
47
106
  int
48
- agents_starter_start(AgentsStarter *as,
49
- int logLevel, const char *debugLogFile,
50
- pid_t webServerPid,
51
- const char *tempDir, int userSwitching,
52
- const char *defaultUser, const char *defaultGroup,
53
- uid_t webServerWorkerUid, gid_t webServerWorkerGid,
54
- const char *passengerRoot,
55
- const char *defaultRubyCommand, unsigned int maxPoolSize,
56
- unsigned int maxInstancesPerApp,
57
- unsigned int poolIdleTime,
58
- const char *analyticsServer,
59
- const char *analyticsLogUser,
60
- const char *analyticsLogGroup,
61
- const char *unionStationGatewayAddress,
62
- unsigned short unionStationGatewayPort,
63
- const char *unionStationGatewayCert,
64
- const char *unionStationProxyAddress,
65
- const char **prestartURLs, unsigned int prestartURLsCount,
66
- const AfterForkCallback afterFork,
67
- void *callbackArgument,
68
- char **errorMessage)
107
+ psg_agents_starter_start(PSG_AgentsStarter *as,
108
+ const char *passengerRoot,
109
+ PSG_VariantMap *extraParams,
110
+ const PSG_AfterForkCallback afterFork,
111
+ void *callbackArgument,
112
+ char **errorMessage)
69
113
  {
70
114
  Passenger::AgentsStarter *agentsStarter = (Passenger::AgentsStarter *) as;
71
115
  this_thread::disable_syscall_interruption dsi;
72
116
  try {
73
117
  function<void ()> afterForkFunctionObject;
74
- set<string> setOfprestartURLs;
75
- unsigned int i;
76
118
 
77
119
  if (afterFork != NULL) {
78
120
  afterForkFunctionObject = boost::bind(afterFork, callbackArgument);
79
121
  }
80
- for (i = 0; i < prestartURLsCount; i++) {
81
- setOfprestartURLs.insert(prestartURLs[i]);
82
- }
83
- agentsStarter->start(logLevel, debugLogFile,
84
- webServerPid, tempDir, userSwitching,
85
- defaultUser, defaultGroup,
86
- webServerWorkerUid, webServerWorkerGid,
87
- passengerRoot, defaultRubyCommand,
88
- maxPoolSize, maxInstancesPerApp, poolIdleTime,
89
- analyticsServer,
90
- analyticsLogUser, analyticsLogGroup,
91
- unionStationGatewayAddress,
92
- unionStationGatewayPort,
93
- unionStationGatewayCert,
94
- unionStationProxyAddress,
95
- setOfprestartURLs,
122
+ agentsStarter->start(passengerRoot,
123
+ *((Passenger::VariantMap *) extraParams),
96
124
  afterForkFunctionObject);
97
125
  return 1;
98
126
  } catch (const Passenger::SystemException &e) {
@@ -107,7 +135,7 @@ agents_starter_start(AgentsStarter *as,
107
135
  }
108
136
 
109
137
  const char *
110
- agents_starter_get_request_socket_filename(AgentsStarter *as, unsigned int *size) {
138
+ psg_agents_starter_get_request_socket_filename(PSG_AgentsStarter *as, unsigned int *size) {
111
139
  Passenger::AgentsStarter *agentsStarter = (Passenger::AgentsStarter *) as;
112
140
  if (size != NULL) {
113
141
  *size = agentsStarter->getRequestSocketFilename().size();
@@ -116,7 +144,7 @@ agents_starter_get_request_socket_filename(AgentsStarter *as, unsigned int *size
116
144
  }
117
145
 
118
146
  const char *
119
- agents_starter_get_request_socket_password(AgentsStarter *as, unsigned int *size) {
147
+ psg_agents_starter_get_request_socket_password(PSG_AgentsStarter *as, unsigned int *size) {
120
148
  Passenger::AgentsStarter *agentsStarter = (Passenger::AgentsStarter *) as;
121
149
  if (size != NULL) {
122
150
  *size = agentsStarter->getRequestSocketPassword().size();
@@ -125,31 +153,31 @@ agents_starter_get_request_socket_password(AgentsStarter *as, unsigned int *size
125
153
  }
126
154
 
127
155
  const char *
128
- agents_starter_get_server_instance_dir(AgentsStarter *as) {
156
+ psg_agents_starter_get_server_instance_dir(PSG_AgentsStarter *as) {
129
157
  Passenger::AgentsStarter *agentsStarter = (Passenger::AgentsStarter *) as;
130
158
  return agentsStarter->getServerInstanceDir()->getPath().c_str();
131
159
  }
132
160
 
133
161
  const char *
134
- agents_starter_get_generation_dir(AgentsStarter *as) {
162
+ psg_agents_starter_get_generation_dir(PSG_AgentsStarter *as) {
135
163
  Passenger::AgentsStarter *agentsStarter = (Passenger::AgentsStarter *) as;
136
164
  return agentsStarter->getGeneration()->getPath().c_str();
137
165
  }
138
166
 
139
167
  pid_t
140
- agents_starter_get_pid(AgentsStarter *as) {
168
+ psg_agents_starter_get_pid(PSG_AgentsStarter *as) {
141
169
  Passenger::AgentsStarter *agentsStarter = (Passenger::AgentsStarter *) as;
142
170
  return agentsStarter->getPid();
143
171
  }
144
172
 
145
173
  void
146
- agents_starter_detach(AgentsStarter *as) {
174
+ psg_agents_starter_detach(PSG_AgentsStarter *as) {
147
175
  Passenger::AgentsStarter *agentsStarter = (Passenger::AgentsStarter *) as;
148
176
  agentsStarter->detach();
149
177
  }
150
178
 
151
179
  void
152
- agents_starter_free(AgentsStarter *as) {
180
+ psg_agents_starter_free(PSG_AgentsStarter *as) {
153
181
  Passenger::AgentsStarter *agentsStarter = (Passenger::AgentsStarter *) as;
154
182
  delete agentsStarter;
155
183
  }
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Phusion Passenger - https://www.phusionpassenger.com/
3
- * Copyright (c) 2010-2012 Phusion
3
+ * Copyright (c) 2010-2013 Phusion
4
4
  *
5
5
  * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
6
  *
@@ -22,10 +22,9 @@
22
22
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
23
  * THE SOFTWARE.
24
24
  */
25
+ #ifndef _PASSENGER_AGENTS_STARTER_HPP_
26
+ #define _PASSENGER_AGENTS_STARTER_HPP_
25
27
 
26
- /* C wrappers for Passenger::AgentsStarter. */
27
- #ifndef _PASSENGER_AGENTS_STARTER_H_
28
- #define _PASSENGER_AGENTS_STARTER_H_
29
28
 
30
29
  #include <sys/types.h>
31
30
  #include <unistd.h>
@@ -34,49 +33,578 @@
34
33
  extern "C" {
35
34
  #endif
36
35
 
37
-
38
- typedef void AgentsStarter;
39
-
40
36
  typedef enum {
41
37
  AS_APACHE,
42
38
  AS_NGINX
43
- } AgentsStarterType;
44
-
45
- typedef void (*AfterForkCallback)(void *);
46
-
47
- AgentsStarter *agents_starter_new(AgentsStarterType type, char **error_message);
48
- int agents_starter_start(AgentsStarter *as,
49
- int logLevel, const char *debugLogFile,
50
- pid_t webServerPid,
51
- const char *tempDir, int userSwitching,
52
- const char *defaultUser, const char *defaultGroup,
53
- uid_t webServerWorkerUid, gid_t webServerWorkerGid,
54
- const char *passengerRoot,
55
- const char *defaultRubyCommand, unsigned int maxPoolSize,
56
- unsigned int maxInstancesPerApp,
57
- unsigned int poolIdleTime,
58
- const char *analyticsServer,
59
- const char *analyticsLogUser,
60
- const char *analyticsLogGroup,
61
- const char *unionStationGatewayAddress,
62
- unsigned short unionStationGatewayPort,
63
- const char *unionStationGatewayCert,
64
- const char *unionStationProxyAddress,
65
- const char **prestartURLs, unsigned int prestartURLsCount,
66
- const AfterForkCallback afterFork,
67
- void *callbackArgument,
68
- char **errorMessage);
69
- const char *agents_starter_get_request_socket_filename(AgentsStarter *as, unsigned int *size);
70
- const char *agents_starter_get_request_socket_password(AgentsStarter *as, unsigned int *size);
71
- const char *agents_starter_get_server_instance_dir(AgentsStarter *as);
72
- const char *agents_starter_get_generation_dir(AgentsStarter *as);
73
- pid_t agents_starter_get_pid(AgentsStarter *as);
74
- void agents_starter_detach(AgentsStarter *as);
75
- void agents_starter_free(AgentsStarter *as);
39
+ } PSG_AgentsStarterType;
40
+
41
+ typedef void PSG_AgentsStarter;
42
+ typedef void PSG_VariantMap;
43
+ typedef void (*PSG_AfterForkCallback)(void *);
76
44
 
45
+ PSG_VariantMap *psg_variant_map_new();
46
+ void psg_variant_map_set(PSG_VariantMap *m,
47
+ const char *name,
48
+ const char *value,
49
+ unsigned int value_len);
50
+ void psg_variant_map_set2(PSG_VariantMap *m,
51
+ const char *name,
52
+ unsigned int name_len,
53
+ const char *value,
54
+ unsigned int value_len);
55
+ void psg_variant_map_set_int(PSG_VariantMap *m,
56
+ const char *name,
57
+ int value);
58
+ void psg_variant_map_set_bool(PSG_VariantMap *m,
59
+ const char *name,
60
+ int value);
61
+ void psg_variant_map_set_strset(PSG_VariantMap *m,
62
+ const char *name,
63
+ const char **strs,
64
+ unsigned int count);
65
+ void psg_variant_map_free(PSG_VariantMap *m);
66
+
67
+ PSG_AgentsStarter *psg_agents_starter_new(PSG_AgentsStarterType type,
68
+ char **error_message);
69
+ int psg_agents_starter_start(PSG_AgentsStarter *as,
70
+ const char *passengerRoot,
71
+ PSG_VariantMap *params,
72
+ const PSG_AfterForkCallback afterFork,
73
+ void *callbackArgument,
74
+ char **errorMessage);
75
+ const char *psg_agents_starter_get_request_socket_filename(PSG_AgentsStarter *as, unsigned int *size);
76
+ const char *psg_agents_starter_get_request_socket_password(PSG_AgentsStarter *as, unsigned int *size);
77
+ const char *psg_agents_starter_get_server_instance_dir(PSG_AgentsStarter *as);
78
+ const char *psg_agents_starter_get_generation_dir(PSG_AgentsStarter *as);
79
+ pid_t psg_agents_starter_get_pid(PSG_AgentsStarter *as);
80
+ void psg_agents_starter_detach(PSG_AgentsStarter *as);
81
+ void psg_agents_starter_free(PSG_AgentsStarter *as);
77
82
 
78
83
  #ifdef __cplusplus
79
- }
84
+ } /* extern "C" */
80
85
  #endif
81
86
 
82
- #endif /* _PASSENGER_AGENTS_STARTER_H_ */
87
+
88
+ #ifdef __cplusplus
89
+
90
+ #include <boost/function.hpp>
91
+ #include <oxt/system_calls.hpp>
92
+ #include <oxt/backtrace.hpp>
93
+ #include <string>
94
+ #include <vector>
95
+ #include <set>
96
+
97
+ #include <signal.h>
98
+
99
+ #include <Constants.h>
100
+ #include <FileDescriptor.h>
101
+ #include <MessageClient.h>
102
+ #include <ServerInstanceDir.h>
103
+ #include <Exceptions.h>
104
+ #include <ResourceLocator.h>
105
+ #include <Logging.h>
106
+ #include <Utils.h>
107
+ #include <Utils/IOUtils.h>
108
+ #include <Utils/MessageIO.h>
109
+ #include <Utils/Timer.h>
110
+ #include <Utils/ScopeGuard.h>
111
+ #include <Utils/VariantMap.h>
112
+
113
+ namespace Passenger {
114
+
115
+ using namespace std;
116
+ using namespace boost;
117
+ using namespace oxt;
118
+
119
+ /**
120
+ * Utility class for starting various Phusion Passenger agents through the watchdog.
121
+ */
122
+ class AgentsStarter {
123
+ private:
124
+ PSG_AgentsStarterType type;
125
+
126
+ /** The watchdog's PID. Equals 0 if the watchdog hasn't been started yet
127
+ * or if detach() is called. */
128
+ pid_t pid;
129
+
130
+ /******* Information about the started services. Only valid when pid != 0. *******/
131
+
132
+ /** The watchdog's feedback file descriptor. */
133
+ FileDescriptor feedbackFd;
134
+
135
+ /** The helper agent's request socket filename and its password. This socket
136
+ * is for serving SCGI requests. */
137
+ string requestSocketFilename;
138
+ string requestSocketPassword;
139
+
140
+ /** The socket on which the helper agent listens for administration commands,
141
+ * and the corresponding password for the "web_server" account, which has the
142
+ * authorization to shutdown the helper agent.
143
+ */
144
+ string helperAgentAdminSocketAddress;
145
+ string helperAgentExitPassword;
146
+
147
+ /** The logging agent's socket address and its password. */
148
+ string loggingSocketAddress;
149
+ string loggingSocketPassword;
150
+
151
+ /** The server instance dir and generation dir of the agents. */
152
+ ServerInstanceDirPtr serverInstanceDir;
153
+ /** The generation dir of the agents. */
154
+ ServerInstanceDir::GenerationPtr generation;
155
+
156
+ /**
157
+ * Safely dup2() the given file descriptor to 3 (FEEDBACK_FD).
158
+ */
159
+ void installFeedbackFd(const FileDescriptor &fd) {
160
+ if (fd != FEEDBACK_FD && syscalls::dup2(fd, FEEDBACK_FD) == -1) {
161
+ int e = errno;
162
+ try {
163
+ writeArrayMessage(fd,
164
+ "system error",
165
+ "dup2() failed",
166
+ toString(e).c_str(),
167
+ NULL);
168
+ _exit(1);
169
+ } catch (...) {
170
+ fprintf(stderr, "Passenger AgentsStarter: dup2() failed: %s (%d)\n",
171
+ strerror(e), e);
172
+ fflush(stderr);
173
+ _exit(1);
174
+ }
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Call this if the watchdog seems to have crashed. This function will try
180
+ * to determine whether the watchdog is still running, whether it crashed
181
+ * with a signal, etc. If it has detected that the watchdog is no longer running
182
+ * then it will set `pid` to -1.
183
+ */
184
+ void inspectWatchdogCrashReason(pid_t &pid) {
185
+ this_thread::disable_interruption di;
186
+ this_thread::disable_syscall_interruption dsi;
187
+ int ret, status;
188
+
189
+ /* Upon noticing that something went wrong, the watchdog
190
+ * or its subprocesses might still be writing out an error
191
+ * report, so we wait a while before killing the watchdog.
192
+ */
193
+ ret = timedWaitPid(pid, &status, 5000);
194
+ if (ret == 0) {
195
+ /* Looks like the watchdog didn't crash and is still running. */
196
+ throw RuntimeException(
197
+ "Unable to start the Phusion Passenger watchdog: "
198
+ "it froze during startup and reported an unknown error");
199
+ } else if (ret != -1 && WIFSIGNALED(status)) {
200
+ /* Looks like a crash which caused a signal. */
201
+ pid = -1;
202
+ throw RuntimeException(
203
+ "Unable to start the Phusion Passenger watchdog: "
204
+ "it seems to have been killed with signal " +
205
+ getSignalName(WTERMSIG(status)) + " during startup");
206
+ } else if (ret == -1) {
207
+ /* Looks like it exited for a different reason and has no exit code. */
208
+ pid = -1;
209
+ throw RuntimeException(
210
+ "Unable to start the Phusion Passenger watchdog: "
211
+ "it seems to have crashed during startup for an unknown reason");
212
+ } else {
213
+ /* Looks like it exited for a different reason, but has an exit code. */
214
+ pid = -1;
215
+ throw RuntimeException(
216
+ "Unable to start the Phusion Passenger watchdog: "
217
+ "it seems to have crashed during startup for an unknown reason, "
218
+ "with exit code " + toString(WEXITSTATUS(status)));
219
+ }
220
+ }
221
+
222
+ static void killProcessGroupAndWait(pid_t *pid, unsigned long long timeout = 0) {
223
+ if (*pid != -1 && (timeout == 0 || timedWaitPid(*pid, NULL, timeout) <= 0)) {
224
+ this_thread::disable_syscall_interruption dsi;
225
+ syscalls::killpg(*pid, SIGKILL);
226
+ syscalls::waitpid(*pid, NULL, 0);
227
+ *pid = -1;
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Behaves like `waitpid(pid, status, WNOHANG)`, but waits at most
233
+ * `timeout` miliseconds for the process to exit.
234
+ */
235
+ static int timedWaitPid(pid_t pid, int *status, unsigned long long timeout) {
236
+ Timer timer;
237
+ int ret;
238
+
239
+ do {
240
+ ret = syscalls::waitpid(pid, status, WNOHANG);
241
+ if (ret > 0 || ret == -1) {
242
+ return ret;
243
+ } else {
244
+ syscalls::usleep(10000);
245
+ }
246
+ } while (timer.elapsed() < timeout);
247
+ return 0; // timed out
248
+ }
249
+
250
+ /**
251
+ * Gracefully shutdown an agent process by sending an exit command to its socket.
252
+ * Returns whether the agent has successfully processed the exit command.
253
+ * Any exceptions are caught and will cause false to be returned.
254
+ */
255
+ bool gracefullyShutdownAgent(const string &address, const string &username,
256
+ const string &password)
257
+ {
258
+ try {
259
+ MessageClient client;
260
+ vector<string> args;
261
+
262
+ client.connect(address, username, password);
263
+ client.write("exit", NULL);
264
+ return client.read(args) && args[0] == "Passed security" &&
265
+ client.read(args) && args[0] == "exit command received";
266
+ } catch (const SystemException &) {
267
+ } catch (const IOException &) {
268
+ } catch (const SecurityException &) {
269
+ }
270
+ return false;
271
+ }
272
+
273
+ string findUnionStationGatewayCert(const ResourceLocator &locator,
274
+ const VariantMap &params) const
275
+ {
276
+ string value = params.get("union_station_gateway_cert", false);
277
+ if (value.empty()) {
278
+ return locator.getResourcesDir() + "/union_station_gateway.crt";
279
+ } else {
280
+ return value;
281
+ }
282
+ }
283
+
284
+ public:
285
+ /**
286
+ * Construct a AgentsStarter object. The watchdog and the agents
287
+ * aren't started yet until you call start().
288
+ *
289
+ * @param type Whether one wants to start the Apache or the Nginx helper agent.
290
+ */
291
+ AgentsStarter(PSG_AgentsStarterType type) {
292
+ this->type = type;
293
+ pid = 0;
294
+ }
295
+
296
+ ~AgentsStarter() {
297
+ if (pid != 0) {
298
+ this_thread::disable_syscall_interruption dsi;
299
+ bool cleanShutdown = gracefullyShutdownAgent(helperAgentAdminSocketAddress,
300
+ "_web_server", helperAgentExitPassword);
301
+ cleanShutdown = cleanShutdown &&
302
+ gracefullyShutdownAgent(loggingSocketAddress,
303
+ "logging", loggingSocketPassword);
304
+
305
+ /* Send a message down the feedback fd to tell the watchdog
306
+ * Whether this is a clean shutdown. Closing the fd without
307
+ * sending anything also indicates an unclean shutdown,
308
+ * but we send a byte anyway in case there are other processes
309
+ * who have the fd open.
310
+ */
311
+ if (cleanShutdown) {
312
+ syscalls::write(feedbackFd, "c", 1);
313
+ } else {
314
+ syscalls::write(feedbackFd, "u", 1);
315
+ }
316
+
317
+ /* If we failed to send an exit command to one of the agents then we have
318
+ * to forcefully kill all agents now because otherwise one of them might
319
+ * never exit. We do this by closing the feedback fd without sending a
320
+ * random byte, to indicate that this is an abnormal shutdown. The watchdog
321
+ * will then kill all agents.
322
+ */
323
+
324
+ feedbackFd.close();
325
+ syscalls::waitpid(pid, NULL, 0);
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Returns the type as was passed to the constructor.
331
+ */
332
+ PSG_AgentsStarterType getType() const {
333
+ return type;
334
+ }
335
+
336
+ /**
337
+ * Returns the watchdog's PID. Equals 0 if the watchdog hasn't been started yet
338
+ * or if detach() is called.
339
+ */
340
+ pid_t getPid() const {
341
+ return pid;
342
+ }
343
+
344
+ // The 'const string &' here is on purpose. The C getter functions
345
+ // return the string pointer directly.
346
+ const string &getRequestSocketFilename() const {
347
+ return requestSocketFilename;
348
+ }
349
+
350
+ const string &getRequestSocketPassword() const {
351
+ return requestSocketPassword;
352
+ }
353
+
354
+ string getHelperAgentAdminSocketFilename() const {
355
+ return parseUnixSocketAddress(helperAgentAdminSocketAddress);
356
+ }
357
+
358
+ string getHelperAgentExitPassword() const {
359
+ return helperAgentExitPassword;
360
+ }
361
+
362
+ string getLoggingSocketAddress() const {
363
+ return loggingSocketAddress;
364
+ }
365
+
366
+ string getLoggingSocketPassword() const {
367
+ return loggingSocketPassword;
368
+ }
369
+
370
+ ServerInstanceDirPtr getServerInstanceDir() const {
371
+ return serverInstanceDir;
372
+ }
373
+
374
+ ServerInstanceDir::GenerationPtr getGeneration() const {
375
+ return generation;
376
+ }
377
+
378
+ /**
379
+ * Start the agents through the watchdog.
380
+ *
381
+ * @throws SystemException Something went wrong.
382
+ * @throws IOException Something went wrong while communicating with one
383
+ * of the agents during its initialization phase.
384
+ * @throws RuntimeException Something went wrong.
385
+ */
386
+ void start(const string &passengerRoot,
387
+ const VariantMap &extraParams = VariantMap(),
388
+ const function<void ()> &afterFork = function<void ()>())
389
+ {
390
+ TRACE_POINT();
391
+ this_thread::disable_interruption di;
392
+ this_thread::disable_syscall_interruption dsi;
393
+ ResourceLocator locator(passengerRoot);
394
+ string realUnionStationGatewayCert = findUnionStationGatewayCert(locator, extraParams);
395
+ string watchdogFilename = locator.getAgentsDir() + "/PassengerWatchdog";
396
+ SocketPair fds;
397
+ int e;
398
+ pid_t pid;
399
+
400
+ VariantMap params;
401
+ params
402
+ .set ("web_server_type", type == AS_APACHE ? "apache" : "nginx")
403
+ .setPid ("web_server_pid", getpid())
404
+ .set ("passenger_root", passengerRoot)
405
+ .setInt ("log_level", getLogLevel())
406
+ .set ("temp_dir", getSystemTempDir());
407
+ extraParams.addTo(params);
408
+
409
+ // .setUid ("web_server_worker_uid", webServerWorkerUid)
410
+ // .setGid ("web_server_worker_gid", webServerWorkerGid)
411
+ // .set ("debug_log_file", debugLogFile)
412
+ // .set ("temp_dir", tempDir.empty() ? getSystemTempDir() : tempDir)
413
+ // .setBool("user_switching", userSwitching)
414
+ // .set ("default_user", defaultUser)
415
+ // .set ("default_group", defaultGroup)
416
+ // .set ("default_ruby", defaultRubyCommand)
417
+ // .setInt ("max_pool_size", maxPoolSize)
418
+ // .setInt ("max_instances_per_app", maxInstancesPerApp)
419
+ /*
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
+
431
+ fds = createUnixSocketPair();
432
+ pid = syscalls::fork();
433
+ if (pid == 0) {
434
+ // Child
435
+
436
+ /* Become the session leader so that Apache can't kill the
437
+ * watchdog with killpg() during shutdown, so that a
438
+ * Ctrl-C only affects the web server, and so that
439
+ * we can kill all of our subprocesses in a single killpg().
440
+ */
441
+ setsid();
442
+
443
+ // Make sure the feedback fd is 3 and close all file descriptors
444
+ // except stdin, stdout, stderr and 3.
445
+ syscalls::close(fds[0]);
446
+ installFeedbackFd(fds[1]);
447
+ closeAllFileDescriptors(FEEDBACK_FD);
448
+
449
+ /* We don't know how the web server or the environment affect
450
+ * signal handlers and the signal mask, so reset this stuff
451
+ * just in case.
452
+ */
453
+ resetSignalHandlersAndMask();
454
+
455
+ if (afterFork) {
456
+ afterFork();
457
+ }
458
+
459
+ execl(watchdogFilename.c_str(), "PassengerWatchdog", (char *) 0);
460
+ e = errno;
461
+ try {
462
+ writeArrayMessage(FEEDBACK_FD,
463
+ "exec error",
464
+ toString(e).c_str(),
465
+ NULL);
466
+ _exit(1);
467
+ } catch (...) {
468
+ fprintf(stderr, "Passenger AgentsStarter: could not execute %s: %s (%d)\n",
469
+ watchdogFilename.c_str(), strerror(e), e);
470
+ fflush(stderr);
471
+ _exit(1);
472
+ }
473
+ } else if (pid == -1) {
474
+ // Error
475
+ e = errno;
476
+ throw SystemException("Cannot fork a new process", e);
477
+ } else {
478
+ // Parent
479
+ UPDATE_TRACE_POINT();
480
+ FileDescriptor feedbackFd = fds[0];
481
+ vector<string> args;
482
+ bool result;
483
+
484
+ ScopeGuard guard(boost::bind(&AgentsStarter::killProcessGroupAndWait, &pid, 0));
485
+ fds[1].close();
486
+
487
+
488
+ /****** Send arguments to watchdog through the feedback channel ******/
489
+
490
+ UPDATE_TRACE_POINT();
491
+ /* Here we don't care about EPIPE and ECONNRESET errors. The watchdog
492
+ * could have sent an error message over the feedback fd without
493
+ * reading the arguments. We'll notice that later.
494
+ */
495
+ try {
496
+ params.writeToFd(feedbackFd);
497
+ } catch (const SystemException &e) {
498
+ if (e.code() != EPIPE && e.code() != ECONNRESET) {
499
+ inspectWatchdogCrashReason(pid);
500
+ }
501
+ }
502
+
503
+
504
+ /****** Read agents information report ******/
505
+
506
+ this_thread::restore_interruption ri(di);
507
+ this_thread::restore_syscall_interruption rsi(dsi);
508
+ UPDATE_TRACE_POINT();
509
+
510
+ try {
511
+ result = readArrayMessage(feedbackFd, args);
512
+ } catch (const SystemException &ex) {
513
+ if (ex.code() == ECONNRESET) {
514
+ inspectWatchdogCrashReason(pid);
515
+ } else {
516
+ killProcessGroupAndWait(&pid, 5000);
517
+ guard.clear();
518
+ throw SystemException("Unable to start the Phusion Passenger watchdog: "
519
+ "unable to read its startup information report",
520
+ ex.code());
521
+ }
522
+ }
523
+ if (!result) {
524
+ UPDATE_TRACE_POINT();
525
+ inspectWatchdogCrashReason(pid);
526
+ }
527
+
528
+ if (args[0] == "Agents information") {
529
+ if ((args.size() - 1) % 2 != 0) {
530
+ throw RuntimeException("Unable to start the Phusion Passenger watchdog "
531
+ "because it sent an invalid startup information report (the number "
532
+ "of items is not an even number)");
533
+ }
534
+
535
+ VariantMap info;
536
+ for (unsigned i = 1; i < args.size(); i += 2) {
537
+ const string &key = args[i];
538
+ const string &value = args[i + 1];
539
+ info.set(key, value);
540
+ }
541
+
542
+ this->pid = pid;
543
+ this->feedbackFd = feedbackFd;
544
+ requestSocketFilename = info.get("request_socket_filename");
545
+ requestSocketPassword = info.get("request_socket_password");
546
+ helperAgentAdminSocketAddress = info.get("helper_agent_admin_socket_address");
547
+ helperAgentExitPassword = info.get("helper_agent_exit_password");
548
+ serverInstanceDir = make_shared<ServerInstanceDir>(info.get("server_instance_dir"), false);
549
+ generation = serverInstanceDir->getGeneration(info.getInt("generation"));
550
+ loggingSocketAddress = info.get("logging_socket_address");
551
+ loggingSocketPassword = info.get("logging_socket_password");
552
+ guard.clear();
553
+ } else if (args[0] == "Watchdog startup error") {
554
+ killProcessGroupAndWait(&pid, 5000);
555
+ guard.clear();
556
+ throw RuntimeException("Unable to start the Phusion Passenger watchdog "
557
+ "because it encountered the following error during startup: " +
558
+ args[1]);
559
+ } else if (args[0] == "system error") {
560
+ killProcessGroupAndWait(&pid, 5000);
561
+ guard.clear();
562
+ throw SystemException(args[1], atoi(args[2]));
563
+ } else if (args[0] == "exec error") {
564
+ e = atoi(args[1]);
565
+ killProcessGroupAndWait(&pid, 5000);
566
+ guard.clear();
567
+ if (e == ENOENT) {
568
+ string passengerRootConfig;
569
+ if (type == AS_APACHE) {
570
+ passengerRootConfig = "PassengerRoot";
571
+ } else {
572
+ passengerRootConfig = "passenger_root";
573
+ }
574
+ throw RuntimeException("Unable to start the Phusion Passenger watchdog "
575
+ "because its executable (" + watchdogFilename + ") does "
576
+ "not exist. This probably means that your Phusion Passenger "
577
+ "installation is broken or incomplete, or that your '" +
578
+ passengerRootConfig + "' directive is set to the wrong value. "
579
+ "Please reinstall Phusion Passenger or fix your '" +
580
+ passengerRootConfig + "' directive, whichever is applicable.");
581
+ } else {
582
+ throw SystemException("Unable to start the Phusion Passenger watchdog (" +
583
+ watchdogFilename + ")", e);
584
+ }
585
+ } else {
586
+ UPDATE_TRACE_POINT();
587
+ killProcessGroupAndWait(&pid, 5000);
588
+ guard.clear();
589
+ throw RuntimeException("One of the Passenger agents sent an unknown feedback message '" + args[0] + "'");
590
+ }
591
+ }
592
+ }
593
+
594
+ /**
595
+ * Close any file descriptors that this object has, and make it so that the destructor
596
+ * doesn't try to shut down the agents.
597
+ *
598
+ * @post getPid() == 0
599
+ */
600
+ void detach() {
601
+ feedbackFd.close();
602
+ pid = 0;
603
+ }
604
+ };
605
+
606
+ } // namespace Passenger
607
+
608
+ #endif /* __cplusplus */
609
+
610
+ #endif /* _PASSENGER_AGENTS_STARTER_HPP_ */