passenger 5.3.7 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
@@ -41,6 +41,7 @@ passenger_enabled on;
41
41
  <%= nginx_option(app, :spawn_method) %>
42
42
  <%= nginx_option(app, :app_type) %>
43
43
  <%= nginx_option(app, :startup_file) %>
44
+ <%= nginx_option(app, :app_start_command) %>
44
45
  <%= nginx_option(app, :start_timeout) %>
45
46
  <%= nginx_option(app, :min_instances) %>
46
47
  <%= nginx_option(app, :max_request_queue_size) %>
@@ -36,6 +36,7 @@ int systemMetricsMain(int argc, char *argv[]);
36
36
  int tempDirToucherMain(int argc, char *argv[]);
37
37
  int spawnEnvSetupperMain(int argc, char *argv[]);
38
38
  int execHelperMain(int argc, char *argv[]);
39
+ int fileReadHelperMain(int argc, char *argv[]);
39
40
 
40
41
  static bool
41
42
  isHelp(const char *arg) {
@@ -58,6 +59,7 @@ usage(int argc, char *argv[]) {
58
59
  printf("Utility subcommands:\n");
59
60
  printf(" system-metrics\n");
60
61
  printf(" exec-helper\n");
62
+ printf(" file-read-helper\n");
61
63
  }
62
64
 
63
65
  static bool
@@ -90,6 +92,8 @@ dispatchSubcommand(int argc, char *argv[]) {
90
92
  exit(spawnEnvSetupperMain(argc, argv));
91
93
  } else if (strcmp(argv[1], "exec-helper") == 0) {
92
94
  exit(execHelperMain(argc, argv));
95
+ } else if (strcmp(argv[1], "file-read-helper") == 0) {
96
+ exit(fileReadHelperMain(argc, argv));
93
97
  } else if (strcmp(argv[1], "test-binary") == 0) {
94
98
  printf("PASS\n");
95
99
  exit(0);
@@ -233,7 +233,7 @@ Group::inspectConfigInAdminPanelFormat(Json::Value &result) const {
233
233
 
234
234
  result["type"] = NON_EMPTY_SVAL(options.appType);
235
235
  result["startup_file"] = NON_EMPTY_SVAL(options.startupFile);
236
- result["start_command"] = NON_EMPTY_SVAL(replaceAll(options.startCommand,
236
+ result["start_command"] = NON_EMPTY_SVAL(replaceAll(options.appStartCommand,
237
237
  P_STATIC_STRING("\t"), P_STATIC_STRING(" ")));
238
238
  result["ruby"] = SVAL(options.ruby, DEFAULT_RUBY);
239
239
  result["python"] = SVAL(options.python, DEFAULT_PYTHON);
@@ -85,7 +85,7 @@ private:
85
85
  result.push_back(&options.appGroupName);
86
86
  result.push_back(&options.appLogFile);
87
87
  result.push_back(&options.appType);
88
- result.push_back(&options.startCommand);
88
+ result.push_back(&options.appStartCommand);
89
89
  result.push_back(&options.startupFile);
90
90
  result.push_back(&options.processTitle);
91
91
 
@@ -181,15 +181,15 @@ public:
181
181
  /** The application's type, used for determining the command to invoke to
182
182
  * spawn an application process as well as determining the startup file's
183
183
  * filename. It can be one of the app type names in AppType.cpp, or the
184
- * empty string (default). In case of the latter, 'startCommand' and
184
+ * empty string (default). In case of the latter, 'appStartCommand' and
185
185
  * 'startupFile' (which MUST be set) will dictate the startup command
186
186
  * and the startup file's filename. */
187
187
  StaticString appType;
188
188
 
189
- /** The command for spawning the application process. This is a list of
190
- * arguments, separated by '\t', e.g. "ruby\tfoo.rb". Only used
191
- * during spawning and only if appType.empty(). */
192
- StaticString startCommand;
189
+ /** The shell command string for spawning the application process.
190
+ * Only used during spawning and only if appType.empty().
191
+ */
192
+ StaticString appStartCommand;
193
193
 
194
194
  /** Filename of the application's startup file. Only actually used for
195
195
  * determining user switching info. Only used during spawning. */
@@ -629,7 +629,7 @@ public:
629
629
  } else if (entry.language == P_STATIC_STRING("meteor")) {
630
630
  interpreter = escapeShell(ruby);
631
631
  } else {
632
- return startCommand;
632
+ return appStartCommand;
633
633
  }
634
634
 
635
635
  return interpreter + " " + escapeShell(resourceLocator.getHelperScriptsDir()
@@ -601,6 +601,9 @@ public:
601
601
  if (inputPipe != -1) {
602
602
  inputPipe.close();
603
603
  }
604
+ if (type == SpawningKit::Result::GENERIC) {
605
+ syscalls::kill(getPid(), SIGTERM);
606
+ }
604
607
  }
605
608
 
606
609
  bool shutdownTimeoutExpired() const {
@@ -160,6 +160,7 @@ using namespace std;
160
160
  * server_software string - default("Phusion_Passenger/5.3.7")
161
161
  * show_version_in_header boolean - default(true)
162
162
  * single_app_mode_app_root string - default,read_only
163
+ * single_app_mode_app_start_command string - read_only
163
164
  * single_app_mode_app_type string - read_only
164
165
  * single_app_mode_startup_file string - read_only
165
166
  * standalone_engine string - default
@@ -253,6 +254,10 @@ private:
253
254
  errors.push_back(Error("If '{{multi_app_mode}}' is set,"
254
255
  " then '{{single_app_mode_startup_file}}' may not be set"));
255
256
  }
257
+ if (!config["single_app_mode_app_start_command"].isNull()) {
258
+ errors.push_back(Error("If '{{multi_app_mode}}' is set,"
259
+ " then '{{single_app_mode_app_start_command}}' may not be set"));
260
+ }
256
261
  }
257
262
 
258
263
  static void validateSingleAppMode(const ConfigKit::Store &config,
@@ -264,8 +269,9 @@ private:
264
269
  return;
265
270
  }
266
271
 
267
- // single_app_mode_app_type and single_app_mode_startup_file are
268
- // autodetected in initializeSingleAppMode()
272
+ // single_app_mode_app_type, single_app_mode_startup_file and
273
+ // single_app_mode_app_start_command are autodetected in
274
+ // initializeSingleAppMode() so no need to validate them.
269
275
 
270
276
  ControllerSingleAppModeSchema::validateAppType("single_app_mode_app_type",
271
277
  wrapperRegistry, config, errors);
@@ -425,6 +431,7 @@ public:
425
431
  ControllerSingleAppModeSchema::getDefaultAppRoot);
426
432
  add("single_app_mode_app_type", STRING_TYPE, OPTIONAL | READ_ONLY);
427
433
  add("single_app_mode_startup_file", STRING_TYPE, OPTIONAL | READ_ONLY);
434
+ add("single_app_mode_app_start_command", STRING_TYPE, OPTIONAL | READ_ONLY);
428
435
 
429
436
  // Add subschema: controllerServerKit
430
437
  controllerServerKit.translator.setPrefixAndFinalize("controller_");
@@ -223,9 +223,10 @@ public:
223
223
  * (do not edit: following text is automatically generated
224
224
  * by 'rake configkit_schemas_inline_comments')
225
225
  *
226
- * app_root string - default,read_only
227
- * app_type string required read_only
228
- * startup_file string required read_only
226
+ * app_root string - default,read_only
227
+ * app_start_command string - read_only
228
+ * app_type string - read_only
229
+ * startup_file string - read_only
229
230
  *
230
231
  * END
231
232
  */
@@ -235,9 +236,11 @@ struct ControllerSingleAppModeSchema: public ConfigKit::Schema {
235
236
 
236
237
  addWithDynamicDefault("app_root", STRING_TYPE, OPTIONAL | READ_ONLY | CACHE_DEFAULT_VALUE,
237
238
  getDefaultAppRoot);
238
- add("app_type", STRING_TYPE, REQUIRED | READ_ONLY);
239
- add("startup_file", STRING_TYPE, REQUIRED | READ_ONLY);
239
+ add("app_type", STRING_TYPE, OPTIONAL | READ_ONLY);
240
+ add("startup_file", STRING_TYPE, OPTIONAL | READ_ONLY);
241
+ add("app_start_command", STRING_TYPE, OPTIONAL | READ_ONLY);
240
242
 
243
+ addValidator(validateAppTypeOrAppStartCommandSet);
241
244
  addValidator(boost::bind(validateAppType, "app_type", wrapperRegistry,
242
245
  boost::placeholders::_1, boost::placeholders::_2));
243
246
  addNormalizer(normalizeAppRoot);
@@ -257,6 +260,21 @@ struct ControllerSingleAppModeSchema: public ConfigKit::Schema {
257
260
  return result;
258
261
  }
259
262
 
263
+ static void validateAppTypeOrAppStartCommandSet(const ConfigKit::Store &config,
264
+ vector<ConfigKit::Error> &errors)
265
+ {
266
+ typedef ConfigKit::Error Error;
267
+
268
+ if (config["app_type"].isNull() && config["app_start_command"].isNull()) {
269
+ errors.push_back(Error(
270
+ "Either '{{app_type}}' or '{{app_start_command}}' must be set"));
271
+ }
272
+ if (!config["app_type"].isNull() && config["startup_file"].isNull()) {
273
+ errors.push_back(Error(
274
+ "If '{{app_type}}' is set, then '{{startup_file}}' must also be set"));
275
+ }
276
+ }
277
+
260
278
  static void validateAppType(const string &appTypeKey,
261
279
  const WrapperRegistry::Registry *wrapperRegistry,
262
280
  const ConfigKit::Store &config, vector<ConfigKit::Error> &errors)
@@ -290,7 +308,10 @@ struct ControllerSingleAppModeSchema: public ConfigKit::Schema {
290
308
 
291
309
  static Json::Value normalizeStartupFile(const Json::Value &effectiveValues) {
292
310
  Json::Value updates;
293
- updates["startup_file"] = absolutizePath(effectiveValues["startup_file"].asString());
311
+ if (effectiveValues.isMember("startup_file")) {
312
+ updates["startup_file"] = absolutizePath(
313
+ effectiveValues["startup_file"].asString());
314
+ }
294
315
  return updates;
295
316
  }
296
317
  };
@@ -337,13 +337,18 @@ Controller::createNewPoolOptions(Client *client, Request *req,
337
337
 
338
338
  const LString *appType = secureHeaders.lookup("!~PASSENGER_APP_TYPE");
339
339
  if (appType == NULL || appType->size == 0) {
340
- AppTypeDetector::Detector detector(*wrapperRegistry);
341
- AppTypeDetector::Detector::Result result = detector.checkAppRoot(options.appRoot);
342
- if (result.isNull()) {
343
- disconnectWithError(&client, "client did not send a recognized !~PASSENGER_APP_TYPE header");
344
- return;
340
+ const LString *appStartCommand = secureHeaders.lookup("!~PASSENGER_APP_START_COMMAND");
341
+ if (appStartCommand == NULL || appStartCommand->size == 0) {
342
+ AppTypeDetector::Detector detector(*wrapperRegistry);
343
+ AppTypeDetector::Detector::Result result = detector.checkAppRoot(options.appRoot);
344
+ if (result.isNull()) {
345
+ disconnectWithError(&client, "client did not send a recognized !~PASSENGER_APP_TYPE header");
346
+ return;
347
+ }
348
+ options.appType = result.wrapperRegistryEntry->language;
349
+ } else {
350
+ fillPoolOption(req, options.appStartCommand, "!~PASSENGER_APP_START_COMMAND");
345
351
  }
346
- options.appType = result.wrapperRegistryEntry->language;
347
352
  } else {
348
353
  fillPoolOption(req, options.appType, "!~PASSENGER_APP_TYPE");
349
354
  }
@@ -360,7 +365,7 @@ Controller::createNewPoolOptions(Client *client, Request *req,
360
365
  fillPoolOption(req, options.group, "!~PASSENGER_GROUP");
361
366
  fillPoolOption(req, options.minProcesses, "!~PASSENGER_MIN_PROCESSES");
362
367
  fillPoolOption(req, options.spawnMethod, "!~PASSENGER_SPAWN_METHOD");
363
- fillPoolOption(req, options.startCommand, "!~PASSENGER_START_COMMAND");
368
+ fillPoolOption(req, options.appStartCommand, "!~PASSENGER_APP_START_COMMAND");
364
369
  fillPoolOptionSecToMsec(req, options.startTimeout, "!~PASSENGER_START_TIMEOUT");
365
370
  fillPoolOption(req, options.maxPreloaderIdleTime, "!~PASSENGER_MAX_PRELOADER_IDLE_TIME");
366
371
  fillPoolOption(req, options.maxRequestQueueSize, "!~PASSENGER_MAX_REQUEST_QUEUE_SIZE");
@@ -112,11 +112,13 @@ Controller::initialize() {
112
112
  string environment = config["default_environment"].asString();
113
113
  string appType = singleAppModeConfig->get("app_type").asString();
114
114
  string startupFile = singleAppModeConfig->get("startup_file").asString();
115
+ string appStartCommand = singleAppModeConfig->get("app_start_command").asString();
115
116
 
116
117
  options->appRoot = appRoot;
117
118
  options->environment = environment;
118
119
  options->appType = appType;
119
120
  options->startupFile = startupFile;
121
+ options->appStartCommand = appStartCommand;
120
122
  *options = options->copyAndPersist();
121
123
  poolOptionsCache.insert(options->getAppGroupName(), options);
122
124
  }
@@ -258,53 +258,83 @@ initializeSingleAppMode() {
258
258
  }
259
259
 
260
260
  WorkingObjects *wo = workingObjects;
261
- string appType, startupFile;
261
+ string appType, startupFile, appStartCommand;
262
262
  string appRoot = coreConfig->get("single_app_mode_app_root").asString();
263
263
 
264
- if (coreConfig->get("single_app_mode_app_type").isNull()) {
264
+ if (!coreConfig->get("single_app_mode_app_type").isNull()
265
+ && !coreConfig->get("single_app_mode_app_start_command").isNull())
266
+ {
267
+ fprintf(stderr, "ERROR: it is not allowed for both --app-type and"
268
+ " --app-start-command to be set.\n");
269
+ exit(1);
270
+ }
271
+
272
+ if (!coreConfig->get("single_app_mode_app_start_command").isNull()) {
273
+ // The config specified that this is a generic app or a Kuria app.
274
+ appStartCommand = coreConfig->get("single_app_mode_app_start_command").asString();
275
+ } else if (coreConfig->get("single_app_mode_app_type").isNull()) {
276
+ // Autodetect whether this is generic app, Kuria app or auto-supported app.
265
277
  P_DEBUG("Autodetecting application type...");
266
- AppTypeDetector::Detector detector(*coreWrapperRegistry, NULL, 0);
267
- AppTypeDetector::Detector::Result result = detector.checkAppRoot(appRoot);
268
- if (result.isNull()) {
269
- fprintf(stderr, "ERROR: unable to autodetect what kind of application "
270
- "lives in %s. Please specify information about the app using "
271
- "--app-type and --startup-file, or specify a correct location to "
272
- "the application you want to serve.\n"
273
- "Type '" SHORT_PROGRAM_NAME " core --help' for more information.\n",
274
- appRoot.c_str());
275
- exit(1);
276
- }
278
+ AppTypeDetector::Detector detector(*coreWrapperRegistry);
279
+ AppTypeDetector::Detector::Result detectorResult = detector.checkAppRoot(appRoot);
277
280
 
278
- appType = result.wrapperRegistryEntry->language;
281
+ if (!detectorResult.appStartCommand.empty()) {
282
+ // This is a generic or Kuria app.
283
+ appStartCommand = detectorResult.appStartCommand;
284
+ } else {
285
+ // This is an auto-supported app.
286
+ if (coreConfig->get("single_app_mode_app_type").isNull()) {
287
+ if (detectorResult.isNull()) {
288
+ fprintf(stderr, "ERROR: unable to autodetect what kind of application "
289
+ "lives in %s. Please specify information about the app using "
290
+ "--app-type, --startup-file and --app-start-command, or specify a "
291
+ "correct location to the application you want to serve.\n"
292
+ "Type '" SHORT_PROGRAM_NAME " core --help' for more information.\n",
293
+ appRoot.c_str());
294
+ exit(1);
295
+ }
296
+ appType = detectorResult.wrapperRegistryEntry->language;
297
+ } else {
298
+ appType = coreConfig->get("single_app_mode_app_type").asString();
299
+ }
300
+ }
279
301
  } else {
302
+ // This is an auto-supported app.
280
303
  appType = coreConfig->get("single_app_mode_app_type").asString();
281
304
  }
282
305
 
283
- if (coreConfig->get("single_app_mode_startup_file").isNull()) {
284
- const WrapperRegistry::Entry &entry = coreWrapperRegistry->lookup(appType);
285
- if (entry.defaultStartupFiles.empty()) {
286
- startupFile = appRoot + "/";
306
+ if (!appType.empty()) {
307
+ if (coreConfig->get("single_app_mode_startup_file").isNull()) {
308
+ const WrapperRegistry::Entry &entry = coreWrapperRegistry->lookup(appType);
309
+ if (entry.defaultStartupFiles.empty()) {
310
+ startupFile = appRoot + "/";
311
+ } else {
312
+ startupFile = appRoot + "/" + entry.defaultStartupFiles[0];
313
+ }
287
314
  } else {
288
- startupFile = appRoot + "/" + entry.defaultStartupFiles[0];
315
+ startupFile = coreConfig->get("single_app_mode_startup_file").asString();
316
+ }
317
+ if (!fileExists(startupFile)) {
318
+ fprintf(stderr, "ERROR: unable to find expected startup file %s."
319
+ " Please specify its correct path with --startup-file.\n",
320
+ startupFile.c_str());
321
+ exit(1);
289
322
  }
290
- } else {
291
- startupFile = coreConfig->get("single_app_mode_startup_file").asString();
292
- }
293
- if (!fileExists(startupFile)) {
294
- fprintf(stderr, "ERROR: unable to find expected startup file %s."
295
- " Please specify its correct path with --startup-file.\n",
296
- startupFile.c_str());
297
- exit(1);
298
323
  }
299
324
 
300
325
  wo->singleAppModeConfig["app_root"] = appRoot;
301
- wo->singleAppModeConfig["app_type"] = appType;
302
- wo->singleAppModeConfig["startup_file"] = startupFile;
303
326
 
304
327
  P_NOTICE(SHORT_PROGRAM_NAME " core running in single-application mode.");
305
- P_NOTICE("Serving app : " << appRoot);
306
- P_NOTICE("App type : " << appType);
307
- P_NOTICE("App startup file: " << startupFile);
328
+ P_NOTICE("Serving app : " << appRoot);
329
+ if (!appType.empty()) {
330
+ P_NOTICE("App type : " << appType);
331
+ P_NOTICE("App startup file : " << startupFile);
332
+ wo->singleAppModeConfig["app_type"] = appType;
333
+ wo->singleAppModeConfig["startup_file"] = startupFile;
334
+ } else {
335
+ P_NOTICE("App start command: " << appStartCommand);
336
+ wo->singleAppModeConfig["app_start_command"] = appStartCommand;
337
+ }
308
338
  }
309
339
 
310
340
  static void
@@ -852,7 +882,6 @@ initializeSecurityUpdateChecker() {
852
882
 
853
883
  static void
854
884
  initializeTelemetryCollector() {
855
- return; // disable for now
856
885
  TRACE_POINT();
857
886
  WorkingObjects &wo = *workingObjects;
858
887
 
@@ -109,6 +109,9 @@ coreUsage() {
109
109
  printf(" (single-app mode only)\n");
110
110
  printf(" --startup-file PATH The path of the app's startup file, relative to\n");
111
111
  printf(" the app root directory (single-app mode only)\n");
112
+ printf(" --app-start-command COMMAND\n");
113
+ printf(" The command string with which to start the app\n");
114
+ printf(" (single-app mode only)\n");
112
115
  printf(" --spawn-method NAME Spawn method to use. Can either be 'smart' or\n");
113
116
  printf(" 'direct'. Default: %s\n", DEFAULT_SPAWN_METHOD);
114
117
  printf(" --load-shell-envvars Load shell startup files before loading application\n");
@@ -317,6 +320,9 @@ parseCoreOption(int argc, const char *argv[], int &i, Json::Value &updates) {
317
320
  } else if (p.isValueFlag(argc, i, argv[i], '\0', "--startup-file")) {
318
321
  updates["single_app_mode_startup_file"] = argv[i + 1];
319
322
  i += 2;
323
+ } else if (p.isValueFlag(argc, i, argv[i], '\0', "--app-start-command")) {
324
+ updates["single_app_mode_app_start_command"] = argv[i + 1];
325
+ i += 2;
320
326
  } else if (p.isValueFlag(argc, i, argv[i], '\0', "--spawn-method")) {
321
327
  updates["default_spawn_method"] = argv[i + 1];
322
328
  i += 2;
@@ -31,6 +31,7 @@
31
31
 
32
32
  #include <modp_b64.h>
33
33
 
34
+ #include <AppLocalConfigFileUtils.h>
34
35
  #include <LoggingKit/Logging.h>
35
36
  #include <SystemTools/SystemTime.h>
36
37
  #include <Core/SpawningKit/Context.h>
@@ -79,8 +80,7 @@ protected:
79
80
  void setConfigFromAppPoolOptions(Config *config, Json::Value &extraArgs,
80
81
  const AppPoolOptions &options)
81
82
  {
82
- string startCommand = options.getStartCommand(*context->resourceLocator,
83
- *context->wrapperRegistry);
83
+ TRACE_POINT();
84
84
  string envvarsData;
85
85
  try {
86
86
  envvarsData = modp::b64_decode(options.environmentVariables.data(),
@@ -91,15 +91,30 @@ protected:
91
91
  envvarsData.clear();
92
92
  }
93
93
 
94
+ AppLocalConfig appLocalConfig = parseAppLocalConfigFile(options.appRoot);
95
+ string startCommand;
96
+
97
+ if (appLocalConfig.appSupportsKuriaProtocol) {
98
+ config->genericApp = false;
99
+ config->startsUsingWrapper = false;
100
+ config->startCommand = options.appStartCommand;
101
+ } else if (options.appType.empty()) {
102
+ config->genericApp = true;
103
+ config->startCommand = options.appStartCommand;
104
+ } else {
105
+ startCommand = options.getStartCommand(*context->resourceLocator,
106
+ *context->wrapperRegistry);
107
+ config->genericApp = false;
108
+ config->startsUsingWrapper = true;
109
+ config->startCommand = startCommand;
110
+ }
111
+
94
112
  config->appGroupName = options.getAppGroupName();
95
113
  config->appRoot = options.appRoot;
96
114
  config->logLevel = options.logLevel;
97
- config->genericApp = false;
98
- config->startsUsingWrapper = true;
99
115
  config->wrapperSuppliedByThirdParty = false;
100
116
  config->findFreePort = false;
101
117
  config->loadShellEnvvars = options.loadShellEnvvars;
102
- config->startCommand = startCommand;
103
118
  config->startupFile = options.getStartupFile(*context->wrapperRegistry);
104
119
  config->appType = options.appType;
105
120
  config->appEnv = options.environment;
@@ -98,8 +98,8 @@ prepareUserSwitching(const AppPoolOptions &options,
98
98
 
99
99
  UPDATE_TRACE_POINT();
100
100
  string defaultGroup;
101
- string startupFile = absolutizePath(options.getStartupFile(wrapperRegistry),
102
- absolutizePath(options.appRoot));
101
+ // This is the file that determines what user we lower privilege to.
102
+ string referenceFile;
103
103
  struct passwd &pwd = info.lveUserPwd;
104
104
  boost::shared_array<char> &pwdBuf = info.lveUserPwdStrBuf;
105
105
  struct passwd *userInfo;
@@ -109,6 +109,13 @@ prepareUserSwitching(const AppPoolOptions &options,
109
109
  boost::shared_array<char> grpBuf;
110
110
  int ret;
111
111
 
112
+ if (options.appType.empty()) {
113
+ referenceFile = absolutizePath(options.appRoot);
114
+ } else {
115
+ referenceFile = absolutizePath(options.getStartupFile(wrapperRegistry),
116
+ absolutizePath(options.appRoot));
117
+ }
118
+
112
119
  // _SC_GETPW_R_SIZE_MAX/_SC_GETGR_R_SIZE_MAX are not maximums:
113
120
  // http://tomlee.co/2012/10/problems-with-large-linux-unix-groups-and-getgrgid_r-getgrnam_r/
114
121
  pwdBufSize = std::max<long>(1024 * 128, sysconf(_SC_GETPW_R_SIZE_MAX));
@@ -158,9 +165,9 @@ prepareUserSwitching(const AppPoolOptions &options,
158
165
  }
159
166
  } else {
160
167
  struct stat buf;
161
- if (syscalls::lstat(startupFile.c_str(), &buf) == -1) {
168
+ if (syscalls::lstat(referenceFile.c_str(), &buf) == -1) {
162
169
  int e = errno;
163
- throw SystemException("Cannot lstat(\"" + startupFile +
170
+ throw SystemException("Cannot lstat(\"" + referenceFile +
164
171
  "\")", e);
165
172
  }
166
173
  ret = getpwuid_r(buf.st_uid, &pwd, pwdBuf.get(),
@@ -187,10 +194,10 @@ prepareUserSwitching(const AppPoolOptions &options,
187
194
  if (options.group == "!STARTUP_FILE!") {
188
195
  struct stat buf;
189
196
 
190
- if (syscalls::lstat(startupFile.c_str(), &buf) == -1) {
197
+ if (syscalls::lstat(referenceFile.c_str(), &buf) == -1) {
191
198
  int e = errno;
192
199
  throw SystemException("Cannot lstat(\"" +
193
- startupFile + "\")", e);
200
+ referenceFile + "\")", e);
194
201
  }
195
202
 
196
203
  ret = getgrgid_r(buf.st_gid, &grp, grpBuf.get(), grpBufSize,