passenger 4.0.5 → 4.0.6

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 (162) hide show
  1. data.tar.gz.asc +7 -7
  2. data/.travis.yml +1 -2
  3. data/CONTRIBUTING.md +20 -5
  4. data/CONTRIBUTORS +67 -0
  5. data/LICENSE +1 -1
  6. data/NEWS +48 -0
  7. data/Rakefile +2 -2
  8. data/bin/passenger-config +18 -0
  9. data/bin/passenger-install-apache2-module +2 -0
  10. data/bin/passenger-install-nginx-module +11 -12
  11. data/bin/passenger-memory-stats +2 -0
  12. data/bin/passenger-status +152 -69
  13. data/build/agents.rb +1 -5
  14. data/build/basics.rb +26 -13
  15. data/build/cplusplus_support.rb +9 -0
  16. data/build/debian.rb +129 -0
  17. data/build/documentation.rb +6 -2
  18. data/build/integration_tests.rb +13 -2
  19. data/build/misc.rb +16 -0
  20. data/build/packaging.rb +67 -51
  21. data/build/preprocessor.rb +314 -0
  22. data/build/test_basics.rb +1 -0
  23. data/{debian → debian.template}/README.Debian +0 -0
  24. data/{debian → debian.template}/changelog +131 -0
  25. data/debian.template/compat +1 -0
  26. data/debian.template/control +71 -0
  27. data/debian.template/copyright +385 -0
  28. data/debian.template/libapache2-mod-passenger.install +3 -0
  29. data/{debian → debian.template}/libapache2-mod-passenger.postinst +0 -0
  30. data/{debian → debian.template}/libapache2-mod-passenger.prerm +0 -0
  31. data/debian.template/locations.ini +12 -0
  32. data/debian.template/passenger.conf +4 -0
  33. data/{debian → debian.template}/passenger.load +0 -0
  34. data/debian.template/patches/series +0 -0
  35. data/debian.template/repack.sh +42 -0
  36. data/debian.template/ruby-passenger-dev.install +3 -0
  37. data/debian.template/ruby-passenger-doc.install +2 -0
  38. data/debian.template/ruby-passenger.docs +4 -0
  39. data/debian.template/ruby-passenger.install +11 -0
  40. data/debian.template/ruby-passenger.manpages +4 -0
  41. data/debian.template/rules.template +35 -0
  42. data/debian.template/source/format +1 -0
  43. data/debian.template/watch +3 -0
  44. data/dev/run_travis.sh +46 -46
  45. data/doc/Architectural overview.html +2 -2
  46. data/doc/Packaging.html +27 -18
  47. data/doc/Packaging.txt.md +27 -18
  48. data/doc/Security of user switching support.html +2 -2
  49. data/doc/Users guide Apache.html +881 -95
  50. data/doc/Users guide Apache.idmap.txt +48 -6
  51. data/doc/Users guide Apache.txt +13 -1
  52. data/doc/Users guide Nginx.html +1063 -190
  53. data/doc/Users guide Nginx.idmap.txt +89 -45
  54. data/doc/Users guide Nginx.txt +45 -0
  55. data/doc/Users guide Standalone.html +7 -7
  56. data/doc/users_guide_snippets/alternative_for_flying_passenger.txt +1 -0
  57. data/doc/users_guide_snippets/environment_variables.txt +221 -0
  58. data/doc/users_guide_snippets/installation.txt +66 -17
  59. data/doc/users_guide_snippets/support_information.txt +3 -3
  60. data/doc/users_guide_snippets/tips.txt +352 -40
  61. data/ext/common/Account.h +4 -3
  62. data/ext/common/AccountsDatabase.h +6 -6
  63. data/ext/common/AgentsStarter.h +1 -13
  64. data/ext/common/ApplicationPool2/DirectSpawner.h +4 -4
  65. data/ext/common/ApplicationPool2/DummySpawner.h +1 -1
  66. data/ext/common/ApplicationPool2/Group.h +9 -4
  67. data/ext/common/ApplicationPool2/Implementation.cpp +6 -1
  68. data/ext/common/ApplicationPool2/Options.h +65 -37
  69. data/ext/common/ApplicationPool2/Pool.h +91 -41
  70. data/ext/common/ApplicationPool2/Process.h +6 -6
  71. data/ext/common/ApplicationPool2/SmartSpawner.h +14 -14
  72. data/ext/common/ApplicationPool2/Socket.h +1 -1
  73. data/ext/common/ApplicationPool2/Spawner.h +24 -16
  74. data/ext/common/ApplicationPool2/SpawnerFactory.h +9 -1
  75. data/ext/common/ApplicationPool2/SuperGroup.h +1 -1
  76. data/ext/common/Constants.h +1 -1
  77. data/ext/common/Logging.cpp +12 -7
  78. data/ext/common/MessageServer.h +7 -12
  79. data/ext/common/MultiLibeio.cpp +5 -5
  80. data/ext/common/ResourceLocator.h +2 -6
  81. data/ext/common/ServerInstanceDir.h +37 -10
  82. data/ext/common/UnionStation.h +10 -10
  83. data/ext/common/Utils.cpp +30 -4
  84. data/ext/common/Utils.h +7 -0
  85. data/ext/common/Utils/BlockingQueue.h +2 -2
  86. data/ext/common/Utils/Lock.h +2 -2
  87. data/ext/common/Utils/MessagePassing.h +2 -2
  88. data/ext/common/Utils/Timer.h +4 -4
  89. data/ext/common/agents/HelperAgent/AgentOptions.h +2 -0
  90. data/ext/common/agents/HelperAgent/Main.cpp +57 -16
  91. data/ext/common/agents/HelperAgent/RequestHandler.h +4 -1
  92. data/ext/common/agents/LoggingAgent/AdminController.h +91 -0
  93. data/ext/common/agents/LoggingAgent/LoggingServer.h +46 -29
  94. data/ext/common/agents/LoggingAgent/Main.cpp +43 -16
  95. data/ext/common/agents/LoggingAgent/RemoteSender.h +7 -7
  96. data/ext/common/agents/Watchdog/AgentWatcher.cpp +11 -11
  97. data/ext/common/agents/Watchdog/LoggingAgentWatcher.cpp +3 -1
  98. data/ext/common/agents/Watchdog/Main.cpp +62 -0
  99. data/ext/libeio/config.guess +206 -167
  100. data/ext/libeio/config.sub +142 -68
  101. data/ext/libev/config.guess +304 -290
  102. data/ext/libev/config.sub +198 -77
  103. data/ext/nginx/config +4 -0
  104. data/ext/nginx/ngx_http_passenger_module.c +1 -0
  105. data/ext/oxt/implementation.cpp +4 -4
  106. data/lib/phusion_passenger.rb +14 -5
  107. data/lib/phusion_passenger/abstract_installer.rb +41 -0
  108. data/lib/phusion_passenger/admin_tools/server_instance.rb +48 -39
  109. data/lib/phusion_passenger/message_client.rb +31 -7
  110. data/lib/phusion_passenger/native_support.rb +35 -12
  111. data/lib/phusion_passenger/packaging.rb +16 -2
  112. data/lib/phusion_passenger/platform_info/binary_compatibility.rb +6 -31
  113. data/lib/phusion_passenger/platform_info/operating_system.rb +1 -1
  114. data/lib/phusion_passenger/preloader_shared_helpers.rb +3 -1
  115. data/lib/phusion_passenger/request_handler.rb +1 -1
  116. data/lib/phusion_passenger/standalone/command.rb +6 -6
  117. data/lib/phusion_passenger/standalone/main.rb +23 -8
  118. data/lib/phusion_passenger/standalone/package_runtime_command.rb +9 -5
  119. data/lib/phusion_passenger/standalone/runtime_installer.rb +9 -10
  120. data/lib/phusion_passenger/standalone/start_command.rb +20 -4
  121. data/resources/templates/installer_common/freebsd9_broken_cxx_runtime.txt.erb +19 -0
  122. data/resources/templates/installer_common/low_amount_of_memory_warning.txt.erb +22 -0
  123. data/resources/templates/standalone/config.erb +3 -2
  124. data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +4 -4
  125. data/test/cxx/ApplicationPool2/PoolTest.cpp +1 -1
  126. data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +7 -7
  127. data/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +9 -9
  128. data/test/cxx/EventedBufferedInputTest.cpp +17 -17
  129. data/test/cxx/RequestHandlerTest.cpp +5 -5
  130. data/test/cxx/ServerInstanceDirTest.cpp +3 -1
  131. data/test/cxx/TestSupport.h +4 -4
  132. data/test/cxx/UnionStationTest.cpp +3 -1
  133. data/test/cxx/UtilsTest.cpp +2 -0
  134. data/test/integration_tests/apache2_tests.rb +2 -2
  135. data/test/integration_tests/native_packaging_spec.rb +170 -0
  136. data/test/ruby/spec_helper.rb +0 -1
  137. data/test/stub/apache2/httpd.conf.erb +1 -1
  138. data/test/stub/nginx/nginx.conf.erb +1 -0
  139. data/test/support/apache2_controller.rb +1 -1
  140. data/test/support/placebo-preloader.rb +1 -1
  141. data/test/support/test_helper.rb +5 -2
  142. metadata +32 -26
  143. metadata.gz.asc +7 -7
  144. data/debian/compat +0 -1
  145. data/debian/control +0 -49
  146. data/debian/copyright +0 -20
  147. data/debian/libapache2-mod-passenger.install +0 -1
  148. data/debian/passenger-common.install +0 -4
  149. data/debian/passenger.conf +0 -4
  150. data/debian/prerm +0 -2
  151. data/debian/rules +0 -37
  152. data/debian/watch +0 -3
  153. data/dev/googlecode_upload.py +0 -265
  154. data/ext/common/agents/HelperAgent/BacktracesServer.h +0 -60
  155. data/resources/templates/nginx/not_available_when_natively_packaged.txt.erb +0 -8
  156. data/test/stub/rails3.1/app/assets/javascripts/application.js +0 -9
  157. data/test/stub/rails3.2/app/assets/javascripts/application.js +0 -15
  158. data/test/stub/rails_apps/2.3/mycook/public/javascripts/application.js +0 -2
  159. data/test/stub/rails_apps/2.3/mycook/public/javascripts/controls.js +0 -963
  160. data/test/stub/rails_apps/2.3/mycook/public/javascripts/dragdrop.js +0 -973
  161. data/test/stub/rails_apps/2.3/mycook/public/javascripts/effects.js +0 -1128
  162. data/test/stub/rails_apps/2.3/mycook/public/javascripts/prototype.js +0 -4320
