passenger 5.3.7 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +14 -0
  3. data/build/agent.rb +4 -2
  4. data/build/support/cxx_dependency_map.rb +134 -0
  5. data/resources/templates/standalone/server.erb +1 -0
  6. data/src/agent/AgentMain.cpp +4 -0
  7. data/src/agent/Core/ApplicationPool/Group/StateInspection.cpp +1 -1
  8. data/src/agent/Core/ApplicationPool/Options.h +7 -7
  9. data/src/agent/Core/ApplicationPool/Process.h +3 -0
  10. data/src/agent/Core/Config.h +9 -2
  11. data/src/agent/Core/Controller/Config.h +27 -6
  12. data/src/agent/Core/Controller/InitRequest.cpp +12 -7
  13. data/src/agent/Core/Controller/InitializationAndShutdown.cpp +2 -0
  14. data/src/agent/Core/CoreMain.cpp +62 -33
  15. data/src/agent/Core/OptionParser.h +6 -0
  16. data/src/agent/Core/SpawningKit/Spawner.h +20 -5
  17. data/src/agent/Core/SpawningKit/UserSwitchingRules.h +13 -6
  18. data/src/agent/Core/TelemetryCollector.h +1 -0
  19. data/src/agent/FileReadHelper/FileReadHelperMain.cpp +198 -0
  20. data/src/agent/Watchdog/Config.h +1 -0
  21. data/src/apache2_module/ConfigGeneral/AutoGeneratedDefinitions.cpp +5 -0
  22. data/src/apache2_module/ConfigGeneral/AutoGeneratedSetterFuncs.cpp +15 -0
  23. data/src/apache2_module/DirConfig/AutoGeneratedCreateFunction.cpp +5 -0
  24. data/src/apache2_module/DirConfig/AutoGeneratedManifestGeneration.cpp +13 -0
  25. data/src/apache2_module/DirConfig/AutoGeneratedMergeFunction.cpp +7 -0
  26. data/src/apache2_module/DirConfig/AutoGeneratedStruct.h +13 -0
  27. data/src/apache2_module/DirectoryMapper.h +14 -3
  28. data/src/apache2_module/Hooks.cpp +15 -4
  29. data/src/cxx_supportlib/AppLocalConfigFileUtils.h +148 -0
  30. data/src/cxx_supportlib/AppTypeDetector/CBindings.cpp +12 -1
  31. data/src/cxx_supportlib/AppTypeDetector/CBindings.h +2 -0
  32. data/src/cxx_supportlib/AppTypeDetector/Detector.h +38 -4
  33. data/src/cxx_supportlib/Constants.h +1 -1
  34. data/src/nginx_module/ConfigGeneral/AutoGeneratedDefinitions.c +16 -0
  35. data/src/nginx_module/ConfigGeneral/AutoGeneratedManifestDefaultsInitialization.c +6 -0
  36. data/src/nginx_module/ConfigGeneral/AutoGeneratedSetterFuncs.c +12 -0
  37. data/src/nginx_module/Configuration.c +20 -0
  38. data/src/nginx_module/ContentHandler.c +301 -23
  39. data/src/nginx_module/ContentHandler.h +5 -0
  40. data/src/nginx_module/LocationConfig/AutoGeneratedCreateFunction.c +10 -0
  41. data/src/nginx_module/LocationConfig/AutoGeneratedManifestGeneration.c +27 -0
  42. data/src/nginx_module/LocationConfig/AutoGeneratedMergeFunction.c +3 -0
  43. data/src/nginx_module/LocationConfig/AutoGeneratedStruct.h +7 -0
  44. data/src/nginx_module/ngx_http_passenger_module.h +6 -1
  45. data/src/ruby_supportlib/phusion_passenger.rb +6 -5
  46. data/src/ruby_supportlib/phusion_passenger/apache2/config_options.rb +6 -0
  47. data/src/ruby_supportlib/phusion_passenger/nginx/config_options.rb +14 -0
  48. data/src/ruby_supportlib/phusion_passenger/standalone/app_finder.rb +1 -0
  49. data/src/ruby_supportlib/phusion_passenger/standalone/config_options_list.rb +11 -1
  50. data/src/ruby_supportlib/phusion_passenger/standalone/start_command/builtin_engine.rb +1 -0
  51. metadata +4 -2
@@ -288,6 +288,7 @@ private:
288
288
  lastTelemetryData.timestamp);
