passenger 3.9.2.beta → 4.0.0.rc4

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 (159) hide show
  1. data/.travis.yml +3 -0
  2. data/NEWS +77 -7
  3. data/README.md +3 -11
  4. data/bin/passenger-install-apache2-module +24 -20
  5. data/bin/passenger-install-nginx-module +25 -23
  6. data/build/agents.rb +11 -0
  7. data/build/apache2.rb +9 -5
  8. data/build/basics.rb +37 -30
  9. data/build/common_library.rb +4 -1
  10. data/build/cplusplus_support.rb +5 -5
  11. data/build/cxx_tests.rb +28 -8
  12. data/build/integration_tests.rb +6 -3
  13. data/build/nginx.rb +3 -3
  14. data/build/packaging.rb +95 -57
  15. data/build/ruby_extension.rb +34 -21
  16. data/build/ruby_tests.rb +4 -2
  17. data/build/test_basics.rb +1 -1
  18. data/dev/run_travis.sh +36 -1
  19. data/doc/Users guide Apache.html +425 -308
  20. data/doc/Users guide Apache.idmap.txt +78 -70
  21. data/doc/Users guide Apache.index.sqlite3 +0 -0
  22. data/doc/Users guide Apache.txt +33 -92
  23. data/doc/Users guide Nginx.html +519 -220
  24. data/doc/Users guide Nginx.idmap.txt +78 -60
  25. data/doc/Users guide Nginx.txt +115 -26
  26. data/doc/Users guide Standalone.html +8 -2
  27. data/doc/users_guide_snippets/analysis_and_system_maintenance.txt +1 -7
  28. data/doc/users_guide_snippets/installation.txt +167 -22
  29. data/doc/users_guide_snippets/rackup_specifications.txt +4 -0
  30. data/doc/users_guide_snippets/since_version.txt +1 -0
  31. data/doc/users_guide_snippets/support_information.txt +3 -7
  32. data/doc/users_guide_snippets/tips.txt +0 -24
  33. data/ext/apache2/Configuration.cpp +11 -33
  34. data/ext/apache2/Configuration.hpp +3 -18
  35. data/ext/apache2/DirectoryMapper.h +20 -70
  36. data/ext/apache2/Hooks.cpp +2 -2
  37. data/ext/common/AgentsStarter.cpp +0 -2
  38. data/ext/common/AgentsStarter.h +0 -1
  39. data/ext/common/AgentsStarter.hpp +1 -3
  40. data/ext/common/ApplicationPool2/AppTypes.cpp +74 -0
  41. data/ext/common/ApplicationPool2/AppTypes.h +202 -0
  42. data/ext/common/ApplicationPool2/Common.h +12 -10
  43. data/ext/common/ApplicationPool2/DirectSpawner.h +256 -0
  44. data/ext/common/ApplicationPool2/DummySpawner.h +90 -0
  45. data/ext/common/ApplicationPool2/Group.h +311 -94
  46. data/ext/common/ApplicationPool2/Implementation.cpp +405 -145
  47. data/ext/common/ApplicationPool2/Options.h +24 -26
  48. data/ext/common/ApplicationPool2/PipeWatcher.h +20 -13
  49. data/ext/common/ApplicationPool2/Pool.h +326 -183
  50. data/ext/common/ApplicationPool2/Process.h +205 -55
  51. data/ext/common/ApplicationPool2/README.md +1 -1
  52. data/ext/common/ApplicationPool2/Session.h +21 -10
  53. data/ext/common/ApplicationPool2/SmartSpawner.h +801 -0
  54. data/ext/common/ApplicationPool2/Spawner.h +141 -1149
  55. data/ext/common/ApplicationPool2/SpawnerFactory.h +132 -0
  56. data/ext/common/ApplicationPool2/SuperGroup.h +146 -223
  57. data/ext/common/Constants.h +4 -2
  58. data/ext/common/Exceptions.h +23 -1
  59. data/ext/common/Logging.cpp +17 -6
  60. data/ext/common/Logging.h +37 -7
  61. data/ext/common/ResourceLocator.h +1 -1
  62. data/ext/common/Utils.cpp +49 -1
  63. data/ext/common/Utils.h +13 -4
  64. data/ext/common/{AnsiColorConstants.h → Utils/AnsiColorConstants.h} +0 -0
  65. data/ext/common/{BCrypt.cpp → Utils/BCrypt.cpp} +0 -0
  66. data/ext/common/{BCrypt.h → Utils/BCrypt.h} +0 -0
  67. data/ext/common/{Blowfish.c → Utils/Blowfish.c} +0 -0
  68. data/ext/common/{Blowfish.h → Utils/Blowfish.h} +0 -0
  69. data/ext/common/Utils/CachedFileStat.hpp +27 -25
  70. data/ext/common/Utils/Curl.h +184 -0
  71. data/ext/common/{HttpConstants.h → Utils/HttpConstants.h} +3 -0
  72. data/ext/common/Utils/IOUtils.cpp +6 -2
  73. data/ext/common/{IniFile.h → Utils/IniFile.h} +0 -0
  74. data/ext/common/Utils/LargeFiles.cpp +30 -0
  75. data/ext/common/Utils/LargeFiles.h +40 -0
  76. data/ext/common/Utils/StrIntUtils.cpp +72 -8
  77. data/ext/common/Utils/StrIntUtils.h +24 -2
  78. data/ext/common/Utils/StringMap.h +12 -2
  79. data/ext/common/Utils/VariantMap.h +51 -2
  80. data/ext/common/Utils/jsoncpp.cpp +1 -1
  81. data/ext/common/agents/Base.cpp +147 -11
  82. data/ext/common/agents/HelperAgent/AgentOptions.h +14 -6
  83. data/ext/common/agents/HelperAgent/Main.cpp +79 -19
  84. data/ext/common/agents/HelperAgent/RequestHandler.h +36 -16
  85. data/ext/common/agents/LoggingAgent/LoggingServer.h +3 -5
  86. data/ext/common/agents/LoggingAgent/Main.cpp +2 -4
  87. data/ext/common/agents/LoggingAgent/RemoteSender.h +18 -24
  88. data/ext/common/agents/SpawnPreparer.cpp +7 -0
  89. data/ext/common/agents/Watchdog/Main.cpp +96 -38
  90. data/ext/nginx/Configuration.c +26 -22
  91. data/ext/nginx/Configuration.h +4 -2
  92. data/ext/nginx/ContentHandler.c +23 -52
  93. data/ext/nginx/ContentHandler.h +5 -11
  94. data/ext/nginx/config +10 -3
  95. data/ext/nginx/ngx_http_passenger_module.c +21 -6
  96. data/ext/nginx/ngx_http_passenger_module.h +4 -1
  97. data/ext/oxt/dynamic_thread_group.hpp +9 -1
  98. data/ext/oxt/system_calls.cpp +2 -2
  99. data/ext/ruby/extconf.rb +2 -1
  100. data/helper-scripts/backtrace-sanitizer.rb +2 -0
  101. data/helper-scripts/wsgi-loader.py +54 -21
  102. data/lib/phusion_passenger.rb +5 -3
  103. data/lib/phusion_passenger/abstract_installer.rb +18 -41
  104. data/lib/phusion_passenger/admin_tools/memory_stats.rb +2 -2
  105. data/lib/phusion_passenger/admin_tools/server_instance.rb +2 -2
  106. data/lib/phusion_passenger/common_library.rb +23 -3
  107. data/lib/phusion_passenger/debug_logging.rb +10 -3
  108. data/lib/phusion_passenger/packaging.rb +1 -0
  109. data/lib/phusion_passenger/platform_info.rb +113 -115
  110. data/lib/phusion_passenger/platform_info/compiler.rb +224 -134
  111. data/lib/phusion_passenger/platform_info/cxx_portability.rb +143 -0
  112. data/lib/phusion_passenger/platform_info/depcheck.rb +371 -0
  113. data/lib/phusion_passenger/platform_info/depcheck_specs/apache2.rb +124 -0
  114. data/lib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb +97 -0
  115. data/lib/phusion_passenger/platform_info/depcheck_specs/gems.rb +39 -0
  116. data/lib/phusion_passenger/platform_info/depcheck_specs/libs.rb +118 -0
  117. data/lib/phusion_passenger/platform_info/depcheck_specs/ruby.rb +137 -0
  118. data/lib/phusion_passenger/platform_info/depcheck_specs/utilities.rb +15 -0
  119. data/lib/phusion_passenger/platform_info/operating_system.rb +6 -5
  120. data/lib/phusion_passenger/platform_info/ruby.rb +45 -34
  121. data/lib/phusion_passenger/request_handler.rb +35 -22
  122. data/lib/phusion_passenger/request_handler/thread_handler.rb +5 -6
  123. data/lib/phusion_passenger/ruby_core_enhancements.rb +7 -1
  124. data/lib/phusion_passenger/standalone/runtime_installer.rb +43 -34
  125. data/lib/phusion_passenger/utils/robust_interruption.rb +34 -18
  126. data/passenger.gemspec +25 -0
  127. data/resources/templates/standalone/config.erb +3 -1
  128. data/test/config.json.travis +2 -2
  129. data/test/cxx/ApplicationPool2/DirectSpawnerTest.cpp +37 -5
  130. data/test/cxx/ApplicationPool2/PoolTest.cpp +143 -50
  131. data/test/cxx/ApplicationPool2/ProcessTest.cpp +8 -0
  132. data/test/cxx/ApplicationPool2/SmartSpawnerTest.cpp +28 -17
  133. data/test/cxx/ApplicationPool2/SpawnerTestCases.cpp +31 -26
  134. data/test/cxx/RequestHandlerTest.cpp +17 -1
  135. data/test/cxx/UtilsTest.cpp +84 -10
  136. data/test/integration_tests/apache2_tests.rb +49 -163
  137. data/test/integration_tests/hello_world_wsgi_spec.rb +2 -2
  138. data/test/integration_tests/mycook_spec.rb +1 -1
  139. data/test/integration_tests/nginx_tests.rb +37 -19
  140. data/test/ruby/request_handler_spec.rb +1 -0
  141. data/test/ruby/spec_helper.rb +52 -1
  142. data/test/stub/nginx/nginx.conf.erb +2 -0
  143. data/test/stub/rack/start.rb +5 -0
  144. data/test/stub/rails3.0/Gemfile.lock +30 -30
  145. data/test/stub/rails3.1/Gemfile +1 -1
  146. data/test/stub/rails3.1/Gemfile.lock +3 -3
  147. data/test/stub/rails3.2/Gemfile +1 -1
  148. data/test/stub/rails3.2/Gemfile.lock +4 -4
  149. data/test/stub/rails_apps/2.3/mycook/app/controllers/welcome_controller.rb +1 -1
  150. data/test/stub/rails_apps/2.3/mycook/app/helpers/recipes_helper.rb +2 -0
  151. data/test/stub/rails_apps/2.3/mycook/app/helpers/test_helper.rb +2 -0
  152. data/test/stub/rails_apps/2.3/mycook/app/helpers/uploads_helper.rb +2 -0
  153. data/test/stub/rails_apps/2.3/mycook/app/helpers/welcome_helper.rb +2 -0
  154. data/test/support/nginx_controller.rb +2 -1
  155. metadata +160 -156
  156. data/build/gempackagetask.rb +0 -99
  157. data/build/packagetask.rb +0 -186
  158. data/ext/common/StringListCreator.h +0 -83
  159. data/lib/phusion_passenger/dependencies.rb +0 -657
