passenger 5.1.7 → 5.1.8

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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +13 -2
  3. data/CONTRIBUTING.md +1 -1
  4. data/build/agent.rb +1 -1
  5. data/build/cxx_tests.rb +6 -0
  6. data/build/support/cxx_dependency_map.rb +1286 -391
  7. data/build/support/general.rb +0 -26
  8. data/resources/templates/standalone/rails_asset_pipeline.erb +2 -2
  9. data/src/agent/Core/ApiServer.h +49 -44
  10. data/src/agent/Core/ApplicationPool/Pool.h +1 -1
  11. data/src/agent/Core/ApplicationPool/Process.h +1 -1
  12. data/src/agent/Core/ApplicationPool/Socket.h +1 -1
  13. data/src/agent/Core/Controller.h +16 -8
  14. data/src/agent/Core/Controller/CheckoutSession.cpp +1 -1
  15. data/src/agent/Core/Controller/Config.cpp +68 -0
  16. data/src/agent/Core/Controller/Config.h +70 -34
  17. data/src/agent/Core/Controller/ForwardResponse.cpp +5 -5
  18. data/src/agent/Core/Controller/Hooks.cpp +5 -14
  19. data/src/agent/Core/Controller/Implementation.cpp +1 -1
  20. data/src/agent/Core/Controller/InitRequest.cpp +31 -29
  21. data/src/agent/Core/Controller/InitializationAndShutdown.cpp +4 -4
  22. data/src/agent/Core/Controller/InternalUtils.cpp +3 -3
  23. data/src/agent/Core/Controller/Miscellaneous.cpp +1 -1
  24. data/src/agent/Core/Controller/Request.h +2 -2
  25. data/src/agent/Core/Controller/SendRequest.cpp +5 -5
  26. data/src/agent/Core/Controller/StateInspection.cpp +1 -1
  27. data/src/agent/Core/Controller/TurboCaching.h +2 -2
  28. data/src/agent/Core/CoreMain.cpp +2 -2
  29. data/src/agent/Core/ResponseCache.h +3 -3
  30. data/src/agent/Core/SpawningKit/BackgroundIOCapturer.h +3 -3
  31. data/src/agent/Core/SpawningKit/DirectSpawner.h +2 -2
  32. data/src/agent/Core/SpawningKit/PipeWatcher.h +3 -3
  33. data/src/agent/Core/SpawningKit/SmartSpawner.h +2 -2
  34. data/src/agent/Core/SpawningKit/Spawner.h +1 -1
  35. data/src/agent/Core/UnionStation/Connection.h +1 -1
  36. data/src/agent/Core/UnionStation/Context.h +1 -1
  37. data/src/agent/Core/UnionStation/Transaction.h +1 -1
  38. data/src/agent/Shared/ApiServerUtils.h +73 -27
  39. data/src/agent/Shared/Base.cpp +61 -73
  40. data/src/agent/UstRouter/ApiServer.h +34 -45
  41. data/src/agent/UstRouter/Controller.h +86 -60
  42. data/src/agent/UstRouter/RemoteSender.h +1 -1
  43. data/src/agent/UstRouter/RemoteSink.h +1 -1
  44. data/src/agent/Watchdog/ApiServer.h +42 -50
  45. data/src/agent/Watchdog/WatchdogMain.cpp +1 -1
  46. data/src/apache2_module/Configuration.hpp +1 -1
  47. data/src/apache2_module/Hooks.cpp +27 -13
  48. data/src/cxx_supportlib/AppTypes.h +1 -1
  49. data/src/cxx_supportlib/BackgroundEventLoop.cpp +1 -1
  50. data/src/cxx_supportlib/ConfigKit/AsyncUtils.h +86 -0
  51. data/src/cxx_supportlib/ConfigKit/Common.h +6 -3
  52. data/src/cxx_supportlib/ConfigKit/IN_PRACTICE.md +1039 -0
  53. data/src/cxx_supportlib/ConfigKit/README.md +112 -497
  54. data/src/cxx_supportlib/ConfigKit/Schema.h +78 -15
  55. data/src/cxx_supportlib/ConfigKit/Store.h +272 -53
  56. data/src/cxx_supportlib/ConfigKit/SubComponentUtils.h +59 -0
  57. data/src/cxx_supportlib/ConfigKit/Utils.h +26 -65
  58. data/src/cxx_supportlib/ConfigKit/ValidationUtils.h +69 -0
  59. data/src/cxx_supportlib/ConfigKit/VariantMapUtils.h +7 -4
  60. data/src/cxx_supportlib/Constants.h +4 -1
  61. data/src/cxx_supportlib/Crypto.cpp +1 -1
  62. data/src/cxx_supportlib/DataStructures/StringKeyTable.h +26 -7
  63. data/src/cxx_supportlib/FileDescriptor.h +1 -1
  64. data/src/cxx_supportlib/Hooks.h +1 -1
  65. data/src/cxx_supportlib/LoggingKit/Assert.h +130 -0
  66. data/src/cxx_supportlib/LoggingKit/Config.h +97 -0
  67. data/src/cxx_supportlib/LoggingKit/Context.h +94 -0
  68. data/src/cxx_supportlib/LoggingKit/Forward.h +95 -0
  69. data/src/cxx_supportlib/LoggingKit/Implementation.cpp +695 -0
  70. data/src/cxx_supportlib/LoggingKit/Logging.h +204 -0
  71. data/src/cxx_supportlib/LoggingKit/LoggingKit.h +33 -0
  72. data/src/cxx_supportlib/LveLoggingDecorator.h +1 -1
  73. data/src/cxx_supportlib/MemoryKit/mbuf.cpp +1 -1
  74. data/src/cxx_supportlib/RandomGenerator.h +1 -1
  75. data/src/cxx_supportlib/SafeLibev.h +1 -1
  76. data/src/cxx_supportlib/ServerKit/AcceptLoadBalancer.h +1 -1
  77. data/src/cxx_supportlib/ServerKit/Channel.h +1 -1
  78. data/src/cxx_supportlib/ServerKit/FileBufferedChannel.h +1 -1
  79. data/src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h +1 -1
  80. data/src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h +1 -1
  81. data/src/cxx_supportlib/ServerKit/HttpHeaderParser.h +1 -1
  82. data/src/cxx_supportlib/ServerKit/HttpServer.h +48 -15
  83. data/src/cxx_supportlib/ServerKit/Server.h +79 -52
  84. data/src/cxx_supportlib/StaticString.h +12 -0
  85. data/src/cxx_supportlib/Utils/Curl.h +16 -0
  86. data/src/cxx_supportlib/Utils/FastStringStream.h +6 -1
  87. data/src/cxx_supportlib/Utils/ScopeGuard.h +1 -1
  88. data/src/cxx_supportlib/Utils/StrIntUtils.cpp +2 -19
  89. data/src/cxx_supportlib/WatchdogLauncher.h +3 -2
  90. data/src/ruby_supportlib/phusion_passenger.rb +3 -3
  91. data/src/ruby_supportlib/phusion_passenger/common_library.rb +12 -12
  92. data/src/ruby_supportlib/phusion_passenger/constants.rb +6 -3
  93. data/src/ruby_supportlib/phusion_passenger/standalone/start_command.rb +1 -0
  94. data/src/ruby_supportlib/phusion_passenger/standalone/stop_command.rb +1 -0
  95. metadata +14 -4
  96. data/src/cxx_supportlib/Logging.cpp +0 -295
  97. data/src/cxx_supportlib/Logging.h +0 -385
