serverengine 1.6.4 → 2.0.0pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Changelog +11 -0
- data/README.md +31 -3
- data/Rakefile +16 -3
- data/appveyor.yml +11 -5
- data/examples/server.rb +138 -0
- data/examples/spawn_worker_script.rb +38 -0
- data/lib/serverengine/blocking_flag.rb +2 -3
- data/lib/serverengine/command_sender.rb +89 -0
- data/lib/serverengine/config_loader.rb +2 -0
- data/lib/serverengine/daemon.rb +114 -86
- data/lib/serverengine/daemon_logger.rb +3 -139
- data/lib/serverengine/embedded_server.rb +2 -0
- data/lib/serverengine/multi_process_server.rb +28 -7
- data/lib/serverengine/multi_spawn_server.rb +17 -18
- data/lib/serverengine/multi_thread_server.rb +6 -0
- data/lib/serverengine/multi_worker_server.rb +14 -0
- data/lib/serverengine/privilege.rb +57 -0
- data/lib/serverengine/process_manager.rb +66 -48
- data/lib/serverengine/server.rb +45 -11
- data/lib/serverengine/signal_thread.rb +0 -2
- data/lib/serverengine/signals.rb +31 -0
- data/lib/serverengine/socket_manager.rb +3 -5
- data/lib/serverengine/socket_manager_unix.rb +1 -0
- data/lib/serverengine/socket_manager_win.rb +4 -2
- data/lib/serverengine/supervisor.rb +105 -25
- data/lib/serverengine/utils.rb +23 -0
- data/lib/serverengine/version.rb +1 -1
- data/lib/serverengine/worker.rb +10 -7
- data/lib/serverengine.rb +9 -27
- data/serverengine.gemspec +12 -1
- data/spec/daemon_logger_spec.rb +17 -12
- data/spec/daemon_spec.rb +147 -24
- data/spec/multi_process_server_spec.rb +59 -7
- data/spec/server_worker_context.rb +104 -0
- data/spec/signal_thread_spec.rb +61 -56
- data/spec/supervisor_spec.rb +113 -99
- metadata +40 -6
@@ -0,0 +1,57 @@
|
|
1
|
+
#
|
2
|
+
# ServerEngine
|
3
|
+
#
|
4
|
+
# Copyright (C) 2012-2013 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
require 'etc'
|
19
|
+
|
20
|
+
module ServerEngine
|
21
|
+
module Privilege
|
22
|
+
def self.get_etc_passwd(user)
|
23
|
+
if user.to_i.to_s == user
|
24
|
+
Etc.getpwuid(user.to_i)
|
25
|
+
else
|
26
|
+
Etc.getpwnam(user)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.get_etc_group(group)
|
31
|
+
if group.to_i.to_s == group
|
32
|
+
Etc.getgrgid(group.to_i)
|
33
|
+
else
|
34
|
+
Etc.getgrnam(group)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.change(user, group)
|
39
|
+
if user
|
40
|
+
etc_pw = get_etc_passwd(user)
|
41
|
+
user_groups = [etc_pw.gid]
|
42
|
+
Etc.setgrent
|
43
|
+
Etc.group { |gr| user_groups << gr.gid if gr.mem.include?(etc_pw.name) } # emulate 'id -G'
|
44
|
+
|
45
|
+
Process.groups = Process.groups | user_groups
|
46
|
+
Process::UID.change_privilege(etc_pw.uid)
|
47
|
+
end
|
48
|
+
|
49
|
+
if group
|
50
|
+
etc_group = get_etc_group(group)
|
51
|
+
Process::GID.change_privilege(etc_group.gid)
|
52
|
+
end
|
53
|
+
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -15,9 +15,9 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
|
-
|
18
|
+
require 'fcntl'
|
19
19
|
|
20
|
-
|
20
|
+
module ServerEngine
|
21
21
|
|
22
22
|
class ProcessManager
|
23
23
|
def initialize(config={})
|
@@ -57,7 +57,7 @@ module ServerEngine
|
|
57
57
|
@read_buffer = ''
|
58
58
|
|
59
59
|
if @auto_tick
|
60
|
-
TickThread.new(
|
60
|
+
TickThread.new(@auto_tick_interval, &method(:tick))
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
@@ -69,6 +69,9 @@ module ServerEngine
|
|
69
69
|
attr_reader :auto_tick, :auto_tick_interval
|
70
70
|
attr_reader :enable_heartbeat, :auto_heartbeat
|
71
71
|
|
72
|
+
attr_accessor :command_sender
|
73
|
+
attr_reader :command_sender_pipe
|
74
|
+
|
72
75
|
CONFIG_PARAMS = {
|
73
76
|
heartbeat_interval: 1,
|
74
77
|
heartbeat_timeout: 180,
|
@@ -96,6 +99,21 @@ module ServerEngine
|
|
96
99
|
}
|
97
100
|
end
|
98
101
|
|
102
|
+
def monitor_options
|
103
|
+
{
|
104
|
+
enable_heartbeat: @enable_heartbeat,
|
105
|
+
heartbeat_timeout: @heartbeat_timeout,
|
106
|
+
graceful_kill_signal: @graceful_kill_signal,
|
107
|
+
graceful_kill_timeout: @graceful_kill_timeout,
|
108
|
+
graceful_kill_interval: @graceful_kill_interval,
|
109
|
+
graceful_kill_interval_increment: @graceful_kill_interval_increment,
|
110
|
+
immediate_kill_signal: @immediate_kill_signal,
|
111
|
+
immediate_kill_timeout: @immediate_kill_timeout,
|
112
|
+
immediate_kill_interval: @immediate_kill_interval,
|
113
|
+
immediate_kill_interval_increment: @immediate_kill_interval_increment,
|
114
|
+
}
|
115
|
+
end
|
116
|
+
|
99
117
|
def fork(&block)
|
100
118
|
if ServerEngine.windows?
|
101
119
|
raise NotImplementedError, "fork is not available on this platform. Please use spawn (worker_type: 'spawn')."
|
@@ -109,7 +127,7 @@ module ServerEngine
|
|
109
127
|
begin
|
110
128
|
t = Target.new(wpipe)
|
111
129
|
if @enable_heartbeat && @auto_heartbeat
|
112
|
-
HeartbeatThread.new(
|
130
|
+
HeartbeatThread.new(@heartbeat_interval, t, @heartbeat_error_proc)
|
113
131
|
end
|
114
132
|
|
115
133
|
block.call(t)
|
@@ -122,7 +140,7 @@ module ServerEngine
|
|
122
140
|
end
|
123
141
|
end
|
124
142
|
|
125
|
-
m = Monitor.new(
|
143
|
+
m = Monitor.new(pid, monitor_options)
|
126
144
|
|
127
145
|
@monitors << m
|
128
146
|
@rpipes[rpipe] = m
|
@@ -161,9 +179,18 @@ module ServerEngine
|
|
161
179
|
end
|
162
180
|
end
|
163
181
|
|
182
|
+
if @command_sender == "pipe"
|
183
|
+
inpipe, @command_sender_pipe = IO.pipe
|
184
|
+
@command_sender_pipe.sync = true
|
185
|
+
@command_sender_pipe.binmode
|
186
|
+
options[:in] = inpipe
|
187
|
+
end
|
164
188
|
pid = Process.spawn(env, *args, options)
|
189
|
+
if @command_sender == "pipe"
|
190
|
+
inpipe.close
|
191
|
+
end
|
165
192
|
|
166
|
-
m = Monitor.new(
|
193
|
+
m = Monitor.new(pid, monitor_options)
|
167
194
|
|
168
195
|
@monitors << m
|
169
196
|
|
@@ -251,38 +278,28 @@ module ServerEngine
|
|
251
278
|
nil
|
252
279
|
end
|
253
280
|
|
254
|
-
def self.format_signal_name(n)
|
255
|
-
Signal.list.each_pair {|k,v|
|
256
|
-
return "SIG#{k}" if n == v
|
257
|
-
}
|
258
|
-
return n
|
259
|
-
end
|
260
|
-
|
261
|
-
def self.format_join_status(code)
|
262
|
-
case code
|
263
|
-
when Process::Status
|
264
|
-
if code.signaled?
|
265
|
-
"signal #{format_signal_name(code.termsig)}"
|
266
|
-
else
|
267
|
-
"status #{code.exitstatus}"
|
268
|
-
end
|
269
|
-
when Exception
|
270
|
-
"exception #{code}"
|
271
|
-
when nil
|
272
|
-
"unknown reason"
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
281
|
class AlreadyClosedError < EOFError
|
277
282
|
end
|
278
283
|
|
279
284
|
HEARTBEAT_MESSAGE = [0].pack('C')
|
280
285
|
|
281
286
|
class Monitor
|
282
|
-
def initialize(
|
283
|
-
@pm = pm
|
287
|
+
def initialize(pid, opts={})
|
284
288
|
@pid = pid
|
285
289
|
|
290
|
+
@enable_heartbeat = opts[:enable_heartbeat]
|
291
|
+
@heartbeat_timeout = opts[:heartbeat_timeout]
|
292
|
+
|
293
|
+
@graceful_kill_signal = opts[:graceful_kill_signal]
|
294
|
+
@graceful_kill_timeout = opts[:graceful_kill_timeout]
|
295
|
+
@graceful_kill_interval = opts[:graceful_kill_interval]
|
296
|
+
@graceful_kill_interval_increment = opts[:graceful_kill_interval_increment]
|
297
|
+
|
298
|
+
@immediate_kill_signal = opts[:immediate_kill_signal]
|
299
|
+
@immediate_kill_timeout = opts[:immediate_kill_timeout]
|
300
|
+
@immediate_kill_interval = opts[:immediate_kill_interval]
|
301
|
+
@immediate_kill_interval_increment = opts[:immediate_kill_interval_increment]
|
302
|
+
|
286
303
|
@error = false
|
287
304
|
@last_heartbeat_time = Time.now
|
288
305
|
@next_kill_time = nil
|
@@ -367,13 +384,13 @@ module ServerEngine
|
|
367
384
|
# check heartbeat timeout or escalation
|
368
385
|
if (
|
369
386
|
# heartbeat timeout
|
370
|
-
@
|
371
|
-
heartbeat_delay >= @
|
387
|
+
@enable_heartbeat &&
|
388
|
+
heartbeat_delay >= @heartbeat_timeout
|
372
389
|
) || (
|
373
390
|
# escalation
|
374
391
|
@graceful_kill_start_time &&
|
375
|
-
@
|
376
|
-
@graceful_kill_start_time < now - @
|
392
|
+
@graceful_kill_timeout >= 0 &&
|
393
|
+
@graceful_kill_start_time < now - @graceful_kill_timeout
|
377
394
|
)
|
378
395
|
# escalate to immediate kill
|
379
396
|
@kill_count = 0
|
@@ -390,20 +407,20 @@ module ServerEngine
|
|
390
407
|
# send signal now
|
391
408
|
|
392
409
|
if @immediate_kill_start_time
|
393
|
-
interval = @
|
394
|
-
interval_incr = @
|
395
|
-
if @
|
396
|
-
@immediate_kill_start_time <= now - @
|
410
|
+
interval = @immediate_kill_interval
|
411
|
+
interval_incr = @immediate_kill_interval_increment
|
412
|
+
if @immediate_kill_timeout >= 0 &&
|
413
|
+
@immediate_kill_start_time <= now - @immediate_kill_timeout
|
397
414
|
# escalate to SIGKILL
|
398
415
|
signal = :KILL
|
399
416
|
else
|
400
|
-
signal = @
|
417
|
+
signal = @immediate_kill_signal
|
401
418
|
end
|
402
419
|
|
403
420
|
else
|
404
|
-
signal = @
|
405
|
-
interval = @
|
406
|
-
interval_incr = @
|
421
|
+
signal = @graceful_kill_signal
|
422
|
+
interval = @graceful_kill_interval
|
423
|
+
interval_incr = @graceful_kill_interval_increment
|
407
424
|
end
|
408
425
|
|
409
426
|
begin
|
@@ -427,8 +444,9 @@ module ServerEngine
|
|
427
444
|
end
|
428
445
|
|
429
446
|
class TickThread < Thread
|
430
|
-
def initialize(
|
431
|
-
@
|
447
|
+
def initialize(auto_tick_interval, &tick)
|
448
|
+
@auto_tick_interval = auto_tick_interval
|
449
|
+
@tick = tick
|
432
450
|
super(&method(:main))
|
433
451
|
end
|
434
452
|
|
@@ -436,7 +454,7 @@ module ServerEngine
|
|
436
454
|
|
437
455
|
def main
|
438
456
|
while true
|
439
|
-
@
|
457
|
+
@tick.call(@auto_tick_interval)
|
440
458
|
end
|
441
459
|
nil
|
442
460
|
rescue AlreadyClosedError
|
@@ -464,8 +482,8 @@ module ServerEngine
|
|
464
482
|
end
|
465
483
|
|
466
484
|
class HeartbeatThread < Thread
|
467
|
-
def initialize(
|
468
|
-
@
|
485
|
+
def initialize(heartbeat_interval, target, error_proc)
|
486
|
+
@heartbeat_interval = heartbeat_interval
|
469
487
|
@target = target
|
470
488
|
@error_proc = error_proc
|
471
489
|
super(&method(:main))
|
@@ -475,7 +493,7 @@ module ServerEngine
|
|
475
493
|
|
476
494
|
def main
|
477
495
|
while true
|
478
|
-
sleep @
|
496
|
+
sleep @heartbeat_interval
|
479
497
|
@target.heartbeat!
|
480
498
|
end
|
481
499
|
nil
|
data/lib/serverengine/server.rb
CHANGED
@@ -15,6 +15,10 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
|
+
require 'serverengine/signals'
|
19
|
+
require 'serverengine/signal_thread'
|
20
|
+
require 'serverengine/worker'
|
21
|
+
|
18
22
|
module ServerEngine
|
19
23
|
|
20
24
|
class Server
|
@@ -24,6 +28,7 @@ module ServerEngine
|
|
24
28
|
@worker_module = worker_module
|
25
29
|
|
26
30
|
@stop = false
|
31
|
+
@stop_status = nil
|
27
32
|
|
28
33
|
super(load_config_proc, &block)
|
29
34
|
|
@@ -31,6 +36,9 @@ module ServerEngine
|
|
31
36
|
@log_stderr = !!@config.fetch(:log_stderr, true)
|
32
37
|
@log_stdout = false if logdev_from_config(@config) == STDOUT
|
33
38
|
@log_stderr = false if logdev_from_config(@config) == STDERR
|
39
|
+
|
40
|
+
@command_sender = @config.fetch(:command_sender, ServerEngine.windows? ? "pipe" : "signal")
|
41
|
+
@command_pipe = @config.fetch(:command_pipe, nil)
|
34
42
|
end
|
35
43
|
|
36
44
|
def before_run
|
@@ -64,17 +72,40 @@ module ServerEngine
|
|
64
72
|
|
65
73
|
def install_signal_handlers
|
66
74
|
s = self
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
75
|
+
if @command_pipe
|
76
|
+
Thread.new do
|
77
|
+
until @command_pipe.closed?
|
78
|
+
case @command_pipe.gets.chomp
|
79
|
+
when "GRACEFUL_STOP"
|
80
|
+
s.stop(true)
|
81
|
+
when "IMMEDIATE_STOP"
|
82
|
+
s.stop(false)
|
83
|
+
when "GRACEFUL_RESTART"
|
84
|
+
s.restart(true)
|
85
|
+
when "IMMEDIATE_RESTART"
|
86
|
+
s.restart(false)
|
87
|
+
when "RELOAD"
|
88
|
+
s.reload
|
89
|
+
when "DETACH"
|
90
|
+
s.detach(true)
|
91
|
+
when "DUMP"
|
92
|
+
Sigdump.dump
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
else
|
97
|
+
SignalThread.new do |st|
|
98
|
+
st.trap(@config[:signal_graceful_stop] || Signals::GRACEFUL_STOP) { s.stop(true) }
|
99
|
+
st.trap(@config[:signal_detach] || Signals::DETACH) { s.stop(true) }
|
100
|
+
# Here disables signals excepting GRACEFUL_STOP == :SIGTERM because
|
101
|
+
# only SIGTERM is available on all version of Windows.
|
102
|
+
unless ServerEngine.windows?
|
103
|
+
st.trap(@config[:signal_immediate_stop] || Signals::IMMEDIATE_STOP) { s.stop(false) }
|
104
|
+
st.trap(@config[:signal_graceful_restart] || Signals::GRACEFUL_RESTART) { s.restart(true) }
|
105
|
+
st.trap(@config[:signal_immediate_restart] || Signals::IMMEDIATE_RESTART) { s.restart(false) }
|
106
|
+
st.trap(@config[:signal_reload] || Signals::RELOAD) { s.reload }
|
107
|
+
st.trap(@config[:signal_dump] || Signals::DUMP) { Sigdump.dump }
|
108
|
+
end
|
78
109
|
end
|
79
110
|
end
|
80
111
|
end
|
@@ -93,6 +124,9 @@ module ServerEngine
|
|
93
124
|
ensure
|
94
125
|
after_run
|
95
126
|
end
|
127
|
+
if @stop_status
|
128
|
+
raise SystemExit.new(@stop_status)
|
129
|
+
end
|
96
130
|
end
|
97
131
|
|
98
132
|
module WorkerInitializer
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#
|
2
|
+
# ServerEngine
|
3
|
+
#
|
4
|
+
# Copyright (C) 2012-2013 FURUHASHI Sadayuki
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'serverengine/utils'
|
20
|
+
|
21
|
+
module ServerEngine
|
22
|
+
module Signals
|
23
|
+
GRACEFUL_STOP = :TERM
|
24
|
+
IMMEDIATE_STOP = ServerEngine::windows? ? :KILL : :QUIT
|
25
|
+
GRACEFUL_RESTART = :USR1
|
26
|
+
IMMEDIATE_RESTART = :HUP
|
27
|
+
RELOAD = :USR2
|
28
|
+
DETACH = :INT
|
29
|
+
DUMP = :CONT
|
30
|
+
end
|
31
|
+
end
|
@@ -15,12 +15,12 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
|
+
require 'socket'
|
19
|
+
require 'ipaddr'
|
20
|
+
|
18
21
|
module ServerEngine
|
19
22
|
module SocketManager
|
20
23
|
|
21
|
-
require 'socket'
|
22
|
-
require 'ipaddr'
|
23
|
-
|
24
24
|
class Client
|
25
25
|
def initialize(path)
|
26
26
|
@path = path
|
@@ -157,8 +157,6 @@ module ServerEngine
|
|
157
157
|
Marshal.load(data)
|
158
158
|
end
|
159
159
|
|
160
|
-
require_relative 'utils'
|
161
|
-
|
162
160
|
if ServerEngine.windows?
|
163
161
|
require_relative 'socket_manager_win'
|
164
162
|
Client.include(SocketManagerWin::ClientModule)
|
@@ -15,12 +15,14 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
|
+
require 'socket'
|
19
|
+
require 'ipaddr'
|
20
|
+
|
21
|
+
require_relative 'winsock'
|
18
22
|
|
19
23
|
module ServerEngine
|
20
24
|
module SocketManagerWin
|
21
25
|
|
22
|
-
require_relative 'winsock'
|
23
|
-
|
24
26
|
module ClientModule
|
25
27
|
private
|
26
28
|
|
@@ -15,6 +15,17 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
|
+
require 'serverengine/config_loader'
|
19
|
+
require 'serverengine/blocking_flag'
|
20
|
+
require 'serverengine/process_manager'
|
21
|
+
require 'serverengine/command_sender'
|
22
|
+
require 'serverengine/signals'
|
23
|
+
|
24
|
+
require 'serverengine/embedded_server'
|
25
|
+
require 'serverengine/multi_process_server'
|
26
|
+
require 'serverengine/multi_thread_server'
|
27
|
+
require 'serverengine/multi_spawn_server'
|
28
|
+
|
18
29
|
module ServerEngine
|
19
30
|
|
20
31
|
class Supervisor
|
@@ -29,8 +40,8 @@ module ServerEngine
|
|
29
40
|
|
30
41
|
@pm = ProcessManager.new(
|
31
42
|
auto_tick: false,
|
32
|
-
graceful_kill_signal:
|
33
|
-
immediate_kill_signal:
|
43
|
+
graceful_kill_signal: Signals::GRACEFUL_STOP,
|
44
|
+
immediate_kill_signal: Signals::IMMEDIATE_STOP,
|
34
45
|
enable_heartbeat: true,
|
35
46
|
auto_heartbeat: true,
|
36
47
|
)
|
@@ -41,9 +52,19 @@ module ServerEngine
|
|
41
52
|
@server_process_name = @config[:server_process_name]
|
42
53
|
|
43
54
|
@restart_server_process = !!@config[:restart_server_process]
|
55
|
+
@unrecoverable_exit_codes = @config.fetch(:unrecoverable_exit_codes, [])
|
44
56
|
@enable_detach = !!@config[:enable_detach]
|
45
57
|
@exit_on_detach = !!@config[:exit_on_detach]
|
46
58
|
@disable_reload = !!@config[:disable_reload]
|
59
|
+
|
60
|
+
@command_pipe = @config.fetch(:command_pipe, nil)
|
61
|
+
|
62
|
+
@command_sender = @config.fetch(:command_sender, ServerEngine.windows? ? "pipe" : "signal")
|
63
|
+
if @command_sender == "pipe"
|
64
|
+
extend CommandSender::Pipe
|
65
|
+
else
|
66
|
+
extend CommandSender::Signal
|
67
|
+
end
|
47
68
|
end
|
48
69
|
|
49
70
|
# server is available after start_server() call.
|
@@ -97,16 +118,16 @@ module ServerEngine
|
|
97
118
|
|
98
119
|
def stop(stop_graceful)
|
99
120
|
@stop = true
|
100
|
-
|
121
|
+
_stop(stop_graceful)
|
101
122
|
end
|
102
123
|
|
103
124
|
def restart(stop_graceful)
|
104
125
|
reload_config
|
105
126
|
@logger.reopen! if @logger
|
106
127
|
if @restart_server_process
|
107
|
-
|
128
|
+
_stop(stop_graceful)
|
108
129
|
else
|
109
|
-
|
130
|
+
_restart(stop_graceful)
|
110
131
|
end
|
111
132
|
end
|
112
133
|
|
@@ -115,13 +136,13 @@ module ServerEngine
|
|
115
136
|
reload_config
|
116
137
|
end
|
117
138
|
@logger.reopen! if @logger
|
118
|
-
|
139
|
+
_reload
|
119
140
|
end
|
120
141
|
|
121
142
|
def detach(stop_graceful)
|
122
143
|
if @enable_detach
|
123
144
|
@detach_flag.set!
|
124
|
-
|
145
|
+
_stop(stop_graceful)
|
125
146
|
else
|
126
147
|
stop(stop_graceful)
|
127
148
|
end
|
@@ -129,14 +150,37 @@ module ServerEngine
|
|
129
150
|
|
130
151
|
def install_signal_handlers
|
131
152
|
s = self
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
153
|
+
if @command_pipe
|
154
|
+
Thread.new do
|
155
|
+
until @command_pipe.closed?
|
156
|
+
case @command_pipe.gets.chomp
|
157
|
+
when "GRACEFUL_STOP"
|
158
|
+
s.stop(true)
|
159
|
+
when "IMMEDIATE_STOP"
|
160
|
+
s.stop(false)
|
161
|
+
when "GRACEFUL_RESTART"
|
162
|
+
s.restart(true)
|
163
|
+
when "IMMEDIATE_RESTART"
|
164
|
+
s.restart(false)
|
165
|
+
when "RELOAD"
|
166
|
+
s.reload
|
167
|
+
when "DETACH"
|
168
|
+
s.detach(true)
|
169
|
+
when "DUMP"
|
170
|
+
Sigdump.dump
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
else
|
175
|
+
SignalThread.new do |st|
|
176
|
+
st.trap(Signals::GRACEFUL_STOP) { s.stop(true) }
|
177
|
+
st.trap(Signals::IMMEDIATE_STOP) { s.stop(false) }
|
178
|
+
st.trap(Signals::GRACEFUL_RESTART) { s.restart(true) }
|
179
|
+
st.trap(Signals::IMMEDIATE_RESTART) { s.restart(false) }
|
180
|
+
st.trap(Signals::RELOAD) { s.reload }
|
181
|
+
st.trap(Signals::DETACH) { s.detach(true) }
|
182
|
+
st.trap(Signals::DUMP) { Sigdump.dump }
|
183
|
+
end
|
140
184
|
end
|
141
185
|
end
|
142
186
|
|
@@ -151,6 +195,9 @@ module ServerEngine
|
|
151
195
|
until @detach_flag.wait(0.5)
|
152
196
|
if try_join
|
153
197
|
return if @stop # supervisor stoppped explicitly
|
198
|
+
if @stop_status # set exit code told by server
|
199
|
+
raise SystemExit.new(@stop_status)
|
200
|
+
end
|
154
201
|
|
155
202
|
# child process died unexpectedly.
|
156
203
|
# sleep @server_detach_wait sec and reboot process
|
@@ -184,7 +231,10 @@ module ServerEngine
|
|
184
231
|
|
185
232
|
def try_join
|
186
233
|
if stat = @pmon.try_join
|
187
|
-
@logger.info "Server finished#{@stop ? '' : ' unexpectedly'} with #{
|
234
|
+
@logger.info "Server finished#{@stop ? '' : ' unexpectedly'} with #{ServerEngine.format_join_status(stat)}"
|
235
|
+
if !@stop && stat.is_a?(Process::Status) && stat.exited? && @unrecoverable_exit_codes.include?(stat.exitstatus)
|
236
|
+
@stop_status = stat.exitstatus
|
237
|
+
end
|
188
238
|
@pmon = nil
|
189
239
|
return stat
|
190
240
|
else
|
@@ -194,20 +244,50 @@ module ServerEngine
|
|
194
244
|
end
|
195
245
|
|
196
246
|
def start_server
|
197
|
-
|
198
|
-
|
247
|
+
if @command_sender == "pipe"
|
248
|
+
inpipe, @command_sender_pipe = IO.pipe
|
249
|
+
end
|
199
250
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
s.install_signal_handlers
|
251
|
+
unless ServerEngine.windows?
|
252
|
+
s = create_server(logger)
|
253
|
+
@last_start_time = Time.now
|
204
254
|
|
205
|
-
|
255
|
+
begin
|
256
|
+
m = @pm.fork do
|
257
|
+
$0 = @server_process_name if @server_process_name
|
258
|
+
if @command_sender == "pipe"
|
259
|
+
@command_sender_pipe.close
|
260
|
+
s.instance_variable_set(:@command_pipe, inpipe)
|
261
|
+
end
|
262
|
+
s.install_signal_handlers
|
263
|
+
|
264
|
+
begin
|
265
|
+
s.main
|
266
|
+
rescue SystemExit => e
|
267
|
+
@logger.info "Server is exitting with code #{e.status}"
|
268
|
+
exit! e.status
|
269
|
+
end
|
270
|
+
end
|
271
|
+
if @command_sender == "pipe"
|
272
|
+
inpipe.close
|
273
|
+
end
|
274
|
+
|
275
|
+
return m
|
276
|
+
ensure
|
277
|
+
s.after_start
|
278
|
+
end
|
279
|
+
else # if ServerEngine.windows?
|
280
|
+
exconfig = {}
|
281
|
+
if @command_sender == "pipe"
|
282
|
+
exconfig[:in] = inpipe
|
283
|
+
end
|
284
|
+
@last_start_time = Time.now
|
285
|
+
m = @pm.spawn(*Array(config[:windows_daemon_cmdline]), exconfig)
|
286
|
+
if @command_sender == "pipe"
|
287
|
+
inpipe.close
|
206
288
|
end
|
207
289
|
|
208
290
|
return m
|
209
|
-
ensure
|
210
|
-
s.after_start
|
211
291
|
end
|
212
292
|
end
|
213
293
|
|