@@ -60,6 +60,7 @@
60
60
  #include <boost/make_shared.hpp>
61
61
  #include <boost/shared_array.hpp>
62
62
  #include <boost/bind.hpp>
63
+ #include <boost/foreach.hpp>
63
64
  #include <oxt/system_calls.hpp>
64
65
  #include <oxt/backtrace.hpp>
65
66
  #include <sys/types.h>
@@ -69,8 +70,6 @@
69
70
  #include <cerrno>
70
71
  #include <cassert>
71
72
  #include <unistd.h>
72
- #include <pthread.h>
73
- #include <limits.h> // for PTHREAD_STACK_MIN
74
73
  #include <pwd.h>
75
74
  #include <grp.h>
76
75
  #include <dirent.h>
@@ -83,6 +82,7 @@
83
82
  #include <ResourceLocator.h>
84
83
  #include <StaticString.h>
85
84
  #include <ServerInstanceDir.h>
85
+ #include <Utils.h>
86
86
  #include <Utils/BufferedIO.h>
87
87
  #include <Utils/ScopeGuard.h>
88
88
  #include <Utils/Timer.h>
@@ -117,7 +117,8 @@ protected:
117
117
  class BackgroundIOCapturer {
118
118
  private:
119
119
  FileDescriptor fd;
120
- int target;
120
+ string prefix;
121
+ bool print;
121
122
  boost::mutex dataSyncher;
122
123
  string data;
123
124
  oxt::thread *thr;
@@ -145,18 +146,28 @@ protected:
145
146
  lock_guard<boost::mutex> l(dataSyncher);
146
147
  data.append(buf, ret);
147
148
  }
148
- if (target != -1) {
149
- UPDATE_TRACE_POINT();
150
- writeExact(target, buf, ret);
149
+ UPDATE_TRACE_POINT();
150
+ if (print && ret == 1 && buf[0] == '\n') {
151
+ P_INFO(prefix);
152
+ } else if (print) {
153
+ vector<StaticString> lines;
154
+ if (ret > 0 && buf[ret - 1] == '\n') {
155
+ ret--;
156
+ }
157
+ split(StaticString(buf, ret), '\n', lines);
158
+ foreach (const StaticString line, lines) {
159
+ P_INFO(prefix << line);
160
+ }
151
161
  }
152
162
  }
153
163
  }
154
164
  }
155
165
 
156
166
  public:
157
- BackgroundIOCapturer(const FileDescriptor &_fd, int _target)
167
+ BackgroundIOCapturer(const FileDescriptor &_fd, const string &_prefix, bool _print)
158
168
  : fd(_fd),
159
- target(_target),
169
+ prefix(_prefix),
170
+ print(_print),
160
171
  thr(NULL)
161
172
  { }
162
173
 
@@ -257,6 +268,12 @@ protected:
257
268
 
258
269
  typedef shared_ptr<DebugDir> DebugDirPtr;
259
270
 
271
+ /**
272
+ * Contains information that will be used after fork()ing but before exec()ing,
273
+ * such as the intended app root, the UID it should switch to, the
274
+ * groups it should assume, etc. This structure is allocated before forking
275
+ * because after forking and before exec() it may not be safe to allocate memory.
276
+ */
260
277
  struct SpawnPreparationInfo {
261
278
  // General
262
279
 
@@ -302,9 +319,21 @@ protected:
302
319
  * the spawning protocol.
303
320
  */
304
321
  struct NegotiationDetails {
305
- // Arguments.
322
+ /****** Arguments ******/
323
+
324
+ /** The preparation info of the process we're negotiating with. It's used
325
+ * by security validators to check whether the information sent back by the
326
+ * process make any sense. */
327
+ SpawnPreparationInfo *preparation;
328
+ /** The SafeLibev that the returned Process should be initialized with. */
306
329
  SafeLibevPtr libev;
330
+ /** This object captures the process's stderr while negotiation is in progress.
331
+ * (Recall that negotiation is performed over the process's stdout while stderr
332
+ * is used purely for outputting messages.)
333
+ * If the negotiation protocol fails, then any output captured by this object
334
+ * will be stored into the resulting SpawnException's error page. */
307
335
  BackgroundIOCapturerPtr stderrCapturer;
336
+ /** The PID of the process we're negotiating with. */
308
337
  pid_t pid;
309
338
  FileDescriptor adminSocket;
310
339
  FileDescriptor errorPipe;
@@ -313,7 +342,7 @@ protected:
313
342
  int forwardStderrTo;
314
343
  DebugDirPtr debugDir;
315
344
 
316
- // Working state.
345
+ /****** Working state ******/
317
346
  BufferedIO io;
318
347
  string gupid;
319
348
  string connectPassword;
@@ -321,6 +350,7 @@ protected:
321
350
  unsigned long long timeout;
322
351
 
323
352
  NegotiationDetails() {
353
+ preparation = NULL;
324
354
  pid = 0;
325
355
  options = NULL;
326
356
  forwardStderr = false;
@@ -329,31 +359,6 @@ protected:
329
359
  timeout = 0;
330
360
  }
331
361
  };
332
-
333
- /**
334
- * Structure containing arguments and working state for negotiating
335
- * the preloader startup protocol.
336
- */
337
- struct StartupDetails {
338
- // Arguments.
339
- FileDescriptor adminSocket;
340
- BufferedIO io;
341
- BackgroundIOCapturerPtr stderrCapturer;
342
- DebugDirPtr debugDir;
343
- const Options *options;
344
- bool forwardStderr;
345
- int forwardStderrTo;
346
-
347
- // Working state.
348
- unsigned long long timeout;
349
-
350
- StartupDetails() {
351
- options = NULL;
352
- forwardStderr = false;
353
- forwardStderrTo = STDERR_FILENO;
354
- timeout = 0;
355
- }
356
- };
357
362
 
358
363
 
359
364
  private:
@@ -394,8 +399,12 @@ private:
394
399
  data.append(key + ": " + value + "\n");
395
400
  }
396
401
 
402
+ vector<StaticString> lines;
403
+ split(data, '\n', lines);
404
+ foreach (const StaticString line, lines) {
405
+ P_DEBUG("[App " << details.pid << " stdin >>] " << line);
406
+ }
397
407
  writeExact(details.adminSocket, data, &details.timeout);