289
289
  doc["end_time"] = (Json::UInt64) monoTimeToRealTime(
290
290
  tmData.timestamp);
291
+ doc["version"] = PASSENGER_VERSION;
291
292
  #ifdef PASSENGER_IS_ENTERPRISE
292
293
  doc["edition"] = "enterprise";
293
294
  #else
@@ -0,0 +1,198 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2018 Phusion Holding B.V.
4
+ *
5
+ * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
+ * trademarks of Phusion Holding B.V.
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ * THE SOFTWARE.
25
+ */
26
+
27
+ // A helper app that reads from an arbitrary file.
28
+ // Its main reason for existance is to allow root processes (such as the Core)
29
+ // to read from arbitrary files in a way that's safe from symlink and other
30
+ // kinds of attacks. See the documentation for safeReadFile()
31
+ // to learn more about the different types of attacks.
32
+ //
33
+ // file-read-helper is used when the caller cannot use safeReadFile(),
34
+ // e.g. when the following two conditions hold at the same time:
35
+ //
36
+ // 1. The caller does not have control over the safety of the parent
37
+ // directories leading to the file.
38
+ // 2. The caller cannot choose not to disclose the contents of the file.
39
+ //
40
+ // file-read-helper MUST be used in combination with exec-helper in order
41
+ // to lower its privilege, otherwise no protection is provided.
42
+
43
+ #include <cstdio>
44
+ #include <cstdlib>
45
+ #include <cstddef>
46
+ #include <cerrno>
47
+ #include <cstring>
48
+ #include <string>
49
+ #include <algorithm>
50
+ #include <limits>
51
+
52
+ #include <fcntl.h>
53
+ #include <unistd.h>
54
+
55
+ #include <Constants.h>
56
+ #include <ProcessManagement/Utils.h>
57
+ #include <IOTools/IOUtils.h>
58
+ #include <Utils/OptionParsing.h>
59
+
60
+ namespace Passenger {
61
+ namespace FileReadHelper {
62
+
63
+ using namespace std;
64
+
65
+
66
+ struct Options {
67
+ size_t limit;
68
+ int programArgStart;
69
+
70
+ Options()
71
+ : limit(std::numeric_limits<size_t>::max()),
72
+ programArgStart(2)
73
+ { }
74
+ };
75
+
76
+ static void
77
+ usage() {
78
+ // ....|---------------Keep output within standard terminal width (80 chars)------------|
79
+ printf("Usage: " AGENT_EXE " file-read-helper [OPTIONS...] <PATH>\n");
80
+ printf("Reads the given file with O_NONBLOCK.\n");
81
+ printf("\n");
82
+ printf("Options:\n");
83
+ printf(" --limit <SIZE> Limit the number of bytes read (default: unlimited).\n");
84
+ printf(" --help Show this help message.\n");
85
+ }
86
+
87
+ static bool
88
+ parseOption(int argc, const char *argv[], int &i, Options &options) {
89
+ OptionParser p(usage);
90
+
91
+ if (p.isValueFlag(argc, i, argv[i], '\0', "--limit")) {
92
+ options.limit = atoi(argv[i + 1]);
93
+ i += 2;
94
+ } else {
95
+ return false;
96
+ }
97
+ return true;
98
+ }
99
+
100
+ static bool
101
+ parseOptions(int argc, const char *argv[], Options &options) {
102
+ OptionParser p(usage);
103
+ int i = 2;
104
+
105
+ while (i < argc) {
106
+ if (parseOption(argc, argv, i, options)) {
107
+ continue;
108
+ } else if (p.isFlag(argv[i], 'h', "--help")) {
109
+ usage();
110
+ exit(0);
111
+ } else if (*argv[i] == '-') {
112
+ fprintf(stderr, "ERROR: unrecognized argument %s. Please type "
113
+ "'%s file-read-helper --help' for usage.\n", argv[i], argv[0]);
114
+ exit(1);
115
+ } else {
116
+ options.programArgStart = i;
117
+ return true;
118
+ }
119
+ }
120
+
121
+ return true;
122
+ }
123
+
124
+ int
125
+ fileReadHelperMain(int argc, char *argv[]) {
126
+ if (argc < 3) {
127
+ usage();
128
+ exit(1);
129
+ }
130
+
131
+ Options options;
132
+ if (!parseOptions(argc, (const char **) argv, options)) {
133
+ fprintf(stderr, "Error parsing arguments.\n");
134
+ usage();
135
+ exit(1);
136
+ }
137
+
138
+ if (argc != options.programArgStart + 1) {
139
+ fprintf(stderr, "ERROR: no file path given. Please type "
140
+ "'%s file-read-helper --help' for usage.\n", argv[0]);
141
+ exit(1);
142
+ }
143
+
144
+ if (geteuid() == 0) {
145
+ fprintf(stderr, "ERROR: file-read-helper cannot be run with root"
146
+ " privileges. Please use in combination with exec-helper.\n");
147
+ exit(1);
148
+ }
149
+
150
+ resetSignalHandlersAndMask();
151
+ disableMallocDebugging();
152
+
153
+ const char *path = argv[options.programArgStart];
154
+ int fd;
155
+ do {
156
+ fd = open(path, O_RDONLY | O_NONBLOCK);
157
+ } while (fd == -1 && errno == EINTR);
158
+ if (fd == -1) {
159
+ int e = errno;
160
+ fprintf(stderr, "Error opening %s for reading: %s (errno=%d)\n",
161
+ path, strerror(e), e);
162
+ exit(1);
163
+ }
164
+
165
+ size_t totalRead = 0;
166
+ char buf[1024 * 16];
167
+
168
+ while (totalRead < options.limit) {
169
+ ssize_t ret;
170
+ do {
171
+ ret = read(fd, buf,
172
+ std::min<size_t>(sizeof(buf), options.limit - totalRead));
173
+ } while (ret == -1 && errno == EINTR);
174
+ if (ret == -1) {
175
+ int e = errno;
176
+ fprintf(stderr, "Error reading from %s: %s (errno=%d)\n",
177
+ path, strerror(e), e);
178
+ exit(1);
179
+ } else if (ret == 0) {
180
+ break;
181
+ } else {
182
+ totalRead += ret;
183
+ writeExact(1, StaticString(buf, ret));
184
+ }
185
+ }
186
+
187
+ return 0;
188
+ }
189
+
190
+
191
+ } // namespace FileReadHelper
192
+ } // namespace Passenger
193
+
194
+
195
+ int
196
+ fileReadHelperMain(int argc, char *argv[]) {
197
+ return Passenger::FileReadHelper::fileReadHelperMain(argc, argv);
198
+ }
@@ -150,6 +150,7 @@ using namespace std;
150
150
  * setsid boolean - default(false)
