zeus 0.15.10 → 0.15.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +15 -15
- data/build/zeus-darwin-amd64 +0 -0
- data/build/zeus-linux-386 +0 -0
- data/build/zeus-linux-amd64 +0 -0
- data/examples/custom_plan/cucumber_plan.rb +1 -0
- data/lib/zeus.rb +18 -26
- data/lib/zeus/load_tracking.rb +39 -33
- data/lib/zeus/version.rb +1 -1
- data/man/build/zeus +1 -1
- data/man/build/zeus-init +1 -1
- data/man/build/zeus-init.txt +1 -1
- data/man/build/zeus-start +1 -1
- data/man/build/zeus-start.txt +1 -1
- data/man/build/zeus.txt +1 -1
- data/spec/assets/boot.rb +2 -0
- data/spec/assets/boot_delayed.rb +2 -0
- data/spec/load_tracking_spec.rb +41 -49
- data/spec/zeus_spec.rb +85 -0
- data/zeus.gemspec +1 -1
- metadata +7 -5
- data/spec/assets/exit.rb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0b6652301511b13e06a1b60dbb808cc5e2161a2
|
4
|
+
data.tar.gz: 4898481f7b4acec5828dfa25a2351d31b5f166b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 187015a45fdb160f364da8c1ee05de7577642e79d114ca0982f5409cc582cb02bb764a0b0074750d72fa492dd52782ad1b77580691963c1fb83900d13d703345
|
7
|
+
data.tar.gz: c837a56c542b72057a3dbc324bf42dbc4f8cd75080f6b211e01f7e83053d3f9f15470eedfc40310d313e2399a3a53f8382d28c9ac38a95ab21c86602b3c90729
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
zeus (0.15.
|
4
|
+
zeus (0.15.12)
|
5
5
|
method_source (>= 0.6.7)
|
6
6
|
|
7
7
|
GEM
|
@@ -10,24 +10,24 @@ GEM
|
|
10
10
|
coderay (1.1.1)
|
11
11
|
diff-lcs (1.2.5)
|
12
12
|
method_source (0.8.2)
|
13
|
-
pry (0.10.
|
13
|
+
pry (0.10.4)
|
14
14
|
coderay (~> 1.1.0)
|
15
15
|
method_source (~> 0.8.1)
|
16
16
|
slop (~> 3.4)
|
17
|
-
rake (
|
18
|
-
rspec (3.
|
19
|
-
rspec-core (~> 3.
|
20
|
-
rspec-expectations (~> 3.
|
21
|
-
rspec-mocks (~> 3.
|
22
|
-
rspec-core (3.
|
23
|
-
rspec-support (~> 3.
|
24
|
-
rspec-expectations (3.
|
17
|
+
rake (12.0.0)
|
18
|
+
rspec (3.5.0)
|
19
|
+
rspec-core (~> 3.5.0)
|
20
|
+
rspec-expectations (~> 3.5.0)
|
21
|
+
rspec-mocks (~> 3.5.0)
|
22
|
+
rspec-core (3.5.4)
|
23
|
+
rspec-support (~> 3.5.0)
|
24
|
+
rspec-expectations (3.5.0)
|
25
25
|
diff-lcs (>= 1.2.0, < 2.0)
|
26
|
-
rspec-support (~> 3.
|
27
|
-
rspec-mocks (3.
|
26
|
+
rspec-support (~> 3.5.0)
|
27
|
+
rspec-mocks (3.5.0)
|
28
28
|
diff-lcs (>= 1.2.0, < 2.0)
|
29
|
-
rspec-support (~> 3.
|
30
|
-
rspec-support (3.
|
29
|
+
rspec-support (~> 3.5.0)
|
30
|
+
rspec-support (3.5.0)
|
31
31
|
slop (3.6.0)
|
32
32
|
|
33
33
|
PLATFORMS
|
@@ -41,4 +41,4 @@ DEPENDENCIES
|
|
41
41
|
zeus!
|
42
42
|
|
43
43
|
BUNDLED WITH
|
44
|
-
1.
|
44
|
+
1.13.7
|
data/build/zeus-darwin-amd64
CHANGED
Binary file
|
data/build/zeus-linux-386
CHANGED
Binary file
|
data/build/zeus-linux-amd64
CHANGED
Binary file
|
data/lib/zeus.rb
CHANGED
@@ -69,7 +69,9 @@ module Zeus
|
|
69
69
|
local.send_io(feature_pipe_r)
|
70
70
|
feature_pipe_r.close
|
71
71
|
|
72
|
-
|
72
|
+
Zeus::LoadTracking.set_feature_pipe(feature_pipe_w)
|
73
|
+
|
74
|
+
run_action(local, identifier)
|
73
75
|
|
74
76
|
# We are now 'connected'. From this point, we may receive requests to fork.
|
75
77
|
children = Set.new
|
@@ -97,10 +99,16 @@ module Zeus
|
|
97
99
|
elsif code == "S"
|
98
100
|
# Child, supposed to start another step:
|
99
101
|
@parent_pid = forked_from
|
102
|
+
|
103
|
+
Zeus::LoadTracking.clear_feature_pipe
|
104
|
+
|
100
105
|
throw(:boot_step, ident.to_sym)
|
101
106
|
else
|
102
107
|
# Child, supposed to run a command:
|
103
108
|
@parent_pid = forked_from
|
109
|
+
|
110
|
+
Zeus::LoadTracking.clear_feature_pipe
|
111
|
+
|
104
112
|
return [ident.to_sym, local]
|
105
113
|
end
|
106
114
|
end
|
@@ -176,12 +184,6 @@ module Zeus
|
|
176
184
|
}
|
177
185
|
end
|
178
186
|
|
179
|
-
def notify_features(pipe, features)
|
180
|
-
features.each do |t|
|
181
|
-
pipe.puts t
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
187
|
def report_error_to_master(local, error)
|
186
188
|
str = "R:"
|
187
189
|
str << "#{error.backtrace[0]}: #{error.message} (#{error.class})\n"
|
@@ -192,28 +194,18 @@ module Zeus
|
|
192
194
|
local.write str
|
193
195
|
end
|
194
196
|
|
195
|
-
def run_action(socket, identifier
|
197
|
+
def run_action(socket, identifier)
|
196
198
|
# Now we run the action and report its success/fail status to the master.
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
if err
|
203
|
-
# If we received an error, report features to the master syncronously.
|
204
|
-
# We need to do this before reporting the error to the master
|
205
|
-
# otherwise it will kill us before we can report features.
|
206
|
-
begin
|
207
|
-
notify_features(feature_pipe_w, features)
|
208
|
-
feature_pipe_w.close
|
209
|
-
ensure
|
210
|
-
report_error_to_master(socket, err)
|
199
|
+
begin
|
200
|
+
Zeus::LoadTracking.track_features_loaded_by do
|
201
|
+
plan.after_fork unless identifier == :boot
|
202
|
+
plan.send(identifier)
|
211
203
|
end
|
212
|
-
|
213
|
-
# If we booted successfully, report features in a new thread
|
214
|
-
# so we can immediately begin listening for commands.
|
204
|
+
|
215
205
|
socket.write "R:OK\0"
|
216
|
-
|
206
|
+
rescue => err
|
207
|
+
report_error_to_master(socket, err)
|
208
|
+
raise
|
217
209
|
end
|
218
210
|
end
|
219
211
|
end
|
data/lib/zeus/load_tracking.rb
CHANGED
@@ -1,8 +1,15 @@
|
|
1
1
|
module Zeus
|
2
2
|
class LoadTracking
|
3
3
|
class << self
|
4
|
-
def
|
5
|
-
|
4
|
+
def add_feature(file)
|
5
|
+
full_path = File.expand_path(file)
|
6
|
+
return unless File.exist?(full_path) && @feature_pipe
|
7
|
+
notify_features([full_path])
|
8
|
+
end
|
9
|
+
|
10
|
+
# Internal: This should only be called by Zeus code
|
11
|
+
def track_features_loaded_by
|
12
|
+
old_features = $LOADED_FEATURES.dup
|
6
13
|
|
7
14
|
# Catch exceptions so we can determine the features
|
8
15
|
# that were being loaded at the time of the exception.
|
@@ -14,48 +21,47 @@ module Zeus
|
|
14
21
|
# the error is not in the backtrace, only the error message.
|
15
22
|
match = /\A([^:]+):\d+: syntax error/.match(err.message)
|
16
23
|
err_features << match[1] if match
|
17
|
-
|
18
|
-
|
19
|
-
|
24
|
+
raise
|
25
|
+
rescue ScriptError => err
|
26
|
+
raise
|
27
|
+
rescue => err
|
28
|
+
raise
|
29
|
+
ensure
|
30
|
+
if err && err.backtrace
|
31
|
+
err_features += err.backtrace.map { |b| b.split(':').first }
|
32
|
+
.select { |f| f.start_with?('/') }
|
33
|
+
.take_while { |f| f != __FILE__ }
|
34
|
+
end
|
20
35
|
|
21
|
-
|
22
|
-
err_features += err.backtrace.map { |b| b.split(':').first }
|
23
|
-
.select { |f| f.start_with?('/') }
|
24
|
-
.take_while { |f| f != __FILE__ }
|
36
|
+
notify_features(Set.new($LOADED_FEATURES) + err_features - old_features)
|
25
37
|
end
|
26
|
-
|
27
|
-
new_features = all_features + err_features - old_features
|
28
|
-
new_features.uniq!
|
29
|
-
|
30
|
-
[new_features, err]
|
31
38
|
end
|
32
39
|
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
full_path = File.expand_path(file)
|
38
|
-
|
39
|
-
if find_in_load_path(full_path) || File.exist?(full_path)
|
40
|
-
add_extra_feature(full_path)
|
41
|
-
end
|
40
|
+
# Internal: This should only be called by Zeus code
|
41
|
+
def set_feature_pipe(feature_pipe)
|
42
|
+
@feature_mutex = Mutex.new
|
43
|
+
@feature_pipe = feature_pipe
|
42
44
|
end
|
43
45
|
|
44
|
-
#
|
45
|
-
def
|
46
|
-
|
47
|
-
|
46
|
+
# Internal: This should only be called by Zeus code
|
47
|
+
def clear_feature_pipe
|
48
|
+
@feature_pipe.close
|
49
|
+
@feature_pipe = nil
|
50
|
+
@feature_mutex = nil
|
48
51
|
end
|
49
52
|
|
50
53
|
private
|
51
54
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
def notify_features(features)
|
56
|
+
unless @feature_pipe
|
57
|
+
raise "Attempted to report features to Zeus when not running as part of a Zeus process"
|
58
|
+
end
|
56
59
|
|
57
|
-
|
58
|
-
|
60
|
+
@feature_mutex.synchronize do
|
61
|
+
features.each do |t|
|
62
|
+
@feature_pipe.puts(t)
|
63
|
+
end
|
64
|
+
end
|
59
65
|
end
|
60
66
|
end
|
61
67
|
end
|
data/lib/zeus/version.rb
CHANGED
data/man/build/zeus
CHANGED
data/man/build/zeus-init
CHANGED
data/man/build/zeus-init.txt
CHANGED
data/man/build/zeus-start
CHANGED
data/man/build/zeus-start.txt
CHANGED
data/man/build/zeus.txt
CHANGED
data/spec/assets/boot.rb
ADDED
data/spec/load_tracking_spec.rb
CHANGED
@@ -6,55 +6,53 @@ describe "Zeus::LoadTracking" do
|
|
6
6
|
|
7
7
|
class MyError < StandardError; end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
# add the dir path of the tempfile to LOAD_PATH
|
13
|
-
$LOAD_PATH << test_dirname
|
14
|
-
end
|
15
|
-
|
16
|
-
after { $LOAD_PATH.delete test_dirname }
|
17
|
-
|
18
|
-
it 'adds full filepath to $untracked_features' do
|
19
|
-
Zeus::LoadTracking.add_feature(test_filename)
|
9
|
+
def expect_to_load(expect_features, expect_err=NilClass)
|
10
|
+
buf = StringIO.new
|
11
|
+
Zeus::LoadTracking.set_feature_pipe(buf)
|
20
12
|
|
21
|
-
|
13
|
+
begin
|
14
|
+
Zeus::LoadTracking.track_features_loaded_by do
|
15
|
+
yield
|
22
16
|
end
|
17
|
+
rescue ScriptError => err
|
18
|
+
rescue => err
|
23
19
|
end
|
24
20
|
|
25
|
-
|
26
|
-
|
27
|
-
|
21
|
+
expect(err).to be_instance_of(expect_err)
|
22
|
+
expect(buf.string.strip.split("\n").sort).to eq(expect_features.sort)
|
23
|
+
end
|
28
24
|
|
29
|
-
|
30
|
-
|
31
|
-
|
25
|
+
def expand_asset_path(path)
|
26
|
+
File.join(__dir__, 'assets', path)
|
27
|
+
end
|
32
28
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
$untracked_features << "an_untracked_feature.rb"
|
37
|
-
end
|
29
|
+
describe '.add_feature' do
|
30
|
+
it 'tracks full filepath' do
|
31
|
+
relative_path = Pathname.new(test_filename).relative_path_from(Pathname.new(Dir.pwd)).to_s
|
38
32
|
|
39
|
-
|
33
|
+
expect_to_load([test_filename]) do
|
34
|
+
Zeus::LoadTracking.add_feature(relative_path)
|
40
35
|
end
|
41
36
|
end
|
42
|
-
end
|
43
37
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
38
|
+
it 'tracks loads' do
|
39
|
+
target = expand_asset_path('load.rb')
|
40
|
+
|
41
|
+
# The first `load` in Travis also pulls enc/trans/single_byte.so from the
|
42
|
+
# Ruby VM for some reason. Loading twice is harmless since this file is empty.
|
43
|
+
load(target)
|
49
44
|
|
50
|
-
|
51
|
-
|
45
|
+
expect_to_load([target]) do
|
46
|
+
load(target)
|
47
|
+
end
|
52
48
|
end
|
53
49
|
|
54
|
-
|
55
|
-
|
50
|
+
it 'does not error outside a tracking block without Zeus configured' do
|
51
|
+
Zeus::LoadTracking.add_feature(test_filename)
|
56
52
|
end
|
53
|
+
end
|
57
54
|
|
55
|
+
describe '.track_features_loaded_by' do
|
58
56
|
context 'loading valid code' do
|
59
57
|
it 'tracks successful require_relative' do
|
60
58
|
expect_to_load([expand_asset_path('require_relative.rb')]) do
|
@@ -67,35 +65,29 @@ describe "Zeus::LoadTracking" do
|
|
67
65
|
require expand_asset_path('require')
|
68
66
|
end
|
69
67
|
end
|
70
|
-
|
71
|
-
it 'tracks loads' do
|
72
|
-
expect_to_load([expand_asset_path('load.rb')]) do
|
73
|
-
load expand_asset_path('load.rb')
|
74
|
-
end
|
75
|
-
end
|
76
68
|
end
|
77
69
|
|
78
70
|
context 'loading invalid code' do
|
79
71
|
it 'tracks requires that raise a SyntaxError' do
|
80
|
-
|
72
|
+
files = [expand_asset_path('invalid_syntax.rb')]
|
73
|
+
# SyntaxError does not have a backtrace in 2.3+ (https://bugs.ruby-lang.org/issues/12811)
|
74
|
+
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.3.0')
|
75
|
+
files << test_filename
|
76
|
+
end
|
77
|
+
|
78
|
+
expect_to_load(files, SyntaxError) do
|
81
79
|
require expand_asset_path('invalid_syntax')
|
82
80
|
end
|
83
81
|
end
|
84
82
|
|
85
83
|
it 'tracks requires that raise a RuntimeError' do
|
86
|
-
expect_to_load([expand_asset_path('runtime_error.rb')], RuntimeError) do
|
84
|
+
expect_to_load([test_filename, expand_asset_path('runtime_error.rb')], RuntimeError) do
|
87
85
|
require expand_asset_path('runtime_error')
|
88
86
|
end
|
89
87
|
end
|
90
88
|
|
91
|
-
it 'tracks requires that exit' do
|
92
|
-
expect_to_load([expand_asset_path('exit.rb')], SystemExit) do
|
93
|
-
require expand_asset_path('exit')
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
89
|
it 'tracks requires that throw in a method call' do
|
98
|
-
expect_to_load([expand_asset_path('raise.rb')], MyError) do
|
90
|
+
expect_to_load([test_filename, expand_asset_path('raise.rb')], MyError) do
|
99
91
|
require expand_asset_path('raise')
|
100
92
|
raise_it(MyError)
|
101
93
|
end
|
data/spec/zeus_spec.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'zeus'
|
2
|
+
|
3
|
+
describe Zeus do
|
4
|
+
class MyTestPlan < Zeus::Plan
|
5
|
+
def self.mutex
|
6
|
+
@mutex ||= Mutex.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.boot
|
10
|
+
require_relative 'assets/boot'
|
11
|
+
Thread.new do
|
12
|
+
mutex.synchronize do
|
13
|
+
Zeus::LoadTracking.add_feature(File.join(__dir__, 'assets', 'boot_delayed.rb'))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Zeus.plan = MyTestPlan
|
20
|
+
|
21
|
+
before do
|
22
|
+
MyTestPlan.mutex.lock
|
23
|
+
end
|
24
|
+
|
25
|
+
after do
|
26
|
+
MyTestPlan.mutex.unlock if MyTestPlan.mutex.locked?
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'booting' do
|
30
|
+
before do
|
31
|
+
# Don't reopen STDOUT
|
32
|
+
Zeus.dummy_tty = true
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'boots and tracks features' do
|
36
|
+
master_r, master_w = UNIXSocket.pair(Socket::SOCK_STREAM)
|
37
|
+
ENV['ZEUS_MASTER_FD'] = master_w.to_i.to_s
|
38
|
+
|
39
|
+
thr = Thread.new do
|
40
|
+
begin
|
41
|
+
Zeus.go
|
42
|
+
rescue Interrupt
|
43
|
+
return
|
44
|
+
rescue => e
|
45
|
+
STDERR.puts "Zeus terminated with exception: #{e.message}"
|
46
|
+
STDERR.puts e.backtrace.map {|line| " #{line}"}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
begin
|
51
|
+
# Receive the control IO and start message
|
52
|
+
ctrl_io = master_r.recv_io(UNIXSocket)
|
53
|
+
begin
|
54
|
+
# We use recv instead of readline on the UNIXSocket to avoid
|
55
|
+
# converting it to a buffered reader. That seems to interact
|
56
|
+
# badly with passing file descriptors around on Linux.
|
57
|
+
proc_msg = "P:#{Process.pid}:0:boot\0"
|
58
|
+
expect(ctrl_io.recv(proc_msg.length)).to eq(proc_msg)
|
59
|
+
|
60
|
+
feature_io = ctrl_io.recv_io
|
61
|
+
|
62
|
+
ready_msg = "R:OK\0"
|
63
|
+
expect(ctrl_io.recv(ready_msg.length)).to eq(ready_msg)
|
64
|
+
begin
|
65
|
+
# We should receive the synchronously required feature immediately
|
66
|
+
expect(feature_io.readline).to eq(File.join(__dir__, 'assets', 'boot.rb') + "\n")
|
67
|
+
|
68
|
+
# We should receive the delayed feature after unlocking its mutex
|
69
|
+
MyTestPlan.mutex.unlock
|
70
|
+
expect(feature_io.readline).to eq(File.join(__dir__, 'assets', 'boot_delayed.rb') + "\n")
|
71
|
+
ensure
|
72
|
+
feature_io.close
|
73
|
+
end
|
74
|
+
ensure
|
75
|
+
ctrl_io.close
|
76
|
+
end
|
77
|
+
ensure
|
78
|
+
thr.raise(Interrupt.new)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'tracks features after booting has completed' do
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/zeus.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.email = ["burke@libbey.me"]
|
17
17
|
gem.description = %q{Boot any rails app in under a second}
|
18
18
|
gem.summary = %q{Zeus is an intelligent preloader for ruby applications. It allows normal development tasks to be run in a fraction of a second.}
|
19
|
-
gem.homepage = "
|
19
|
+
gem.homepage = "https://github.com/burke/zeus"
|
20
20
|
|
21
21
|
gem.files = files
|
22
22
|
gem.extensions = []
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zeus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.15.
|
4
|
+
version: 0.15.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Burke Libbey
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: method_source
|
@@ -75,7 +75,8 @@ files:
|
|
75
75
|
- man/build/zeus-start
|
76
76
|
- man/build/zeus-start.txt
|
77
77
|
- man/build/zeus.txt
|
78
|
-
- spec/assets/
|
78
|
+
- spec/assets/boot.rb
|
79
|
+
- spec/assets/boot_delayed.rb
|
79
80
|
- spec/assets/invalid_syntax.rb
|
80
81
|
- spec/assets/load.rb
|
81
82
|
- spec/assets/raise.rb
|
@@ -87,8 +88,9 @@ files:
|
|
87
88
|
- spec/load_tracking_spec.rb
|
88
89
|
- spec/m_spec.rb
|
89
90
|
- spec/rails_spec.rb
|
91
|
+
- spec/zeus_spec.rb
|
90
92
|
- zeus.gemspec
|
91
|
-
homepage:
|
93
|
+
homepage: https://github.com/burke/zeus
|
92
94
|
licenses:
|
93
95
|
- MIT
|
94
96
|
metadata: {}
|
@@ -108,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
110
|
version: '0'
|
109
111
|
requirements: []
|
110
112
|
rubyforge_project:
|
111
|
-
rubygems_version: 2.4.
|
113
|
+
rubygems_version: 2.4.8
|
112
114
|
signing_key:
|
113
115
|
specification_version: 4
|
114
116
|
summary: Zeus is an intelligent preloader for ruby applications. It allows normal
|
data/spec/assets/exit.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
exit(1)
|