passenger 5.1.11 → 5.1.12

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1dcd88ceab959baa6975ea6d4305a925af272321
4
- data.tar.gz: 44883c1837663f92487534c6069ba313a53062d8
3
+ metadata.gz: b0ca744c34e7b85ec574e802d3d5f51ba0e65ba4
4
+ data.tar.gz: f886554ad447e878969606ef9bf7facb430d9467
5
5
  SHA512:
6
- metadata.gz: b7946c6e1585492f3f6e05e153893ef54b403e2850844ecafa228408d2f03168cd12e61cfdbe33735145d3d047b07375911e09b3c7b5bef52c5fd8978a0f80c1
7
- data.tar.gz: 88bd249512da994cb367ec337be1e0ca9a986f017f1a911cbe5d4cd45f2880ff9fad63a68563b8d90fecdd3000f265b3048ef5d88155868f6c9aee20a3a74f06
6
+ metadata.gz: 6bf8a6e154ee6fe1128ee388e716bcd297d60ab84976c3969741d48cff84864276561759a52e7af2e2f23ef8ce9b0b93d82b1ae08fd1c9fee7113560f06f0552
7
+ data.tar.gz: 43e60afd9fb51149dcdaefb513d3d8ee6b9237828313d25bd9ca1f9cbde1c22ab26fa426ce49858b3e827734f3fe2cde654901bd8bd488fa4b1f8c8379a7b9d8
@@ -90,6 +90,14 @@ indent_size = 4
90
90
  indent_style = space
91
91
  indent_size = 4
92
92
 