@@ -62,7 +62,7 @@ public:
62
62
  ALL = ~0,
63
63
  NONE = 0,
64
64
 
65
- // ApplicationPool::Server rights.
65
+ // ApplicationPool2::Server rights.
66
66
  GET = 1 << 0,
67
67
  CLEAR = 1 << 1,
68
68
  DETACH = 1 << 2,
@@ -73,8 +73,9 @@ public:
73
73
  //INSPECT_BACKEND_ADDRESSES = 1 << 6,
74
74
  //INSPECT_DETACH_KEYS = 1 << 7,
75
75
 
76
- // BacktracesServer rights.
77
- INSPECT_BACKTRACES = 1 << 8,
76
+ // HelperAgent admin rights.
77
+ INSPECT_REQUESTS = 1 << 8,
78
+ INSPECT_BACKTRACES = 1 << 9,
78
79
 
79
80
  // Other rights.
80
81
  EXIT = 1 << 31
@@ -60,7 +60,7 @@ public:
60
60
  }
61
61
 
62
62
  unsigned int size() const {
63
- lock_guard<boost::mutex> l(lock);
63
+ boost::lock_guard<boost::mutex> l(lock);
64
64
  return (unsigned int) accounts.size();
65
65
  }
66
66
 
@@ -76,13 +76,13 @@ public:
76
76
 