151
151
  * show_version_in_header boolean - default(true)
152
152
  * single_app_mode_app_root string - default,read_only
153
+ * single_app_mode_app_start_command string - read_only
153
154
  * single_app_mode_app_type string - read_only
154
155
  * single_app_mode_startup_file string - read_only
155
156
  * standalone_engine string - default
@@ -107,6 +107,11 @@ extern "C" const command_rec passenger_commands[] = {
107
107
  NULL,
108
108
  RSRC_CONF | ACCESS_CONF,
109
109
  "The application's root directory."),
110
+ AP_INIT_TAKE1("PassengerAppStartCommand",
111
+ (Take1Func) cmd_passenger_app_start_command,
112
+ NULL,
113
+ RSRC_CONF | ACCESS_CONF,
114
+ "Command string for starting the application."),
110
115
  AP_INIT_TAKE1("PassengerAppType",
111
116
  (Take1Func) cmd_passenger_app_type,
112
117
  NULL,
@@ -203,6 +203,21 @@ cmd_passenger_app_root(cmd_parms *cmd, void *pcfg, const char *arg) {
203
203
  return NULL;
204
204
  }
205
205
 
206
+ static const char *
207
+ cmd_passenger_app_start_command(cmd_parms *cmd, void *pcfg, const char *arg) {
208
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_FILES);
209
+ if (err != NULL) {
210
+ return err;
211
+ }
212
+
213
+ DirConfig *config = (DirConfig *) pcfg;
214
+ config->mAppStartCommandSourceFile = cmd->directive->filename;
215
+ config->mAppStartCommandSourceLine = cmd->directive->line_num;
216
+ config->mAppStartCommandExplicitlySet = true;
217
+ config->mAppStartCommand = arg;
218
+ return NULL;
219
+ }
220
+
206
221
  static const char *