93
+ [src/nginx_module/*/*.c]
94
+ indent_style = space
95
+ indent_size = 4
96
+
97
+ [src/nginx_module/*/*.h]
98
+ indent_style = space
99
+ indent_size = 4
100
+
93
101
  [src/cxx_supportlib/vendor-modified/libev/*.c]
94
102
  indent_style = space
95
103
  indent_size = 2
data/CHANGELOG CHANGED
@@ -1,3 +1,20 @@
1
+ Release 5.1.12
2
+ ---------------------------------
3
+
4
+ * Changes the Debian/Ubuntu install script to completely restart Apache upon upgrade. This prevents issues due to a "half upgraded" state (old Passenger Apache module + new Passenger core). Closes GH-2000.
5
+ * Adds Ubuntu 17.10 "Artful" packages.
6
+ * Fixes a case in which -- when Passenger is configured with user switching turned off -- it is unable to open the web server log file and aborts during startup. This regression was introduced in 5.1.8. Closes GH-1990.
7
+ * [Nginx] The preferred Nginx version is now 1.12.2
8
+ * [Nginx] The preferred PCRE version is now 8.41 (previously 8.39).
9
+ * [Standalone] Adds support for using `start_timeout` in Passengerfile.json.
10
+ * [Enterprise] Uses libuv to detect total system RAM, allows for compilation on pre-10.11 macOS.
11
+ * [Enterprise] Added a max request queue time option, to limit time requests spend in the request queue. Closes GH-1688.
12
+ * Updated libcurl version used in precompiled binaries (used for e.g. gem installs) to 7.56.1 (was: 7.54.1).
13
+ * Updated OpenSSL version used in precompiled binaries (used for e.g. gem installs) to 1.0.2m (was: 1.0.2l).
14
+ * Updated PCRE version used in precompiled binaries (used for e.g. gem installs) to 8.41 (was: 8.40).
15
+ * Updated Ruby versions used in precompiled binaries (used for e.g. gem installs) to include 2.1.10, 2.2.8, 2.3.5, and 2.4.2 (removed: 2.1.9, 2.2.7, 2.3.4, and 2.4.1).
16
+
17
+
1
18
  Release 5.1.11
2
19
  --------------
3
20
 
@@ -54,7 +54,7 @@ When filing a bug report, please ensure that you include the following informati
54
54
  <a name="contrib_docs"></a>
55
55
  ## Contributing documentation
56
56
 
57
- All good software should have good documentation, and we take this very seriously. However writing and maintaing quality documentation is not an easy task. If you are not skilled in C++ or programming, then writing documentation is the easiest way to contribute.
57
+ All good software should have good documentation, and we take this very seriously. However writing and maintaining quality documentation is not an easy task. If you are not skilled in C++ or programming, then writing documentation is the easiest way to contribute.
58
58
 
59
59
  Most documentation can be located in the `doc` directory, and are either written in Markdown or in Asciidoc format. They can be compiled to HTML with `rake doc`. You need [Mizuho](https://github.com/FooBarWidget/mizuho) to compile Asciidoc and [BlueCloth](http://deveiate.org/projects/BlueCloth) to compile Markdown. Both gems are automatically installed as part of the Phusion Passenger developer tools.
60
60
 
@@ -278,7 +278,7 @@ Less important directories:
278
278
 
279
279
  foo (1, 2, 3);
280
280
 
281
- * Seperate arguments and parts of expressions by spaces:
281
+ * Separate arguments and parts of expressions by spaces:
282
282
 
283
283
  foo(1, 2, foo == bar, 5 + 6);
284
284
  if (foo && bar) {
@@ -6683,6 +6683,7 @@ CXX_DEPENDENCY_MAP =
6683
6683
  "src/cxx_supportlib/Utils/IOUtils.h",
6684
6684
  "src/cxx_supportlib/Utils/LargeFiles.h",
6685
6685
  "src/cxx_supportlib/Utils/StrIntUtils.h",
6686
+ "src/cxx_supportlib/Utils/SystemTime.h",
6686
6687
  "src/cxx_supportlib/oxt/backtrace.hpp",
6687
6688
  "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp",
6688
6689
  "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp",
@@ -0,0 +1,90 @@
1
+ def getDefaultDistros() {
2
+ def distroInfo = readFile("packaging/debian/internal/lib/distro_info.sh")
3
+ def matcher = distroInfo =~ /DEFAULT_DISTROS="(.+?)"/
4
+ if (matcher.find()) {
5
+ matcher.group(1).split().sort()
6
+ } else {
7
+ error("Unable to parse packaging/debian/internal/lib/distro_info.sh")
8
+ }
9
+ }
10
+
11
+ def testDebianPackages(distro, params) {
12
+ if ((!distro in params) || params[distro]) {
13
+ node('linux') {
14
+ def env = [
15
+ "CACHE_DIR=${env.JENKINS_HOME}/cache/${env.JOB_NAME}/${distro}",
16
+ "DISTRIBUTION=${distro}",
17
+ "ARCHITECTURE=amd64"
18
+ ]
19
+ withEnv(env) {
20
+ checkout scm
21
+ sh './dev/ci/tests/debian/run'
22
+ }
23
+ }
24
+ } else {
25
+ echo 'Test skipped.'
26
+ }
27
+ }
28
+
29
+ pipeline {
30
+ agent any
31
+
32
+ options {
33
+ buildDiscarder(logRotator(numToKeepStr: '10'))
34
+ timeout(time: 45, unit: 'MINUTES')
35
+ disableConcurrentBuilds()
36
+ timestamps()
37
+ ansiColor('xterm')
38
+ }
39
+
40
+ parameters {
41
+ booleanParam(name: 'trusty', defaultValue: true, description: 'Test Ubuntu 14.04 packages')
42
+ booleanParam(name: 'xenial', defaultValue: true, description: 'Test Ubuntu 16.04 packages')
43
+ booleanParam(name: 'zesty', defaultValue: true, description: 'Test Ubuntu 17.04 packages')
44
+ booleanParam(name: 'artful', defaultValue: true, description: 'Test Ubuntu 17.10 packages')
45
+
46
+ booleanParam(name: 'wheezy', defaultValue: true, description: 'Test Debian 7 packages')
47
+ booleanParam(name: 'jessie', defaultValue: true, description: 'Test Debian 8 packages')
48
+ booleanParam(name: 'stretch', defaultValue: true, description: 'Test Debian 9 packages')
49
+ }
50
+
51
+ stages {
52
+ stage('Initialize') {
53
+ steps {
54
+ script {
55
+ if (env.JOB_NAME.indexOf('Enterprise') != -1) {
56
+ env.ENTERPRISE = '1'
57
+ } else {
58
+ env.ENTERPRISE = '0'
59
+ }
60
+
61
+ // For debugging purposes
62
+ sh 'env | sort'
63
+ }
64
+ }
65
+ }
66
+
67
+
68
+
69
+ stage('Test') {
70
+ steps {
71
+ script {
72
+ def defaultDistros = getDefaultDistros()
73
+ def i
74
+ def parallelSteps = [:]
75
+
76
+ // We use a plain loop over .each because of this bug:
77
+ // https://issues.jenkins-ci.org/browse/JENKINS-27421
78
+ for (i = 0; i < defaultDistros.length; i++) {
79
+ def distro = defaultDistros[i]
80
+ parallelSteps[distro] = {
81
+ testDebianPackages(distro, params)
82
+ }
83
+ }
84
+
85
+ parallel(parallelSteps)
86
+ }
87
+ }
88
+ }
89
+ }
90
+ }
@@ -0,0 +1,60 @@
1
+ #!/bin/bash
2
+ # This script is from the "Passenger Debian packaging test" Jenkins job. It builds
3
+ # packages for a specific distribution and architecture and runs tests on the resulting packages.
4
+ #
5
+ # Required environment variables:
6
+ #
7
+ # WORKSPACE
8
+ # DISTRIBUTION
9
+ # ARCHITECTURE
10
+ #
11
+ # Optional environment variables:
12
+ #
13
+ # PASSENGER_ROOT (defaults to $WORKSPACE)
14
+ # CACHE_DIR (defaults to $WORKSPACE/cache)
15
+ # ENTERPRISE
16
+ # DEBUG_CONSOLE
17
+ #
18
+ # Sample invocation in Vagrant dev environment:
19
+ #
20
+ # env WORKSPACE=$HOME DISTRIBUTION=el7 ARCHITECTURE=x86_64 PASSENGER_ROOT=/passenger ./dev/ci/debian/run
21
+
22
+ set -e
23
+ SELFDIR=$(dirname "$0")
24
+ cd "$SELFDIR/../../../../packaging/debian"
25
+ # shellcheck source=../../../../packaging/debian/internal/lib/library.sh
26
+ source "./internal/lib/library.sh"
27
+
28
+ require_envvar WORKSPACE "$WORKSPACE"
29
+ require_envvar DISTRIBUTION "$DISTRIBUTION"
30
+ require_envvar ARCHITECTURE "$ARCHITECTURE"
31
+
32
+ PASSENGER_ROOT="${PASSENGER_ROOT:-$WORKSPACE}"
33
+ CACHE_DIR="${CACHE_DIR:-$WORKSPACE/cache}"
34
+
35
+ if [[ "$DEBUG_CONSOLE" = true ]]; then
36
+ EXTRA_TEST_PARAMS=-D
37
+ else
38
+ EXTRA_TEST_PARAMS=
39
+ fi
40
+ if [[ "$ENTERPRISE" = 1 ]]; then
41
+ EXTRA_TEST_PARAMS="$EXTRA_TEST_PARAMS -e /etc/passenger-enterprise-license"
42
+ fi
43
+
44
+ run mkdir -p "$CACHE_DIR"
45
+ run ./build \
46
+ -w "$WORKSPACE/work" \
47
+ -c "$CACHE_DIR" \
48
+ -o "$WORKSPACE/output" \
49
+ -p "$PASSENGER_ROOT" \
50
+ -d "$DISTRIBUTION" \
51
+ -a "$ARCHITECTURE" \
52
+ -R \
53
+ pkg:all
54
+ run ./test \
55
+ -p "$PASSENGER_ROOT" \
56
+ -d "$WORKSPACE/output/$DISTRIBUTION" \
57
+ -c "$CACHE_DIR" \
58
+ -x "$DISTRIBUTION" \
59
+ -j \
60
+ $EXTRA_TEST_PARAMS
@@ -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, :start_timeout) %>
44
45
  <%= nginx_option(app, :min_instances) %>
45
46
  <%= nginx_option(app, :max_request_queue_size) %>
46
47
  <%= nginx_option(app, :restart_dir) %>
@@ -206,7 +206,7 @@ public:
206
206
  }
207
207
 
208
208
  /**
209
- * This Session object becomes fully unsable after closing.
209
+ * This Session object becomes fully unusable after closing.
210
210
  */
211
211
  virtual void close(bool success, bool wantKeepAlive = false) {
212
212
  if (OXT_LIKELY(initiated())) {
@@ -286,6 +286,6 @@ typedef boost::shared_ptr<SocketList> SocketListPtr;
286
286
 
287
287
 
288
288
  } // namespace ApplicationPool2
289
- } // namespace Pasenger
289
+ } // namespace Passenger
290
290
 