398
- P_TRACE(2, "Spawn request for " << details.options->appRoot << ":\n" << data);
399
408
  writeExact(details.adminSocket, "\n", &details.timeout);
400
409
  } catch (const SystemException &e) {
401
410
  if (e.code() == EPIPE) {
@@ -465,6 +474,13 @@ private:
465
474
  vector<string> args;
466
475
  split(value, ';', args);
467
476
  if (args.size() == 4) {
477
+ string error = validateSocketAddress(details, args[1]);
478
+ if (!error.empty()) {
479
+ throwAppSpawnException(
480
+ "An error occurred while starting the web application. " + error,
481
+ SpawnException::APP_STARTUP_PROTOCOL_ERROR,
482
+ details);
483
+ }
468
484
  sockets->add(args[0],
469
485
  fixupSocketAddress(*details.options, args[1]),
470
486
  args[2],
@@ -501,7 +517,6 @@ private:
501
517
 
502
518
  protected:
503
519
  ResourceLocator resourceLocator;
504
- RandomGeneratorPtr randomGenerator;
505
520
  ServerInstanceDir::GenerationPtr generation;
506
521
  SpawnerConfigPtr config;
507
522
 
@@ -555,6 +570,73 @@ protected:
555
570
  }
556
571
  }
557
572
 
573
+ bool isAbsolutePath(const StaticString &path) const {
574
+ if (path.empty() || path[0] != '/') {
575
+ return false;
576
+ } else {
577
+ vector<string> components;
578
+ string component;
579
+
580
+ split(path, '/', components);
581
+ components.erase(components.begin());
582
+ foreach (component, components) {
583
+ if (component.empty() || component == "." || component == "..") {
584
+ return false;
585
+ }
586
+ }
587
+ return true;
588
+ }
589
+ }
590
+
591
+ /**
592
+ * Given a 'socket:' information string obtained from the spawned process,
593
+ * validates whether it is correct.
594
+ */
595
+ string validateSocketAddress(NegotiationDetails &details, const string &_address) const {
596
+ string address = _address;
597
+ stringstream error;
598
+
599
+ switch (getSocketAddressType(address)) {
600
+ case SAT_UNIX: {
601
+ address = fixupSocketAddress(*details.options, address);
602
+ string filename = parseUnixSocketAddress(address);
603
+
604
+ // Verify that the socket filename is absolute.
605
+ if (!isAbsolutePath(filename)) {
606
+ error << "It reported a non-absolute socket filename: \"" <<
607
+ cEscapeString(filename) << "\"";
608
+ break;
609
+ }
610
+
611
+ // Verify that the process owns the socket.
612
+ struct stat buf;
613
+ if (lstat(filename.c_str(), &buf) == -1) {
614
+ int e = errno;
615
+ error << "It reported an inaccessible socket filename: \"" <<
616
+ cEscapeString(filename) << "\" (lstat() failed with errno " <<
617
+ e << ": " << strerror(e) << ")";
618
+ break;
619
+ }
620
+ if (buf.st_uid != details.preparation->uid) {
621
+ error << "It advertised a Unix domain socket that has a different " <<
622
+ "owner than expected (should be UID " << details.preparation->uid <<
623
+ ", but actual UID was " << buf.st_uid << ")";
624
+ break;
625
+ }
626
+ break;
627
+ }
628
+ case SAT_TCP:
629
+ // TODO: validate that the socket is localhost.
630
+ break;
631
+ default:
632
+ error << "It reported an unsupported socket address type: \"" <<
633
+ cEscapeString(address) << "\"";
634
+ break;
635
+ }
636
+
637
+ return error.str();
638
+ }
639
+
558
640
  static void checkChrootDirectories(const Options &options) {
559
641
  if (!options.preexecChroot.empty()) {
560
642
  // TODO: check whether appRoot is a child directory of preexecChroot
@@ -633,22 +715,28 @@ protected:
633
715
  }
634
716
 
635
717
  template<typename Details>
636
- static string readMessageLine(Details &details) {
718
+ string readMessageLine(Details &details) {
637
719
  TRACE_POINT();
638
720
  while (true) {
639
721
  string result = details.io.readLine(1024 * 4, &details.timeout);
722
+ string line = result;
723
+ if (!line.empty() && line[line.size() - 1] == '\n') {
724
+ line.erase(line.size() - 1, 1);
725
+ }
726
+
640
727
  if (result.empty()) {
728
+ // EOF
641
729
  return result;
642
730
  } else if (startsWith(result, "!> ")) {
731
+ P_DEBUG("[App " << details.pid << " stdout] " << line);
643
732
  result.erase(0, sizeof("!> ") - 1);
644
733
  return result;
645
734
  } else {
646
735
  if (details.stderrCapturer != NULL) {
647
736
  details.stderrCapturer->appendToBuffer(result);
648
737
  }
649
- if (details.forwardStderr) {
650
- write(details.forwardStderrTo, result.data(), result.size());
651
- }
738
+ P_LOG(config->forwardStdout ? LVL_INFO : LVL_DEBUG,
739
+ "[App " << details.pid << " stdout] " << line);
652
740
  }
653
741
  }
654
742
  }
@@ -783,7 +871,6 @@ protected:
783
871
  gid_t groups[1024];
784
872
  info.ngroups = sizeof(groups) / sizeof(gid_t);
785
873
  #endif
786
- int ret;
787
874
  info.switchUser = true;
788
875
  info.username = userInfo->pw_name;
789
876
  info.groupname = groupInfo->gr_name;
@@ -795,7 +882,7 @@ protected:
795
882
  #define HAVE_GETGROUPLIST
796
883
  #endif
797
884
  #ifdef HAVE_GETGROUPLIST
798
- ret = getgrouplist(userInfo->pw_name, groupInfo->gr_gid,
885
+ int ret = getgrouplist(userInfo->pw_name, groupInfo->gr_gid,
799
886
  groups, &info.ngroups);
800
887
  if (ret == -1) {
801
888
  int e = errno;
@@ -847,13 +934,13 @@ protected:
847
934
  if (!options.baseURI.empty() && options.baseURI != "/") {
848
935
  appendNullTerminatedKeyValue(result,
849
936
  "RAILS_RELATIVE_URL_ROOT",
850
- options.environment);
937
+ options.baseURI);
851
938
  appendNullTerminatedKeyValue(result,
852
939
  "RACK_BASE_URI",
853
- options.environment);
940
+ options.baseURI);
854
941
  appendNullTerminatedKeyValue(result,
855
942
  "PASSENGER_BASE_URI",
856
- options.environment);
943
+ options.baseURI);
857
944
  }
858
945
 
859
946
  it = options.environmentVariables.begin();
@@ -1001,12 +1088,15 @@ protected:
1001
1088
  }
1002
1089
  }
1003
1090
 
1091
+ /**
1092
+ * Execute the process spawning negotiation protocol.
1093
+ */
1004
1094
  ProcessPtr negotiateSpawn(NegotiationDetails &details) {
1005
1095
  TRACE_POINT();
1006
1096
  details.spawnStartTime = SystemTime::getUsec();
1007
1097
  details.gupid = integerToHex(SystemTime::get() / 60) + "-" +
1008
- randomGenerator->generateAsciiString(11);
1009
- details.connectPassword = randomGenerator->generateAsciiString(43);
1098
+ config->randomGenerator->generateAsciiString(11);
1099
+ details.connectPassword = config->randomGenerator->generateAsciiString(43);
1010
1100
  details.timeout = details.options->startTimeout * 1000;
1011
1101
 
1012
1102
  string result;
@@ -1025,6 +1115,7 @@ protected:
1025
1115
  details);
1026
1116
  }
1027
1117
 
1118
+ protocol_begin:
1028
1119
  if (result == "I have control 1.0\n") {
1029
1120
  UPDATE_TRACE_POINT();
1030
1121
  sendSpawnRequest(details);
@@ -1046,6 +1137,8 @@ protected:
1046
1137
  return handleSpawnResponse(details);
1047
1138
  } else if (result == "Error\n") {
1048
1139
  handleSpawnErrorResponse(details);
1140
+ } else if (result == "I have control 1.0\n") {
1141
+ goto protocol_begin;
1049
1142
  } else {
1050
1143
  handleInvalidSpawnResponseType(result, details);
1051
1144
  }
@@ -1161,1107 +1254,6 @@ public:
1161
1254
  typedef shared_ptr<Spawner> SpawnerPtr;
1162
1255
 
1163
1256
 
