passenger 4.0.17 → 4.0.18
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.
- data.tar.gz.asc +7 -7
- data/NEWS +21 -0
- data/bin/passenger +2 -0
- data/bin/passenger-config +2 -0
- data/bin/passenger-install-apache2-module +2 -0
- data/bin/passenger-install-nginx-module +2 -0
- data/bin/passenger-memory-stats +2 -0
- data/bin/passenger-status +2 -0
- data/build/debian.rb +3 -1
- data/build/packaging.rb +42 -2
- data/build/preprocessor.rb +7 -0
- data/debian.template/control.template +9 -3
- data/debian.template/rules.template +2 -1
- data/doc/Users guide Apache.idmap.txt +5 -1
- data/doc/Users guide Nginx.idmap.txt +5 -1
- data/doc/users_guide_snippets/installation.txt +71 -64
- data/ext/apache2/Hooks.cpp +17 -1
- data/ext/common/ApplicationPool2/Process.h +38 -2
- data/ext/common/Constants.h +5 -1
- data/ext/common/EventedClient.h +22 -1
- data/ext/common/EventedMessageServer.h +15 -0
- data/ext/common/UnionStation.h +2 -2
- data/ext/common/agents/HelperAgent/RequestHandler.h +24 -9
- data/ext/common/agents/LoggingAgent/LoggingServer.h +24 -11
- data/ext/oxt/backtrace.hpp +4 -6
- data/ext/oxt/detail/backtrace_disabled.hpp +1 -0
- data/ext/oxt/detail/backtrace_enabled.hpp +10 -4
- data/ext/oxt/detail/context.hpp +1 -0
- data/ext/oxt/implementation.cpp +21 -7
- data/helper-scripts/download_binaries/extconf.rb +1 -1
- data/lib/phusion_passenger.rb +1 -1
- data/lib/phusion_passenger/analytics_logger.rb +3 -3
- data/lib/phusion_passenger/constants.rb +2 -0
- data/lib/phusion_passenger/packaging.rb +16 -0
- data/lib/phusion_passenger/standalone/command.rb +3 -0
- data/lib/phusion_passenger/standalone/start_command.rb +40 -2
- data/resources/templates/standalone/config.erb +3 -0
- data/test/cxx/UnionStationTest.cpp +11 -11
- data/test/ruby/analytics_logger_spec.rb +1 -1
- metadata +2 -2
- metadata.gz.asc +7 -7
data/ext/oxt/detail/context.hpp
CHANGED
data/ext/oxt/implementation.cpp
CHANGED
@@ -64,7 +64,7 @@ static global_context_t *global_context = NULL;
|
|
64
64
|
/* Do nothing. */
|
65
65
|
}
|
66
66
|
|
67
|
-
|
67
|
+
void
|
68
68
|
set_thread_local_context(const thread_local_context_ptr &ctx) {
|
69
69
|
local_context = new thread_local_context_ptr(ctx);
|
70
70
|
}
|
@@ -99,7 +99,7 @@ static global_context_t *global_context = NULL;
|
|
99
99
|
local_context = new thread_specific_ptr<thread_local_context_ptr>();
|
100
100
|
}
|
101
101
|
|
102
|
-
|
102
|
+
void
|
103
103
|
set_thread_local_context(const thread_local_context_ptr &ctx) {
|
104
104
|
if (local_context != NULL) {
|
105
105
|
local_context->reset(new thread_local_context_ptr(ctx));
|
@@ -131,9 +131,11 @@ static global_context_t *global_context = NULL;
|
|
131
131
|
|
132
132
|
#ifdef OXT_BACKTRACE_IS_ENABLED
|
133
133
|
|
134
|
-
trace_point::trace_point(const char *_function, const char *_source, unsigned
|
134
|
+
trace_point::trace_point(const char *_function, const char *_source, unsigned short _line,
|
135
|
+
const char *_data)
|
135
136
|
: function(_function),
|
136
137
|
source(_source),
|
138
|
+
data(_data),
|
137
139
|
line(_line),
|
138
140
|
m_detached(false)
|
139
141
|
{
|
@@ -146,9 +148,11 @@ trace_point::trace_point(const char *_function, const char *_source, unsigned in
|
|
146
148
|
}
|
147
149
|
}
|
148
150
|
|
149
|
-
trace_point::trace_point(const char *_function, const char *_source, unsigned
|
151
|
+
trace_point::trace_point(const char *_function, const char *_source, unsigned short _line,
|
152
|
+
const char *_data, const detached &detached_tag)
|
150
153
|
: function(_function),
|
151
154
|
source(_source),
|
155
|
+
data(_data),
|
152
156
|
line(_line),
|
153
157
|
m_detached(true)
|
154
158
|
{ }
|
@@ -165,7 +169,7 @@ trace_point::~trace_point() {
|
|
165
169
|
}
|
166
170
|
|
167
171
|
void
|
168
|
-
trace_point::update(const char *source, unsigned
|
172
|
+
trace_point::update(const char *source, unsigned short line) {
|
169
173
|
this->source = source;
|
170
174
|
this->line = line;
|
171
175
|
}
|
@@ -183,7 +187,8 @@ tracable_exception::tracable_exception() {
|
|
183
187
|
(*it)->function,
|
184
188
|
(*it)->source,
|
185
189
|
(*it)->line,
|
186
|
-
|
190
|
+
(*it)->data,
|
191
|
+
trace_point::detached());
|
187
192
|
backtrace_copy.push_back(p);
|
188
193
|
}
|
189
194
|
}
|
@@ -199,7 +204,8 @@ tracable_exception::tracable_exception(const tracable_exception &other)
|
|
199
204
|
(*it)->function,
|
200
205
|
(*it)->source,
|
201
206
|
(*it)->line,
|
202
|
-
|
207
|
+
(*it)->data,
|
208
|
+
trace_point::detached());
|
203
209
|
backtrace_copy.push_back(p);
|
204
210
|
}
|
205
211
|
}
|
@@ -239,6 +245,9 @@ format_backtrace(const Collection &backtrace_list) {
|
|
239
245
|
source = p->source;
|
240
246
|
}
|
241
247
|
result << " (" << source << ":" << p->line << ")";
|
248
|
+
if (p->data != NULL) {
|
249
|
+
result << " -- " << p->data;
|
250
|
+
}
|
242
251
|
}
|
243
252
|
result << endl;
|
244
253
|
}
|
@@ -268,6 +277,11 @@ void initialize() {
|
|
268
277
|
ctx->thread_number = 1;
|
269
278
|
ctx->thread_name = "Main thread";
|
270
279
|
set_thread_local_context(ctx);
|
280
|
+
|
281
|
+
ctx->thread = pthread_self();
|
282
|
+
global_context->registered_threads.push_back(ctx);
|
283
|
+
ctx->iterator = global_context->registered_threads.end();
|
284
|
+
ctx->iterator--;
|
271
285
|
}
|
272
286
|
|
273
287
|
|
@@ -35,7 +35,7 @@ end
|
|
35
35
|
|
36
36
|
# Don't do anything on Windows. We don't support Windows but exiting now
|
37
37
|
# will at least prevent the gem from being not installable on Windows.
|
38
|
-
exit if RUBY_PLATFORM =~ /mswin/i || RUBY_PLATFORM =~ /win32/i
|
38
|
+
exit if RUBY_PLATFORM =~ /mswin/i || RUBY_PLATFORM =~ /win32/i || RUBY_PLATFORM =~ /mingw/
|
39
39
|
|
40
40
|
source_root = File.expand_path("../..", File.dirname(__FILE__))
|
41
41
|
$LOAD_PATH.unshift("#{source_root}/lib")
|
data/lib/phusion_passenger.rb
CHANGED
@@ -30,7 +30,7 @@ module PhusionPassenger
|
|
30
30
|
|
31
31
|
PACKAGE_NAME = 'passenger'
|
32
32
|
# Run 'rake ext/common/Constants.h' after changing this number.
|
33
|
-
VERSION_STRING = '4.0.
|
33
|
+
VERSION_STRING = '4.0.18'
|
34
34
|
|
35
35
|
PREFERRED_NGINX_VERSION = '1.4.2'
|
36
36
|
NGINX_SHA256_CHECKSUM = '5361ffb7b0ebf8b1a04369bc3d1295eaed091680c1c58115f88d56c8e51f3611'
|
@@ -125,7 +125,7 @@ class AnalyticsLogger
|
|
125
125
|
@connection.synchronize do
|
126
126
|
return if !@connection.connected?
|
127
127
|
begin
|
128
|
-
# We need an ACK here. See
|
128
|
+
# We need an ACK here. See thread_handler.rb finalize_request.
|
129
129
|
@connection.channel.write("closeTransaction", @txn_id,
|
130
130
|
AnalyticsLogger.timestamp_string, true)
|
131
131
|
result = @connection.channel.read
|
@@ -228,7 +228,7 @@ class AnalyticsLogger
|
|
228
228
|
end
|
229
229
|
end
|
230
230
|
|
231
|
-
def new_transaction(group_name, category = :requests, union_station_key =
|
231
|
+
def new_transaction(group_name, category = :requests, union_station_key = "-")
|
232
232
|
if !@server_address
|
233
233
|
return Log.new
|
234
234
|
elsif !group_name || group_name.empty?
|
@@ -287,7 +287,7 @@ class AnalyticsLogger
|
|
287
287
|
end
|
288
288
|
end
|
289
289
|
|
290
|
-
def continue_transaction(txn_id, group_name, category = :requests, union_station_key =
|
290
|
+
def continue_transaction(txn_id, group_name, category = :requests, union_station_key = "-")
|
291
291
|
if !@server_address
|
292
292
|
return Log.new
|
293
293
|
elsif !txn_id || txn_id.empty?
|
@@ -42,6 +42,8 @@ module PhusionPassenger
|
|
42
42
|
DEFAULT_START_TIMEOUT = 90_000
|
43
43
|
DEFAULT_MAX_INSTANCES_PER_APP = 0
|
44
44
|
DEFAULT_WEB_APP_USER = "nobody"
|
45
|
+
DEFAULT_CONCURRENCY_MODEL = "process"
|
46
|
+
DEFAULT_THREAD_COUNT = 1
|
45
47
|
DEFAULT_ANALYTICS_LOG_USER = DEFAULT_WEB_APP_USER
|
46
48
|
DEFAULT_ANALYTICS_LOG_GROUP = ""
|
47
49
|
DEFAULT_ANALYTICS_LOG_PERMISSIONS = "u=rwx,g=rx,o=rx"
|
@@ -50,6 +50,17 @@ module Packaging
|
|
50
50
|
'passenger-status',
|
51
51
|
'passenger-memory-stats'
|
52
52
|
]
|
53
|
+
|
54
|
+
# Used during native packaging. Specifies executables for
|
55
|
+
# which the shebang should NOT be set to #!/usr/bin/ruby,
|
56
|
+
# so that these executables can be run with any Ruby interpreter
|
57
|
+
# the user desires.
|
58
|
+
EXECUTABLES_WITH_FREE_RUBY = [
|
59
|
+
'passenger',
|
60
|
+
'passenger-config',
|
61
|
+
'passenger-install-apache2-module',
|
62
|
+
'passenger-install-nginx-module'
|
63
|
+
]
|
53
64
|
|
54
65
|
# A list of globs which match all files that should be packaged
|
55
66
|
# in the Phusion Passenger gem or tarball.
|
@@ -119,6 +130,11 @@ module Packaging
|
|
119
130
|
"debian.template/**/*",
|
120
131
|
]
|
121
132
|
|
133
|
+
# Files and directories that should be excluded from the Homebrew installation.
|
134
|
+
HOMEBREW_EXCLUDE = [
|
135
|
+
"dev", "test", ".gitignore", ".travis.yml", "debian.template", "rpm"
|
136
|
+
]
|
137
|
+
|
122
138
|
def self.files
|
123
139
|
return Dir[*GLOB] - Dir[*EXCLUDE_GLOB]
|
124
140
|
end
|
@@ -36,6 +36,8 @@ class Command
|
|
36
36
|
:max_pool_size => 6,
|
37
37
|
:min_instances => 1,
|
38
38
|
:spawn_method => Kernel.respond_to?(:fork) ? 'smart' : 'direct',
|
39
|
+
:concurrency_model => DEFAULT_CONCURRENCY_MODEL,
|
40
|
+
:thread_count => DEFAULT_THREAD_COUNT,
|
39
41
|
:nginx_version => PREFERRED_NGINX_VERSION,
|
40
42
|
:friendly_error_pages => true
|
41
43
|
}.freeze
|
@@ -187,6 +189,7 @@ private
|
|
187
189
|
@config_filename = "#{@temp_dir}/config"
|
188
190
|
location_config_filename = "#{@temp_dir}/locations.ini"
|
189
191
|
File.chmod(0755, @temp_dir)
|
192
|
+
Dir.mkdir("#{@temp_dir}/logs")
|
190
193
|
|
191
194
|
locations_ini_fields =
|
192
195
|
PhusionPassenger::REQUIRED_LOCATIONS_INI_FIELDS +
|
@@ -85,10 +85,12 @@ class StartCommand < Command
|
|
85
85
|
watch_log_files_in_background if should_watch_logs?
|
86
86
|
wait_until_nginx_has_exited if should_wait_until_nginx_has_exited?
|
87
87
|
rescue Interrupt
|
88
|
+
begin_shutdown
|
88
89
|
stop_threads
|
89
90
|
stop_nginx
|
90
91
|
exit 2
|
91
92
|
rescue SignalException => signal
|
93
|
+
begin_shutdown
|
92
94
|
stop_threads
|
93
95
|
stop_nginx
|
94
96
|
if signal.message == 'SIGINT' || signal.message == 'SIGTERM'
|
@@ -97,12 +99,18 @@ class StartCommand < Command
|
|
97
99
|
raise
|
98
100
|
end
|
99
101
|
rescue Exception => e
|
102
|
+
begin_shutdown
|
100
103
|
stop_threads
|
101
104
|
stop_nginx
|
102
105
|
raise
|
103
106
|
ensure
|
104
|
-
|
105
|
-
|
107
|
+
begin_shutdown
|
108
|
+
begin
|
109
|
+
stop_touching_temp_dir_in_background if should_wait_until_nginx_has_exited?
|
110
|
+
stop_threads
|
111
|
+
ensure
|
112
|
+
finalize_shutdown
|
113
|
+
end
|
106
114
|
end
|
107
115
|
ensure
|
108
116
|
if @temp_dir
|
@@ -155,6 +163,14 @@ private
|
|
155
163
|
wrap_desc("The spawn method to use (default: #{@options[:spawn_method]})")) do |value|
|
156
164
|
@options[:spawn_method] = value
|
157
165
|
end
|
166
|
+
opts.on("--concurrency-model NAME", String,
|
167
|
+
wrap_desc("The concurrency model to use, either 'process' or 'thread' (default: #{@options[:concurrency_model]}) (Enterprise only)")) do |value|
|
168
|
+
@options[:concurrency_model] = value
|
169
|
+
end
|
170
|
+
opts.on("--thread-count NAME", Integer,
|
171
|
+
wrap_desc("The number of threads to use when using the 'thread' concurrency model (default: #{@options[:thread_count]}) (Enterprise only)")) do |value|
|
172
|
+
@options[:thread_count] = value
|
173
|
+
end
|
158
174
|
opts.on("--rolling-restarts",
|
159
175
|
wrap_desc("Enable rolling restarts (Enterprise only)")) do
|
160
176
|
@options[:rolling_restarts] = true
|
@@ -560,6 +576,28 @@ private
|
|
560
576
|
@toucher = IO.popen("sh #{script} #{dir}", "r")
|
561
577
|
end
|
562
578
|
|
579
|
+
def begin_shutdown
|
580
|
+
return if @shutting_down
|
581
|
+
@shutting_down = 1
|
582
|
+
trap("INT", &method(:signal_during_shutdown))
|
583
|
+
trap("TERM", &method(:signal_during_shutdown))
|
584
|
+
end
|
585
|
+
|
586
|
+
def finalize_shutdown
|
587
|
+
@shutting_down = nil
|
588
|
+
trap("INT", "DEFAULT")
|
589
|
+
trap("TERM", "DEFAULT")
|
590
|
+
end
|
591
|
+
|
592
|
+
def signal_during_shutdown(signal)
|
593
|
+
if @shutting_down == 1
|
594
|
+
@shutting_down += 1
|
595
|
+
puts "Ignoring signal #{signal} during shutdown. Send it again to force exit."
|
596
|
+
else
|
597
|
+
exit!(1)
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
563
601
|
def stop_touching_temp_dir_in_background
|
564
602
|
if @toucher
|
565
603
|
begin
|
@@ -47,6 +47,7 @@ http {
|
|
47
47
|
|
48
48
|
default_type application/octet-stream;
|
49
49
|
types_hash_max_size 2048;
|
50
|
+
server_names_hash_bucket_size 64;
|
50
51
|
client_max_body_size 1024m;
|
51
52
|
access_log off;
|
52
53
|
keepalive_timeout 60;
|
@@ -81,6 +82,8 @@ http {
|
|
81
82
|
passenger_enabled on;
|
82
83
|
rails_env <%= app[:env] %>;
|
83
84
|
passenger_spawn_method <%= app[:spawn_method] %>;
|
85
|
+
<% if app[:concurrency_model] != DEFAULT_CONCURRENCY_MODEL %>passenger_concurrency_model <%= app[:concurrency_model] %>;<% end %>
|
86
|
+
<% if app[:thread_count] != DEFAULT_THREAD_COUNT %>passenger_thread_count <%= app[:thread_count] %>;<% end %>
|
84
87
|
<% if app[:min_instances] %>passenger_min_instances <%= app[:min_instances] %>;<% end %>
|
85
88
|
<% if app[:union_station_key] %>
|
86
89
|
union_station_support on;
|
@@ -340,11 +340,11 @@ namespace tut {
|
|
340
340
|
|
341
341
|
client1.write("openTransaction",
|
342
342
|
TODAY_TXN_ID, "foobar", "", "requests", TODAY_TIMESTAMP_STR,
|
343
|
-
"", "true", "true", NULL);
|
343
|
+
"-", "true", "true", NULL);
|
344
344
|
client1.read(args);
|
345
345
|
client2.write("openTransaction",
|
346
346
|
TODAY_TXN_ID, "foobar", "", "requests", TODAY_TIMESTAMP_STR,
|
347
|
-
"", "true", NULL);
|
347
|
+
"-", "true", NULL);
|
348
348
|
client2.write("log", TODAY_TXN_ID, "1000", NULL);
|
349
349
|
client2.writeScalar("hello world");
|
350
350
|
client2.write("flush", NULL);
|
@@ -376,11 +376,11 @@ namespace tut {
|
|
376
376
|
|
377
377
|
client1.write("openTransaction",
|
378
378
|
TODAY_TXN_ID, "foobar", "", "requests", TODAY_TIMESTAMP_STR,
|
379
|
-
"", "false", "true", NULL);
|
379
|
+
"-", "false", "true", NULL);
|
380
380
|
client1.read(args);
|
381
381
|
client2.write("openTransaction",
|
382
382
|
TODAY_TXN_ID, "foobar", "", "requests", TODAY_TIMESTAMP_STR,
|
383
|
-
"", "false", NULL);
|
383
|
+
"-", "false", NULL);
|
384
384
|
client2.write("flush", NULL);
|
385
385
|
client2.read(args);
|
386
386
|
client2.disconnect();
|
@@ -403,11 +403,11 @@ namespace tut {
|
|
403
403
|
|
404
404
|
client1.write("openTransaction",
|
405
405
|
TODAY_TXN_ID, "foobar", "", "requests", TODAY_TIMESTAMP_STR,
|
406
|
-
"", "true", "true", NULL);
|
406
|
+
"-", "true", "true", NULL);
|
407
407
|
client1.read(args);
|
408
408
|
client2.write("openTransaction",
|
409
409
|
TODAY_TXN_ID, "foobar", "", "requests", TODAY_TIMESTAMP_STR,
|
410
|
-
"", "true", NULL);
|
410
|
+
"-", "true", NULL);
|
411
411
|
client2.write("flush", NULL);
|
412
412
|
client2.read(args);
|
413
413
|
|
@@ -428,11 +428,11 @@ namespace tut {
|
|
428
428
|
|
429
429
|
client1.write("openTransaction",
|
430
430
|
TODAY_TXN_ID, "foobar", "", "requests", TODAY_TIMESTAMP_STR,
|
431
|
-
"", "false", "true", NULL);
|
431
|
+
"-", "false", "true", NULL);
|
432
432
|
client1.read(args);
|
433
433
|
client2.write("openTransaction",
|
434
434
|
TODAY_TXN_ID, "foobar", "", "requests", TODAY_TIMESTAMP_STR,
|
435
|
-
"", "false", NULL);
|
435
|
+
"-", "false", NULL);
|
436
436
|
client2.write("flush", NULL);
|
437
437
|
client2.read(args);
|
438
438
|
|
@@ -704,7 +704,7 @@ namespace tut {
|
|
704
704
|
|
705
705
|
client1.write("openTransaction",
|
706
706
|
TODAY_TXN_ID, "foobar", "remote", "requests", TODAY_TIMESTAMP_STR,
|
707
|
-
"", "true", NULL);
|
707
|
+
"-", "true", NULL);
|
708
708
|
client1.write("closeTransaction", TODAY_TXN_ID, TODAY_TIMESTAMP_STR, NULL);
|
709
709
|
client1.write("flush", NULL);
|
710
710
|
client1.read(args);
|
@@ -718,7 +718,7 @@ namespace tut {
|
|
718
718
|
// Test logging of new transaction.
|
719
719
|
SystemTime::forceAll(YESTERDAY);
|
720
720
|
|
721
|
-
LoggerPtr log = factory->newTransaction("foobar", "requests", "",
|
721
|
+
LoggerPtr log = factory->newTransaction("foobar", "requests", "-",
|
722
722
|
"uri == \"/foo\""
|
723
723
|
"\1"
|
724
724
|
"uri != \"/bar\"");
|
@@ -727,7 +727,7 @@ namespace tut {
|
|
727
727
|
log->flushToDiskAfterClose(true);
|
728
728
|
log.reset();
|
729
729
|
|
730
|
-
log = factory->newTransaction("foobar", "requests", "",
|
730
|
+
log = factory->newTransaction("foobar", "requests", "-",
|
731
731
|
"uri == \"/foo\""
|
732
732
|
"\1"
|
733
733
|
"uri == \"/bar\"");
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: passenger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.18
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-09-
|
12
|
+
date: 2013-09-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
metadata.gz.asc
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
|
3
3
|
Comment: GPGTools - http://gpgtools.org
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
iQEcBAABAgAGBQJSOKPwAAoJECrHRaUKISqMlkcIAIyquE2ypVcCq8Kds8gncXoI
|
6
|
+
T94rU5e49Jxqfzd8jxz0CDihW6wI+CvkuVOxziZdZaYfVYaBm3oGn7zhmhC6mQhL
|
7
|
+
+B6g5NqzANlaty3OBCufl4yvAKMFw4M8GLc0nxtSdVdAwEKiI45gZY3WEZxwA74q
|
8
|
+
aMZrVFACgXmYijayxqiHBUqITYEjMqrlAbpLjUNgulhKzwNNlpGhL2VeBg4t0Bpr
|
9
|
+
NMldNutbg1Sc0PmIyIXOXmZJWtpYBm24RcrGyWCF6qikPWIEtpU9l3edLTKVmUHP
|
10
|
+
J0hO08TMC7bDfE3qT3jB+KxGlXDPVFyO2cIn7+73llsZvricCWqr+GxTq5bCm88=
|
11
|
+
=49PA
|
12
12
|
-----END PGP SIGNATURE-----
|