291
291
  #endif /* _PASSENGER_APPLICATION_POOL2_SOCKET_H_ */
@@ -172,7 +172,7 @@ Controller::initializePoolOptions(Client *client, Request *req, RequestAnalysis
172
172
  req->envvars->size);
173
173
  }
174
174
 
175
- // Allow certain options to be overriden on a per-request basis
175
+ // Allow certain options to be overridden on a per-request basis
176
176
  fillPoolOption(req, req->options.maxRequests, PASSENGER_MAX_REQUESTS);
177
177
  }
178
178
  }
@@ -151,7 +151,7 @@ private:
151
151
  // A problem occurred somewhere in the SSL/TLS handshake. Not sure what's up, but in this case the
152
152
  // error buffer (printed in DEBUG) should pinpoint the problem slightly more.
153
153
  case CURLE_OPERATION_TIMEDOUT:
154
- // This is not a normal connect timeout, there are some refs to it occuring while downloading large
154
+ // This is not a normal connect timeout, there are some refs to it occurring while downloading large
155
155
  // files, but we don't do that so fall through to default.
156
156
  default:
157
157
  error.append(" while connecting to " CHECK_URL_DEFAULT " " +
@@ -784,7 +784,12 @@ apiServerProcessReopenLogs(Server *server, Client *client, Request *req) {
784
784
  vector<ConfigKit::Error> errors;
785
785
  bool ok;
786
786
  try {
787
- ok = LoggingKit::context->prepareConfigChange(Json::objectValue,
787
+ // We deliberately ignore the target.stderr key.
788
+ // If the log file was equal to stderr then we'll want
789
+ // to reopen the log file anyway.
790
+ Json::Value updates;
791
+ updates["target"] = config["target"]["path"];
792
+ ok = LoggingKit::context->prepareConfigChange(updates,
788
793
  errors, configReq);
789
794
  } catch (const SystemException &e) {
790
795
  unsigned int bufsize = 2048;
@@ -867,6 +872,7 @@ _apiServerProcessReinheritLogsResponseBody(
867
872
 
868
873
  config["target"] = oldConfig["target"];
869
874
  config["target"]["fd"] = fd;
875
+ config["target"].removeMember("stderr");
870
876
  try {
871
877
  ok = LoggingKit::context->prepareConfigChange(config,
872
878
  errors, configReq);
@@ -445,9 +445,12 @@ initializeLoggingKit(const char *processName, VariantMap &options) {
445
445
  config["level"] = options.get("log_level");
446
446
 
447
447
  if (options.has("log_file")) {
448
- config["target"] = options.get("log_file");
448
+ config["target"]["path"] = options.get("log_file");
449
449
  } else if (options.has("debug_log_file")) {
450
- config["target"] = options.get("debug_log_file");
450
+ config["target"]["path"] = options.get("debug_log_file");
451
+ }
452
+ if (options.getBool("log_file_is_stderr", false, false)) {
453
+ config["target"]["stderr"] = true;
451
454
  }
452
455
 
453
456
  if (options.has("file_descriptor_log_file")) {
@@ -42,8 +42,10 @@
42
42
 
43
43
  #include <sys/time.h>
44
44
  #include <sys/resource.h>
45
+ #include <sys/stat.h>
45
46
  #include <exception>
46
47
  #include <cstdio>
48
+ #include <fcntl.h>
47
49
  #include <unistd.h>
48
50
 
49
51
  #include <oxt/initialize.hpp>
@@ -358,6 +360,27 @@ private:
358
360
  return m_hasModXsendfile == YES;
359
361
  }
360
362
 
363
+ static bool stderrEqualsFile(const char *path) {
364
+ struct stat s1, s2;
365
+
366
+ if (fstat(STDERR_FILENO, &s1) == -1) {
367
+ return false;
368
+ }
369
+
370
+ // No O_CREAT: we don't care if the file does not exist.
371
+ int fd = open(path, O_WRONLY | O_APPEND, 0600);
372
+ if (fd == -1) {
373
+ return false;
374
+ }
375
+ if (fstat(fd, &s2) == -1) {
376
+ close(fd);
377
+ return false;
378
+ }
379
+ close(fd);
380
+
381
+ return s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino && s1.st_rdev == s2.st_rdev;
382
+ }
383
+
361
384
  int reportBusyException(request_rec *r) {
362
385
  ap_custom_response(r, HTTP_SERVICE_UNAVAILABLE,
363
386
  "This website is too busy right now. Please try again later.");
@@ -1347,6 +1370,8 @@ public:
1347
1370
  " with an explicit log file using the `PassengerLogFile` directive.");
1348
1371
  } else {
1349
1372
  params.set("log_file", ap_server_root_relative(pconf, s->error_fname));
1373
+ params.setBool("log_file_is_stderr", stderrEqualsFile(
1374
+ ap_server_root_relative(pconf, s->error_fname)));
1350
1375
  }
1351
1376
 
1352
1377
  serverConfig.ctl.addTo(params);
@@ -154,6 +154,8 @@ public:
154
154
  : finalized(false)
155
155
  { }
156
156
 
157
+ virtual ~Schema() { }
158
+
157
159
  /**
158
160
  * Register a new schema entry, possibly with a static default value.
159
161
  */
@@ -82,7 +82,7 @@
82
82
  #define PASSENGER_API_VERSION_MAJOR 0
83
83
  #define PASSENGER_API_VERSION_MINOR 3
84
84
  #define PASSENGER_DEFAULT_USER "nobody"
85
- #define PASSENGER_VERSION "5.1.11"
85
+ #define PASSENGER_VERSION "5.1.12"
86
86
  #define POOL_HELPER_THREAD_STACK_SIZE 262144
87
87
  #define PROCESS_SHUTDOWN_TIMEOUT 60
88
88
  #define PROCESS_SHUTDOWN_TIMEOUT_DISPLAY "1 minute"
@@ -43,6 +43,7 @@
43
43
  #include <RandomGenerator.h>
44
44
  #include <Utils.h>
45
45
  #include <Utils/IOUtils.h>
46
+ #include <Utils/SystemTime.h>
46
47
  #include <jsoncpp/json.h>
47
48
 
48
49
  namespace Passenger {
@@ -220,6 +221,7 @@ private:
220
221
  props["instance_dir"]["major_version"] = SERVER_INSTANCE_DIR_STRUCTURE_MAJOR_VERSION;
221
222
  props["instance_dir"]["minor_version"] = SERVER_INSTANCE_DIR_STRUCTURE_MINOR_VERSION;
222
223
  props["instance_dir"]["created_at"] = (Json::Int64) time(NULL);
224
+ props["instance_dir"]["created_at_monotonic_usec"] = (Json::UInt64) SystemTime::getMonotonicUsec();
223
225
  props["passenger_version"] = PASSENGER_VERSION;
224
226
  props["watchdog_pid"] = (Json::UInt64) getpid();
225
227
 
@@ -577,18 +577,24 @@ Schema::validateTarget(const string &key, const ConfigKit::Store &store,
577
577
  return;
578
578
  }
579
579
 
580
+ // Allowed formats:
581
+ // "/path-to-file"
582
+ // { "stderr": true }
583
+ // { "path": "/path" }
584
+ // { "path": "/path", "fd": 123 }
585
+ // { "path": "/path", "stderr": true }
586
+
580
587
  if (value.isObject()) {
581
588
  if (value.isMember("stderr")) {
582
- if (value.size() > 1) {
583
- errors.push_back(Error("When " + keyQuote
584
- + " is an object containing the 'stderr' key,"
585
- " it may not contain any other keys"));
586
- } else if (!value["stderr"].asBool()) {
589
+ if (!value["stderr"].isBool() || !value["stderr"].asBool()) {
587
590
  errors.push_back(Error("When " + keyQuote
588
591
  + " is an object containing the 'stderr' key,"
589
592
  " it must have the 'true' value"));
593
+ return;
590
594
  }
591
- } else if (value.isMember("path")) {
595
+ }
596
+
597
+ if (value.isMember("path")) {
592
598
  if (!value["path"].isString()) {
593
599
  errors.push_back(Error("When " + keyQuote
594
600
  + " is an object containing the 'path' key,"
@@ -605,6 +611,21 @@ Schema::validateTarget(const string &key, const ConfigKit::Store &store,
605
611
  " it must be 0 or greater"));
606
612
  }
607
613
  }
614
+ if (value.isMember("fd") && value.isMember("stderr")) {
615
+ errors.push_back(Error(keyQuote
616
+ + " may contain either the 'fd' or the"
617
+ " 'stderr' key, but not both"));
618
+ }
619
+ } else if (value.isMember("stderr")) {
620
+ if (value.size() > 1) {
621
+ errors.push_back(Error("When " + keyQuote
622
+ + " is an object containing the 'stderr' key,"
623
+ " it may not contain any other keys"));
624
+ } else if (!value["stderr"].asBool()) {
625
+ errors.push_back(Error("When " + keyQuote
626
+ + " is an object containing the 'stderr' key,"
627
+ " it must have the 'true' value"));
628
+ }
608
629
  } else {
609
630
  errors.push_back(Error("When " + keyQuote
610
631
  + " is an object, it must contain either"
@@ -661,15 +682,20 @@ ConfigRealization::ConfigRealization(const ConfigKit::Store &store)
661
682
  } else if (store["target"]["fd"].isNull()) {
662
683
  string path = store["target"]["path"].asString();
663
684
  targetType = FILE_TARGET;
664
- targetFd = syscalls::open(path.c_str(),
665
- O_WRONLY | O_APPEND | O_CREAT, 0644);
666
- if (targetFd == -1) {
667
- int e = errno;
668
- throw FileSystemException(
669
- "Cannot open " + path + " for writing",
670
- e, path);
685
+ if (store["target"]["stderr"].asBool()) {
686
+ targetFd = STDERR_FILENO;
687
+ targetFdClosePolicy = NEVER_CLOSE;
688
+ } else {
689
+ targetFd = syscalls::open(path.c_str(),
690
+ O_WRONLY | O_APPEND | O_CREAT, 0644);
691
+ if (targetFd == -1) {
692
+ int e = errno;
693
+ throw FileSystemException(
694
+ "Cannot open " + path + " for writing",
695
+ e, path);
696
+ }
697
+ targetFdClosePolicy = ALWAYS_CLOSE;
671
698
  }
672
- targetFdClosePolicy = ALWAYS_CLOSE;
673
699
  } else {
674
700
  targetType = FILE_TARGET;
675
701
  targetFd = store["target"]["fd"].asInt();
@@ -690,15 +716,20 @@ ConfigRealization::ConfigRealization(const ConfigKit::Store &store)
690
716
  } else if (store["file_descriptor_log_target"]["fd"].isNull()) {
691
717
  string path = store["file_descriptor_log_target"]["path"].asString();
692
718
  fileDescriptorLogTargetType = FILE_TARGET;
693
- fileDescriptorLogTargetFd = syscalls::open(path.c_str(),
694
- O_WRONLY | O_APPEND | O_CREAT, 0644);
695
- if (fileDescriptorLogTargetFd == -1) {
696
- int e = errno;
697
- throw FileSystemException(
698
- "Cannot open " + path + " for writing",
699
- e, path);
719
+ if (store["file_descriptor_log_target"]["stderr"].asBool()) {
720
+ fileDescriptorLogTargetFd = STDERR_FILENO;
721
+ fileDescriptorLogTargetFdClosePolicy = NEVER_CLOSE;
722
+ } else {
723
+ fileDescriptorLogTargetFd = syscalls::open(path.c_str(),
724
+ O_WRONLY | O_APPEND | O_CREAT, 0644);
725
+ if (fileDescriptorLogTargetFd == -1) {
726
+ int e = errno;
727
+ throw FileSystemException(
728
+ "Cannot open " + path + " for writing",
729
+ e, path);
730
+ }
731
+ fileDescriptorLogTargetFdClosePolicy = ALWAYS_CLOSE;
700
732
  }
701
- fileDescriptorLogTargetFdClosePolicy = ALWAYS_CLOSE;
702
733
  } else {
703
734
  fileDescriptorLogTargetType = FILE_TARGET;
704
735
  fileDescriptorLogTargetFd = store["file_descriptor_log_target"]["fd"].asInt();
@@ -390,7 +390,6 @@ public:
390
390
  // except stdin, stdout, stderr and 3.
391
391
  close(fds[0]);
392
392
  installFeedbackFd(fds[1]);
393
- closeAllFileDescriptors(FEEDBACK_FD);
394
393
 
395
394
  setenv("PASSENGER_USE_FEEDBACK_FD", "true", 1);
396
395
 
@@ -398,6 +397,8 @@ public:
398
397
  afterFork();
399
398
  }
400
399
 
400
+ closeAllFileDescriptors(FEEDBACK_FD, true);
401
+
401
402
  execl(agentFilename.c_str(), AGENT_EXE, "watchdog",
402
403
  // Some extra space to allow the child process to change its process title.
403
404
  " ",
@@ -35,6 +35,7 @@
35
35
  #include <unistd.h>
36
36
  #include <stdio.h>
37
37
  #include <stdlib.h>
38
+ #include <fcntl.h>
38
39
  #include <time.h>
39
40
  #include <signal.h>
40
41
  #include <string.h>
@@ -124,16 +125,18 @@ save_master_process_pid(ngx_cycle_t *cycle) {
124
125
  return NGX_OK;
125
126
  }
126
127
 
128
+ typedef struct {
129
+ ngx_cycle_t *cycle;
130
+ int log_fd;
131
+ int stderr_equals_log_file;
132
+ } AfterForkData;
133
+
127
134
  /**
128
135
  * This function is called after forking and just before exec()ing the watchdog.
129
136
  */
130
137
  static void
131
- starting_watchdog_after_fork(void *paramCycle, void *paramParams) {
132
- ngx_cycle_t *cycle = (void *) paramCycle;
133
- PsgVariantMap *params = (void *) paramParams;
134
-
135
- char *log_filename;
136
- FILE *log_file;
138
+ starting_watchdog_after_fork(void *_data, void *_params) {
139
+ AfterForkData *data = (AfterForkData *) _data;
137
140
  ngx_core_conf_t *ccf;
138
141
  ngx_uint_t i;
139
142
  ngx_str_t *envs;
@@ -142,48 +145,74 @@ starting_watchdog_after_fork(void *paramCycle, void *paramParams) {
142
145
  /* At this point, stdout and stderr may still point to the console.
143
146
  * Make sure that they're both redirected to the log file.
144
147
  */
145
- log_file = NULL;
148
+ if (data->log_fd != -1) {
149
+ dup2(data->log_fd, 1);
150
+ dup2(data->log_fd, 2);
151
+ close(data->log_fd);
152
+ }
153
+
154
+ /* Set environment variables in Nginx config file. */
155
+ ccf = (ngx_core_conf_t *) ngx_get_conf(data->cycle->conf_ctx, ngx_core_module);
156
+ envs = ccf->env.elts;
157
+ for (i = 0; i < ccf->env.nelts; i++) {
158
+ env = (const char *) envs[i].data;
159
+ if (strchr(env, '=') != NULL) {
160
+ putenv(strdup(env));
161
+ }
162
+ }
163
+ }
164
+
165
+ /**
166
+ * This function provides a file descriptor that will be used
167
+ * to redirect stderr to after the upcoming fork. This prevents
168
+ * EIO errors on Linux if the user disconnects from the console
169
+ * on which Nginx is started.
170
+ *
171
+ * The fd will point to the log file, or to /dev/null if that
172
+ * fails (or -1 if that fails too).
173
+ */
174
+ static void
175
+ open_log_file_for_after_forking(AfterForkData *data, PsgVariantMap *params) {
176
+ char *log_filename;
177
+ int fd;
178
+
146
179
  log_filename = psg_variant_map_get_optional(params, "log_file");
147
180
  if (log_filename == NULL) {
148
- ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
149
- "no passenger log file configured, discarding log output");
181
+ ngx_log_error(NGX_LOG_ALERT, data->cycle->log, 0,
182
+ "no " PROGRAM_NAME " log file configured, discarding log output");
183
+ fd = -1;
150
184
  } else {
151
- log_file = fopen(log_filename, "a");
152
- if (log_file == NULL) {
153
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
154
- "could not open the passenger log file for writing during Nginx startup, some log lines might be lost (will retry from Passenger core)");
185
+ fd = open(log_filename, O_WRONLY | O_APPEND | O_CREAT, 0644);
186
+ if (fd == -1) {
187
+ ngx_log_error(NGX_LOG_ALERT, data->cycle->log, ngx_errno,
188
+ "could not open the " PROGRAM_NAME " log file for writing during Nginx startup,"
189
+ " some log lines might be lost (will retry from " SHORT_PROGRAM_NAME " core)");
155
190
  }
156
191
  free(log_filename);
157
192
  log_filename = NULL;
158
193
  }
159
194
 
160
- if (log_file == NULL) {
161
- /* If the log file cannot be opened then we redirect stdout
162
- * and stderr to /dev/null, because if the user disconnects
163
- * from the console on which Nginx is started, then on Linux
164
- * any writes to stdout or stderr will result in an EIO error.
165
- */
166
- log_file = fopen("/dev/null", "w");
167
- if (log_file == NULL) {
168
- ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
169
- "could not open /dev/null for logs, this will probably cause EIO errors");
195
+ if (fd == -1) {
196
+ fd = open("/dev/null", O_WRONLY | O_APPEND);
197
+ if (fd == -1) {
198
+ ngx_log_error(NGX_LOG_ALERT, data->cycle->log, ngx_errno,
199
+ "could not open /dev/null for logs, this will probably cause EIO errors");
170
200
  }
171
- }
172
- if (log_file != NULL) {
173
- dup2(fileno(log_file), 1);
174
- dup2(fileno(log_file), 2);
175
- fclose(log_file);
201
+ /**
202
+ * The log file open failed, so the after fork isn't going to be able to redirect
203
+ * stderr to it.
204
+ */
205
+ data->stderr_equals_log_file = 0;
206
+ } else {
207
+ /**
208
+ * Technically not true until after the fork when starting_watchdog_after_fork does
209
+ * the redirection (dup2), but that never seems to fail and we need to know here
210
+ * already.
211
+ */
212
+ data->stderr_equals_log_file = 1;
176
213
  }
177
214
 
178
- /* Set environment variables in Nginx config file. */
179
- ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
180
- envs = ccf->env.elts;
181
- for (i = 0; i < ccf->env.nelts; i++) {
182
- env = (const char *) envs[i].data;
183
- if (strchr(env, '=') != NULL) {
184
- putenv(strdup(env));
185
- }
186
- }
215
+ data->log_fd = fd;
187
216
  }
188
217
 
189
218
  static ngx_int_t
@@ -228,6 +257,7 @@ start_watchdog(ngx_cycle_t *cycle) {
228
257
  ngx_uint_t i;
229
258
  ngx_str_t *prestart_uris;
230
259
  char **prestart_uris_ary = NULL;
260
+ AfterForkData after_fork_data;
231
261
  ngx_keyval_t *ctl = NULL;
232
262
  PsgVariantMap *params = NULL;
233
263
  u_char filename[NGX_MAX_PATH], *last;
@@ -237,6 +267,8 @@ start_watchdog(ngx_cycle_t *cycle) {
237
267
  core_conf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
238
268
  result = NGX_OK;
239
269
  params = psg_variant_map_new();
270
+ after_fork_data.cycle = cycle;
271
+ after_fork_data.log_fd = -1;
240
272
  passenger_root = ngx_str_null_terminate(&passenger_main_conf.root_dir);
241
273
  if (passenger_root == NULL) {
242
274
  goto error_enomem;
@@ -304,6 +336,10 @@ start_watchdog(ngx_cycle_t *cycle) {
304
336
  psg_variant_map_set_ngx_str(params, "log_file", &cycle->log->file->name);
305
337
  }
306
338
 
339
+ open_log_file_for_after_forking(&after_fork_data, params);
340
+ psg_variant_map_set_bool(params, "log_file_is_stderr",
341
+ after_fork_data.stderr_equals_log_file);
342
+
307
343
  ctl = (ngx_keyval_t *) passenger_main_conf.ctl->elts;
308
344
  for (i = 0; i < passenger_main_conf.ctl->nelts; i++) {
309
345
  psg_variant_map_set2(params,
@@ -315,7 +351,7 @@ start_watchdog(ngx_cycle_t *cycle) {
315
351
  passenger_root,
316
352
  params,
317
353
  starting_watchdog_after_fork,
318
- cycle,
354
+ &after_fork_data,
319
355
  &error_message);
320
356
  if (!ret) {
321
357
  ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "%s", error_message);
@@ -354,6 +390,10 @@ cleanup:
354
390
  free(prestart_uris_ary);
355
391
  }
356
392
 
393
+ if (after_fork_data.log_fd != -1) {
394
+ close(after_fork_data.log_fd);
395
+ }
396
+
357
397
  if (result == NGX_ERROR && passenger_main_conf.abort_on_startup_error) {
358
398
  exit(1);
359
399
  }
@@ -31,18 +31,18 @@ module PhusionPassenger
31
31
 
32
32
  PACKAGE_NAME = 'passenger'
33
33
  # Run 'rake src/cxx_supportlib/Constants.h' after changing this number.
34
- VERSION_STRING = '5.1.11'
34
+ VERSION_STRING = '5.1.12'
35
35
 
36
- PREFERRED_NGINX_VERSION = '1.12.1'
37
- NGINX_SHA256_CHECKSUM = '8793bf426485a30f91021b6b945a9fd8a84d87d17b566562c3797aba8fac76fb'
36
+ PREFERRED_NGINX_VERSION = '1.12.2'
37
+ NGINX_SHA256_CHECKSUM = '305f379da1d5fb5aefa79e45c829852ca6983c7cd2a79328f8e084a324cf0416'
38
38
 
39
39
  # Packaging may be locked to an older version due to the specific module configuration being
40
40
  # incompatible with the version we prefer (latest stable).
41
- PACKAGING_PREFERRED_NGINX_VERSION = '1.12.1'
42
- PACKAGING_NGINX_SHA256_CHECKSUM = '8793bf426485a30f91021b6b945a9fd8a84d87d17b566562c3797aba8fac76fb'
41
+ PACKAGING_PREFERRED_NGINX_VERSION = '1.12.2'
42
+ PACKAGING_NGINX_SHA256_CHECKSUM = '305f379da1d5fb5aefa79e45c829852ca6983c7cd2a79328f8e084a324cf0416'
43
43
 
44
- PREFERRED_PCRE_VERSION = '8.39'
45
- PCRE_SHA256_CHECKSUM = 'ccdf7e788769838f8285b3ee672ed573358202305ee361cfec7a4a4fb005bbc7'
44
+ PREFERRED_PCRE_VERSION = '8.41'
45
+ PCRE_SHA256_CHECKSUM = '244838e1f1d14f7e2fa7681b857b3a8566b74215f28133f14a8f5e59241b682c'
46
46
 
47
47
  STANDALONE_INTERFACE_VERSION = 1
48
48
 
@@ -173,7 +173,7 @@ module PhusionPassenger
173
173
 
174
174
  # Whether the current Phusion Passenger installation is installed
175
175
  # from a release package, e.g. an official gem or official tarball.
176
- # Retruns false if e.g. the gem was built by the user, or if this
176
+ # Returns false if e.g. the gem was built by the user, or if this
177
177
  # install is from a git repository.
178
178
  def self.installed_from_release_package?
179
179
  File.exist?("#{resources_dir}/release.txt")
@@ -100,7 +100,7 @@ module PhusionPassenger
100
100
  if cache_file && #{cache_to_disk} # if cache_file && #{cache_to_disk}
101
101
  begin # begin
102
102
  if !File.directory?(@@cache_dir) # if !File.directory?(@@cache_dir)
103
- Dir.mkdir(@@cache_dir) # Dir.mkdir(@@cache_dir)
103
+ FileUtils.mkdir_p(@@cache_dir) # FileUtils.mkdir_p(@@cache_dir)
104
104
  end # end
105
105
  File.open(cache_file, "wb") do |f| # File.open(cache_file, "wb") do |f|
106
106
  f.write(Marshal.dump(#{variable_name})) # f.write(Marshal.dump(@@memoized_httpd))
@@ -174,7 +174,7 @@ module PhusionPassenger
174
174
  # We want the command line options to override the options in the local
175
175
  # config file, but the local config file could only be parsed when the
176
176
  # command line options have been parsed. This method remerges all the
177
- # config options from different sources so that options are overriden
177
+ # config options from different sources so that options are overridden
178
178
  # according to the following order:
179
179
  #
180
180
  # - CONFIG_DEFAULTS
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passenger
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.11
4
+ version: 5.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Phusion - http://www.phusion.nl/
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-16 00:00:00.000000000 Z
11
+ date: 2017-11-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -114,6 +114,8 @@ files:
114
114
  - dev/ci/tests/apache2/setup
115
115
  - dev/ci/tests/cxx/run
116
116
  - dev/ci/tests/cxx/setup
117
+ - dev/ci/tests/debian/Jenkinsfile
118
+ - dev/ci/tests/debian/run
117
119
  - dev/ci/tests/nginx-dynamic/run
118
120
  - dev/ci/tests/nginx-dynamic/setup
119
121
  - dev/ci/tests/nginx/run