@@ -0,0 +1,97 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2017 Phusion Holding B.V.
4
+ *
5
+ * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
+ * trademarks of Phusion Holding B.V.
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ * THE SOFTWARE.
25
+ */
26
+ #ifndef _PASSENGER_LOGGING_KIT_CONFIG_H_
27
+ #define _PASSENGER_LOGGING_KIT_CONFIG_H_
28
+
29
+ #include <boost/config.hpp>
30
+ #include <boost/scoped_ptr.hpp>
31
+ #include <boost/noncopyable.hpp>
32
+
33
+ #include <string>
34
+ #include <vector>
35
+
36
+ #include <LoggingKit/Forward.h>
37
+ #include <ConfigKit/Schema.h>
38
+
39
+ #include <jsoncpp/json.h>
40
+
41
+ namespace Passenger {
42
+ namespace LoggingKit {
43
+
44
+ using namespace std;
45
+
46
+
47
+ class Schema: public ConfigKit::Schema {
48
+ private:
49
+ static Json::Value createStderrTarget();
50
+ static void validateLogLevel(const string &key, const ConfigKit::Store &store,
51
+ vector<ConfigKit::Error> &errors);
52
+ static void validateTarget(const string &key, const ConfigKit::Store &store,
53
+ vector<ConfigKit::Error> &errors);
54
+
55
+ public:
56
+ Schema();
57
+ };
58
+
59
+ struct ConfigRealization {
60
+ enum FdClosePolicy {
61
+ NEVER_CLOSE,
62
+ ALWAYS_CLOSE,
63
+ CLOSE_WHEN_FINALIZED
64
+ };
65
+
66
+ Level level;
67
+ Level appOutputLogLevel;
68
+
69
+ TargetType targetType;
70
+ TargetType fileDescriptorLogTargetType;
71
+ int targetFd;
72
+ int fileDescriptorLogTargetFd;
73
+ FdClosePolicy targetFdClosePolicy;
74
+ FdClosePolicy fileDescriptorLogTargetFdClosePolicy;
75
+ bool finalized;
76
+
77
+ ConfigRealization(const ConfigKit::Store &store);
78
+ ~ConfigRealization();
79
+
80
+ void apply(const ConfigKit::Store &config, ConfigRealization *oldConfigRlz)
81
+ BOOST_NOEXCEPT_OR_NOTHROW;
82
+ void finalize();
83
+ };
84
+
85
+ struct ConfigChangeRequest: public boost::noncopyable {
86
+ boost::scoped_ptr<ConfigKit::Store> config;
87
+ ConfigRealization *configRlz;
88
+
89
+ ConfigChangeRequest();
90
+ ~ConfigChangeRequest();
91
+ };
92
+
93
+
94
+ } // namespace LoggingKit
95
+ } // namespace Passenger
96
+
97
+ #endif /* _PASSENGER_LOGGING_KIT_CONFIG_H_ */
@@ -0,0 +1,94 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2017 Phusion Holding B.V.
4
+ *
5
+ * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
+ * trademarks of Phusion Holding B.V.
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ * THE SOFTWARE.
25
+ */
26
+ #ifndef _PASSENGER_LOGGING_KIT_CONTEXT_H_
27
+ #define _PASSENGER_LOGGING_KIT_CONTEXT_H_
28
+
29
+ #include <oxt/macros.hpp>
30
+ #include <boost/thread.hpp>
31
+ #include <boost/atomic.hpp>
32
+ #include <ConfigKit/ConfigKit.h>
33
+ #include <LoggingKit/Forward.h>
34
+ #include <LoggingKit/Config.h>
35
+
36
+ namespace Passenger {
37
+ namespace LoggingKit {
38
+
39
+ using namespace std;
40
+ using namespace oxt;
41
+
42
+
43
+ /**
44
+ * Note about file descriptor handling:
45
+ * the "target" and "file_descriptor_log_target" config options
46
+ * accept an "fd" suboption to force LoggingKit to use a specific
47
+ * file descriptor. LoggingKit will take over ownership of this fd,
48
+ * but only in the following circumstances:
49
+ *
50
+ * - If you pass this "fd" option to the Context constructor, then
51
+ * LoggingKit takes ownership only when the constructor
52
+ * succeeds.
53
+ * - If you pass this "fd" option to `prepareConfigChange()`, then
54
+ * LoggingKit takes ownership only when `commitConfigChange()`
55
+ * returns.
56
+ *
57
+ * If anything goes wrong in the constructor, or if
58
+ * `commitConfigChange()` is never called, then the caller is
59
+ * responsible for cleaning up the fd.
60
+ */
61
+ class Context {
62
+ public:
63
+ typedef LoggingKit::ConfigChangeRequest ConfigChangeRequest;
64
+
65
+ private:
66
+ Schema schema;
67
+ mutable boost::mutex syncher;
68
+ ConfigKit::Store config;
69
+ boost::atomic<ConfigRealization *> configRlz;
70
+
71
+ public:
72
+ Context(const Json::Value &initialConfig = Json::Value());
73
+ ConfigKit::Store getConfig() const;
74
+
75
+ bool prepareConfigChange(const Json::Value &updates,
76
+ vector<ConfigKit::Error> &errors,
77
+ LoggingKit::ConfigChangeRequest &req);
78
+ void commitConfigChange(LoggingKit::ConfigChangeRequest &req)
79
+ BOOST_NOEXCEPT_OR_NOTHROW;
80
+
81
+ OXT_FORCE_INLINE
82
+ const ConfigRealization *getConfigRealization() const {
83
+ return configRlz.load(boost::memory_order_acquire);
84
+ }
85
+ };
86
+
87
+
88
+ void initialize(const Json::Value &initialConfig);
89
+
90
+
91
+ } // namespace LoggingKit
92
+ } // namespace Passenger
93
+
94
+ #endif /* _PASSENGER_LOGGING_KIT_CONTEXT_H_ */
@@ -0,0 +1,95 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2017 Phusion Holding B.V.
4
+ *
5
+ * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
+ * trademarks of Phusion Holding B.V.
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ * THE SOFTWARE.
25
+ */
26
+ #ifndef _PASSENGER_LOGGING_KIT_FORWARD_H_
27
+ #define _PASSENGER_LOGGING_KIT_FORWARD_H_
28
+
29
+ #include <StaticString.h>
30
+ #include <jsoncpp/json.h>
31
+
32
+ namespace Passenger {
33
+ #ifndef _PASSENGER_FAST_STRING_STREAM_FORWARD_DECLARED_
34
+ #define _PASSENGER_FAST_STRING_STREAM_FORWARD_DECLARED_
35
+ template<size_t staticCapacity = 1024>
36
+ class FastStringStream;
37
+ #endif
38
+
39
+ namespace ConfigKit {
40
+ class Schema;
41
+ class Store;
42
+ class Error;
43
+ }
44
+ }
45
+
46
+ namespace Passenger {
47
+ namespace LoggingKit {
48
+
49
+
50
+ class Schema;
51
+ struct ConfigRealization;
52
+ class Context;
53
+
54
+ enum Level {
55
+ CRIT = 0,
56
+ ERROR = 1,
57
+ WARN = 2,
58
+ NOTICE = 3,
59
+ INFO = 4,
60
+ DEBUG = 5,
61
+ DEBUG2 = 6,
62
+ DEBUG3 = 7,
63
+
64
+ UNKNOWN_LEVEL = 99
65
+ };
66
+
67
+ enum TargetType {
68
+ STDERR_TARGET,
69
+ FILE_TARGET,
70
+ NO_TARGET,
71
+ UNKNOWN_TARGET
72
+ };
73
+
74
+ extern Context *context;
75
+
76
+
77
+ void initialize(const Json::Value &initialConfig = Json::Value());
78
+
79
+ const char *_strdupFastStringStream(const FastStringStream<> &stream);
80
+ bool _passesLogLevel(const Context *context, Level level, const ConfigRealization **outputConfigRlz);
81
+ bool _shouldLogFileDescriptors(const Context *context, const ConfigRealization **outputConfigRlz);
82
+ void _prepareLogEntry(FastStringStream<> &sstream, Level level, const char *file, unsigned int line);
83
+ void _writeLogEntry(const ConfigRealization *configRlz, const char *str, unsigned int size);
84
+ void _writeFileDescriptorLogEntry(const ConfigRealization *configRlz, const char *str, unsigned int size);
85
+
86
+ Level getLevel();
87
+ void setLevel(Level level);
88
+ Level parseLevel(const StaticString &name);
89
+ StaticString levelToString(Level level);
90
+
91
+
92
+ } // namespace LoggingKit
93
+ } // namespace Passenger
94
+
95
+ #endif /* _PASSENGER_LOGGING_KIT_FORWARD_H_ */
@@ -0,0 +1,695 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2010-2017 Phusion Holding B.V.
4
+ *
5
+ * "Passenger", "Phusion Passenger" and "Union Station" are registered
6
+ * trademarks of Phusion Holding B.V.
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ * THE SOFTWARE.
25
+ */
26
+
27
+ #include <ios>
28
+ #include <algorithm>
29
+ #include <stdexcept>
30
+ #include <cstdio>
31
+ #include <cstddef>
32
+ #include <cstring>
33
+ #include <cerrno>
34
+ #include <cassert>
35
+ #include <sys/time.h>
36
+ #include <fcntl.h>
37
+ #include <unistd.h>
38
+ #include <time.h>
39
+ #include <pthread.h>
40
+
41
+ #include <boost/cstdint.hpp>
42
+ #include <oxt/system_calls.hpp>
43
+ #include <oxt/thread.hpp>
44
+ #include <oxt/detail/context.hpp>
45
+
46
+ #include <Constants.h>
47
+ #include <StaticString.h>
48
+ #include <Exceptions.h>
49
+ #include <LoggingKit/Logging.h>
50
+ #include <LoggingKit/Assert.h>
51
+ #include <LoggingKit/Config.h>
52
+ #include <LoggingKit/Context.h>
53
+ #include <ConfigKit/ConfigKit.h>
54
+ #include <Utils.h>
55
+ #include <Utils/StrIntUtils.h>
56
+
57
+ namespace Passenger {
58
+ namespace LoggingKit {
59
+
60
+ using namespace std;
61
+
62
+
63
+ #define TRUNCATE_LOGPATHS_TO_MAXCHARS 3 // set to 0 to disable truncation
64
+
65
+ Context *context = NULL;
66
+ AssertionFailureInfo lastAssertionFailure;
67
+
68
+
69
+ void
70
+ initialize(const Json::Value &initialConfig) {
71
+ context = new Context(initialConfig);
72
+ }
73
+
74
+
75
+ Level getLevel() {
76
+ if (OXT_LIKELY(context != NULL)) {
77
+ return context->getConfigRealization()->level;
78
+ } else {
79
+ return Level(DEFAULT_LOG_LEVEL);
80
+ }
81
+ }
82
+
83
+ void
84
+ setLevel(Level level) {
85
+ Json::Value config;
86
+ vector<ConfigKit::Error> errors;
87
+ ConfigChangeRequest req;
88
+
89
+ config["level"] = levelToString(level).toString();
90
+ if (context->prepareConfigChange(config, errors, req)) {
91
+ context->commitConfigChange(req);
92
+ } else {
93
+ P_BUG("Error setting log level: " << ConfigKit::toString(errors));
94
+ }
95
+ }
96
+
97
+ Level
98
+ parseLevel(const StaticString &name) {
99
+ if (name == "crit" || name == "0") {
100
+ return CRIT;
101
+ } else if (name == "error" || name == "1") {
102
+ return ERROR;
103
+ } else if (name == "warn" || name == "2") {
104
+ return WARN;
105
+ } else if (name == "notice" || name == "3") {
106
+ return NOTICE;
107
+ } else if (name == "info" || name == "4") {
108
+ return INFO;
109
+ } else if (name == "debug" || name == "5") {
110
+ return DEBUG;
111
+ } else if (name == "debug2" || name == "6") {
112
+ return DEBUG2;
113
+ } else if (name == "debug3" || name == "7") {
114
+ return DEBUG3;
115
+ } else {
116
+ return UNKNOWN_LEVEL;
117
+ }
118
+ }
119
+
120
+ StaticString
121
+ levelToString(Level level) {
122
+ switch (level) {
123
+ case CRIT:
124
+ return P_STATIC_STRING("crit");
125
+ case ERROR:
126
+ return P_STATIC_STRING("error");
127
+ case WARN:
128
+ return P_STATIC_STRING("warn");
129
+ case NOTICE:
130
+ return P_STATIC_STRING("notice");
131
+ case INFO:
132
+ return P_STATIC_STRING("info");
133
+ case DEBUG:
134
+ return P_STATIC_STRING("debug");
135
+ case DEBUG2:
136
+ return P_STATIC_STRING("debug2");
137
+ case DEBUG3:
138
+ return P_STATIC_STRING("debug3");
139
+ default:
140
+ return P_STATIC_STRING("unknown");
141
+ }
142
+ }
143
+
144
+
145
+ const char *
146
+ _strdupFastStringStream(const FastStringStream<> &stream) {
147
+ char *buf = (char *) malloc(stream.size() + 1);
148
+ memcpy(buf, stream.data(), stream.size());
149
+ buf[stream.size()] = '\0';
150
+ return buf;
151
+ }
152
+
153
+ bool
154
+ _passesLogLevel(const Context *context, Level level, const ConfigRealization **outputConfigRlz) {
155
+ if (OXT_UNLIKELY(context == NULL)) {
156
+ *outputConfigRlz = NULL;
157
+ return Level(DEFAULT_LOG_LEVEL) >= level;
158
+ } else {
159
+ const ConfigRealization *configRlz = context->getConfigRealization();
160
+ *outputConfigRlz = configRlz;
161
+ return configRlz->level >= level;
162
+ }
163
+ }
164
+
165
+ bool
166
+ _shouldLogFileDescriptors(const Context *context, const ConfigRealization **outputConfigRlz) {
167
+ if (OXT_UNLIKELY(context == NULL)) {
168
+ return false;
169
+ } else {
170
+ const ConfigRealization *configRlz = context->getConfigRealization();
171
+ *outputConfigRlz = configRlz;
172
+ return configRlz->fileDescriptorLogTargetType != NO_TARGET;
173
+ }
174
+ }
175
+
176
+ void
177
+ _prepareLogEntry(FastStringStream<> &sstream, Level level, const char *file, unsigned int line) {
178
+ struct tm the_tm;
179
+ char datetime_buf[32];
180
+ char threadIdBuf[std::max<unsigned int>(
181
+ std::max<unsigned int>(
182
+ 2 * sizeof(boost::uintptr_t) + 1,
183
+ 2 * sizeof(unsigned int) + 1
184
+ ),
185
+ 32
186
+ )];
187
+ int datetime_size;
188
+ unsigned int threadIdSize;
189
+ struct timeval tv;
190
+ StaticString logLevelMarkers[] = {
191
+ P_STATIC_STRING("C"),
192
+ P_STATIC_STRING("E"),
193
+ P_STATIC_STRING("W"),
194
+ P_STATIC_STRING("N"),
195
+ P_STATIC_STRING("I"),
196
+ P_STATIC_STRING("D"),
197
+ P_STATIC_STRING("D2"),
198
+ P_STATIC_STRING("D3")
199
+ };
200
+
201
+ gettimeofday(&tv, NULL);
202
+ localtime_r(&tv.tv_sec, &the_tm);
203
+ datetime_size = snprintf(datetime_buf, sizeof(datetime_buf),
204
+ "%d-%02d-%02d %02d:%02d:%02d.%04llu",
205
+ the_tm.tm_year + 1900, the_tm.tm_mon + 1, the_tm.tm_mday,
206
+ the_tm.tm_hour, the_tm.tm_min, the_tm.tm_sec,
207
+ (unsigned long long) tv.tv_usec / 100);
208
+
209
+ #ifdef OXT_THREAD_LOCAL_KEYWORD_SUPPORTED
210
+ // We only use oxt::get_thread_local_context() if it is fast enough.
211
+ oxt::thread_local_context *ctx = oxt::get_thread_local_context();
212
+ if (OXT_LIKELY(ctx != NULL)) {
213
+ threadIdSize = integerToHexatri(ctx->thread_number,
214
+ threadIdBuf);
215
+ } else {
216
+ threadIdSize = integerToHexatri((boost::uintptr_t) pthread_self(),
217
+ threadIdBuf);
218
+ }
219
+ #else
220
+ threadIdSize = integerToHexatri((boost::uintptr_t) pthread_self(),
221
+ threadIdBuf);
222
+ #endif
223
+
224
+ sstream <<
225
+ P_STATIC_STRING("[ ") <<
226
+ logLevelMarkers[int(level)] <<
227
+ P_STATIC_STRING(" ") <<
228
+ StaticString(datetime_buf, datetime_size) <<
229
+ P_STATIC_STRING(" ") <<
230
+ std::dec << getpid() <<
231
+ P_STATIC_STRING("/T") <<
232
+ StaticString(threadIdBuf, threadIdSize) <<
233
+ P_STATIC_STRING(" ");
234
+
235
+ if (startsWith(file, P_STATIC_STRING("src/"))) { // special reduncancy filter because most code resides in these paths
236
+ file += sizeof("src/") - 1;
237
+ if (startsWith(file, P_STATIC_STRING("cxx_supportlib/"))) {
238
+ file += sizeof("cxx_supportlib/") - 1;
239
+ }
240
+ }
241
+
242
+ if (TRUNCATE_LOGPATHS_TO_MAXCHARS > 0) {
243
+ truncateBeforeTokens(file, P_STATIC_STRING("/\\"), TRUNCATE_LOGPATHS_TO_MAXCHARS, sstream);
244
+ } else {
245
+ sstream << file;
246
+ }
247
+
248
+ sstream << P_STATIC_STRING(":") <<
249
+ line << P_STATIC_STRING(" ]: ");
250
+ }
251
+
252
+ static void
253
+ writeExactWithoutOXT(int fd, const char *str, unsigned int size) {
254
+ /* We do not use writeExact() here because writeExact()
255
+ * uses oxt::syscalls::write(), which is an interruption point and
256
+ * which is slightly more expensive than a plain write().
257
+ * Logging may block, but in most cases not indefinitely,
258
+ * so we don't care if the write() here is not an interruption
259
+ * point. If the write does block indefinitely then it's
260
+ * probably a FIFO that is not opened on the other side.
261
+ * In that case we can blame the user.
262
+ */
263
+ ssize_t ret;
264
+ unsigned int written = 0;
265
+ while (written < size) {
266
+ do {
267
+ ret = write(fd, str + written, size - written);
268
+ } while (ret == -1 && errno == EINTR);
269
+ if (ret == -1) {
270
+ /* The most likely reason why this fails is when the user has setup
271
+ * Apache to log to a pipe (e.g. to a log rotation script). Upon
272
+ * restarting the web server, the process that reads from the pipe
273
+ * shuts down, so we can't write to it anymore. That's why we
274
+ * just ignore write errors. It doesn't make sense to abort for
275
+ * something like this.
276
+ */
277
+ break;
278
+ } else {
279
+ written += ret;
280
+ }
281
+ }
282
+ }
283
+
284
+ void
285
+ _writeLogEntry(const ConfigRealization *configRealization, const char *str, unsigned int size) {
286
+ if (OXT_LIKELY(configRealization != NULL)) {
287
+ writeExactWithoutOXT(configRealization->targetFd, str, size);
288
+ } else {
289
+ writeExactWithoutOXT(STDERR_FILENO, str, size);
290
+ }
291
+ }
292
+
293
+ void
294
+ _writeFileDescriptorLogEntry(const ConfigRealization *configRealization,
295
+ const char *str, unsigned int size)
296
+ {
297
+ assert(configRealization != NULL);
298
+ assert(configRealization->fileDescriptorLogTargetType != UNKNOWN_TARGET);
299
+ assert(configRealization->fileDescriptorLogTargetFd != -1);
300
+ writeExactWithoutOXT(configRealization->fileDescriptorLogTargetFd, str, size);
301
+ }
302
+
303
+ static void
304
+ realLogAppOutput(int targetFd, char *buf, unsigned int bufSize,
305
+ const char *pidStr, unsigned int pidStrLen,
306
+ const char *channelName, unsigned int channelNameLen,
307
+ const char *message, unsigned int messageLen)
308
+ {
309
+ char *pos = buf;
310
+ char *end = buf + bufSize;
311
+
312
+ pos = appendData(pos, end, "App ");
313
+ pos = appendData(pos, end, pidStr, pidStrLen);
314
+ pos = appendData(pos, end, " ");
315
+ pos = appendData(pos, end, channelName, channelNameLen);
316
+ pos = appendData(pos, end, ": ");
317
+ pos = appendData(pos, end, message, messageLen);
318
+ pos = appendData(pos, end, "\n");
319
+ writeExactWithoutOXT(targetFd, buf, pos - buf);
320
+ }
321
+
322
+ void
323
+ logAppOutput(pid_t pid, const char *channelName, const char *message, unsigned int size) {
324
+ int targetFd;
325
+
326
+ if (OXT_LIKELY(context != NULL)) {
327
+ const ConfigRealization *configRealization = context->getConfigRealization();
328
+ if (configRealization->level < configRealization->appOutputLogLevel) {
329
+ return;
330
+ }
331
+
332
+ targetFd = configRealization->targetFd;
333
+ } else {
334
+ targetFd = STDERR_FILENO;
335
+ }
336
+
337
+ char pidStr[sizeof("4294967295")];
338
+ unsigned int pidStrLen, channelNameLen, totalLen;
339
+
340
+ try {
341
+ pidStrLen = integerToOtherBase<pid_t, 10>(pid, pidStr, sizeof(pidStr));
342
+ } catch (const std::length_error &) {
343
+ pidStr[0] = '?';
344
+ pidStr[1] = '\0';
345
+ pidStrLen = 1;
346
+ }
347
+
348
+ channelNameLen = strlen(channelName);
349
+ totalLen = (sizeof("App X Y: \n") - 2) + pidStrLen + channelNameLen + size;
350
+ if (totalLen < 1024) {
351
+ char buf[1024];
352
+ realLogAppOutput(targetFd,
353
+ buf, sizeof(buf),
354
+ pidStr, pidStrLen,
355
+ channelName, channelNameLen,
356
+ message, size);
357
+ } else {
358
+ DynamicBuffer buf(totalLen);
359
+ realLogAppOutput(targetFd,
360
+ buf.data, totalLen,
361
+ pidStr, pidStrLen,
362
+ channelName, channelNameLen,
363
+ message, size);
364
+ }
365
+ }
366
+
367
+
368
+ static Json::Value
369
+ normalizeConfig(const Json::Value &effectiveValues) {
370
+ Json::Value updates(Json::objectValue);
371
+
372
+ updates["level"] = levelToString(parseLevel(
373
+ effectiveValues["level"].asString())).toString();
374
+ updates["app_output_log_level"] = levelToString(parseLevel(
375
+ effectiveValues["app_output_log_level"].asString())).toString();
376
+
377
+ if (effectiveValues["target"].isString()) {
378
+ updates["target"]["path"] = absolutizePath(effectiveValues["target"].asString());
379
+ } else if (!effectiveValues["target"]["path"].isNull()) {
380
+ updates["target"] = effectiveValues["target"];
381
+ updates["target"]["path"] = absolutizePath(effectiveValues["target"]["path"].asString());
382
+ }
383
+
384
+ if (effectiveValues["file_descriptor_log_target"].isString()) {
385
+ updates["file_descriptor_log_target"]["path"] =
386
+ absolutizePath(effectiveValues["file_descriptor_log_target"].asString());
387
+ } else if (effectiveValues["file_descriptor_log_target"].isObject()
388
+ && !effectiveValues["file_descriptor_log_target"]["path"].isNull())
389
+ {
390
+ updates["file_descriptor_log_target"] = effectiveValues["file_descriptor_log_target"];
391
+ updates["file_descriptor_log_target"]["path"] =
392
+ absolutizePath(effectiveValues["file_descriptor_log_target"]["path"].asString());
393
+ }
394
+
395
+ return updates;
396
+ }
397
+
398
+
399
+ Context::Context(const Json::Value &initialConfig)
400
+ : config(schema, initialConfig)
401
+ {
402
+ configRlz.store(new ConfigRealization(config));
403
+ configRlz.load()->apply(config, NULL);
404
+ configRlz.load()->finalize();
405
+ }
406
+
407
+ ConfigKit::Store
408
+ Context::getConfig() const {
409
+ boost::lock_guard<boost::mutex> l(syncher);
410
+ return config;
411
+ }
412
+
413
+ bool
414
+ Context::prepareConfigChange(const Json::Value &updates,
415
+ vector<ConfigKit::Error> &errors, LoggingKit::ConfigChangeRequest &req)
416
+ {
417
+ {
418
+ boost::lock_guard<boost::mutex> l(syncher);
419
+ req.config.reset(new ConfigKit::Store(config, updates, errors));
420
+ }
421
+ if (!errors.empty()) {
422
+ return false;
423
+ }
424
+
425
+ req.configRlz = new ConfigRealization(*req.config);
426
+ return true;
427
+ }
428
+
429
+ void
430
+ Context::commitConfigChange(LoggingKit::ConfigChangeRequest &req) BOOST_NOEXCEPT_OR_NOTHROW {
431
+ boost::lock_guard<boost::mutex> l(syncher);
432
+ ConfigRealization *oldConfigRlz = configRlz.load();
433
+ ConfigRealization *newConfigRlz = req.configRlz;
434
+
435
+ req.configRlz->apply(*req.config, oldConfigRlz);
436
+
437
+ config.swap(*req.config);
438
+
439
+ configRlz.store(newConfigRlz, boost::memory_order_release);
440
+ req.configRlz = NULL; // oldConfigRlz will be garbage collected by apply()
441
+
442
+ newConfigRlz->finalize();
443
+ }
444
+
445
+
446
+ Json::Value
447
+ Schema::createStderrTarget() {
448
+ Json::Value doc;
449
+ doc["stderr"] = true;
450
+ return doc;
451
+ }
452
+
453
+ void
454
+ Schema::validateLogLevel(const string &key, const ConfigKit::Store &store,
455
+ vector<ConfigKit::Error> &errors)
456
+ {
457
+ typedef ConfigKit::Error Error;
458
+ Level level = parseLevel(store[key].asString());
459
+ if (level == UNKNOWN_LEVEL) {
460
+ errors.push_back(Error("'{{" + key + "}}' must be one of"
461
+ " 'crit', 'error', 'warn', 'notice', 'info', 'debug', 'debug2' or 'debug3'"));
462
+ }
463
+ }
464
+
465
+ void
466
+ Schema::validateTarget(const string &key, const ConfigKit::Store &store,
467
+ vector<ConfigKit::Error> &errors)
468
+ {
469
+ typedef ConfigKit::Error Error;
470
+ Json::Value value = store[key];
471
+ string keyQuote = "'{{" + key + "}}'";
472
+
473
+ if (value.isNull()) {
474
+ return;
475
+ }
476
+
477
+ if (value.isObject()) {
478
+ if (value.isMember("stderr")) {
479
+ if (value.size() > 1) {
480
+ errors.push_back(Error("When " + keyQuote
481
+ + " is an object containing the 'stderr' key,"
482
+ " it may not contain any other keys"));
483
+ } else if (!value["stderr"].asBool()) {
484
+ errors.push_back(Error("When " + keyQuote
485
+ + " is an object containing the 'stderr' key,"
486
+ " it must have the 'true' value"));
487
+ }
488
+ } else if (value.isMember("path")) {
489
+ if (!value["path"].isString()) {
490
+ errors.push_back(Error("When " + keyQuote
491
+ + " is an object containing the 'path' key,"
492
+ " it must be a string"));
493
+ }
494
+ if (value.isMember("fd")) {
495
+ if (!value["fd"].isInt()) {
496
+ errors.push_back(Error("When " + keyQuote
497
+ + " is an object containing the 'fd' key,"
498
+ " it must be an integer"));
499
+ } else if (value["fd"].asInt() < 0) {
500
+ errors.push_back(Error("When " + keyQuote
501
+ + " is an object containing the 'fd' key,"
502
+ " it must be 0 or greater"));
503
+ }
504
+ }
505
+ } else {
506
+ errors.push_back(Error("When " + keyQuote
507
+ + " is an object, it must contain either"
508
+ " the 'stderr' or 'path' key"));
509
+ }
510
+ } else if (!value.isString()) {
511
+ errors.push_back(Error(keyQuote
512
+ + " must be either a string or an object"));
513
+ }
514
+ }
515
+
516
+ static Json::Value
517
+ filterTargetFd(const Json::Value &value) {
518
+ Json::Value result = value;
519
+ result.removeMember("fd");
520
+ return result;
521
+ }
522
+
523
+ Schema::Schema() {
524
+ using namespace ConfigKit;
525
+
526
+ add("level", STRING_TYPE, OPTIONAL, DEFAULT_LOG_LEVEL_NAME);
527
+ add("target", ANY_TYPE, OPTIONAL, createStderrTarget())
528
+ .setInspectFilter(filterTargetFd);
529
+ add("file_descriptor_log_target", ANY_TYPE, OPTIONAL)
530
+ .setInspectFilter(filterTargetFd);
531
+ add("redirect_stderr", BOOL_TYPE, OPTIONAL, true);
532
+ add("app_output_log_level", STRING_TYPE, OPTIONAL, DEFAULT_APP_OUTPUT_LOG_LEVEL_NAME);
533
+
534
+ addValidator(boost::bind(validateLogLevel, "level",
535
+ boost::placeholders::_1, boost::placeholders::_2));
536
+ addValidator(boost::bind(validateLogLevel, "app_output_log_level",
537
+ boost::placeholders::_1, boost::placeholders::_2));
538
+ addValidator(boost::bind(validateTarget, "target",
539
+ boost::placeholders::_1, boost::placeholders::_2));
540
+ addValidator(boost::bind(validateTarget, "file_descriptor_log_target",
541
+ boost::placeholders::_1, boost::placeholders::_2));
542
+
543
+ addNormalizer(normalizeConfig);
544
+
545
+ finalize();
546
+ }
547
+
548
+
549
+ ConfigRealization::ConfigRealization(const ConfigKit::Store &store)
550
+ : level(parseLevel(store["level"].asString())),
551
+ appOutputLogLevel(parseLevel(store["app_output_log_level"].asString())),
552
+ finalized(false)
553
+ {
554
+ if (store["target"].isMember("stderr")) {
555
+ targetType = STDERR_TARGET;
556
+ targetFd = STDERR_FILENO;
557
+ targetFdClosePolicy = NEVER_CLOSE;
558
+ } else if (store["target"]["fd"].isNull()) {
559
+ string path = store["target"]["path"].asString();
560
+ targetType = FILE_TARGET;
561
+ targetFd = syscalls::open(path.c_str(),
562
+ O_WRONLY | O_APPEND | O_CREAT, 0644);
563
+ if (targetFd == -1) {
564
+ int e = errno;
565
+ throw FileSystemException(
566
+ "Cannot open " + path + " for writing",
567
+ e, path);
568
+ }
569
+ targetFdClosePolicy = ALWAYS_CLOSE;
570
+ } else {
571
+ targetType = FILE_TARGET;
572
+ targetFd = store["target"]["fd"].asInt();
573
+ // If anything goes wrong before finalization, then
574
+ // the caller is responsible for cleaning up the fd.
575
+ // See the Context class description.
576
+ targetFdClosePolicy = CLOSE_WHEN_FINALIZED;
577
+ }
578
+
579
+ if (store["file_descriptor_log_target"].isNull()) {
580
+ fileDescriptorLogTargetType = NO_TARGET;
581
+ fileDescriptorLogTargetFd = -1;
582
+ fileDescriptorLogTargetFdClosePolicy = NEVER_CLOSE;
583
+ } else if (store["file_descriptor_log_target"].isMember("stderr")) {
584
+ fileDescriptorLogTargetType = STDERR_TARGET;
585
+ fileDescriptorLogTargetFd = STDERR_FILENO;
586
+ fileDescriptorLogTargetFdClosePolicy = NEVER_CLOSE;
587
+ } else if (store["file_descriptor_log_target"]["fd"].isNull()) {
588
+ string path = store["file_descriptor_log_target"]["path"].asString();
589
+ fileDescriptorLogTargetType = FILE_TARGET;
590
+ fileDescriptorLogTargetFd = syscalls::open(path.c_str(),
591
+ O_WRONLY | O_APPEND | O_CREAT, 0644);
592
+ if (fileDescriptorLogTargetFd == -1) {
593
+ int e = errno;
594
+ throw FileSystemException(
595
+ "Cannot open " + path + " for writing",
596
+ e, path);
597
+ }
598
+ fileDescriptorLogTargetFdClosePolicy = ALWAYS_CLOSE;
599
+ } else {
600
+ fileDescriptorLogTargetType = FILE_TARGET;
601
+ fileDescriptorLogTargetFd = store["file_descriptor_log_target"]["fd"].asInt();
602
+ // If anything goes wrong before finalization, then
603
+ // the caller is responsible for cleaning up the fd.
604
+ // See the Context class description.
605
+ fileDescriptorLogTargetFdClosePolicy = CLOSE_WHEN_FINALIZED;
606
+ }
607
+ }
608
+
609
+ ConfigRealization::~ConfigRealization() {
610
+ switch (targetFdClosePolicy) {
611
+ case NEVER_CLOSE:
612
+ // Do nothing.
613
+ break;
614
+ case ALWAYS_CLOSE:
615
+ syscalls::close(targetFd);
616
+ break;
617
+ case CLOSE_WHEN_FINALIZED:
618
+ if (finalized) {
619
+ syscalls::close(targetFd);
620
+ }
621
+ break;
622
+ }
623
+
624
+ switch (fileDescriptorLogTargetFdClosePolicy) {
625
+ case NEVER_CLOSE:
626
+ // Do nothing.
627
+ break;
628
+ case ALWAYS_CLOSE:
629
+ syscalls::close(fileDescriptorLogTargetFd);
630
+ break;
631
+ case CLOSE_WHEN_FINALIZED:
632
+ if (finalized) {
633
+ syscalls::close(fileDescriptorLogTargetFd);
634
+ }
635
+ break;
636
+ }
637
+ }
638
+
639
+ static void
640
+ garbageCollectConfigRealization(ConfigRealization *configRlz) {
641
+ boost::this_thread::disable_interruption di;
642
+ boost::this_thread::disable_syscall_interruption dsi;
643
+ syscalls::sleep(30);
644
+ delete configRlz;
645
+ }
646
+
647
+ void
648
+ ConfigRealization::apply(const ConfigKit::Store &config, ConfigRealization *oldConfigRlz)
649
+ BOOST_NOEXCEPT_OR_NOTHROW
650
+ {
651
+ if (config["redirect_stderr"].asBool()) {
652
+ int ret = syscalls::dup2(targetFd, STDERR_FILENO);
653
+ if (ret == -1) {
654
+ int e = errno;
655
+ P_ERROR("Error redirecting logging target to stderr: "
656
+ << strerror(e) << " (errno=" << e << ")");
657
+ }
658
+ }
659
+
660
+ if (oldConfigRlz != NULL) {
661
+ // Garbage collect old config realization in 30 seconds.
662
+ // There is no way to cheaply find out whether oldConfigRlz
663
+ // is still being used (we don't want to resort to more atomic
664
+ // operations, or conservative garbage collection) but
665
+ // waiting 30 seconds should be good enough.
666
+ try {
667
+ oxt::thread(boost::bind(garbageCollectConfigRealization, oldConfigRlz),
668
+ "LoggingKit config garbage collector " + toString(oldConfigRlz),
669
+ 128 * 1024);
670
+ } catch (const std::exception &e) {
671
+ P_ERROR("Error spawning background thread to garbage collect"
672
+ " old LoggingKit configuration: " << e.what());
673
+ }
674
+ }
675
+ }
676
+
677
+ void
678
+ ConfigRealization::finalize() {
679
+ finalized = true;
680
+ }
681
+
682
+
683
+ ConfigChangeRequest::ConfigChangeRequest()
684
+ : configRlz(NULL)
685
+ {
686
+ // Do nothing.
687
+ }
688
+
689
+ ConfigChangeRequest::~ConfigChangeRequest() {
690
+ delete configRlz;
691
+ }
692
+
693
+
694
+ } // namespace LoggingKit
695
+ } // namespace Passenger