passenger 5.2.1 → 5.2.2
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 +4 -4
- data/CHANGELOG +8 -0
- data/CODE_OF_CONDUCT.md +52 -0
- data/README.md +17 -9
- data/build/agent.rb +3 -1
- data/build/cxx_tests.rb +1 -0
- data/build/schema_printer.rb +1 -0
- data/build/support/cxx_dependency_map.rb +338 -31
- data/dev/configkit-schemas/index.json +64 -15
- data/dev/copy_boost_headers +1 -0
- data/images/justin.png +0 -0
- data/images/passenger_logo.svg +45 -0
- data/images/spark.png +0 -0
- data/resources/templates/standalone/http.erb +4 -0
- data/src/agent/AgentMain.cpp +4 -0
- data/src/agent/Core/AdminPanelConnector.h +133 -5
- data/src/agent/Core/ApplicationPool/Implementation.cpp +1 -0
- data/src/agent/Core/ApplicationPool/Options.h +7 -1
- data/src/agent/Core/ApplicationPool/Pool.h +1 -0
- data/src/agent/Core/ApplicationPool/Pool/GroupUtils.cpp +11 -0
- data/src/agent/Core/ApplicationPool/Process.cpp +52 -0
- data/src/agent/Core/ApplicationPool/Process.h +4 -8
- data/src/agent/Core/Config.h +6 -2
- data/src/agent/Core/ConfigChange.cpp +12 -1
- data/src/agent/Core/ConfigChange.h +3 -0
- data/src/agent/Core/Controller/Config.h +1 -1
- data/src/agent/Core/Controller/InitRequest.cpp +1 -1
- data/src/agent/Core/Controller/InternalUtils.cpp +2 -2
- data/src/agent/Core/CoreMain.cpp +18 -5
- data/src/agent/Core/SpawningKit/BackgroundIOCapturer.h +8 -4
- data/src/agent/Core/SpawningKit/DirectSpawner.h +3 -1
- data/src/agent/Core/SpawningKit/PipeWatcher.h +9 -4
- data/src/agent/Core/SpawningKit/SmartSpawner.h +5 -3
- data/src/agent/Core/SpawningKit/Spawner.h +1 -1
- data/src/agent/ExecHelper/ExecHelperMain.cpp +295 -0
- data/src/agent/Shared/Fundamentals/Initialization.cpp +11 -8
- data/src/agent/Shared/Fundamentals/Initialization.h +2 -2
- data/src/agent/Watchdog/Config.h +5 -2
- data/src/apache2_module/Config.cpp +13 -0
- data/src/apache2_module/ConfigGeneral/AutoGeneratedDefinitions.cpp +30 -0
- data/src/apache2_module/ConfigGeneral/AutoGeneratedSetterFuncs.cpp +90 -0
- data/src/apache2_module/ConfigGeneral/ManifestGeneration.h +18 -2
- data/src/apache2_module/DirConfig/AutoGeneratedCreateFunction.cpp +5 -0
- data/src/apache2_module/DirConfig/AutoGeneratedManifestGeneration.cpp +12 -0
- data/src/apache2_module/DirConfig/AutoGeneratedMergeFunction.cpp +7 -0
- data/src/apache2_module/DirConfig/AutoGeneratedStruct.h +13 -0
- data/src/apache2_module/Hooks.cpp +4 -0
- data/src/apache2_module/ServerConfig/AutoGeneratedManifestGeneration.cpp +55 -0
- data/src/apache2_module/ServerConfig/AutoGeneratedStruct.h +65 -0
- data/src/cxx_supportlib/BackgroundEventLoop.cpp +3 -3
- data/src/cxx_supportlib/ConfigKit/Schema.h +53 -31
- data/src/cxx_supportlib/ConfigKit/Store.h +12 -8
- data/src/cxx_supportlib/Constants.h +2 -1
- data/src/cxx_supportlib/DataStructures/StringKeyTable.h +4 -0
- data/src/cxx_supportlib/FileTools/PathManipCBindings.cpp +22 -1
- data/src/cxx_supportlib/FileTools/PathManipCBindings.h +3 -1
- data/src/cxx_supportlib/LoggingKit/Config.h +2 -0
- data/src/cxx_supportlib/LoggingKit/Context.h +28 -0
- data/src/cxx_supportlib/LoggingKit/Forward.h +0 -1
- data/src/cxx_supportlib/LoggingKit/Implementation.cpp +112 -9
- data/src/cxx_supportlib/LoggingKit/Logging.h +4 -2
- data/src/cxx_supportlib/WebSocketCommandReverseServer.h +34 -43
- data/src/cxx_supportlib/vendor-modified/boost/call_traits.hpp +20 -0
- data/src/cxx_supportlib/vendor-modified/boost/circular_buffer.hpp +62 -0
- data/src/cxx_supportlib/vendor-modified/boost/circular_buffer/base.hpp +3123 -0
- data/src/cxx_supportlib/vendor-modified/boost/circular_buffer/debug.hpp +248 -0
- data/src/cxx_supportlib/vendor-modified/boost/circular_buffer/details.hpp +498 -0
- data/src/cxx_supportlib/vendor-modified/boost/circular_buffer/space_optimized.hpp +1719 -0
- data/src/cxx_supportlib/vendor-modified/boost/circular_buffer_fwd.hpp +43 -0
- data/src/cxx_supportlib/vendor-modified/boost/detail/call_traits.hpp +172 -0
- data/src/nginx_module/ConfigGeneral/AutoGeneratedDefinitions.c +48 -0
- data/src/nginx_module/ConfigGeneral/AutoGeneratedSetterFuncs.c +72 -0
- data/src/nginx_module/ConfigGeneral/ManifestGeneration.c +32 -0
- data/src/nginx_module/ConfigGeneral/ManifestGeneration.h +3 -0
- data/src/nginx_module/Configuration.c +25 -0
- data/src/nginx_module/ContentHandler.c +42 -4
- data/src/nginx_module/LocationConfig/AutoGeneratedCreateFunction.c +5 -0
- data/src/nginx_module/LocationConfig/AutoGeneratedManifestGeneration.c +13 -0
- data/src/nginx_module/LocationConfig/AutoGeneratedMergeFunction.c +5 -0
- data/src/nginx_module/LocationConfig/AutoGeneratedStruct.h +4 -0
- data/src/nginx_module/MainConfig/AutoGeneratedCreateFunction.c +30 -0
- data/src/nginx_module/MainConfig/AutoGeneratedManifestGeneration.c +60 -0
- data/src/nginx_module/MainConfig/AutoGeneratedStruct.h +20 -0
- data/src/nginx_module/ngx_http_passenger_module.c +4 -0
- data/src/ruby_supportlib/phusion_passenger.rb +1 -1
- data/src/ruby_supportlib/phusion_passenger/apache2/config_options.rb +37 -1
- data/src/ruby_supportlib/phusion_passenger/constants.rb +1 -0
- data/src/ruby_supportlib/phusion_passenger/nginx/config_options.rb +42 -1
- data/src/ruby_supportlib/phusion_passenger/packaging.rb +2 -0
- data/src/ruby_supportlib/phusion_passenger/platform_info/crypto.rb +13 -3
- data/src/ruby_supportlib/phusion_passenger/standalone/config_options_list.rb +20 -0
- metadata +16 -2
@@ -167,7 +167,7 @@ pollLibuv(BackgroundEventLoop *bg) {
|
|
167
167
|
|
168
168
|
fd = uv_backend_fd(&bg->priv->libuv_loop);
|
169
169
|
|
170
|
-
while (!this_thread::interruption_requested()) {
|
170
|
+
while (!boost::this_thread::interruption_requested()) {
|
171
171
|
timeout = uv_backend_timeout(&bg->priv->libuv_loop);
|
172
172
|
|
173
173
|
ctx->syscall_interruption_lock.unlock();
|
@@ -203,13 +203,13 @@ pollLibuv(BackgroundEventLoop *bg) {
|
|
203
203
|
} while (ret == -1
|
204
204
|
&& lastErrno == EINTR
|
205
205
|
&& (!boost::this_thread::syscalls_interruptable()
|
206
|
-
|| !(intrRequested = this_thread::interruption_requested())));
|
206
|
+
|| !(intrRequested = boost::this_thread::interruption_requested())));
|
207
207
|
|
208
208
|
ctx->syscall_interruption_lock.lock();
|
209
209
|
|
210
210
|
if (ret == -1
|
211
211
|
&& lastErrno == EINTR
|
212
|
-
&& this_thread::syscalls_interruptable()
|
212
|
+
&& boost::this_thread::syscalls_interruptable()
|
213
213
|
&& intrRequested)
|
214
214
|
{
|
215
215
|
throw boost::thread_interrupted();
|
@@ -74,44 +74,65 @@ public:
|
|
74
74
|
inspectFilter(_inspectFilter)
|
75
75
|
{ }
|
76
76
|
|
77
|
-
Json::Value
|
77
|
+
bool tryTypecastValue(const Json::Value &val, Json::Value &result) const {
|
78
78
|
if (val.isNull()) {
|
79
|
-
|
79
|
+
result = Json::nullValue;
|
80
|
+
return true;
|
80
81
|
}
|
81
82
|
|
82
83
|
switch (type) {
|
83
84
|
case STRING_TYPE:
|
84
|
-
if (val.
|
85
|
-
|
85
|
+
if (val.isConvertibleTo(Json::stringValue)) {
|
86
|
+
result = val.asString();
|
87
|
+
return true;
|
86
88
|
} else {
|
87
|
-
return
|
89
|
+
return false;
|
88
90
|
}
|
89
91
|
case INT_TYPE:
|
90
|
-
if (val.
|
91
|
-
|
92
|
+
if (val.isConvertibleTo(Json::intValue)) {
|
93
|
+
result = val.asInt();
|
94
|
+
return true;
|
92
95
|
} else {
|
93
|
-
return
|
96
|
+
return false;
|
94
97
|
}
|
95
98
|
case UINT_TYPE:
|
96
|
-
if (val.
|
97
|
-
|
99
|
+
if (val.isConvertibleTo(Json::uintValue)) {
|
100
|
+
result = val.asUInt();
|
101
|
+
return true;
|
98
102
|
} else {
|
99
|
-
return
|
103
|
+
return false;
|
100
104
|
}
|
101
105
|
case FLOAT_TYPE:
|
102
|
-
if (val.
|
103
|
-
|
106
|
+
if (val.isConvertibleTo(Json::realValue)) {
|
107
|
+
result = val.asDouble();
|
108
|
+
return true;
|
104
109
|
} else {
|
105
|
-
return
|
110
|
+
return false;
|
106
111
|
}
|
107
112
|
case BOOL_TYPE:
|
108
|
-
if (val.
|
109
|
-
|
113
|
+
if (val.isConvertibleTo(Json::booleanValue)) {
|
114
|
+
result = val.asBool();
|
115
|
+
return true;
|
110
116
|
} else {
|
111
|
-
return
|
117
|
+
return false;
|
118
|
+
}
|
119
|
+
case ARRAY_TYPE:
|
120
|
+
if (val.isConvertibleTo(Json::arrayValue)) {
|
121
|
+
result = val;
|
122
|
+
return true;
|
123
|
+
} else {
|
124
|
+
return false;
|
125
|
+
}
|
126
|
+
case OBJECT_TYPE:
|
127
|
+
if (val.isConvertibleTo(Json::objectValue)) {
|
128
|
+
result = val;
|
129
|
+
return true;
|
130
|
+
} else {
|
131
|
+
return false;
|
112
132
|
}
|
113
133
|
default:
|
114
|
-
|
134
|
+
result = val;
|
135
|
+
return true;
|
115
136
|
}
|
116
137
|
}
|
117
138
|
|
@@ -321,10 +342,11 @@ public:
|
|
321
342
|
* configuration store -- to the given configuration key and value.
|
322
343
|
* Validators added with `addValidator()` won't be applied.
|
323
344
|
*
|
324
|
-
* Returns whether validation passed. If not, then
|
345
|
+
* Returns whether validation passed. If not, then an Error is appended
|
346
|
+
* to `errors`.
|
325
347
|
*/
|
326
348
|
bool validateValue(const HashedStaticString &key, const Json::Value &value,
|
327
|
-
Error &
|
349
|
+
vector<Error> &errors) const
|
328
350
|
{
|
329
351
|
const Entry *entry;
|
330
352
|
|
@@ -335,7 +357,7 @@ public:
|
|
335
357
|
|
336
358
|
if (value.isNull()) {
|
337
359
|
if (entry->flags & REQUIRED) {
|
338
|
-
|
360
|
+
errors.push_back(Error("'{{" + key + "}}' is required"));
|
339
361
|
return false;
|
340
362
|
} else {
|
341
363
|
return true;
|
@@ -347,14 +369,14 @@ public:
|
|
347
369
|
if (value.isConvertibleTo(Json::stringValue)) {
|
348
370
|
return true;
|
349
371
|
} else {
|
350
|
-
|
372
|
+
errors.push_back(Error("'{{" + key + "}}' must be a string"));
|
351
373
|
return false;
|
352
374
|
}
|
353
375
|
case INT_TYPE:
|
354
376
|
if (value.isConvertibleTo(Json::intValue)) {
|
355
377
|
return true;
|
356
378
|
} else {
|
357
|
-
|
379
|
+
errors.push_back(Error("'{{" + key + "}}' must be an integer"));
|
358
380
|
return false;
|
359
381
|
}
|
360
382
|
case UINT_TYPE:
|
@@ -362,32 +384,32 @@ public:
|
|
362
384
|
if (value.isConvertibleTo(Json::uintValue)) {
|
363
385
|
return true;
|
364
386
|
} else {
|
365
|
-
|
387
|
+
errors.push_back(Error("'{{" + key + "}}' must be greater than 0"));
|
366
388
|
return false;
|
367
389
|
}
|
368
390
|
} else {
|
369
|
-
|
391
|
+
errors.push_back(Error("'{{" + key + "}}' must be an integer"));
|
370
392
|
return false;
|
371
393
|
}
|
372
394
|
case FLOAT_TYPE:
|
373
395
|
if (value.isConvertibleTo(Json::realValue)) {
|
374
396
|
return true;
|
375
397
|
} else {
|
376
|
-
|
398
|
+
errors.push_back(Error("'{{" + key + "}}' must be a number"));
|
377
399
|
return false;
|
378
400
|
}
|
379
401
|
case BOOL_TYPE:
|
380
402
|
if (value.isConvertibleTo(Json::booleanValue)) {
|
381
403
|
return true;
|
382
404
|
} else {
|
383
|
-
|
405
|
+
errors.push_back(Error("'{{" + key + "}}' must be a boolean"));
|
384
406
|
return false;
|
385
407
|
}
|
386
408
|
case ARRAY_TYPE:
|
387
409
|
if (value.isConvertibleTo(Json::arrayValue)) {
|
388
410
|
return true;
|
389
411
|
} else {
|
390
|
-
|
412
|
+
errors.push_back(Error("'{{" + key + "}}' must be an array"));
|
391
413
|
return false;
|
392
414
|
}
|
393
415
|
case STRING_ARRAY_TYPE:
|
@@ -395,20 +417,20 @@ public:
|
|
395
417
|
Json::Value::const_iterator it, end = value.end();
|
396
418
|
for (it = value.begin(); it != end; it++) {
|
397
419
|
if (it->type() != Json::stringValue) {
|
398
|
-
|
420
|
+
errors.push_back(Error("'{{" + key + "}}' may only contain strings"));
|
399
421
|
return false;
|
400
422
|
}
|
401
423
|
}
|
402
424
|
return true;
|
403
425
|
} else {
|
404
|
-
|
426
|
+
errors.push_back(Error("'{{" + key + "}}' must be an array"));
|
405
427
|
return false;
|
406
428
|
}
|
407
429
|
case OBJECT_TYPE:
|
408
430
|
if (value.isObject()) {
|
409
431
|
return true;
|
410
432
|
} else {
|
411
|
-
|
433
|
+
errors.push_back(Error("'{{" + key + "}}' must be a JSON object"));
|
412
434
|
return false;
|
413
435
|
}
|
414
436
|
case ANY_TYPE:
|
@@ -385,14 +385,17 @@ public:
|
|
385
385
|
StringKeyTable<Entry>::Iterator p_it(storeWithPreviewData.entries);
|
386
386
|
StringKeyTable<Entry>::ConstIterator it(entries);
|
387
387
|
vector<Error> tmpErrors;
|
388
|
-
Error error;
|
389
388
|
|
390
389
|
while (*p_it != NULL) {
|
391
390
|
const HashedStaticString &key = p_it.getKey();
|
392
391
|
Entry &entry = p_it.getValue();
|
393
392
|
|
394
393
|
if (isWritable(entry) && updates.isMember(key)) {
|
395
|
-
|
394
|
+
bool ok = entry.schemaEntry->tryTypecastValue(
|
395
|
+
updates[key], entry.userValue);
|
396
|
+
if (!ok) {
|
397
|
+
entry.userValue = updates[key];
|
398
|
+
}
|
396
399
|
}
|
397
400
|
|
398
401
|
p_it.next();
|
@@ -406,7 +409,11 @@ public:
|
|
406
409
|
entry.schemaEntry->inspect(subdoc);
|
407
410
|
|
408
411
|
if (isWritable(entry) && updates.isMember(key)) {
|
409
|
-
|
412
|
+
bool ok = entry.schemaEntry->tryTypecastValue(updates[key],
|
413
|
+
subdoc["user_value"]);
|
414
|
+
if (!ok) {
|
415
|
+
subdoc["user_value"] = updates[key];
|
416
|
+
}
|
410
417
|
} else {
|
411
418
|
subdoc["user_value"] = entry.userValue;
|
412
419
|
}
|
@@ -419,9 +426,7 @@ public:
|
|
419
426
|
subdoc["effective_value"] =
|
420
427
|
getEffectiveValue(subdoc["user_value"],
|
421
428
|
subdoc["default_value"]);
|
422
|
-
|
423
|
-
tmpErrors.push_back(error);
|
424
|
-
}
|
429
|
+
schema->validateValue(it.getKey(), effectiveValue, tmpErrors);
|
425
430
|
|
426
431
|
result[it.getKey()] = subdoc;
|
427
432
|
it.next();
|
@@ -469,8 +474,7 @@ public:
|
|
469
474
|
if (isWritable(entry)) {
|
470
475
|
const Json::Value &subdoc =
|
471
476
|
const_cast<const Json::Value &>(preview)[it.getKey()];
|
472
|
-
entry.userValue =
|
473
|
-
subdoc["user_value"]);
|
477
|
+
entry.userValue = subdoc["user_value"];
|
474
478
|
}
|
475
479
|
it.next();
|
476
480
|
}
|
@@ -74,13 +74,14 @@
|
|
74
74
|
#define FEEDBACK_FD 3
|
75
75
|
#define FLYING_PASSENGER_NAME "Flying Passenger"
|
76
76
|
#define GLOBAL_NAMESPACE_DIRNAME "passenger"
|
77
|
+
#define LOG_MONITORING_MAX_LINES 200
|
77
78
|
#define MESSAGE_SERVER_MAX_PASSWORD_SIZE 100
|
78
79
|
#define MESSAGE_SERVER_MAX_USERNAME_SIZE 100
|
79
80
|
#define PASSENGER_API_VERSION "0.3"
|
80
81
|
#define PASSENGER_API_VERSION_MAJOR 0
|
81
82
|
#define PASSENGER_API_VERSION_MINOR 3
|
82
83
|
#define PASSENGER_DEFAULT_USER "nobody"
|
83
|
-
#define PASSENGER_VERSION "5.2.
|
84
|
+
#define PASSENGER_VERSION "5.2.2"
|
84
85
|
#define POOL_HELPER_THREAD_STACK_SIZE 262144
|
85
86
|
#define PROCESS_SHUTDOWN_TIMEOUT 60
|
86
87
|
#define PROCESS_SHUTDOWN_TIMEOUT_DISPLAY "1 minute"
|
@@ -386,6 +386,10 @@ public:
|
|
386
386
|
}
|
387
387
|
}
|
388
388
|
|
389
|
+
bool contains(const HashedStaticString &key) const {
|
390
|
+
return (lookupCell(key) != NULL);
|
391
|
+
}
|
392
|
+
|
389
393
|
bool lookup(const HashedStaticString &key, const T **result) const {
|
390
394
|
const Cell * const cell = lookupCell(key);
|
391
395
|
if (cell != NULL) {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
-
* Copyright (c) 2010-
|
3
|
+
* Copyright (c) 2010-2018 Phusion Holding B.V.
|
4
4
|
*
|
5
5
|
* "Passenger", "Phusion Passenger" and "Union Station" are registered
|
6
6
|
* trademarks of Phusion Holding B.V.
|
@@ -26,7 +26,9 @@
|
|
26
26
|
|
27
27
|
#include <FileTools/PathManipCBindings.h>
|
28
28
|
#include <FileTools/PathManip.h>
|
29
|
+
#include <Exceptions.h>
|
29
30
|
#include <cstring>
|
31
|
+
#include <cerrno>
|
30
32
|
|
31
33
|
using namespace std;
|
32
34
|
using namespace Passenger;
|
@@ -47,6 +49,25 @@ psg_absolutize_path(const char *path, size_t path_len,
|
|
47
49
|
return strdup(result.c_str());
|
48
50
|
}
|
49
51
|
|
52
|
+
char *
|
53
|
+
psg_resolve_symlink(const char *path, size_t path_len,
|
54
|
+
size_t *result_len)
|
55
|
+
{
|
56
|
+
try {
|
57
|
+
string result = resolveSymlink(StaticString(path, path_len));
|
58
|
+
if (result_len != NULL) {
|
59
|
+
*result_len = result.size();
|
60
|
+
}
|
61
|
+
return strdup(result.c_str());
|
62
|
+
} catch (const SystemException &e) {
|
63
|
+
errno = e.code();
|
64
|
+
return NULL;
|
65
|
+
} catch (const std::bad_alloc &) {
|
66
|
+
errno = ENOMEM;
|
67
|
+
return NULL;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
50
71
|
const char *
|
51
72
|
psg_extract_dir_name_static(const char *path,
|
52
73
|
size_t path_len, size_t *result_len)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
-
* Copyright (c)
|
3
|
+
* Copyright (c) 2018 Phusion Holding B.V.
|
4
4
|
*
|
5
5
|
* "Passenger", "Phusion Passenger" and "Union Station" are registered
|
6
6
|
* trademarks of Phusion Holding B.V.
|
@@ -36,6 +36,8 @@ extern "C" {
|
|
36
36
|
char *psg_absolutize_path(const char *path, size_t path_len,
|
37
37
|
const char *working_dir, size_t working_dir_len,
|
38
38
|
size_t *result_len);
|
39
|
+
char *psg_resolve_symlink(const char *path, size_t path_len,
|
40
|
+
size_t *result_len);
|
39
41
|
const char *psg_extract_dir_name_static(const char *path,
|
40
42
|
size_t path_len, size_t *result_len);
|
41
43
|
|
@@ -50,6 +50,7 @@ using namespace std;
|
|
50
50
|
* by 'rake configkit_schemas_inline_comments')
|
51
51
|
*
|
52
52
|
* app_output_log_level string - default("notice")
|
53
|
+
* buffer_logs boolean - default(false)
|
53
54
|
* file_descriptor_log_target any - -
|
54
55
|
* level string - default("notice")
|
55
56
|
* redirect_stderr boolean - default(true)
|
@@ -82,6 +83,7 @@ struct ConfigRealization {
|
|
82
83
|
TargetType targetType;
|
83
84
|
TargetType fileDescriptorLogTargetType;
|
84
85
|
int targetFd;
|
86
|
+
bool saveLog;
|
85
87
|
int fileDescriptorLogTargetFd;
|
86
88
|
FdClosePolicy targetFdClosePolicy;
|
87
89
|
FdClosePolicy fileDescriptorLogTargetFdClosePolicy;
|
@@ -28,14 +28,17 @@
|
|
28
28
|
|
29
29
|
#include <queue>
|
30
30
|
|
31
|
+
#include <jsoncpp/json.h>
|
31
32
|
#include <oxt/macros.hpp>
|
32
33
|
#include <oxt/thread.hpp>
|
34
|
+
#include <boost/circular_buffer.hpp>
|
33
35
|
#include <boost/thread.hpp>
|
34
36
|
#include <boost/atomic.hpp>
|
35
37
|
#include <ConfigKit/ConfigKit.h>
|
36
38
|
#include <LoggingKit/Forward.h>
|
37
39
|
#include <LoggingKit/Config.h>
|
38
40
|
#include <Utils/SystemTime.h>
|
41
|
+
#include <DataStructures/StringKeyTable.h>
|
39
42
|
|
40
43
|
namespace Passenger {
|
41
44
|
namespace LoggingKit {
|
@@ -78,12 +81,37 @@ private:
|
|
78
81
|
queue< pair<ConfigRealization *, MonotonicTimeUsec> > oldConfigs;
|
79
82
|
bool shuttingDown;
|
80
83
|
|
84
|
+
struct TimestampedLog {
|
85
|
+
// time at which time the log entered the core, which is unfortunately somewhat
|
86
|
+
// arbitrarily later than that it was logged in the user program
|
87
|
+
unsigned long long timestamp;
|
88
|
+
string sourceId;
|
89
|
+
string lineText;
|
90
|
+
};
|
91
|
+
typedef boost::circular_buffer<TimestampedLog> TimestampedLogBuffer;
|
92
|
+
|
93
|
+
typedef boost::circular_buffer<string> SimpleLogBuffer;
|
94
|
+
typedef StringKeyTable<SimpleLogBuffer> SimpleLogMap;
|
95
|
+
struct AppGroupLog {
|
96
|
+
TimestampedLogBuffer pidLog; // combined logs from PIDs
|
97
|
+
SimpleLogMap watchFileLog; // a separate log buffer per (watched file name)
|
98
|
+
};
|
99
|
+
typedef StringKeyTable<AppGroupLog> LogStore;
|
100
|
+
LogStore logStore;
|
101
|
+
|
81
102
|
public:
|
82
103
|
Context(const Json::Value &initialConfig = Json::Value(),
|
83
104
|
const ConfigKit::Translator &translator = ConfigKit::DummyTranslator());
|
84
105
|
~Context();
|
85
106
|
ConfigKit::Store getConfig() const;
|
86
107
|
|
108
|
+
// specifically for logging output from application processes
|
109
|
+
void saveNewLog(const HashedStaticString &groupName, const char *sourceStr, unsigned int sourceStrLen, const char *message, unsigned int messageLen);
|
110
|
+
// the message might already have been logged (e.g. because the source is a `tail -n`)
|
111
|
+
void updateLog(const HashedStaticString &groupName, const char *sourceStr, unsigned int sourceStrLen, const char *message, unsigned int messageLen);
|
112
|
+
// snapshot logStore to a JSON structure for external relay
|
113
|
+
Json::Value convertLog();
|
114
|
+
|
87
115
|
bool prepareConfigChange(const Json::Value &updates,
|
88
116
|
vector<ConfigKit::Error> &errors,
|
89
117
|
LoggingKit::ConfigChangeRequest &req);
|
@@ -41,7 +41,8 @@
|
|
41
41
|
#include <pthread.h>
|
42
42
|
|
43
43
|
#include <boost/cstdint.hpp>
|
44
|
-
#include <
|
44
|
+
#include <boost/circular_buffer.hpp>
|
45
|
+
#include <boost/foreach.hpp>
|
45
46
|
#include <oxt/thread.hpp>
|
46
47
|
#include <oxt/detail/context.hpp>
|
47
48
|
|
@@ -69,7 +70,6 @@ using namespace std;
|
|
69
70
|
Context *context = NULL;
|
70
71
|
AssertionFailureInfo lastAssertionFailure;
|
71
72
|
|
72
|
-
|
73
73
|
void
|
74
74
|
initialize(const Json::Value &initialConfig, const ConfigKit::Translator &translator) {
|
75
75
|
context = new Context(initialConfig, translator);
|
@@ -309,11 +309,94 @@ _writeFileDescriptorLogEntry(const ConfigRealization *configRealization,
|
|
309
309
|
writeExactWithoutOXT(configRealization->fileDescriptorLogTargetFd, str, size);
|
310
310
|
}
|
311
311
|
|
312
|
+
void
|
313
|
+
Context::saveNewLog(const HashedStaticString &groupName, const char *sourceStr, unsigned int sourceStrLen, const char *message, unsigned int messageLen) {
|
314
|
+
boost::lock_guard<boost::mutex> l(syncher); //lock
|
315
|
+
|
316
|
+
unsigned long long timestamp = SystemTime::getUsec();
|
317
|
+
|
318
|
+
LogStore::Cell *c = logStore.lookupCell(groupName);
|
319
|
+
if (c == NULL) {
|
320
|
+
AppGroupLog appGroupLog;
|
321
|
+
appGroupLog.pidLog = TimestampedLogBuffer(LOG_MONITORING_MAX_LINES * 5);
|
322
|
+
c = logStore.insert(groupName, appGroupLog);
|
323
|
+
}
|
324
|
+
AppGroupLog &rec = c->value;
|
325
|
+
|
326
|
+
TimestampedLog ll;
|
327
|
+
ll.timestamp = timestamp;
|
328
|
+
ll.sourceId = string(sourceStr, sourceStrLen);
|
329
|
+
ll.lineText = string(message, messageLen);
|
330
|
+
rec.pidLog.push_back(ll);
|
331
|
+
//unlock
|
332
|
+
}
|
333
|
+
|
334
|
+
void
|
335
|
+
Context::updateLog(const HashedStaticString &groupName, const char *sourceStr, unsigned int sourceStrLen, const char *message, unsigned int messageLen) {
|
336
|
+
boost::lock_guard<boost::mutex> l(syncher); //lock
|
337
|
+
|
338
|
+
LogStore::Cell *c = logStore.lookupCell(groupName);
|
339
|
+
if (c == NULL) {
|
340
|
+
AppGroupLog appGroupLog;
|
341
|
+
appGroupLog.pidLog = TimestampedLogBuffer(LOG_MONITORING_MAX_LINES * 5);
|
342
|
+
c = logStore.insert(groupName, appGroupLog);
|
343
|
+
}
|
344
|
+
AppGroupLog &rec = c->value;
|
345
|
+
|
346
|
+
HashedStaticString source(sourceStr, sourceStrLen);
|
347
|
+
if (!rec.watchFileLog.contains(source)) {
|
348
|
+
SimpleLogBuffer logBuffer(LOG_MONITORING_MAX_LINES);
|
349
|
+
rec.watchFileLog.insert(source, logBuffer);
|
350
|
+
}
|
351
|
+
rec.watchFileLog.lookupCell(source)->value.push_back(string(message, messageLen));
|
352
|
+
//unlock
|
353
|
+
}
|
354
|
+
|
355
|
+
Json::Value
|
356
|
+
Context::convertLog(){
|
357
|
+
boost::lock_guard<boost::mutex> l(syncher); //lock
|
358
|
+
Json::Value reply = Json::objectValue;
|
359
|
+
|
360
|
+
if (!logStore.empty()) {
|
361
|
+
Context::LogStore::ConstIterator appGroupIter(logStore);
|
362
|
+
while (*appGroupIter != NULL) {
|
363
|
+
reply[appGroupIter.getKey()] = Json::objectValue;
|
364
|
+
|
365
|
+
Json::Value &processLog = reply[appGroupIter.getKey()]["Application process log (combined)"];
|
366
|
+
foreach (TimestampedLog logLine, appGroupIter->value.pidLog) {
|
367
|
+
Json::Value logLineJson = Json::objectValue;
|
368
|
+
logLineJson["source_id"] = logLine.sourceId;
|
369
|
+
logLineJson["timestamp"] = (Json::UInt64) logLine.timestamp;
|
370
|
+
logLineJson["line"] = logLine.lineText;
|
371
|
+
processLog.append(logLineJson);
|
372
|
+
}
|
373
|
+
|
374
|
+
Context::SimpleLogMap::ConstIterator watchFileLogIter(appGroupIter->value.watchFileLog);
|
375
|
+
while (*watchFileLogIter != NULL) {
|
376
|
+
if (!reply[appGroupIter.getKey()].isMember(watchFileLogIter.getKey())){
|
377
|
+
reply[appGroupIter.getKey()][watchFileLogIter.getKey()] = Json::arrayValue;
|
378
|
+
}
|
379
|
+
foreach (string line, watchFileLogIter->value) {
|
380
|
+
reply[appGroupIter.getKey()][watchFileLogIter.getKey()].append(line);
|
381
|
+
}
|
382
|
+
watchFileLogIter.next();
|
383
|
+
}
|
384
|
+
|
385
|
+
appGroupIter.next();
|
386
|
+
}
|
387
|
+
}
|
388
|
+
|
389
|
+
return reply;
|
390
|
+
//unlock
|
391
|
+
}
|
392
|
+
|
312
393
|
static void
|
313
|
-
realLogAppOutput(
|
394
|
+
realLogAppOutput(const HashedStaticString &groupName, int targetFd,
|
395
|
+
char *buf, unsigned int bufSize,
|
314
396
|
const char *pidStr, unsigned int pidStrLen,
|
315
397
|
const char *channelName, unsigned int channelNameLen,
|
316
|
-
const char *message, unsigned int messageLen
|
398
|
+
const char *message, unsigned int messageLen, int appLogFile,
|
399
|
+
bool saveLog)
|
317
400
|
{
|
318
401
|
char *pos = buf;
|
319
402
|
char *end = buf + bufSize;
|
@@ -325,12 +408,20 @@ realLogAppOutput(int targetFd, char *buf, unsigned int bufSize,
|
|
325
408
|
pos = appendData(pos, end, ": ");
|
326
409
|
pos = appendData(pos, end, message, messageLen);
|
327
410
|
pos = appendData(pos, end, "\n");
|
411
|
+
|
412
|
+
if (OXT_UNLIKELY(context != NULL && saveLog)) {
|
413
|
+
context->saveNewLog(groupName, pidStr, pidStrLen, message, messageLen);
|
414
|
+
}
|
415
|
+
if (appLogFile > -1) {
|
416
|
+
writeExactWithoutOXT(appLogFile, buf, pos - buf);
|
417
|
+
}
|
328
418
|
writeExactWithoutOXT(targetFd, buf, pos - buf);
|
329
419
|
}
|
330
420
|
|
331
421
|
void
|
332
|
-
logAppOutput(pid_t pid, const char *channelName, const char *message, unsigned int size) {
|
422
|
+
logAppOutput(const HashedStaticString &groupName, pid_t pid, const char *channelName, const char *message, unsigned int size, const StaticString &appLogFile) {
|
333
423
|
int targetFd;
|
424
|
+
bool saveLog = false;
|
334
425
|
|
335
426
|
if (OXT_LIKELY(context != NULL)) {
|
336
427
|
const ConfigRealization *configRealization = context->getConfigRealization();
|
@@ -339,10 +430,19 @@ logAppOutput(pid_t pid, const char *channelName, const char *message, unsigned i
|
|
339
430
|
}
|
340
431
|
|
341
432
|
targetFd = configRealization->targetFd;
|
433
|
+
saveLog = configRealization->saveLog;
|
342
434
|
} else {
|
343
435
|
targetFd = STDERR_FILENO;
|
344
436
|
}
|
345
437
|
|
438
|
+
int fd = -1;
|
439
|
+
if (!appLogFile.empty()) {
|
440
|
+
fd = open(appLogFile.data(), O_WRONLY | O_APPEND | O_CREAT, 0640);
|
441
|
+
if (fd == -1) {
|
442
|
+
int e = errno;
|
443
|
+
P_ERROR("opening file: " << appLogFile << " for logging " << groupName << " failed. Error: " << strerror(e));
|
444
|
+
}
|
445
|
+
}
|
346
446
|
char pidStr[sizeof("4294967295")];
|
347
447
|
unsigned int pidStrLen, channelNameLen, totalLen;
|
348
448
|
|
@@ -358,19 +458,20 @@ logAppOutput(pid_t pid, const char *channelName, const char *message, unsigned i
|
|
358
458
|
totalLen = (sizeof("App X Y: \n") - 2) + pidStrLen + channelNameLen + size;
|
359
459
|
if (totalLen < 1024) {
|
360
460
|
char buf[1024];
|
361
|
-
realLogAppOutput(targetFd,
|
461
|
+
realLogAppOutput(groupName, targetFd,
|
362
462
|
buf, sizeof(buf),
|
363
463
|
pidStr, pidStrLen,
|
364
464
|
channelName, channelNameLen,
|
365
|
-
message, size);
|
465
|
+
message, size, fd, saveLog);
|
366
466
|
} else {
|
367
467
|
DynamicBuffer buf(totalLen);
|
368
|
-
realLogAppOutput(targetFd,
|
468
|
+
realLogAppOutput(groupName, targetFd,
|
369
469
|
buf.data, totalLen,
|
370
470
|
pidStr, pidStrLen,
|
371
471
|
channelName, channelNameLen,
|
372
|
-
message, size);
|
472
|
+
message, size, fd, saveLog);
|
373
473
|
}
|
474
|
+
if(fd > -1){close(fd);}
|
374
475
|
}
|
375
476
|
|
376
477
|
|
@@ -662,6 +763,7 @@ Schema::Schema() {
|
|
662
763
|
.setInspectFilter(filterTargetFd);
|
663
764
|
add("redirect_stderr", BOOL_TYPE, OPTIONAL, true);
|
664
765
|
add("app_output_log_level", STRING_TYPE, OPTIONAL, DEFAULT_APP_OUTPUT_LOG_LEVEL_NAME);
|
766
|
+
add("buffer_logs", BOOL_TYPE, OPTIONAL, false);
|
665
767
|
|
666
768
|
addValidator(boost::bind(validateLogLevel, "level",
|
667
769
|
boost::placeholders::_1, boost::placeholders::_2));
|
@@ -681,6 +783,7 @@ Schema::Schema() {
|
|
681
783
|
ConfigRealization::ConfigRealization(const ConfigKit::Store &store)
|
682
784
|
: level(parseLevel(store["level"].asString())),
|
683
785
|
appOutputLogLevel(parseLevel(store["app_output_log_level"].asString())),
|
786
|
+
saveLog(store["buffer_logs"].asBool()),
|
684
787
|
finalized(false)
|
685
788
|
{
|
686
789
|
if (store["target"].isMember("stderr")) {
|