207
222
  cmd_passenger_app_type(cmd_parms *cmd, void *pcfg, const char *arg) {
208
223
  const char *err = ap_check_cmd_context(cmd, NOT_IN_FILES);
@@ -64,6 +64,9 @@ createDirConfig_autoGenerated(DirConfig *config) {
64
64
  /*
65
65
  * config->mAppRoot: default initialized
66
66
  */
67
+ /*
68
+ * config->mAppStartCommand: default initialized
69
+ */
67
70
  /*
68
71
  * config->mAppType: default initialized
69
72
  */
@@ -124,6 +127,7 @@ createDirConfig_autoGenerated(DirConfig *config) {
124
127
  config->mAppGroupNameSourceLine = 0;
125
128
  config->mAppLogFileSourceLine = 0;
126
129
  config->mAppRootSourceLine = 0;
130
+ config->mAppStartCommandSourceLine = 0;
127
131
  config->mAppTypeSourceLine = 0;
128
132
  config->mBaseURIsSourceLine = 0;
129
133
  config->mBufferResponseSourceLine = 0;
@@ -158,6 +162,7 @@ createDirConfig_autoGenerated(DirConfig *config) {
158
162
  config->mAppGroupNameExplicitlySet = false;
159
163
  config->mAppLogFileExplicitlySet = false;
160
164
  config->mAppRootExplicitlySet = false;
165
+ config->mAppStartCommandExplicitlySet = false;
161
166
  config->mAppTypeExplicitlySet = false;
162
167
  config->mBaseURIsExplicitlySet = false;
163
168
  config->mBufferResponseExplicitlySet = false;
@@ -116,6 +116,19 @@ ConfigManifestGenerator::autoGenerated_generateConfigManifestForDirConfig(server
116
116
  pdconf->mAppRoot.data(),
117
117
  pdconf->mAppRoot.data() + pdconf->mAppRoot.size());
118
118
  }
119
+ if (pdconf->mAppStartCommandExplicitlySet) {
120
+ findOrCreateAppAndLocOptionsContainers(serverRec, csconf, cdconf,
121
+ pdconf, context, &appOptionsContainer, &locOptionsContainer);
122
+ Json::Value &optionContainer = findOrCreateOptionContainer(*appOptionsContainer,
123
+ "PassengerAppStartCommand",
124
+ sizeof("PassengerAppStartCommand") - 1);
125
+ Json::Value &hierarchyMember = addOptionContainerHierarchyMember(optionContainer,
126
+ pdconf->mAppStartCommandSourceFile,
127
+ pdconf->mAppStartCommandSourceLine);
128
+ hierarchyMember["value"] = Json::Value(
129
+ pdconf->mAppStartCommand.data(),
130
+ pdconf->mAppStartCommand.data() + pdconf->mAppStartCommand.size());
131
+ }
119
132
  if (pdconf->mAppTypeExplicitlySet) {
120
133
  findOrCreateAppAndLocOptionsContainers(serverRec, csconf, cdconf,
121
134
  pdconf, context, &appOptionsContainer, &locOptionsContainer);
@@ -68,6 +68,10 @@ mergeDirConfig_autoGenerated(DirConfig *config, DirConfig *base, DirConfig *add)
68
68
  (!add->mAppRoot.empty())
69
69
  ? add->mAppRoot
70
70
  : base->mAppRoot;
71
+ config->mAppStartCommand =
72
+ (!add->mAppStartCommand.empty())
73
+ ? add->mAppStartCommand
74
+ : base->mAppStartCommand;
71
75
  config->mAppType =
72
76
  (!add->mAppType.empty())
73
77
  ? add->mAppType
@@ -186,6 +190,7 @@ mergeDirConfig_autoGenerated(DirConfig *config, DirConfig *base, DirConfig *add)
186
190
  config->mAppGroupNameSourceFile = add->mAppGroupNameSourceFile;
187
191
  config->mAppLogFileSourceFile = add->mAppLogFileSourceFile;
188
192
  config->mAppRootSourceFile = add->mAppRootSourceFile;
193
+ config->mAppStartCommandSourceFile = add->mAppStartCommandSourceFile;
189
194
  config->mAppTypeSourceFile = add->mAppTypeSourceFile;
190
195
  config->mBaseURIsSourceFile = add->mBaseURIsSourceFile;
191
196
  config->mBufferResponseSourceFile = add->mBufferResponseSourceFile;
@@ -220,6 +225,7 @@ mergeDirConfig_autoGenerated(DirConfig *config, DirConfig *base, DirConfig *add)
220
225
  config->mAppGroupNameSourceLine = add->mAppGroupNameSourceLine;
221
226
  config->mAppLogFileSourceLine = add->mAppLogFileSourceLine;
222
227
  config->mAppRootSourceLine = add->mAppRootSourceLine;
228
+ config->mAppStartCommandSourceLine = add->mAppStartCommandSourceLine;
223
229
  config->mAppTypeSourceLine = add->mAppTypeSourceLine;
224
230
  config->mBaseURIsSourceLine = add->mBaseURIsSourceLine;
225
231
  config->mBufferResponseSourceLine = add->mBufferResponseSourceLine;
@@ -254,6 +260,7 @@ mergeDirConfig_autoGenerated(DirConfig *config, DirConfig *base, DirConfig *add)
254
260
  config->mAppGroupNameExplicitlySet = add->mAppGroupNameExplicitlySet;
255
261
  config->mAppLogFileExplicitlySet = add->mAppLogFileExplicitlySet;
256
262
  config->mAppRootExplicitlySet = add->mAppRootExplicitlySet;
263
+ config->mAppStartCommandExplicitlySet = add->mAppStartCommandExplicitlySet;
257
264
  config->mAppTypeExplicitlySet = add->mAppTypeExplicitlySet;
258
265
  config->mBaseURIsExplicitlySet = add->mBaseURIsExplicitlySet;
259
266
  config->mBufferResponseExplicitlySet = add->mBufferResponseExplicitlySet;
@@ -156,6 +156,11 @@ struct AutoGeneratedDirConfig {
156
156
  */
157
157
  StaticString mAppRoot;
158
158
 
159
+ /*
160
+ * Command string for starting the application.
161
+ */
162
+ StaticString mAppStartCommand;
163
+
159
164
  /*
160
165
  * Force specific application type.
161
166
  */
@@ -242,6 +247,7 @@ struct AutoGeneratedDirConfig {
242
247
  StaticString mAppGroupNameSourceFile;
243
248
  StaticString mAppLogFileSourceFile;
244
249
  StaticString mAppRootSourceFile;
250
+ StaticString mAppStartCommandSourceFile;
245
251
  StaticString mAppTypeSourceFile;
246
252
  StaticString mGroupSourceFile;
247
253
  StaticString mMeteorAppSettingsSourceFile;
@@ -276,6 +282,7 @@ struct AutoGeneratedDirConfig {
276
282
  unsigned int mAppGroupNameSourceLine;
277
283
  unsigned int mAppLogFileSourceLine;
278
284
  unsigned int mAppRootSourceLine;
285
+ unsigned int mAppStartCommandSourceLine;
279
286
  unsigned int mAppTypeSourceLine;
280
287
  unsigned int mGroupSourceLine;
281
288
  unsigned int mMeteorAppSettingsSourceLine;
@@ -310,6 +317,7 @@ struct AutoGeneratedDirConfig {
310
317
  bool mAppGroupNameExplicitlySet: 1;
311
318
  bool mAppLogFileExplicitlySet: 1;
312
319
  bool mAppRootExplicitlySet: 1;
320
+ bool mAppStartCommandExplicitlySet: 1;
313
321
  bool mAppTypeExplicitlySet: 1;
314
322
  bool mGroupExplicitlySet: 1;
315
323
  bool mMeteorAppSettingsExplicitlySet: 1;
@@ -489,6 +497,11 @@ struct AutoGeneratedDirConfig {
489
497
  return mAppRoot;
490
498
  }
491
499
 
500
+ StaticString
501
+ getAppStartCommand() const {
502
+ return mAppStartCommand;
503
+ }
504
+
492
505
  StaticString
493
506
  getAppType() const {
494
507
  return mAppType;
@@ -77,6 +77,7 @@ private:
77
77
  request_rec *r;
78
78
  CachedFileStat *cstat;
79
79
  boost::mutex *cstatMutex;
80
+ boost::mutex *configMutex;
80
81
  const char *baseURI;
81
82
  string publicDir;
82
83
  string appRoot;
@@ -148,10 +149,16 @@ private:
148
149
 
149
150
  UPDATE_TRACE_POINT();
150
151
  AppTypeDetector::Detector detector(registry, cstat,
151
- cstatMutex, throttleRate);
152
+ cstatMutex, throttleRate, configMutex);
152
153
  AppTypeDetector::Detector::Result detectorResult;
153
154
  string appRoot;
154
- if (config->getAppType().empty()) {
155
+ // If `AppStartCommand` is set, then it means the config specified that it is
156
+ // either a generic app or a Kuria app.
157
+ if (!config->getAppStartCommand().empty()) {
158
+ appRoot = config->getAppRoot();
159
+ } else if (config->getAppType().empty()) {
160
+ // If neither `AppStartCommand` nor `AppType` are set, then
161
+ // autodetect what kind of app this is.
155
162
  if (config->getAppRoot().empty()) {
156
163
  detectorResult = detector.checkDocumentRoot(publicDir,
157
164
  baseURI != NULL,
@@ -161,6 +168,9 @@ private:
161
168
  detectorResult = detector.checkAppRoot(appRoot);
162
169
  }
163
170
  } else if (!config->getAppRoot().empty()) {
171
+ // If `AppStartCommand` is not set but `AppType` is (as well as
172
+ // the required `AppRoot`), then verify whether the given
173
+ // `AppType` value is supported and resolve aliases.
164
174
  appRoot = config->getAppRoot().toString();
165
175
  detectorResult.wrapperRegistryEntry = &registry.lookup(
166
176
  config->getAppType());
@@ -186,13 +196,14 @@ public:
186
196
  DirectoryMapper(request_rec *r, DirConfig *config,
187
197
  const WrapperRegistry::Registry &_registry,
188
198
  CachedFileStat *cstat, boost::mutex *cstatMutex,
189
- unsigned int throttleRate)
199
+ unsigned int throttleRate, boost::mutex *configMutex)
190
200
  : registry(_registry)
191
201
  {
192
202
  this->r = r;
193
203
  this->config = config;
194
204
  this->cstat = cstat;
195
205
  this->cstatMutex = cstatMutex;
206
+ this->configMutex = configMutex;
196
207
  this->throttleRate = throttleRate;
197
208
  baseURI = NULL;
198
209
  autoDetectionDone = false;
@@ -237,6 +237,7 @@ private:
237
237
  CachedFileStat cstat;
238
238
  WatchdogLauncher watchdogLauncher;
239
239
  boost::mutex cstatMutex;
240
+ boost::mutex configMutex;
240
241
 
241
242
  static Json::Value strsetToJson(const set<string> &input) {
242
243
  Json::Value result(Json::arrayValue);
@@ -446,9 +447,11 @@ private:
446
447
  TRACE_POINT();
447
448
 
448
449
  DirectoryMapper mapper(r, config, wrapperRegistry, &cstat,
449
- &cstatMutex, serverConfig.statThrottleRate);
450
+ &cstatMutex, serverConfig.statThrottleRate, &configMutex);
450
451
  try {
451
- if (mapper.getDetectorResult().isNull()) {
452
+ if (config->getAppStartCommand().empty()
453
+ && mapper.getDetectorResult().isNull())
454
+ {
452
455
  // (B) is not true.
453
456
  disableRequestNote(r);
454
457
  return false;
@@ -988,8 +991,16 @@ private:
988
991
  // Phusion Passenger options.
989
992
  addHeader(result, P_STATIC_STRING("!~PASSENGER_APP_ROOT"),
990
993
  mapper.getAppRoot());
991
- addHeader(result, P_STATIC_STRING("!~PASSENGER_APP_TYPE"),
992
- mapper.getDetectorResult().wrapperRegistryEntry->language);
994
+ if (!config->getAppStartCommand().empty()) {
995
+ addHeader(result, P_STATIC_STRING("!~PASSENGER_APP_START_COMMAND"),
996
+ config->getAppStartCommand());
997
+ } else if (mapper.getDetectorResult().wrapperRegistryEntry != NULL) {
998
+ addHeader(result, P_STATIC_STRING("!~PASSENGER_APP_TYPE"),
999
+ mapper.getDetectorResult().wrapperRegistryEntry->language);
1000
+ } else {
1001
+ addHeader(result, P_STATIC_STRING("!~PASSENGER_APP_START_COMMAND"),
1002
+ mapper.getDetectorResult().appStartCommand);
1003
+ }
993
1004
  constructRequestHeaders_autoGenerated(r, config, result);
994
1005
 
995
1006
  /*********************/