1164
- class SmartSpawner: public Spawner, public enable_shared_from_this<SmartSpawner> {
1165
- private:
1166
- struct SpawnResult {
1167
- pid_t pid;
1168
- FileDescriptor adminSocket;
1169
- BufferedIO io;
1170
- };
1171
-
1172
- /** The event loop that created Process objects should use, and that I/O forwarding
1173
- * functions should use. For example data on the error pipe is forwarded using this event loop.
1174
- */
1175
- SafeLibevPtr libev;
1176
- const vector<string> preloaderCommand;
1177
- map<string, string> preloaderAnnotations;
1178
- Options options;
1179
- ev::io preloaderOutputWatcher;
1180
- shared_ptr<PipeWatcher> preloaderErrorWatcher;
1181
-
1182
- // Protects m_lastUsed and pid.
1183
- mutable boost::mutex simpleFieldSyncher;
1184
- // Protects everything else.
1185
- mutable boost::mutex syncher;
1186
-
1187
- pid_t pid;
1188
- FileDescriptor adminSocket;
1189
- string socketAddress;
1190
- unsigned long long m_lastUsed;
1191
-
1192
- void onPreloaderOutputReadable(ev::io &io, int revents) {
1193
- char buf[1024 * 8];
1194
- ssize_t ret;
1195
-
1196
- ret = syscalls::read(adminSocket, buf, sizeof(buf));
1197
- if (ret <= 0) {
1198
- preloaderOutputWatcher.stop();
1199
- } else if (config->forwardStdout) {
1200
- write(config->forwardStdoutTo, buf, ret);
1201
- }
1202
- }
1203
-
1204
- string getPreloaderCommandString() const {
1205
- string result;
1206
- unsigned int i;
1207
-
1208
- for (i = 0; i < preloaderCommand.size(); i++) {
1209
- if (i != 0) {
1210
- result.append(1, '\0');
1211
- }
1212
- result.append(preloaderCommand[i]);
1213
- }
1214
- return result;
1215
- }
1216
-
1217
- vector<string> createRealPreloaderCommand(const Options &options,
1218
- shared_array<const char *> &args)
1219
- {
1220
- string agentsDir = resourceLocator.getAgentsDir();
1221
- vector<string> command;
1222
-
1223
- if (options.loadShellEnvvars) {
1224
- command.push_back("bash");
1225
- command.push_back("bash");
1226
- command.push_back("-lc");
1227
- command.push_back("exec \"$@\"");
1228
- command.push_back("SpawnPreparerShell");
1229
- } else {
1230
- command.push_back(agentsDir + "/SpawnPreparer");
1231
- }
1232
- command.push_back(agentsDir + "/SpawnPreparer");
1233
- command.push_back(serializeEnvvarsFromPoolOptions(options));
1234
- command.push_back(preloaderCommand[0]);
1235
- command.push_back("Passenger AppPreloader: " + options.appRoot);
1236
- for (unsigned int i = 1; i < preloaderCommand.size(); i++) {
1237
- command.push_back(preloaderCommand[i]);
1238
- }
1239
-
1240
- createCommandArgs(command, args);
1241
- return command;
1242
- }
1243
-
1244
- void throwPreloaderSpawnException(const string &msg,
1245
- SpawnException::ErrorKind errorKind,
1246
- StartupDetails &details)
1247
- {
1248
- throwPreloaderSpawnException(msg, errorKind, details.stderrCapturer,
1249
- details.debugDir);
1250
- }
1251
-
1252
- void throwPreloaderSpawnException(const string &msg,
1253
- SpawnException::ErrorKind errorKind,
1254
- BackgroundIOCapturerPtr &stderrCapturer,
1255
- const DebugDirPtr &debugDir)
1256
- {
1257
- TRACE_POINT();
1258
- // Stop the stderr capturing thread and get the captured stderr
1259
- // output so far.
1260
- string stderrOutput;
1261
- if (stderrCapturer != NULL) {
1262
- stderrOutput = stderrCapturer->stop();
1263
- }
1264
-
1265
- // If the exception wasn't due to a timeout, try to capture the
1266
- // remaining stderr output for at most 2 seconds.
1267
- if (errorKind != SpawnException::PRELOADER_STARTUP_TIMEOUT
1268
- && errorKind != SpawnException::APP_STARTUP_TIMEOUT
1269
- && stderrCapturer != NULL) {
1270
- bool done = false;
1271
- unsigned long long timeout = 2000;
1272
- while (!done) {
1273
- char buf[1024 * 32];
1274
- unsigned int ret;
1275
-
1276
- try {
1277
- ret = readExact(stderrCapturer->getFd(), buf,
1278
- sizeof(buf), &timeout);
1279
- if (ret == 0) {
1280
- done = true;
1281
- } else {
1282
- stderrOutput.append(buf, ret);
1283
- }
1284
- } catch (const SystemException &e) {
1285
- P_WARN("Stderr I/O capture error: " << e.what());
1286
- done = true;
1287
- } catch (const TimeoutException &) {
1288
- done = true;
1289
- }
1290
- }
1291
- }
1292
- stderrCapturer.reset();
1293
-
1294
- // Now throw SpawnException with the captured stderr output
1295
- // as error response.
1296
- SpawnException e(msg, stderrOutput, false, errorKind);
1297
- e.setPreloaderCommand(getPreloaderCommandString());
1298
- annotatePreloaderException(e, debugDir);
1299
- throw e;
1300
- }
1301
-
1302
- void annotatePreloaderException(SpawnException &e, const DebugDirPtr &debugDir) {
1303
- if (debugDir != NULL) {
1304
- e.addAnnotations(debugDir->readAll());
1305
- }
1306
- }
1307
-
1308
- bool preloaderStarted() const {
1309
- return pid != -1;
1310
- }
1311
-
1312
- void startPreloader() {
1313
- TRACE_POINT();
1314
- this_thread::disable_interruption di;
1315
- this_thread::disable_syscall_interruption dsi;
1316
- assert(!preloaderStarted());
1317
- checkChrootDirectories(options);
1318
-
1319
- shared_array<const char *> args;
1320
- vector<string> command = createRealPreloaderCommand(options, args);
1321
- SpawnPreparationInfo preparation = prepareSpawn(options);
1322
- SocketPair adminSocket = createUnixSocketPair();
1323
- Pipe errorPipe = createPipe();
1324
- DebugDirPtr debugDir = make_shared<DebugDir>(preparation.uid, preparation.gid);
1325
- pid_t pid;
1326
-
1327
- pid = syscalls::fork();
1328
- if (pid == 0) {
1329
- setenv("PASSENGER_DEBUG_DIR", debugDir->getPath().c_str(), 1);
1330
- purgeStdio(stdout);
1331
- purgeStdio(stderr);
1332
- resetSignalHandlersAndMask();
1333
- disableMallocDebugging();
1334
- int adminSocketCopy = dup2(adminSocket.first, 3);
1335
- int errorPipeCopy = dup2(errorPipe.second, 4);
1336
- dup2(adminSocketCopy, 0);
1337
- dup2(adminSocketCopy, 1);
1338
- dup2(errorPipeCopy, 2);
1339
- closeAllFileDescriptors(2);
1340
- setChroot(preparation);
1341
- switchUser(preparation);
1342
- setWorkingDirectory(preparation);
1343
- execvp(command[0].c_str(), (char * const *) args.get());
1344
-
1345
- int e = errno;
1346
- printf("!> Error\n");
1347
- printf("!> \n");
1348
- printf("Cannot execute \"%s\": %s (errno=%d)\n", command[0].c_str(),
1349
- strerror(e), e);
1350
- fprintf(stderr, "Cannot execute \"%s\": %s (errno=%d)\n",
1351
- command[0].c_str(), strerror(e), e);
1352
- fflush(stdout);
1353
- fflush(stderr);
1354
- _exit(1);
1355
-
1356
- } else if (pid == -1) {
1357
- int e = errno;
1358
- throw SystemException("Cannot fork a new process", e);
1359
-
1360
- } else {
1361
- ScopeGuard guard(boost::bind(nonInterruptableKillAndWaitpid, pid));
1362
- adminSocket.first.close();
1363
- errorPipe.second.close();
1364
-
1365
- StartupDetails details;
1366
- details.adminSocket = adminSocket.second;
1367
- details.io = BufferedIO(adminSocket.second);
1368
- details.stderrCapturer =
1369
- make_shared<BackgroundIOCapturer>(
1370
- errorPipe.first,
1371
- config->forwardStderr ? config->forwardStderrTo : -1);
1372
- details.stderrCapturer->start();
1373
- details.debugDir = debugDir;
1374
- details.options = &options;
1375
- details.timeout = options.startTimeout * 1000;
1376
- details.forwardStderr = config->forwardStderr;
1377
- details.forwardStderrTo = config->forwardStderrTo;
1378
-
1379
- {
1380
- this_thread::restore_interruption ri(di);
1381
- this_thread::restore_syscall_interruption rsi(dsi);
1382
- socketAddress = negotiatePreloaderStartup(details);
1383
- }
1384
- this->adminSocket = adminSocket.second;
1385
- {
1386
- lock_guard<boost::mutex> l(simpleFieldSyncher);
1387
- this->pid = pid;
1388
- }
1389
- preloaderOutputWatcher.set(adminSocket.second, ev::READ);
1390
- libev->start(preloaderOutputWatcher);
1391
- setNonBlocking(errorPipe.first);
1392
- preloaderErrorWatcher = make_shared<PipeWatcher>(libev,
1393
- errorPipe.first,
1394
- config->forwardStderr ? config->forwardStderrTo : -1);
1395
- preloaderErrorWatcher->start();
1396
- preloaderAnnotations = debugDir->readAll();
1397
- P_DEBUG("Preloader for " << options.appRoot <<
1398
- " started on PID " << pid <<
1399
- ", listening on " << socketAddress);
1400
- guard.clear();
1401
- }
1402
- }
1403
-
1404
- void stopPreloader() {
1405
- TRACE_POINT();
1406
- this_thread::disable_interruption di;
1407
- this_thread::disable_syscall_interruption dsi;
1408
-
1409
- if (!preloaderStarted()) {
1410
- return;
1411
- }
1412
- adminSocket.close();
1413
- if (timedWaitpid(pid, NULL, 5000) == 0) {
1414
- P_TRACE(2, "Spawn server did not exit in time, killing it...");
1415
- syscalls::kill(pid, SIGKILL);
1416
- syscalls::waitpid(pid, NULL, 0);
1417
- }
1418
- libev->stop(preloaderOutputWatcher);
1419
- // Detach the error pipe; it will truly be closed after the error
1420
- // pipe has reached EOF.
1421
- preloaderErrorWatcher.reset();
1422
- // Delete socket after the process has exited so that it
1423
- // doesn't crash upon deleting a nonexistant file.
1424
- // TODO: in Passenger 4 we must check whether the file really was
1425
- // owned by the preloader, otherwise this is a potential security flaw.
1426
- if (getSocketAddressType(socketAddress) == SAT_UNIX) {
1427
- string filename = parseUnixSocketAddress(socketAddress);
1428
- syscalls::unlink(filename.c_str());
1429
- }
1430
- {
1431
- lock_guard<boost::mutex> l(simpleFieldSyncher);
1432
- pid = -1;
1433
- }
1434
- socketAddress.clear();
1435
- }
1436
-
1437
- void sendStartupRequest(StartupDetails &details) {
1438
- TRACE_POINT();
1439
- try {
1440
- writeExact(details.adminSocket,
1441
- "You have control 1.0\n"
1442
- "passenger_root: " + resourceLocator.getRoot() + "\n"
1443
- "ruby_libdir: " + resourceLocator.getRubyLibDir() + "\n"
1444
- "passenger_version: " PASSENGER_VERSION "\n"
1445
- "generation_dir: " + generation->getPath() + "\n",
1446
- &details.timeout);
1447
-
1448
- vector<string> args;
1449
- vector<string>::const_iterator it, end;
1450
- details.options->toVector(args, resourceLocator);
1451
- for (it = args.begin(); it != args.end(); it++) {
1452
- const string &key = *it;
1453
- it++;
1454
- const string &value = *it;
1455
- writeExact(details.adminSocket,
1456
- key + ": " + value + "\n",
1457
- &details.timeout);
1458
- }
1459
- writeExact(details.adminSocket, "\n", &details.timeout);
1460
- } catch (const SystemException &e) {
1461
- if (e.code() == EPIPE) {
1462
- /* Ignore this. Process might have written an
1463
- * error response before reading the arguments,
1464
- * in which case we'll want to show that instead.
1465
- */
1466
- } else {
1467
- throwPreloaderSpawnException("An error occurred while starting up "
1468
- "the preloader. There was an I/O error while "
1469
- "sending the startup request message to it: " +
1470
- e.sys(),
1471
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1472
- details);
1473
- }
1474
- } catch (const TimeoutException &) {
1475
- throwPreloaderSpawnException("An error occurred while starting up the "
1476
- "preloader: it did not read the startup request message in time.",
1477
- SpawnException::PRELOADER_STARTUP_TIMEOUT,
1478
- details);
1479
- }
1480
- }
1481
-
1482
- string handleStartupResponse(StartupDetails &details) {
1483
- TRACE_POINT();
1484
- string socketAddress;
1485
-
1486
- while (true) {
1487
- string line;
1488
-
1489
- try {
1490
- line = readMessageLine(details);
1491
- } catch (const SystemException &e) {
1492
- throwPreloaderSpawnException("An error occurred while starting up "
1493
- "the preloader. There was an I/O error while reading its "
1494
- "startup response: " + e.sys(),
1495
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1496
- details);
1497
- } catch (const TimeoutException &) {
1498
- throwPreloaderSpawnException("An error occurred while starting up "
1499
- "the preloader: it did not write a startup response in time.",
1500
- SpawnException::PRELOADER_STARTUP_TIMEOUT,
1501
- details);
1502
- }
1503
-
1504
- if (line.empty()) {
1505
- throwPreloaderSpawnException("An error occurred while starting up "
1506
- "the preloader. It unexpected closed the connection while "
1507
- "sending its startup response.",
1508
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1509
- details);
1510
- } else if (line[line.size() - 1] != '\n') {
1511
- throwPreloaderSpawnException("An error occurred while starting up "
1512
- "the preloader. It sent a line without a newline character "
1513
- "in its startup response.",
1514
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1515
- details);
1516
- } else if (line == "\n") {
1517
- break;
1518
- }
1519
-
1520
- string::size_type pos = line.find(": ");
1521
- if (pos == string::npos) {
1522
- throwPreloaderSpawnException("An error occurred while starting up "
1523
- "the preloader. It sent a startup response line without "
1524
- "separator.",
1525
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1526
- details);
1527
- }
1528
-
1529
- string key = line.substr(0, pos);
1530
- string value = line.substr(pos + 2, line.size() - pos - 3);
1531
- if (key == "socket") {
1532
- socketAddress = fixupSocketAddress(options, value);
1533
- } else {
1534
- throwPreloaderSpawnException("An error occurred while starting up "
1535
- "the preloader. It sent an unknown startup response line "
1536
- "called '" + key + "'.",
1537
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1538
- details);
1539
- }
1540
- }
1541
-
1542
- if (socketAddress.empty()) {
1543
- throwPreloaderSpawnException("An error occurred while starting up "
1544
- "the preloader. It did not report a socket address in its "
1545
- "startup response.",
1546
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1547
- details);
1548
- }
1549
-
1550
- return socketAddress;
1551
- }
1552
-
1553
- void handleErrorResponse(StartupDetails &details) {
1554
- TRACE_POINT();
1555
- map<string, string> attributes;
1556
-
1557
- while (true) {
1558
- string line;
1559
-
1560
- try {
1561
- line = readMessageLine(details);
1562
- } catch (const SystemException &e) {
1563
- throwPreloaderSpawnException("An error occurred while starting up "
1564
- "the preloader. There was an I/O error while reading its "
1565
- "startup response: " + e.sys(),
1566
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1567
- details);
1568
- } catch (const TimeoutException &) {
1569
- throwPreloaderSpawnException("An error occurred while starting up "
1570
- "the preloader: it did not write a startup response in time.",
1571
- SpawnException::PRELOADER_STARTUP_TIMEOUT,
1572
- details);
1573
- }
1574
-
1575
- if (line.empty()) {
1576
- throwPreloaderSpawnException("An error occurred while starting up "
1577
- "the preloader. It unexpected closed the connection while "
1578
- "sending its startup response.",
1579
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1580
- details);
1581
- } else if (line[line.size() - 1] != '\n') {
1582
- throwPreloaderSpawnException("An error occurred while starting up "
1583
- "the preloader. It sent a line without a newline character "
1584
- "in its startup response.",
1585
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1586
- details);
1587
- } else if (line == "\n") {
1588
- break;
1589
- }
1590
-
1591
- string::size_type pos = line.find(": ");
1592
- if (pos == string::npos) {
1593
- throwPreloaderSpawnException("An error occurred while starting up "
1594
- "the preloader. It sent a startup response line without "
1595
- "separator.",
1596
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1597
- details);
1598
- }
1599
-
1600
- string key = line.substr(0, pos);
1601
- string value = line.substr(pos + 2, line.size() - pos - 3);
1602
- attributes[key] = value;
1603
- }
1604
-
1605
- try {
1606
- string message = details.io.readAll(&details.timeout);
1607
- SpawnException e("An error occured while starting up the preloader.",
1608
- message,
1609
- attributes["html"] == "true",
1610
- SpawnException::PRELOADER_STARTUP_EXPLAINABLE_ERROR);
1611
- e.setPreloaderCommand(getPreloaderCommandString());
1612
- annotatePreloaderException(e, details.debugDir);
1613
- throw e;
1614
- } catch (const SystemException &e) {
1615
- throwPreloaderSpawnException("An error occurred while starting up "
1616
- "the preloader. It tried to report an error message, but "
1617
- "an I/O error occurred while reading this error message: " +
1618
- e.sys(),
1619
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1620
- details);
1621
- } catch (const TimeoutException &) {
1622
- throwPreloaderSpawnException("An error occurred while starting up "
1623
- "the preloader. It tried to report an error message, but "
1624
- "it took too much time doing that.",
1625
- SpawnException::PRELOADER_STARTUP_TIMEOUT,
1626
- details);
1627
- }
1628
- }
1629
-
1630
- void handleInvalidResponseType(StartupDetails &details, const string &line) {
1631
- throwPreloaderSpawnException("An error occurred while starting up "
1632
- "the preloader. It sent an unknown response type \"" +
1633
- cEscapeString(line) + "\".",
1634
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1635
- details);
1636
- }
1637
-
1638
- string negotiatePreloaderStartup(StartupDetails &details) {
1639
- TRACE_POINT();
1640
- string result;
1641
- try {
1642
- result = readMessageLine(details);
1643
- } catch (const SystemException &e) {
1644
- throwPreloaderSpawnException("An error occurred while starting up "
1645
- "the preloader. There was an I/O error while reading its "
1646
- "handshake message: " + e.sys(),
1647
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1648
- details);
1649
- } catch (const TimeoutException &) {
1650
- throwPreloaderSpawnException("An error occurred while starting up "
1651
- "the preloader: it did not write a handshake message in time.",
1652
- SpawnException::PRELOADER_STARTUP_TIMEOUT,
1653
- details);
1654
- }
1655
-
1656
- if (result == "I have control 1.0\n") {
1657
- UPDATE_TRACE_POINT();
1658
- sendStartupRequest(details);
1659
- try {
1660
- result = readMessageLine(details);
1661
- } catch (const SystemException &e) {
1662
- throwPreloaderSpawnException("An error occurred while starting up "
1663
- "the preloader. There was an I/O error while reading its "
1664
- "startup response: " + e.sys(),
1665
- SpawnException::PRELOADER_STARTUP_PROTOCOL_ERROR,
1666
- details);
1667
- } catch (const TimeoutException &) {
1668
- throwPreloaderSpawnException("An error occurred while starting up "
1669
- "the preloader: it did not write a startup response in time.",
1670
- SpawnException::PRELOADER_STARTUP_TIMEOUT,
1671
- details);
1672
- }
1673
- if (result == "Ready\n") {
1674
- return handleStartupResponse(details);
1675
- } else if (result == "Error\n") {
1676
- handleErrorResponse(details);
1677
- } else {
1678
- handleInvalidResponseType(details, result);
1679
- }
1680
- } else {
1681
- UPDATE_TRACE_POINT();
1682
- if (result == "Error\n") {
1683
- handleErrorResponse(details);
1684
- } else {
1685
- handleInvalidResponseType(details, result);
1686
- }
1687
- }
1688
-
1689
- // Never reached, shut up compiler warning.
1690
- abort();
1691
- return "";
1692
- }
1693
-
1694
- SpawnResult sendSpawnCommand(const Options &options) {
1695
- TRACE_POINT();
1696
- FileDescriptor fd;
1697
- try {
1698
- fd = connectToServer(socketAddress);
1699
- } catch (const SystemException &e) {
1700
- BackgroundIOCapturerPtr stderrCapturer;
1701
- throwPreloaderSpawnException("An error occurred while starting "
1702
- "the application. Unable to connect to the preloader's "
1703
- "socket: " + string(e.what()),
1704
- SpawnException::APP_STARTUP_PROTOCOL_ERROR,
1705
- stderrCapturer,
1706
- DebugDirPtr());
1707
- }
1708
-
1709
- UPDATE_TRACE_POINT();
1710
- BufferedIO io(fd);
1711
- unsigned long long timeout = options.startTimeout * 1000;
1712
- string result;
1713
- vector<string> args;
1714
- vector<string>::const_iterator it;
1715
-
1716
- writeExact(fd, "spawn\n", &timeout);
1717
- options.toVector(args, resourceLocator);
1718
- for (it = args.begin(); it != args.end(); it++) {
1719
- const string &key = *it;
1720
- it++;
1721
- const string &value = *it;
1722
- writeExact(fd, key + ": " + value + "\n", &timeout);
1723
- }
1724
- writeExact(fd, "\n", &timeout);
1725
-
1726
- result = io.readLine(1024, &timeout);
1727
- if (result == "OK\n") {
1728
- UPDATE_TRACE_POINT();
1729
- pid_t spawnedPid;
1730
-
1731
- spawnedPid = atoi(io.readLine(1024, &timeout).c_str());
1732
- if (spawnedPid <= 0) {
1733
- BackgroundIOCapturerPtr stderrCapturer;
1734
- throwPreloaderSpawnException("An error occurred while starting "
1735
- "the web application. Its preloader responded to the "
1736
- "'spawn' command with an invalid PID: '" +
1737
- toString(spawnedPid) + "'",
1738
- SpawnException::APP_STARTUP_PROTOCOL_ERROR,
1739
- stderrCapturer,
1740
- DebugDirPtr());
1741
- }
1742
- // TODO: we really should be checking UID.
1743
- // FIXME: for Passenger 4 we *must* check the UID otherwise this is a gaping security hole.
1744
- if (getsid(spawnedPid) != getsid(pid)) {
1745
- BackgroundIOCapturerPtr stderrCapturer;
1746
- throwPreloaderSpawnException("An error occurred while starting "
1747
- "the web application. Its preloader responded to the "
1748
- "'spawn' command with a PID that doesn't belong to "
1749
- "the same session: '" + toString(spawnedPid) + "'",
1750
- SpawnException::APP_STARTUP_PROTOCOL_ERROR,
1751
- stderrCapturer,
1752
- DebugDirPtr());
1753
- }
1754
-
1755
- SpawnResult result;
1756
- result.pid = spawnedPid;
1757
- result.adminSocket = fd;
1758
- result.io = io;
1759
- return result;
1760
-
1761
- } else if (result == "Error\n") {
1762
- UPDATE_TRACE_POINT();
1763
- NegotiationDetails details;
1764
- details.io = io;
1765
- details.timeout = timeout;
1766
- handleSpawnErrorResponse(details);
1767
-
1768
- } else {
1769
- UPDATE_TRACE_POINT();
1770
- NegotiationDetails details;
1771
- handleInvalidSpawnResponseType(result, details);
1772
- }
1773
-
1774
- return SpawnResult(); // Never reached.
1775
- }
1776
-
1777
- template<typename Exception>
1778
- SpawnResult sendSpawnCommandAgain(const Exception &e, const Options &options) {
1779
- TRACE_POINT();
1780
- P_WARN("An error occurred while spawning a process: " << e.what());
1781
- P_WARN("The application preloader seems to have crashed, restarting it and trying again...");
1782
- stopPreloader();
1783
- startPreloader();
1784
- ScopeGuard guard(boost::bind(&SmartSpawner::stopPreloader, this));
1785
- SpawnResult result = sendSpawnCommand(options);
1786
- guard.clear();
1787
- return result;
1788
- }
1789
-
1790
- protected:
1791
- virtual void annotateAppSpawnException(SpawnException &e, NegotiationDetails &details) {
1792
- Spawner::annotateAppSpawnException(e, details);
1793
- e.addAnnotations(preloaderAnnotations);
1794
- }
1795
-
1796
- public:
1797
- SmartSpawner(const SafeLibevPtr &_libev,
1798
- const ResourceLocator &_resourceLocator,
1799
- const ServerInstanceDir::GenerationPtr &_generation,
1800
- const vector<string> &_preloaderCommand,
1801
- const Options &_options,
1802
- const RandomGeneratorPtr &_randomGenerator = RandomGeneratorPtr(),
1803
- const SpawnerConfigPtr &_config = SpawnerConfigPtr())
1804
- : Spawner(_resourceLocator),
1805
- libev(_libev),
1806
- preloaderCommand(_preloaderCommand)
1807
- {
1808
- if (preloaderCommand.size() < 2) {
1809
- throw ArgumentException("preloaderCommand must have at least 2 elements");
1810
- }
1811
-
1812
- generation = _generation;
1813
- options = _options.copyAndPersist().clearLogger();
1814
- pid = -1;
1815
- m_lastUsed = SystemTime::getUsec();
1816
-
1817
- preloaderOutputWatcher.set<SmartSpawner, &SmartSpawner::onPreloaderOutputReadable>(this);
1818
-
1819
- if (_randomGenerator == NULL) {
1820
- randomGenerator = make_shared<RandomGenerator>();
1821
- } else {
1822
- randomGenerator = _randomGenerator;
1823
- }
1824
- if (_config == NULL) {
1825
- config = make_shared<SpawnerConfig>();
1826
- } else {
1827
- config = _config;
1828
- }
1829
- }
1830
-
1831
- virtual ~SmartSpawner() {
1832
- lock_guard<boost::mutex> l(syncher);
1833
- stopPreloader();
1834
- }
1835
-
1836
- virtual ProcessPtr spawn(const Options &options) {
1837
- TRACE_POINT();
1838
- assert(options.appType == this->options.appType);
1839
- assert(options.appRoot == this->options.appRoot);
1840
-
1841
- P_DEBUG("Spawning new process: appRoot=" << options.appRoot);
1842
- possiblyRaiseInternalError(options);
1843
-
1844
- {
1845
- lock_guard<boost::mutex> l(simpleFieldSyncher);
1846
- m_lastUsed = SystemTime::getUsec();
1847
- }
1848
- if (!preloaderStarted()) {
1849
- UPDATE_TRACE_POINT();
1850
- startPreloader();
1851
- }
1852
-
1853
- UPDATE_TRACE_POINT();
1854
- SpawnResult result;
1855
- try {
1856
- result = sendSpawnCommand(options);
1857
- } catch (const SystemException &e) {
1858
- result = sendSpawnCommandAgain(e, options);
1859
- } catch (const IOException &e) {
1860
- result = sendSpawnCommandAgain(e, options);
1861
- } catch (const SpawnException &e) {
1862
- result = sendSpawnCommandAgain(e, options);
1863
- }
1864
-
1865
- UPDATE_TRACE_POINT();
1866
- NegotiationDetails details;
1867
- details.libev = libev;
1868
- details.pid = result.pid;
1869
- details.adminSocket = result.adminSocket;
1870
- details.io = result.io;
1871
- details.options = &options;
1872
- details.forwardStderr = config->forwardStderr;
1873
- details.forwardStderrTo = config->forwardStderrTo;
1874
- ProcessPtr process = negotiateSpawn(details);
1875
- P_DEBUG("Process spawning done: appRoot=" << options.appRoot <<
1876
- ", pid=" << process->pid);
1877
- return process;
1878
- }
1879
-
1880
- virtual bool cleanable() const {
1881
- return true;
1882
- }
1883
-
1884
- virtual void cleanup() {
1885
- TRACE_POINT();
1886
- {
1887
- lock_guard<boost::mutex> l(simpleFieldSyncher);
1888
- m_lastUsed = SystemTime::getUsec();
1889
- }
1890
- lock_guard<boost::mutex> lock(syncher);
1891
- stopPreloader();
1892
- }
1893
-
1894
- virtual unsigned long long lastUsed() const {
1895
- lock_guard<boost::mutex> lock(simpleFieldSyncher);
1896
- return m_lastUsed;
1897
- }
1898
-
1899
- pid_t getPreloaderPid() const {
1900
- lock_guard<boost::mutex> lock(simpleFieldSyncher);
1901
- return pid;
1902
- }
1903
- };
1904
-
1905
-
1906
- class DirectSpawner: public Spawner {
1907
- private:
1908
- SafeLibevPtr libev;
1909
-
1910
- static int startBackgroundThread(void *(*mainFunction)(void *), void *arg) {
1911
- // Using raw pthread API because we don't want to register such
1912
- // trivial threads on the oxt::thread list.
1913
- pthread_t thr;
1914
- pthread_attr_t attr;
1915
- size_t stack_size = 96 * 1024;
1916
-
1917
- unsigned long min_stack_size;
1918
- bool stack_min_size_defined;
1919
- bool round_stack_size;
1920
- int ret;
1921
-
1922
- #ifdef PTHREAD_STACK_MIN
1923
- // PTHREAD_STACK_MIN may not be a constant macro so we need
1924
- // to evaluate it dynamically.
1925
- min_stack_size = PTHREAD_STACK_MIN;
1926
- stack_min_size_defined = true;
1927
- #else
1928
- // Assume minimum stack size is 128 KB.
1929
- min_stack_size = 128 * 1024;
1930
- stack_min_size_defined = false;
1931
- #endif
1932
- if (stack_size != 0 && stack_size < min_stack_size) {
1933
- stack_size = min_stack_size;
1934
- round_stack_size = !stack_min_size_defined;
1935
- } else {
1936
- round_stack_size = true;
1937
- }
1938
-
1939
- if (round_stack_size) {
1940
- // Round stack size up to page boundary.
1941
- long page_size;
1942
- #if defined(_SC_PAGESIZE)
1943
- page_size = sysconf(_SC_PAGESIZE);
1944
- #elif defined(_SC_PAGE_SIZE)
1945
- page_size = sysconf(_SC_PAGE_SIZE);
1946
- #elif defined(PAGESIZE)
1947
- page_size = sysconf(PAGESIZE);
1948
- #elif defined(PAGE_SIZE)
1949
- page_size = sysconf(PAGE_SIZE);
1950
- #else
1951
- page_size = getpagesize();
1952
- #endif
1953
- if (stack_size % page_size != 0) {
1954
- stack_size = stack_size - (stack_size % page_size) + page_size;
1955
- }
1956
- }
1957
-
1958
- pthread_attr_init(&attr);
1959
- pthread_attr_setdetachstate(&attr, 1);
1960
- pthread_attr_setstacksize(&attr, stack_size);
1961
- ret = pthread_create(&thr, &attr, mainFunction, arg);
1962
- pthread_attr_destroy(&attr);
1963
- return ret;
1964
- }
1965
-
1966
- static void *detachProcessMain(void *arg) {
1967
- this_thread::disable_syscall_interruption dsi;
1968
- pid_t pid = (pid_t) (long) arg;
1969
- syscalls::waitpid(pid, NULL, 0);
1970
- return NULL;
1971
- }
1972
-
1973
- void detachProcess(pid_t pid) {
1974
- startBackgroundThread(detachProcessMain, (void *) (long) pid);
1975
- }
1976
-
1977
- vector<string> createCommand(const Options &options, shared_array<const char *> &args) const {
1978
- vector<string> startCommandArgs;
1979
- string processTitle;
1980
- string agentsDir = resourceLocator.getAgentsDir();
1981
- vector<string> command;
1982
-
1983
- split(options.getStartCommand(resourceLocator), '\1', startCommandArgs);
1984
- if (startCommandArgs.empty()) {
1985
- throw RuntimeException("No startCommand given");
1986
- }
1987
- if (options.getProcessTitle().empty()) {
1988
- processTitle = startCommandArgs[0];
1989
- } else {
1990
- processTitle = options.getProcessTitle() + ": " + options.appRoot;
1991
- }
1992
-
1993
- if (options.loadShellEnvvars) {
1994
- command.push_back("bash");
1995
- command.push_back("bash");
1996
- command.push_back("-lc");
1997
- command.push_back("exec \"$@\"");
1998
- command.push_back("SpawnPreparerShell");
1999
- } else {
2000
- command.push_back(agentsDir + "/SpawnPreparer");
2001
- }
2002
- command.push_back(agentsDir + "/SpawnPreparer");
2003
- command.push_back(serializeEnvvarsFromPoolOptions(options));
2004
- command.push_back(startCommandArgs[0]);
2005
- command.push_back(processTitle);
2006
- for (unsigned int i = 1; i < startCommandArgs.size(); i++) {
2007
- command.push_back(startCommandArgs[i]);
2008
- }
2009
-
2010
- createCommandArgs(command, args);
2011
- return command;
2012
- }
2013
-
2014
- public:
2015
- DirectSpawner(const SafeLibevPtr &_libev,
2016
- const ResourceLocator &_resourceLocator,
2017
- const ServerInstanceDir::GenerationPtr &_generation,
2018
- const RandomGeneratorPtr &_randomGenerator = RandomGeneratorPtr(),
2019
- const SpawnerConfigPtr &_config = SpawnerConfigPtr())
2020
- : Spawner(_resourceLocator),
2021
- libev(_libev)
2022
- {
2023
- generation = _generation;
2024
- if (_randomGenerator == NULL) {
2025
- randomGenerator = make_shared<RandomGenerator>();
2026
- } else {
2027
- randomGenerator = _randomGenerator;
2028
- }
2029
- if (_config == NULL) {
2030
- config = make_shared<SpawnerConfig>();
2031
- } else {
2032
- config = _config;
2033
- }
2034
- }
2035
-
2036
- virtual ProcessPtr spawn(const Options &options) {
2037
- TRACE_POINT();
2038
- this_thread::disable_interruption di;
2039
- this_thread::disable_syscall_interruption dsi;
2040
- P_DEBUG("Spawning new process: appRoot=" << options.appRoot);
2041
- possiblyRaiseInternalError(options);
2042
-
2043
- shared_array<const char *> args;
2044
- vector<string> command = createCommand(options, args);
2045
- SpawnPreparationInfo preparation = prepareSpawn(options);
2046
- SocketPair adminSocket = createUnixSocketPair();
2047
- Pipe errorPipe = createPipe();
2048
- DebugDirPtr debugDir = make_shared<DebugDir>(preparation.uid, preparation.gid);
2049
- pid_t pid;
2050
-
2051
- pid = syscalls::fork();
2052
- if (pid == 0) {
2053
- setenv("PASSENGER_DEBUG_DIR", debugDir->getPath().c_str(), 1);
2054
- purgeStdio(stdout);
2055
- purgeStdio(stderr);
2056
- resetSignalHandlersAndMask();
2057
- disableMallocDebugging();
2058
- int adminSocketCopy = dup2(adminSocket.first, 3);
2059
- int errorPipeCopy = dup2(errorPipe.second, 4);
2060
- dup2(adminSocketCopy, 0);
2061
- dup2(adminSocketCopy, 1);
2062
- dup2(errorPipeCopy, 2);
2063
- closeAllFileDescriptors(2);
2064
- setChroot(preparation);
2065
- switchUser(preparation);
2066
- setWorkingDirectory(preparation);
2067
- execvp(args[0], (char * const *) args.get());
2068
-
2069
- int e = errno;
2070
- printf("!> Error\n");
2071
- printf("!> \n");
2072
- printf("Cannot execute \"%s\": %s (errno=%d)\n", command[0].c_str(),
2073
- strerror(e), e);
2074
- fprintf(stderr, "Cannot execute \"%s\": %s (errno=%d)\n",
2075
- command[0].c_str(), strerror(e), e);
2076
- fflush(stdout);
2077
- fflush(stderr);
2078
- _exit(1);
2079
-
2080
- } else if (pid == -1) {
2081
- int e = errno;
2082
- throw SystemException("Cannot fork a new process", e);
2083
-
2084
- } else {
2085
- UPDATE_TRACE_POINT();
2086
- ScopeGuard guard(boost::bind(nonInterruptableKillAndWaitpid, pid));
2087
- adminSocket.first.close();
2088
- errorPipe.second.close();
2089
-
2090
- NegotiationDetails details;
2091
- details.libev = libev;
2092
- details.stderrCapturer =
2093
- make_shared<BackgroundIOCapturer>(
2094
- errorPipe.first,
2095
- config->forwardStderr ? config->forwardStderrTo : -1);
2096
- details.stderrCapturer->start();
2097
- details.pid = pid;
2098
- details.adminSocket = adminSocket.second;
2099
- details.io = BufferedIO(adminSocket.second);
2100
- details.errorPipe = errorPipe.first;
2101
- details.options = &options;
2102
- details.forwardStderr = config->forwardStderr;
2103
- details.forwardStderrTo = config->forwardStderrTo;
2104
- details.debugDir = debugDir;
2105
-
2106
- ProcessPtr process;
2107
- {
2108
- this_thread::restore_interruption ri(di);
2109
- this_thread::restore_syscall_interruption rsi(dsi);
2110
- process = negotiateSpawn(details);
2111
- }
2112
- detachProcess(process->pid);
2113
- guard.clear();
2114
- P_DEBUG("Process spawning done: appRoot=" << options.appRoot <<
2115
- ", pid=" << process->pid);
2116
- return process;
2117
- }
2118
- }
2119
- };
2120
-
2121
-
2122
- class DummySpawner: public Spawner {
2123
- private:
2124
- SpawnerConfigPtr config;
2125
- boost::mutex lock;
2126
- unsigned int count;
2127
-
2128
- public:
2129
- unsigned int cleanCount;
2130
-
2131
- DummySpawner(const ResourceLocator &resourceLocator, const SpawnerConfigPtr &_config)
2132
- : Spawner(resourceLocator),
2133
- config(_config)
2134
- {
2135
- count = 0;
2136
- cleanCount = 0;
2137
- }
2138
-
2139
- virtual ProcessPtr spawn(const Options &options) {
2140
- TRACE_POINT();
2141
- possiblyRaiseInternalError(options);
2142
-
2143
- SocketPair adminSocket = createUnixSocketPair();
2144
- SocketListPtr sockets = make_shared<SocketList>();
2145
- sockets->add("main", "tcp://127.0.0.1:1234", "session", config->concurrency);
2146
- syscalls::usleep(config->spawnTime);
2147
-
2148
- lock_guard<boost::mutex> l(lock);
2149
- count++;
2150
- return make_shared<Process>(SafeLibevPtr(),
2151
- (pid_t) count, "gupid-" + toString(count),
2152
- toString(count),
2153
- adminSocket.second, FileDescriptor(), sockets,
2154
- SystemTime::getUsec(), SystemTime::getUsec());
2155
- }
2156
-
2157
- virtual bool cleanable() const {
2158
- return true;
2159
- }
2160
-
2161
- virtual void cleanup() {
2162
- cleanCount++;
2163
- }
2164
- };
2165
-
2166
- typedef shared_ptr<DummySpawner> DummySpawnerPtr;
2167
-
2168
-
2169
- class SpawnerFactory {
2170
- private:
2171
- SafeLibevPtr libev;
2172
- ResourceLocator resourceLocator;
2173
- ServerInstanceDir::GenerationPtr generation;
2174
- RandomGeneratorPtr randomGenerator;
2175
- boost::mutex syncher;
2176
- SpawnerConfigPtr config;
2177
- DummySpawnerPtr dummySpawner;
2178
-
2179
- SpawnerPtr tryCreateSmartSpawner(const Options &options) {
2180
- string dir = resourceLocator.getHelperScriptsDir();
2181
- vector<string> preloaderCommand;
2182
- if (options.appType == "classic-rails") {
2183
- preloaderCommand.push_back(options.ruby);
2184
- preloaderCommand.push_back(dir + "/classic-rails-preloader.rb");
2185
- } else if (options.appType == "rack") {
2186
- preloaderCommand.push_back(options.ruby);
2187
- preloaderCommand.push_back(dir + "/rack-preloader.rb");
2188
- } else {
2189
- return SpawnerPtr();
2190
- }
2191
- return make_shared<SmartSpawner>(libev, resourceLocator,
2192
- generation, preloaderCommand, options,
2193
- randomGenerator);
2194
- }
2195
-
2196
- public:
2197
- SpawnerFactory(const SafeLibevPtr &_libev,
2198
- const ResourceLocator &_resourceLocator,
2199
- const ServerInstanceDir::GenerationPtr &_generation,
2200
- const RandomGeneratorPtr &_randomGenerator = RandomGeneratorPtr(),
2201
- const SpawnerConfigPtr &_config = SpawnerConfigPtr())
2202
- : libev(_libev),
2203
- resourceLocator(_resourceLocator),
2204
- generation(_generation)
2205
- {
2206
- if (_randomGenerator == NULL) {
2207
- randomGenerator = make_shared<RandomGenerator>();
2208
- } else {
2209
- randomGenerator = _randomGenerator;
2210
- }
2211
- if (_config == NULL) {
2212
- config = make_shared<SpawnerConfig>();
2213
- } else {
2214
- config = _config;
2215
- }
2216
- }
2217
-
2218
- virtual ~SpawnerFactory() { }
2219
-
2220
- virtual SpawnerPtr create(const Options &options) {
2221
- if (options.spawnMethod == "smart" || options.spawnMethod == "smart-lv2") {
2222
- SpawnerPtr spawner = tryCreateSmartSpawner(options);
2223
- if (spawner == NULL) {
2224
- spawner = make_shared<DirectSpawner>(libev,
2225
- resourceLocator, generation,
2226
- randomGenerator, config);
2227
- }
2228
- return spawner;
2229
- } else if (options.spawnMethod == "direct" || options.spawnMethod == "conservative") {
2230
- shared_ptr<DirectSpawner> spawner = make_shared<DirectSpawner>(libev, resourceLocator,
2231
- generation, randomGenerator, config);
2232
- return spawner;
2233
- } else if (options.spawnMethod == "dummy") {
2234
- syscalls::usleep(config->spawnerCreationSleepTime);
2235
- return getDummySpawner();
2236
- } else {
2237
- throw ArgumentException("Unknown spawn method '" + options.spawnMethod + "'");
2238
- }
2239
- }
2240
-
2241
- /**
2242
- * SpawnerFactory always returns the same DummyFactory object upon
2243
- * creating a dummy spawner. This allows unit tests to easily
2244
- * set debugging options on the spawner.
2245
- */
2246
- DummySpawnerPtr getDummySpawner() {
2247
- lock_guard<boost::mutex> l(syncher);
2248
- if (dummySpawner == NULL) {
2249
- dummySpawner = make_shared<DummySpawner>(resourceLocator, config);
2250
- }
2251
- return dummySpawner;
2252
- }
2253
-
2254
- /**
2255
- * All created Spawner objects share the same SpawnerConfig object.
2256
- */
2257
- SpawnerConfigPtr getConfig() const {
2258
- return config;
2259
- }
2260
- };
2261
-
2262
- typedef shared_ptr<SpawnerFactory> SpawnerFactoryPtr;
2263
-
2264
-
2265
1257
  } // namespace ApplicationPool2
2266
1258
  } // namespace Passenger
2267
1259