passenger 5.0.27 → 5.0.28
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 +14 -1
- data/CONTRIBUTORS +2 -0
- data/Rakefile +6 -5
- data/build/agent.rb +1 -1
- data/build/basics.rb +6 -71
- data/build/misc.rb +2 -2
- data/build/nginx.rb +18 -1
- data/build/packaging.rb +3 -1
- data/build/{cplusplus_support.rb → support/cplusplus.rb} +0 -0
- data/build/{cxx_dependency_map.rb → support/cxx_dependency_map.rb} +72 -1
- data/build/support/general.rb +106 -0
- data/dev/ci/run_travis.sh +9 -0
- data/dev/vagrant/nginx_rakefile +1 -1
- data/src/agent/Core/ApplicationPool/Options.h +4 -0
- data/src/agent/Core/Controller/CheckoutSession.cpp +1 -2
- data/src/agent/Core/Controller/InitRequest.cpp +1 -0
- data/src/agent/Core/SpawningKit/DirectSpawner.h +12 -0
- data/src/agent/Core/SpawningKit/SmartSpawner.h +13 -2
- data/src/agent/Core/SpawningKit/Spawner.h +35 -0
- data/src/agent/Core/SpawningKit/UserSwitchingRules.h +12 -6
- data/src/apache2_module/ConfigurationCommands.cpp +7 -0
- data/src/apache2_module/ConfigurationFields.hpp +2 -0
- data/src/apache2_module/ConfigurationSetters.cpp +34 -0
- data/src/apache2_module/CreateDirConfig.cpp +1 -0
- data/src/apache2_module/MergeDirConfig.cpp +7 -0
- data/src/apache2_module/SetHeaders.cpp +5 -0
- data/src/cxx_supportlib/Constants.h +3 -1
- data/src/cxx_supportlib/Logging.h +11 -8
- data/src/cxx_supportlib/LveLoggingDecorator.h +92 -0
- data/src/cxx_supportlib/MemoryKit/mbuf.cpp +51 -16
- data/src/cxx_supportlib/MemoryKit/mbuf.h +27 -24
- data/src/cxx_supportlib/ServerKit/HttpServer.h +4 -0
- data/src/cxx_supportlib/vendor-copy/adhoc_lve.h +304 -0
- data/src/nginx_module/Configuration.c +8 -0
- data/src/nginx_module/config +26 -12
- data/src/ruby_supportlib/phusion_passenger.rb +8 -8
- data/src/ruby_supportlib/phusion_passenger/admin_tools/memory_stats.rb +1 -1
- data/src/ruby_supportlib/phusion_passenger/apache2/config_options.rb +8 -0
- data/src/ruby_supportlib/phusion_passenger/common_library.rb +2 -2
- data/src/ruby_supportlib/phusion_passenger/config/about_command.rb +26 -1
- data/src/ruby_supportlib/phusion_passenger/config/main.rb +2 -0
- data/src/ruby_supportlib/phusion_passenger/constants.rb +1 -0
- data/src/ruby_supportlib/phusion_passenger/packaging.rb +1 -1
- data/src/ruby_supportlib/phusion_passenger/platform_info/cxx_portability.rb +1 -0
- data/src/ruby_supportlib/phusion_passenger/standalone/start_command/nginx_engine.rb +1 -1
- data/src/ruby_supportlib/phusion_passenger/vendor/crash_watch/gdb_controller.rb +2 -1
- metadata +7 -4
@@ -339,13 +339,13 @@ void printAppOutput(pid_t pid, const char *channelName, const char *message, uns
|
|
339
339
|
void setPrintAppOutputAsDebuggingMessages(bool enabled);
|
340
340
|
|
341
341
|
/** Print a [BUG] error message and abort with a stack trace. */
|
342
|
-
#define
|
342
|
+
#define P_BUG_WITH_FORMATTER_CODE(varname, code) \
|
343
343
|
do { \
|
344
344
|
TRACE_POINT(); \
|
345
345
|
const char *_exprStr; \
|
346
|
-
Passenger::FastStringStream<>
|
347
|
-
|
348
|
-
_exprStr = Passenger::_strdupFastStringStream(
|
346
|
+
Passenger::FastStringStream<> varname; \
|
347
|
+
code \
|
348
|
+
_exprStr = Passenger::_strdupFastStringStream(varname); \
|
349
349
|
Passenger::lastAssertionFailure.filename = __FILE__; \
|
350
350
|
Passenger::lastAssertionFailure.line = __LINE__; \
|
351
351
|
Passenger::lastAssertionFailure.function = __PRETTY_FUNCTION__; \
|
@@ -354,13 +354,13 @@ void setPrintAppOutputAsDebuggingMessages(bool enabled);
|
|
354
354
|
abort(); \
|
355
355
|
} while (false)
|
356
356
|
|
357
|
-
#define
|
357
|
+
#define P_BUG_UTP_WITH_FORMATTER_CODE(varname, code) \
|
358
358
|
do { \
|
359
359
|
UPDATE_TRACE_POINT(); \
|
360
360
|
const char *_exprStr; \
|
361
|
-
Passenger::FastStringStream<>
|
362
|
-
|
363
|
-
_exprStr = Passenger::_strdupFastStringStream(
|
361
|
+
Passenger::FastStringStream<> varname; \
|
362
|
+
code \
|
363
|
+
_exprStr = Passenger::_strdupFastStringStream(varname); \
|
364
364
|
Passenger::lastAssertionFailure.filename = __FILE__; \
|
365
365
|
Passenger::lastAssertionFailure.line = __LINE__; \
|
366
366
|
Passenger::lastAssertionFailure.function = __PRETTY_FUNCTION__; \
|
@@ -369,6 +369,9 @@ void setPrintAppOutputAsDebuggingMessages(bool enabled);
|
|
369
369
|
abort(); \
|
370
370
|
} while (false)
|
371
371
|
|
372
|
+
#define P_BUG(expr) P_BUG_WITH_FORMATTER_CODE( _sstream , _sstream << expr; )
|
373
|
+
#define P_BUG_UTP(expr) P_BUG_UTP_WITH_FORMATTER_CODE( _sstream , _sstream << expr; )
|
374
|
+
|
372
375
|
#define P_ASSERT_EQ(value, expected) \
|
373
376
|
do { \
|
374
377
|
if (OXT_UNLIKELY(value != expected)) { \
|
@@ -0,0 +1,92 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2010-2016 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_LVE_LOGGING_DECORATOR_H_
|
27
|
+
#define _PASSENGER_LVE_LOGGING_DECORATOR_H_
|
28
|
+
|
29
|
+
#include <Logging.h>
|
30
|
+
#include <adhoc_lve.h>
|
31
|
+
#include <string>
|
32
|
+
#include <sys/types.h>
|
33
|
+
#include <unistd.h>
|
34
|
+
|
35
|
+
namespace Passenger {
|
36
|
+
|
37
|
+
|
38
|
+
class LveLoggingDecorator {
|
39
|
+
public:
|
40
|
+
static adhoc_lve::LibLve &lveInitOnce() {
|
41
|
+
std::string initOneTimeError;
|
42
|
+
adhoc_lve::LibLve &lveLibHandle = adhoc_lve::LveInitSignleton::getInstance(&initOneTimeError);
|
43
|
+
|
44
|
+
if (!lveLibHandle.is_lve_available()) {
|
45
|
+
P_DEBUG("LVE lib is not available");
|
46
|
+
} else if (lveLibHandle.is_error()) {
|
47
|
+
if (!initOneTimeError.empty())
|
48
|
+
P_ERROR("LVE init error: " << initOneTimeError);
|
49
|
+
} else {
|
50
|
+
P_DEBUG("LVE get instance (or init) success");
|
51
|
+
}
|
52
|
+
|
53
|
+
return lveLibHandle;
|
54
|
+
}
|
55
|
+
|
56
|
+
static void logLveEnter(const adhoc_lve::LveEnter &lveEnter, uid_t uid, uid_t min_uid) {
|
57
|
+
if (lveEnter.lveInstance().is_lve_ready() && lveEnter.is_error()) {
|
58
|
+
P_ERROR("LVE enter [pid " << ::getpid() << ", uid " << uid
|
59
|
+
<< ", min_uid " << min_uid
|
60
|
+
<< "] error: " << lveEnter.error());
|
61
|
+
} else if (lveEnter.is_entered()) {
|
62
|
+
P_DEBUG("LVE enter [pid " << ::getpid() << ", uid " << uid
|
63
|
+
<< ", min_uid " << min_uid
|
64
|
+
<< "] success");
|
65
|
+
} else {
|
66
|
+
P_DEBUG("LVE not in [pid " << ::getpid() << ", uid " << uid
|
67
|
+
<< ", min_uid " << min_uid << "]");
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
static void lveExitCallback(bool entered, const std::string &exit_error) {
|
72
|
+
if (!entered) {
|
73
|
+
return;
|
74
|
+
}
|
75
|
+
|
76
|
+
try {
|
77
|
+
bool is_error = !exit_error.empty();
|
78
|
+
if (is_error) {
|
79
|
+
P_ERROR("LVE exit [pid " << ::getpid() << "] error: " << exit_error);
|
80
|
+
} else {
|
81
|
+
P_DEBUG("LVE exit [pid " << ::getpid() << "] success");
|
82
|
+
}
|
83
|
+
} catch(...) {
|
84
|
+
// Can be called from adhoc_lve::LveEnter destructor while stack unwinding.
|
85
|
+
}
|
86
|
+
}
|
87
|
+
}; // class LveLoggingDecorator
|
88
|
+
|
89
|
+
|
90
|
+
} // namespace Passenger
|
91
|
+
|
92
|
+
#endif /* _PASSENGER_LVE_LOGGING_DECORATOR_H_ */
|
@@ -1,6 +1,7 @@
|
|
1
1
|
/*
|
2
2
|
* twemproxy - A fast and lightweight proxy for memcached protocol.
|
3
3
|
* Copyright (C) 2011 Twitter, Inc.
|
4
|
+
* Copyright (C) 2014-2016 Phusion Holding B.V.
|
4
5
|
*
|
5
6
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
6
7
|
* you may not use this file except in compliance with the License.
|
@@ -19,15 +20,29 @@
|
|
19
20
|
#include <cstring>
|
20
21
|
#include <oxt/macros.hpp>
|
21
22
|
#include <oxt/thread.hpp>
|
22
|
-
#include <
|
23
|
+
#include <oxt/backtrace.hpp>
|
23
24
|
#include <algorithm>
|
25
|
+
#include <ostream>
|
24
26
|
#include <MemoryKit/mbuf.h>
|
27
|
+
#include <Logging.h>
|
25
28
|
|
26
29
|
namespace Passenger {
|
27
30
|
namespace MemoryKit {
|
28
31
|
|
29
32
|
//#define MBUF_DEBUG
|
30
33
|
|
34
|
+
#define ASSERT_MBUF_BLOCK_PROPERTY(mbuf_block, expr) \
|
35
|
+
do { \
|
36
|
+
if (OXT_UNLIKELY(!(expr))) { \
|
37
|
+
P_BUG_WITH_FORMATTER_CODE(stream, \
|
38
|
+
stream << "Assertion failed: " #expr "\n"; \
|
39
|
+
mbuf_block_print(mbuf_block, stream); \
|
40
|
+
); \
|
41
|
+
} \
|
42
|
+
} while (false)
|
43
|
+
|
44
|
+
static void mbuf_block_print(struct mbuf_block *mbuf_block, std::ostream &stream);
|
45
|
+
|
31
46
|
|
32
47
|
static void
|
33
48
|
_mbuf_block_mark_as_active(struct mbuf_pool *pool, struct mbuf_block *mbuf_block)
|
@@ -115,7 +130,7 @@ _mbuf_block_get(struct mbuf_pool *pool)
|
|
115
130
|
pool->nfree_mbuf_blockq--;
|
116
131
|
STAILQ_REMOVE_HEAD(&pool->free_mbuf_blockq, next);
|
117
132
|
|
118
|
-
|
133
|
+
ASSERT_MBUF_BLOCK_PROPERTY(mbuf_block, mbuf_block->magic == MBUF_BLOCK_MAGIC);
|
119
134
|
_mbuf_block_mark_as_active(pool, mbuf_block);
|
120
135
|
return mbuf_block;
|
121
136
|
}
|
@@ -143,8 +158,9 @@ mbuf_block_get(struct mbuf_pool *pool)
|
|
143
158
|
mbuf_block->start = buf;
|
144
159
|
mbuf_block->end = buf + pool->mbuf_block_offset;
|
145
160
|
|
146
|
-
|
147
|
-
|
161
|
+
ASSERT_MBUF_BLOCK_PROPERTY(mbuf_block,
|
162
|
+
mbuf_block->end - mbuf_block->start == (int) pool->mbuf_block_offset);
|
163
|
+
ASSERT_MBUF_BLOCK_PROPERTY(mbuf_block, mbuf_block->start < mbuf_block->end);
|
148
164
|
|
149
165
|
#ifdef MBUF_DEBUG
|
150
166
|
printf("[%p] mbuf_block get %p\n", oxt::thread_signature, mbuf_block);
|
@@ -171,8 +187,10 @@ mbuf_block_new_standalone(struct mbuf_pool *pool, size_t size)
|
|
171
187
|
mbuf_block->end = buf + size;
|
172
188
|
mbuf_block->offset = block_offset;
|
173
189
|
|
174
|
-
|
175
|
-
|
190
|
+
ASSERT_MBUF_BLOCK_PROPERTY(mbuf_block,
|
191
|
+
mbuf_block->end - mbuf_block->start == (int) size);
|
192
|
+
ASSERT_MBUF_BLOCK_PROPERTY(mbuf_block,
|
193
|
+
mbuf_block->start < mbuf_block->end);
|
176
194
|
|
177
195
|
#ifdef MBUF_DEBUG
|
178
196
|
printf("[%p] mbuf_block new standalone %p\n", oxt::thread_signature, mbuf_block);
|
@@ -190,8 +208,8 @@ mbuf_block_free(struct mbuf_block *mbuf_block)
|
|
190
208
|
printf("[%p] mbuf_block free %p\n", oxt::thread_signature, mbuf_block);
|
191
209
|
#endif
|
192
210
|
|
193
|
-
|
194
|
-
|
211
|
+
ASSERT_MBUF_BLOCK_PROPERTY(mbuf_block, STAILQ_NEXT(mbuf_block, next) == NULL);
|
212
|
+
ASSERT_MBUF_BLOCK_PROPERTY(mbuf_block, mbuf_block->magic == MBUF_BLOCK_MAGIC);
|
195
213
|
|
196
214
|
#ifdef MBUF_ENABLE_DEBUGGING
|
197
215
|
TAILQ_REMOVE(&mbuf_block->pool->active_mbuf_blockq, mbuf_block, active_q);
|
@@ -215,11 +233,11 @@ mbuf_block_put(struct mbuf_block *mbuf_block)
|
|
215
233
|
printf("[%p] mbuf_block put %p\n", oxt::thread_signature, mbuf_block);
|
216
234
|
#endif
|
217
235
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
236
|
+
ASSERT_MBUF_BLOCK_PROPERTY(mbuf_block, STAILQ_NEXT(mbuf_block, next) == NULL);
|
237
|
+
ASSERT_MBUF_BLOCK_PROPERTY(mbuf_block, mbuf_block->magic == MBUF_BLOCK_MAGIC);
|
238
|
+
ASSERT_MBUF_BLOCK_PROPERTY(mbuf_block, mbuf_block->refcount == 0);
|
239
|
+
ASSERT_MBUF_BLOCK_PROPERTY(mbuf_block, mbuf_block->pool->nactive_mbuf_blockq > 0);
|
240
|
+
ASSERT_MBUF_BLOCK_PROPERTY(mbuf_block, mbuf_block->offset == 0);
|
223
241
|
|
224
242
|
mbuf_block->refcount = 1;
|
225
243
|
mbuf_block->pool->nfree_mbuf_blockq++;
|
@@ -312,7 +330,7 @@ mbuf_block_unref(struct mbuf_block *mbuf_block)
|
|
312
330
|
oxt::thread_signature, mbuf_block,
|
313
331
|
mbuf_block->refcount, mbuf_block->refcount - 1);
|
314
332
|
#endif
|
315
|
-
|
333
|
+
ASSERT_MBUF_BLOCK_PROPERTY(mbuf_block, mbuf_block->refcount > 0);
|
316
334
|
mbuf_block->refcount--;
|
317
335
|
if (mbuf_block->refcount == 0) {
|
318
336
|
if (mbuf_block->offset > 0) {
|
@@ -338,7 +356,7 @@ mbuf_get(struct mbuf_pool *pool)
|
|
338
356
|
return mbuf();
|
339
357
|
}
|
340
358
|
|
341
|
-
|
359
|
+
ASSERT_MBUF_BLOCK_PROPERTY(block, block->refcount == 1);
|
342
360
|
block->refcount--;
|
343
361
|
return mbuf(block, 0, block->end - block->start);
|
344
362
|
}
|
@@ -356,11 +374,28 @@ mbuf_get_with_size(struct mbuf_pool *pool, size_t size)
|
|
356
374
|
return mbuf();
|
357
375
|
}
|
358
376
|
|
359
|
-
|
377
|
+
ASSERT_MBUF_BLOCK_PROPERTY(block, block->refcount == 1);
|
360
378
|
block->refcount--;
|
361
379
|
return mbuf(block, 0, size);
|
362
380
|
}
|
363
381
|
|
382
|
+
static void
|
383
|
+
mbuf_block_print(struct mbuf_block *mbuf_block, std::ostream &stream)
|
384
|
+
{
|
385
|
+
stream << "mbuf_block: " << (void *) mbuf_block << "\n"
|
386
|
+
"mbuf_block.magic: " << mbuf_block->magic << "\n"
|
387
|
+
"mbuf_block.next: " << (void *) STAILQ_NEXT(mbuf_block, next) << "\n"
|
388
|
+
"mbuf_block.start: " << (void *) mbuf_block->start << "\n"
|
389
|
+
"mbuf_block.end: " << (void *) mbuf_block->end << "\n"
|
390
|
+
"mbuf_block.refcount: " << mbuf_block->refcount << "\n"
|
391
|
+
"mbuf_block.offset: " << mbuf_block->offset << "\n"
|
392
|
+
"mbuf_block.pool: " << (void *) mbuf_block->pool << "\n"
|
393
|
+
"mbuf_block.pool.nfree_mbuf_blockq: " << mbuf_block->pool->nfree_mbuf_blockq << "\n"
|
394
|
+
"mbuf_block.pool.nactive_mbuf_blockq: " << mbuf_block->pool->nactive_mbuf_blockq << "\n"
|
395
|
+
"mbuf_block.pool.mbuf_block_chunk_size: " << mbuf_block->pool->mbuf_block_chunk_size << "\n"
|
396
|
+
"mbuf_block.pool.mbuf_block_offset: " << mbuf_block->pool->mbuf_block_offset << "\n";
|
397
|
+
}
|
398
|
+
|
364
399
|
|
365
400
|
template<typename Address>
|
366
401
|
static Address clamp(Address value, Address min, Address max) {
|
@@ -1,7 +1,7 @@
|
|
1
1
|
/*
|
2
2
|
* twemproxy - A fast and lightweight proxy for memcached protocol.
|
3
3
|
* Copyright (C) 2011 Twitter, Inc.
|
4
|
-
* Copyright (
|
4
|
+
* Copyright (C) 2014-2016 Phusion Holding B.V.
|
5
5
|
*
|
6
6
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
7
7
|
* you may not use this file except in compliance with the License.
|
@@ -200,36 +200,39 @@ public:
|
|
200
200
|
|
201
201
|
// Copy assignment.
|
202
202
|
mbuf &operator=(BOOST_COPY_ASSIGN_REF(mbuf) mbuf) {
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
203
|
+
if (&mbuf != this) {
|
204
|
+
struct mbuf_block *old_block = mbuf_block;
|
205
|
+
|
206
|
+
mbuf_block = mbuf.mbuf_block;
|
207
|
+
start = mbuf.start;
|
208
|
+
end = mbuf.end;
|
209
|
+
|
210
|
+
if (mbuf.mbuf_block != NULL) {
|
211
|
+
mbuf_block_ref(mbuf.mbuf_block);
|
212
|
+
}
|
213
|
+
if (old_block != NULL) {
|
214
|
+
mbuf_block_unref(old_block);
|
215
|
+
}
|
214
216
|
}
|
215
217
|
return *this;
|
216
218
|
}
|
217
219
|
|
218
220
|
// Move assignment.
|
219
221
|
mbuf &operator=(BOOST_RV_REF(mbuf) mbuf) {
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
222
|
+
if (&mbuf != this) {
|
223
|
+
struct mbuf_block *old_block = mbuf_block;
|
224
|
+
|
225
|
+
mbuf_block = mbuf.mbuf_block;
|
226
|
+
start = mbuf.start;
|
227
|
+
end = mbuf.end;
|
228
|
+
mbuf.mbuf_block = NULL;
|
229
|
+
mbuf.start = NULL;
|
230
|
+
mbuf.end = NULL;
|
231
|
+
|
232
|
+
if (old_block != NULL) {
|
233
|
+
mbuf_block_unref(old_block);
|
234
|
+
}
|
231
235
|
}
|
232
|
-
|
233
236
|
return *this;
|
234
237
|
}
|
235
238
|
|
@@ -161,6 +161,10 @@ private:
|
|
161
161
|
} else {
|
162
162
|
SKC_TRACE(client, 3, "Request object destroyed; not added to freelist " <<
|
163
163
|
"because it's full (" << freeRequestCount << ")");
|
164
|
+
if (request->pool != NULL) {
|
165
|
+
psg_destroy_pool(request->pool);
|
166
|
+
request->pool = NULL;
|
167
|
+
}
|
164
168
|
delete request;
|
165
169
|
}
|
166
170
|
|
@@ -0,0 +1,304 @@
|
|
1
|
+
/*
|
2
|
+
* Phusion Passenger - https://www.phusionpassenger.com/
|
3
|
+
* Copyright (c) 2011-2016 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 _ADHOC_LVE_H_
|
27
|
+
#define _ADHOC_LVE_H_
|
28
|
+
|
29
|
+
// A library for using CloudLinux's LVE technology.
|
30
|
+
// https://www.cloudlinux.com/lve-manage.php
|
31
|
+
// http://docs.cloudlinux.com/understanding_lve.html
|
32
|
+
|
33
|
+
#include <functional>
|
34
|
+
#include <cstdlib>
|
35
|
+
#include <cstddef>
|
36
|
+
#include <sstream>
|
37
|
+
|
38
|
+
#include <boost/shared_ptr.hpp>
|
39
|
+
#include <boost/scoped_ptr.hpp>
|
40
|
+
#include <boost/thread/once.hpp>
|
41
|
+
|
42
|
+
#include <sys/types.h>
|
43
|
+
#include <unistd.h>
|
44
|
+
#include <dlfcn.h>
|
45
|
+
#include <errno.h>
|
46
|
+
#include <stdint.h>
|
47
|
+
|
48
|
+
|
49
|
+
namespace adhoc_lve {
|
50
|
+
|
51
|
+
struct liblve;
|
52
|
+
|
53
|
+
enum liblve_enter_flags {
|
54
|
+
LVE_NO_UBC = 1 << 0,
|
55
|
+
LVE_NO_NAMESPACE = 1 << 1,
|
56
|
+
LVE_NO_MAXENTER = 1 << 2,
|
57
|
+
LVE_SILENCE = 1 << 3,
|
58
|
+
};
|
59
|
+
|
60
|
+
typedef void* (*liblve_alloc) (size_t size);
|
61
|
+
typedef void (*liblve_free) (void *ptr);
|
62
|
+
|
63
|
+
/**
|
64
|
+
* initializes and create instance of LVE
|
65
|
+
* args
|
66
|
+
* allocator - pointer to function to allocate memory
|
67
|
+
* returns
|
68
|
+
* NULL on error, errno will be set.
|
69
|
+
* errno will be EINVAL if wrong version of library is used
|
70
|
+
* liblve otherwise
|
71
|
+
*/
|
72
|
+
typedef struct liblve* (*init_lve_function_ptr_t)(liblve_alloc alloc, liblve_free free);
|
73
|
+
|
74
|
+
/**
|
75
|
+
* destroy lve library instance
|
76
|
+
* args:
|
77
|
+
* lve = instantiated liblive instance
|
78
|
+
* return 0 on success
|
79
|
+
* negative number on error. errno will be set
|
80
|
+
*/
|
81
|
+
typedef int (*destroy_lve_function_ptr_t)(struct liblve *lve);
|
82
|
+
|
83
|
+
/**
|
84
|
+
* enter into virutal environment
|
85
|
+
* args:
|
86
|
+
* lve = fully initialized liblve instance
|
87
|
+
* lve_id = id associated with LVE
|
88
|
+
* cookie = pointer to cookie, which returned if task correctly migrated
|
89
|
+
* in LVE and used to exit from this LVE
|
90
|
+
* return codes:
|
91
|
+
* 0 = on success, negative number means error:
|
92
|
+
* -EPERM - don't have permission to call, or called from outside root LVE
|
93
|
+
* -ENOMEM - don't have memory to allocate new LVE
|
94
|
+
* -EFAULT - cookie is bad pointer
|
95
|
+
*/
|
96
|
+
typedef int (*lve_enter_flags_function_ptr_t)
|
97
|
+
(struct liblve *lve,
|
98
|
+
uint32_t lve_id,
|
99
|
+
uint32_t *cookie,
|
100
|
+
int liblve_enter_flags);
|
101
|
+
|
102
|
+
/**
|
103
|
+
* exit from virtual environment, same as lve_leave
|
104
|
+
* args:
|
105
|
+
* lve = fully init liblve instance
|
106
|
+
* cookie - pointer to cookie returned from lve_enter
|
107
|
+
* return codes:
|
108
|
+
* 0 = none error, all less zero is errors:
|
109
|
+
* -ESRCH = task not in virutal environment
|
110
|
+
* -EFAULT = bad cookie pointer
|
111
|
+
* -EINVAL = cookie not match to stored in context
|
112
|
+
*/
|
113
|
+
typedef int (*lve_exit_function_ptr_t)(struct liblve *lve, uint32_t *cookie);
|
114
|
+
|
115
|
+
typedef int (*jail_function_ptr_t)(const struct passwd *, char*);
|
116
|
+
|
117
|
+
/**
|
118
|
+
* Must be used once per app instances
|
119
|
+
*/
|
120
|
+
class LibLve : private boost::noncopyable {
|
121
|
+
public:
|
122
|
+
|
123
|
+
#ifdef LIBLVE_LOAD
|
124
|
+
# error LIBLVE_LOAD macro redifinition
|
125
|
+
#else
|
126
|
+
# define LIBLVE_LOAD(fname) \
|
127
|
+
fname ## _function_ptr = \
|
128
|
+
(fname ## _function_ptr_t) dlsym(liblve_handle.get(), #fname); \
|
129
|
+
if (!fname ## _function_ptr && err_buf.str().empty()) \
|
130
|
+
init_error = (std::string) "Failed to init LVE library " + ::dlerror()
|
131
|
+
|
132
|
+
LibLve()
|
133
|
+
: init_lve_function_ptr (NULL)
|
134
|
+
, destroy_lve_function_ptr (NULL)
|
135
|
+
, lve_enter_flags_function_ptr (NULL)
|
136
|
+
, lve_exit_function_ptr (NULL)
|
137
|
+
{
|
138
|
+
void *handle = ::dlopen("liblve.so.0", RTLD_LAZY);
|
139
|
+
if (handle) {
|
140
|
+
liblve_handle.reset(handle, ::dlclose);
|
141
|
+
} else {
|
142
|
+
// No liblve found, but it is OK and we are running
|
143
|
+
// on non LVE capable system
|
144
|
+
return;
|
145
|
+
}
|
146
|
+
|
147
|
+
std::ostringstream err_buf;
|
148
|
+
|
149
|
+
LIBLVE_LOAD(init_lve);
|
150
|
+
LIBLVE_LOAD(destroy_lve);
|
151
|
+
LIBLVE_LOAD(lve_enter_flags);
|
152
|
+
LIBLVE_LOAD(lve_exit);
|
153
|
+
LIBLVE_LOAD(jail);
|
154
|
+
|
155
|
+
if (!err_buf.str().empty())
|
156
|
+
{
|
157
|
+
init_error = err_buf.str();
|
158
|
+
return;
|
159
|
+
}
|
160
|
+
|
161
|
+
struct liblve* init_handle = init_lve_function_ptr(std::malloc, std::free);
|
162
|
+
if (!init_handle)
|
163
|
+
err_buf << "init_lve error [" << errno << "]";
|
164
|
+
else
|
165
|
+
lve_init_handle.reset(init_handle, destroy_lve_function_ptr);
|
166
|
+
|
167
|
+
init_error = err_buf.str();
|
168
|
+
}
|
169
|
+
|
170
|
+
#undef LIBLVE_LOAD
|
171
|
+
#endif
|
172
|
+
|
173
|
+
bool is_error() const { return !init_error.empty(); }
|
174
|
+
const std::string& error() const { return init_error; }
|
175
|
+
|
176
|
+
bool is_lve_available() const { return (bool) liblve_handle; }
|
177
|
+
bool is_lve_ready() const { return is_lve_available() && !is_error(); }
|
178
|
+
|
179
|
+
int jail(const struct passwd *pw, std::string& jail_err) const
|
180
|
+
{
|
181
|
+
char error_msg[8192];
|
182
|
+
int rc = jail_function_ptr(pw, error_msg);
|
183
|
+
if (rc < 0)
|
184
|
+
jail_err.assign(error_msg);
|
185
|
+
return rc;
|
186
|
+
}
|
187
|
+
|
188
|
+
private:
|
189
|
+
friend class LveEnter;
|
190
|
+
|
191
|
+
/**
|
192
|
+
* using boost::shared_ptr because of scoped_ptr does not allow
|
193
|
+
* to specify custom destructor
|
194
|
+
*/
|
195
|
+
boost::shared_ptr<void> liblve_handle;
|
196
|
+
boost::shared_ptr<struct liblve> lve_init_handle;
|
197
|
+
|
198
|
+
init_lve_function_ptr_t init_lve_function_ptr;
|
199
|
+
destroy_lve_function_ptr_t destroy_lve_function_ptr;
|
200
|
+
lve_enter_flags_function_ptr_t lve_enter_flags_function_ptr;
|
201
|
+
lve_exit_function_ptr_t lve_exit_function_ptr;
|
202
|
+
jail_function_ptr_t jail_function_ptr;
|
203
|
+
|
204
|
+
std::string init_error;
|
205
|
+
};
|
206
|
+
|
207
|
+
class LveInitSignleton {
|
208
|
+
public:
|
209
|
+
static LibLve& getInstance(std::string* outInitOneTimeError) {
|
210
|
+
if (!instance())
|
211
|
+
{
|
212
|
+
static boost::once_flag flag = BOOST_ONCE_INIT;
|
213
|
+
boost::call_once(flag, initOnce, outInitOneTimeError);
|
214
|
+
}
|
215
|
+
|
216
|
+
return *instance();
|
217
|
+
}
|
218
|
+
private:
|
219
|
+
typedef boost::scoped_ptr<LibLve> lvelib_scoped_ptr;
|
220
|
+
|
221
|
+
static lvelib_scoped_ptr& instance() {
|
222
|
+
static lvelib_scoped_ptr lvelib_scoped_ptr;
|
223
|
+
return lvelib_scoped_ptr;
|
224
|
+
}
|
225
|
+
|
226
|
+
static void initOnce(std::string *err_buf) {
|
227
|
+
instance().reset(new LibLve);
|
228
|
+
|
229
|
+
if (instance()->is_error() && err_buf)
|
230
|
+
*err_buf = instance()->error();
|
231
|
+
}
|
232
|
+
};
|
233
|
+
|
234
|
+
class LveEnter : private boost::noncopyable {
|
235
|
+
public:
|
236
|
+
typedef void (*exit_callback_t)(bool entered, const std::string& exit_error);
|
237
|
+
|
238
|
+
LveEnter(LibLve& lve, uint32_t uid, uint32_t cfg_min_uid, exit_callback_t cb)
|
239
|
+
: ctx(lve)
|
240
|
+
, cookie(0)
|
241
|
+
, entered(false)
|
242
|
+
, exit_callback(cb)
|
243
|
+
{
|
244
|
+
enter(uid, cfg_min_uid);
|
245
|
+
}
|
246
|
+
~LveEnter()
|
247
|
+
{
|
248
|
+
exit();
|
249
|
+
}
|
250
|
+
|
251
|
+
LveEnter& enter(uint32_t uid, uint32_t min_uid) {
|
252
|
+
bool is_enter_lve_allowed = (min_uid <= uid);
|
253
|
+
|
254
|
+
if (!is_enter_lve_allowed || !ctx.is_lve_ready() || entered)
|
255
|
+
return *this;
|
256
|
+
|
257
|
+
int err = ctx.lve_enter_flags_function_ptr(ctx.lve_init_handle.get(),
|
258
|
+
uid,
|
259
|
+
&cookie,
|
260
|
+
LVE_NO_MAXENTER|LVE_SILENCE);
|
261
|
+
if (!err)
|
262
|
+
entered = true;
|
263
|
+
else
|
264
|
+
enter_exit_error << "lve_enter_flags error [" << err << "]";
|
265
|
+
|
266
|
+
return *this;
|
267
|
+
}
|
268
|
+
|
269
|
+
LveEnter& exit() {
|
270
|
+
const bool memento (entered);
|
271
|
+
|
272
|
+
if (entered)
|
273
|
+
{
|
274
|
+
int err = ctx.lve_exit_function_ptr(ctx.lve_init_handle.get(), &cookie);
|
275
|
+
if (err)
|
276
|
+
enter_exit_error << "lve_exit error [" << err << "]";
|
277
|
+
|
278
|
+
entered = false;
|
279
|
+
}
|
280
|
+
|
281
|
+
if (exit_callback)
|
282
|
+
exit_callback(memento, enter_exit_error.str());
|
283
|
+
|
284
|
+
return *this;
|
285
|
+
}
|
286
|
+
|
287
|
+
bool is_entered() const { return entered; }
|
288
|
+
bool is_error() const { return !enter_exit_error.str().empty(); }
|
289
|
+
std::string error() const { return enter_exit_error.str(); }
|
290
|
+
|
291
|
+
LibLve& lveInstance() { return ctx; }
|
292
|
+
const LibLve& lveInstance() const { return ctx; }
|
293
|
+
|
294
|
+
private:
|
295
|
+
LibLve &ctx;
|
296
|
+
uint32_t cookie;
|
297
|
+
bool entered;
|
298
|
+
std::ostringstream enter_exit_error;
|
299
|
+
exit_callback_t exit_callback;
|
300
|
+
};
|
301
|
+
|
302
|
+
} // adhoc_lve ns
|
303
|
+
|
304
|
+
#endif /* _ADHOC_LVE_H_ */
|