77
77
  AccountPtr add(const string &username, const string &passwordOrHash, bool hashGiven, int rights = Account::ALL) {
78
78
  AccountPtr account(new Account(username, passwordOrHash, hashGiven, rights));
79
- lock_guard<boost::mutex> l(lock);
79
+ boost::lock_guard<boost::mutex> l(lock);
80
80
  accounts[username] = account;
81
81
  return account;
82
82
  }
83
83
 
84
84
  const AccountPtr get(const string &username) const {
85
- lock_guard<boost::mutex> l(lock);
85
+ boost::lock_guard<boost::mutex> l(lock);
86
86
  map<string, AccountPtr>::const_iterator it = accounts.find(username);
87
87
  if (it == accounts.end()) {
88
88
  return AccountPtr();
@@ -92,7 +92,7 @@ public:
92
92
  }
93
93
 
94
94
  AccountPtr authenticate(const string &username, const StaticString &userSuppliedPassword) const {
95
- lock_guard<boost::mutex> l(lock);
95
+ boost::lock_guard<boost::mutex> l(lock);
96
96
  map<string, AccountPtr>::const_iterator it = accounts.find(username);
97
97
  if (it == accounts.end()) {
98
98
  return AccountPtr();
@@ -107,7 +107,7 @@ public:
107
107
  }
108
108
 
109
109
  bool remove(const string &username) {
110
- lock_guard<boost::mutex> l(lock);
110
+ boost::lock_guard<boost::mutex> l(lock);
111
111
  map<string, AccountPtr>::iterator it = accounts.find(username);
112
112
  if (it == accounts.end()) {
113
113
  return false;
@@ -118,7 +118,7 @@ public:
118
118
  }
119
119
 
120
120
  unsigned int getUniqueNumber() {
121
- lock_guard<boost::mutex> l(lock);
121
+ boost::lock_guard<boost::mutex> l(lock);
122
122
  unsigned int result = uniqueNumber;
123
123
  uniqueNumber++;
124
124
  return result;
@@ -269,17 +269,6 @@ private:
269
269
  }
270
270
  return false;
271
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
272
 
284
273
  public:
285
274
  /**
@@ -391,7 +380,6 @@ public:
391
380
  this_thread::disable_interruption di;
392
381
  this_thread::disable_syscall_interruption dsi;
393
382
  ResourceLocator locator(passengerRoot);
394
- string realUnionStationGatewayCert = findUnionStationGatewayCert(locator, extraParams);
395
383
  string watchdogFilename = locator.getAgentsDir() + "/PassengerWatchdog";
396
384
  SocketPair fds;
397
385
  int e;
@@ -586,7 +574,7 @@ public:
586
574
  UPDATE_TRACE_POINT();
587
575
  killProcessGroupAndWait(&pid, 5000);
588
576
  guard.clear();
589
- throw RuntimeException("One of the Passenger agents sent an unknown feedback message '" + args[0] + "'");
577
+ throw RuntimeException("The Phusion Passenger watchdog sent an unknown feedback message '" + args[0] + "'");
590
578
  }
591
579
  }
592
580
  }
@@ -116,14 +116,14 @@ private:
116
116
  string agentsDir = resourceLocator.getAgentsDir();
117
117
  vector<string> command;
118
118
 
119
- split(options.getStartCommand(resourceLocator), '\1', startCommandArgs);
119
+ split(options.getStartCommand(resourceLocator), '\t', startCommandArgs);
120
120
  if (startCommandArgs.empty()) {
121
121
  throw RuntimeException("No startCommand given");
122
122
  }
123
123
 
124
- if (options.loadShellEnvvars) {
125
- command.push_back("bash");
126
- command.push_back("bash");
124
+ if (shouldLoadShellEnvvars(options, preparation)) {
125
+ command.push_back(preparation.shell);
126
+ command.push_back(preparation.shell);
127
127
  command.push_back("-lc");
128
128
  command.push_back("exec \"$@\"");
129
129
  command.push_back("SpawnPreparerShell");
@@ -61,7 +61,7 @@ public:
61
61
  sockets->add("main", "tcp://127.0.0.1:1234", "session", config->concurrency);
62
62
  syscalls::usleep(config->spawnTime);
63
63
 
64
- lock_guard<boost::mutex> l(lock);
64
+ boost::lock_guard<boost::mutex> l(lock);
65
65
  count++;
66
66
  ProcessPtr process = make_shared<Process>(SafeLibevPtr(),
67
67
  (pid_t) count, "gupid-" + toString(count),
@@ -184,6 +184,7 @@ private:
184
184
  void wakeUpGarbageCollector();
185
185
  bool poolAtFullCapacity() const;
186
186
  bool anotherGroupIsWaitingForCapacity() const;
187
+ const ResourceLocator &getResourceLocator() const;
187
188
 
188
189
  void verifyInvariants() const {
189
190
  // !a || b: logical equivalent of a IMPLIES b.
@@ -514,7 +515,7 @@ private:
514
515
  postLockActions.push_back(boost::bind(interruptAndJoinAllThreads,
515
516
  shared_from_this()));
516
517
  {
517
- lock_guard<boost::mutex> l(lifetimeSyncher);
518
+ boost::lock_guard<boost::mutex> l(lifetimeSyncher);
518
519
  lifeStatus = SHUT_DOWN;
519
520
  }
520
521
  selfPointer.reset();
@@ -668,7 +669,7 @@ public:
668
669
  selfPointer = shared_from_this();
669
670
  assert(disableWaitlist.empty());
670
671
  {
671
- lock_guard<boost::mutex> l(lifetimeSyncher);
672
+ boost::lock_guard<boost::mutex> l(lifetimeSyncher);
672
673
  lifeStatus = SHUTTING_DOWN;
673
674
  }
674
675
  }
@@ -766,13 +767,13 @@ public:
766
767
 
767
768
  // Thread-safe.
768
769
  bool isAlive() const {
769
- lock_guard<boost::mutex> lock(lifetimeSyncher);
770
+ boost::lock_guard<boost::mutex> lock(lifetimeSyncher);
770
771
  return lifeStatus == ALIVE;
771
772
  }
772
773
 
773
774
  // Thread-safe.
774
775
  LifeStatus getLifeStatus() const {
775
- lock_guard<boost::mutex> lock(lifetimeSyncher);
776
+ boost::lock_guard<boost::mutex> lock(lifetimeSyncher);
776
777
  return lifeStatus;
777
778
  }
778
779
 
@@ -1080,6 +1081,10 @@ public:
1080
1081
  P_BUG("Unknown 'lifeStatus' state " << (int) lifeStatus);
1081
1082
  }
1082
1083
 
1084
+ stream << "<options>";
1085
+ options.toXml(stream, getResourceLocator());
1086
+ stream << "</options>";
1087
+
1083
1088
  stream << "<processes>";
1084
1089
 
1085
1090
  for (it = enabledProcesses.begin(); it != enabledProcesses.end(); it++) {
@@ -1042,6 +1042,11 @@ Group::anotherGroupIsWaitingForCapacity() const {
1042
1042
  return false;
1043
1043
  }
1044
1044
 
1045
+ const ResourceLocator &
1046
+ Group::getResourceLocator() const {
1047
+ return getPool()->spawnerFactory->getResourceLocator();
1048
+ }
1049
+
1045
1050
  string
1046
1051
  Group::generateSecret(const SuperGroupPtr &superGroup) {
1047
1052
  return superGroup->getPool()->randomGenerator->generateAsciiString(43);
@@ -1117,7 +1122,7 @@ PipeWatcher::initialize() {
1117
1122
 
1118
1123
  void
1119
1124
  PipeWatcher::start() {
1120
- lock_guard<boost::mutex> lock(startSyncher);
1125
+ boost::lock_guard<boost::mutex> lock(startSyncher);
1121
1126
  started = true;
1122
1127
  startCond.notify_all();
1123
1128
  }
@@ -35,6 +35,7 @@
35
35
  #include <Constants.h>
36
36
  #include <ResourceLocator.h>
37
37
  #include <StaticString.h>
38
+ #include <Utils.h>
38
39
 
39
40
  namespace Passenger {
40
41
  namespace ApplicationPool2 {
@@ -172,7 +173,7 @@ public:
172
173
  StaticString appType;
173
174
 
174
175
  /** The command for spawning the application process. This is a list of
175
- * arguments, separated by '\1', e.g. "ruby\1foo.rb". Only used
176
+ * arguments, separated by '\t', e.g. "ruby\tfoo.rb". Only used
176
177
  * during spawning and only if appType.empty(). */
177
178
  StaticString startCommand;
178
179
 
@@ -479,46 +480,73 @@ public:
479
480
  logger.reset();
480
481
  return *this;
481
482
  }
483
+
484
+ enum FieldSet {
485
+ SPAWN_OPTIONS = 1 << 0,
486
+ PER_GROUP_POOL_OPTIONS = 1 << 1,
487
+ ALL_OPTIONS = ~0
488
+ };
482
489
 
483
490
  /**
484
- * Append any spawning-relevant information in this Options object
485
- * to the given string vector, except for environmentVariables.
491
+ * Append information in this Options object to the given string vector, except
492
+ * for environmentVariables. You can customize what information you want through
493
+ * the `elements` argument.
486
494
  */
487
- void toVector(vector<string> &vec, const ResourceLocator &resourceLocator) const {
488
- if (vec.capacity() < vec.size() + 40) {
489
- vec.reserve(vec.size() + 40);
495
+ void toVector(vector<string> &vec, const ResourceLocator &resourceLocator,
496
+ int fields = ALL_OPTIONS) const
497
+ {
498
+ if (fields & SPAWN_OPTIONS) {
499
+ appendKeyValue (vec, "app_root", appRoot);
500
+ appendKeyValue (vec, "app_group_name", getAppGroupName());
501
+ appendKeyValue (vec, "app_type", appType);
502
+ appendKeyValue (vec, "start_command", getStartCommand(resourceLocator));
503
+ appendKeyValue (vec, "startup_file", getStartupFile());
504
+ appendKeyValue (vec, "process_title", getProcessTitle());
505
+ appendKeyValue2(vec, "log_level", logLevel);
506
+ appendKeyValue3(vec, "start_timeout", startTimeout);
507
+ appendKeyValue (vec, "environment", environment);
508
+ appendKeyValue (vec, "base_uri", baseURI);
509
+ appendKeyValue (vec, "spawn_method", spawnMethod);
510
+ appendKeyValue (vec, "user", user);
511
+ appendKeyValue (vec, "group", group);
512
+ appendKeyValue (vec, "default_user", defaultUser);
513
+ appendKeyValue (vec, "default_group", defaultGroup);
514
+ appendKeyValue (vec, "restart_dir", restartDir);
515
+ appendKeyValue (vec, "preexec_chroot", preexecChroot);
516
+ appendKeyValue (vec, "postexec_chroot", postexecChroot);
517
+ appendKeyValue (vec, "ruby", ruby);
518
+ appendKeyValue (vec, "python", python);
519
+ appendKeyValue (vec, "logging_agent_address", loggingAgentAddress);
520
+ appendKeyValue (vec, "logging_agent_username", loggingAgentUsername);
521
+ appendKeyValue (vec, "logging_agent_password", loggingAgentPassword);
522
+ appendKeyValue4(vec, "debugger", debugger);
523
+ appendKeyValue4(vec, "analytics", analytics);
524
+ appendKeyValue (vec, "union_station_key", unionStationKey);
525
+
526
+ appendKeyValue (vec, "group_secret", groupSecret);
527
+ }
528
+ if (fields & PER_GROUP_POOL_OPTIONS) {
529
+ appendKeyValue3(vec, "min_processes", minProcesses);
530
+ appendKeyValue2(vec, "max_preloader_idle_time", maxPreloaderIdleTime);
490
531
  }
491
-
492
- appendKeyValue (vec, "app_root", appRoot);
493
- appendKeyValue (vec, "app_group_name", getAppGroupName());
494
- appendKeyValue (vec, "app_type", appType);
495
- appendKeyValue (vec, "start_command", getStartCommand(resourceLocator));
496
- appendKeyValue (vec, "process_title", getProcessTitle());
497
- appendKeyValue2(vec, "log_level", logLevel);
498
- appendKeyValue3(vec, "start_timeout", startTimeout);
499
- appendKeyValue (vec, "environment", environment);
500
- appendKeyValue (vec, "base_uri", baseURI);
501
- appendKeyValue (vec, "spawn_method", spawnMethod);
502
- appendKeyValue (vec, "user", user);
503
- appendKeyValue (vec, "group", group);
504
- appendKeyValue (vec, "default_user", defaultUser);
505
- appendKeyValue (vec, "default_group", defaultGroup);
506
- appendKeyValue (vec, "restart_dir", restartDir);
507
- appendKeyValue (vec, "preexec_chroot", preexecChroot);
508
- appendKeyValue (vec, "postexec_chroot", postexecChroot);
509
- appendKeyValue (vec, "ruby", ruby);
510
- appendKeyValue (vec, "python", python);
511
- appendKeyValue (vec, "logging_agent_address", loggingAgentAddress);
512
- appendKeyValue (vec, "logging_agent_username", loggingAgentUsername);
513
- appendKeyValue (vec, "logging_agent_password", loggingAgentPassword);
514
- appendKeyValue4(vec, "debugger", debugger);
515
- appendKeyValue4(vec, "analytics", analytics);
516
- appendKeyValue (vec, "union_station_key", unionStationKey);
517
-
518
- appendKeyValue (vec, "group_secret", groupSecret);
519
532
 
520
533
  /*********************************/
521
534
  }
535
+
536
+ template<typename Stream>
537
+ void toXml(Stream &stream, const ResourceLocator &resourceLocator,
538
+ int fields = ALL_OPTIONS) const
539
+ {
540
+ vector<string> args;
541
+ unsigned int i;
542
+
543
+ toVector(args, resourceLocator, fields);
544
+ for (i = 0; i < args.size(); i += 2) {
545
+ stream << "<" << args[i] << ">";
546
+ stream << escapeForXml(args[i + 1]);
547
+ stream << "</" << args[i] << ">";
548
+ }
549
+ }
522
550
 
523
551
  /**
524
552
  * Returns the app group name. If there is no explicitly set app group name
@@ -534,11 +562,11 @@ public:
534
562
 
535
563
  string getStartCommand(const ResourceLocator &resourceLocator) const {
536
564
  if (appType == "classic-rails") {
537
- return ruby + "\1" + resourceLocator.getHelperScriptsDir() + "/classic-rails-loader.rb";
565
+ return ruby + "\t" + resourceLocator.getHelperScriptsDir() + "/classic-rails-loader.rb";
538
566
  } else if (appType == "rack") {
539
- return ruby + "\1" + resourceLocator.getHelperScriptsDir() + "/rack-loader.rb";
567
+ return ruby + "\t" + resourceLocator.getHelperScriptsDir() + "/rack-loader.rb";
540
568
  } else if (appType == "wsgi") {
541
- return python + "\1" + resourceLocator.getHelperScriptsDir() + "/wsgi-loader.py";
569
+ return python + "\t" + resourceLocator.getHelperScriptsDir() + "/wsgi-loader.py";
542
570
  } else {
543
571
  return startCommand;
544
572
  }
@@ -529,6 +529,12 @@ public:
529
529
  return superGroups.get(options.getAppGroupName()).get();
530
530
  }
531
531
 
532
+ struct GarbageCollectorState {
533
+ unsigned long long now;
534
+ unsigned long long nextGcRunTime;
535
+ vector<Callback> actions;
536
+ };
537
+
532
538
  static void garbageCollect(PoolPtr self) {
533
539
  TRACE_POINT();
534
540
  {
@@ -551,13 +557,53 @@ public:
551
557
  }
552
558
  }
553
559
 
560
+ void maybeUpdateNextGcRuntime(GarbageCollectorState &state, unsigned long candidate) {
561
+ if (state.nextGcRunTime == 0 || candidate < state.nextGcRunTime) {
562
+ state.nextGcRunTime = candidate;
563
+ }
564
+ }
565
+
566
+ void maybeDetachIdleProcess(GarbageCollectorState &state, const GroupPtr &group,
567
+ const ProcessPtr &process, ProcessList::iterator &p_it)
568
+ {
569
+ assert(maxIdleTime > 0);
570
+ unsigned long long processGcTime =
571
+ process->lastUsed + maxIdleTime;
572
+ if (process->sessions == 0
573
+ && state.now >= processGcTime
574
+ && (unsigned long) group->enabledCount > group->options.minProcesses) {
575
+ ProcessList::iterator prev = p_it;
576
+ prev--;
577
+ P_DEBUG("Garbage collect idle process: " << process->inspect() <<
578
+ ", group=" << group->name);
579
+ group->detach(process, state.actions);
580
+ p_it = prev;
581
+ } else {
582
+ maybeUpdateNextGcRuntime(state, processGcTime);
583
+ }
584
+ }
585
+
586
+ void maybeCleanPreloader(GarbageCollectorState &state, const GroupPtr &group) {
587
+ if (group->spawner->cleanable() && group->options.getMaxPreloaderIdleTime() != 0) {
588
+ unsigned long long spawnerGcTime =
589
+ group->spawner->lastUsed() +
590
+ group->options.getMaxPreloaderIdleTime() * 1000000;
591
+ if (state.now >= spawnerGcTime) {
592
+ P_DEBUG("Garbage collect idle spawner: group=" << group->name);
593
+ group->cleanupSpawner(state.actions);
594
+ } else {
595
+ maybeUpdateNextGcRuntime(state, spawnerGcTime);
596
+ }
597
+ }
598
+ }
599
+
554
600
  unsigned long long realGarbageCollect() {
555
601
  TRACE_POINT();
556
602
  ScopedLock lock(syncher);
557
603
  SuperGroupMap::iterator it, end = superGroups.end();
558
- vector<Callback> actions;
559
- unsigned long long now = SystemTime::getUsec();
560
- unsigned long long nextGcRunTime = 0;
604
+ GarbageCollectorState state;
605
+ state.now = SystemTime::getUsec();
606
+ state.nextGcRunTime = 0;
561
607
 
562
608
  P_DEBUG("Garbage collection time...");
563
609
  verifyInvariants();
@@ -572,45 +618,22 @@ public:
572
618
 
573
619
  for (g_it = groups.begin(); g_it != g_end; g_it++) {
574
620
  GroupPtr group = *g_it;
575
- ProcessList &processes = group->enabledProcesses;
576
- ProcessList::iterator p_it, p_end = processes.end();
577
-
578
- for (p_it = processes.begin(); p_it != p_end; p_it++) {
579
- ProcessPtr process = *p_it;
580
-
581
- // ...detach processes that have been idle for more than maxIdleTime.
582
- unsigned long long processGcTime =
583
- process->lastUsed + maxIdleTime;
584
- if (process->sessions == 0
585
- && now >= processGcTime
586
- && (unsigned long) group->enabledCount > group->options.minProcesses) {
587
- ProcessList::iterator prev = p_it;
588
- prev--;
589
- P_DEBUG("Garbage collect idle process: " << process->inspect() <<
590
- ", group=" << group->name);
591
- group->detach(process, actions);
592
- p_it = prev;
593
- } else if (nextGcRunTime == 0
594
- || processGcTime < nextGcRunTime) {
595
- nextGcRunTime = processGcTime;
621
+
622
+ if (maxIdleTime > 0) {
623
+ ProcessList &processes = group->enabledProcesses;
624
+ ProcessList::iterator p_it, p_end = processes.end();
625
+
626
+ for (p_it = processes.begin(); p_it != p_end; p_it++) {
627
+ ProcessPtr process = *p_it;
628
+ // ...detach processes that have been idle for more than maxIdleTime.
629
+ maybeDetachIdleProcess(state, group, process, p_it);
596
630
  }
597
631
  }
598
632
 
599
633
  group->verifyInvariants();
600
-
634
+
601
635
  // ...cleanup the spawner if it's been idle for more than preloaderIdleTime.
602
- if (group->spawner->cleanable()) {
603
- unsigned long long spawnerGcTime =
604
- group->spawner->lastUsed() +
605
- group->options.getMaxPreloaderIdleTime() * 1000000;
606
- if (now >= spawnerGcTime) {
607
- P_DEBUG("Garbage collect idle spawner: group=" << group->name);
608
- group->cleanupSpawner(actions);
609
- } else if (nextGcRunTime == 0
610
- || spawnerGcTime < nextGcRunTime) {
611
- nextGcRunTime = spawnerGcTime;
612
- }
613
- }
636
+ maybeCleanPreloader(state, group);
614
637
  }
615
638
 
616
639
  superGroup->verifyInvariants();
@@ -621,22 +644,22 @@ public:
621
644
 
622
645
  // Schedule next garbage collection run.
623
646
  unsigned long long sleepTime;
624
- if (nextGcRunTime == 0 || nextGcRunTime <= now) {
647
+ if (state.nextGcRunTime == 0 || state.nextGcRunTime <= state.now) {
625
648
  if (maxIdleTime == 0) {
626
649
  sleepTime = 10 * 60 * 1000000;
627
650
  } else {
628
651
  sleepTime = maxIdleTime;
629
652
  }
630
653
  } else {
631
- sleepTime = nextGcRunTime - now;
654
+ sleepTime = state.nextGcRunTime - state.now;
632
655
  }
633
656
  P_DEBUG("Garbage collection done; next garbage collect in " <<
634
657
  std::fixed << std::setprecision(3) << (sleepTime / 1000000.0) << " sec");
635
658
 
636
659
  UPDATE_TRACE_POINT();
637
- runAllActions(actions);
660
+ runAllActions(state.actions);
638
661
  UPDATE_TRACE_POINT();
639
- actions.clear();
662
+ state.actions.clear();
640
663
  return sleepTime;
641
664
  }
642
665
 
@@ -1182,6 +1205,18 @@ public:
1182
1205
  return ProcessPtr();
1183
1206
  }
1184
1207
 
1208
+ ProcessPtr findProcessByPid(pid_t pid, bool lock = true) const {
1209
+ vector<ProcessPtr> processes = getProcesses(lock);
1210
+ vector<ProcessPtr>::const_iterator it, end = processes.end();
1211
+ for (it = processes.begin(); it != end; it++) {
1212
+ const ProcessPtr &process = *it;
1213
+ if (process->pid == pid) {
1214
+ return process;
1215
+ }
1216
+ }
1217
+ return ProcessPtr();
1218
+ }
1219
+
1185
1220
  bool detachSuperGroupByName(const string &name) {
1186
1221
  TRACE_POINT();
1187
1222
  ScopedLock l(syncher);
@@ -1261,6 +1296,21 @@ public:
1261
1296
  return result;
1262
1297
  }
1263
1298
 
1299
+ bool detachProcess(pid_t pid) {
1300
+ ScopedLock l(syncher);
1301
+ ProcessPtr process = findProcessByPid(pid, false);
1302
+ if (process != NULL) {
1303
+ vector<Callback> actions;
1304
+ bool result = detachProcessUnlocked(process, actions);
1305
+ fullVerifyInvariants();
1306
+ l.unlock();
1307
+ runAllActions(actions);
1308
+ return result;
1309
+ } else {
1310
+ return false;
1311
+ }
1312
+ }
1313
+
1264
1314
  bool detachProcess(const string &gupid) {
1265
1315
  ScopedLock l(syncher);
1266
1316
  ProcessPtr process = findProcessByGupid(gupid, false);