invoker 1.0.4 → 1.1.0
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 +5 -0
- data/.rubocop.yml +30 -0
- data/.travis.yml +1 -0
- data/Gemfile +1 -0
- data/bin/invoker +4 -8
- data/invoker.gemspec +10 -11
- data/lib/invoker.rb +95 -21
- data/lib/invoker/cli.rb +126 -0
- data/lib/invoker/cli/pinger.rb +23 -0
- data/lib/invoker/cli/question.rb +15 -0
- data/lib/invoker/cli/tail.rb +34 -0
- data/lib/invoker/cli/tail_watcher.rb +34 -0
- data/lib/invoker/command_worker.rb +28 -2
- data/lib/invoker/commander.rb +34 -236
- data/lib/invoker/config.rb +5 -0
- data/lib/invoker/daemon.rb +126 -0
- data/lib/invoker/dns_cache.rb +23 -0
- data/lib/invoker/errors.rb +1 -0
- data/lib/invoker/ipc.rb +45 -0
- data/lib/invoker/ipc/add_command.rb +12 -0
- data/lib/invoker/ipc/add_http_command.rb +10 -0
- data/lib/invoker/ipc/base_command.rb +24 -0
- data/lib/invoker/ipc/client_handler.rb +26 -0
- data/lib/invoker/ipc/dns_check_command.rb +16 -0
- data/lib/invoker/ipc/list_command.rb +11 -0
- data/lib/invoker/ipc/message.rb +170 -0
- data/lib/invoker/ipc/message/list_response.rb +33 -0
- data/lib/invoker/ipc/message/tail_response.rb +10 -0
- data/lib/invoker/ipc/ping_command.rb +10 -0
- data/lib/invoker/ipc/reload_command.rb +12 -0
- data/lib/invoker/ipc/remove_command.rb +12 -0
- data/lib/invoker/{command_listener → ipc}/server.rb +6 -11
- data/lib/invoker/ipc/tail_command.rb +11 -0
- data/lib/invoker/ipc/unix_client.rb +60 -0
- data/lib/invoker/parsers/config.rb +1 -0
- data/lib/invoker/power/balancer.rb +17 -7
- data/lib/invoker/power/config.rb +6 -3
- data/lib/invoker/power/dns.rb +22 -21
- data/lib/invoker/power/http_response.rb +1 -1
- data/lib/invoker/power/power.rb +3 -0
- data/lib/invoker/power/powerup.rb +3 -2
- data/lib/invoker/power/setup.rb +6 -4
- data/lib/invoker/process_manager.rb +187 -0
- data/lib/invoker/process_printer.rb +27 -38
- data/lib/invoker/reactor.rb +19 -38
- data/lib/invoker/reactor/reader.rb +53 -0
- data/lib/invoker/version.rb +1 -1
- data/readme.md +1 -1
- data/spec/invoker/cli/pinger_spec.rb +22 -0
- data/spec/invoker/cli/tail_watcher_spec.rb +39 -0
- data/spec/invoker/cli_spec.rb +27 -0
- data/spec/invoker/command_worker_spec.rb +30 -0
- data/spec/invoker/commander_spec.rb +57 -127
- data/spec/invoker/config_spec.rb +21 -0
- data/spec/invoker/daemon_spec.rb +34 -0
- data/spec/invoker/invoker_spec.rb +31 -0
- data/spec/invoker/ipc/client_handler_spec.rb +44 -0
- data/spec/invoker/ipc/dns_check_command_spec.rb +32 -0
- data/spec/invoker/ipc/message/list_response_spec.rb +22 -0
- data/spec/invoker/ipc/message_spec.rb +45 -0
- data/spec/invoker/ipc/unix_client_spec.rb +29 -0
- data/spec/invoker/power/setup_spec.rb +1 -1
- data/spec/invoker/process_manager_spec.rb +98 -0
- data/spec/invoker/reactor_spec.rb +6 -0
- data/spec/spec_helper.rb +15 -24
- metadata +107 -77
- data/lib/invoker/command_listener/client.rb +0 -45
- data/lib/invoker/parsers/option_parser.rb +0 -106
- data/lib/invoker/power.rb +0 -7
- data/lib/invoker/runner.rb +0 -98
- data/spec/invoker/command_listener/client_spec.rb +0 -52
@@ -0,0 +1,34 @@
|
|
1
|
+
module Invoker
|
2
|
+
# This class defines sockets which are open for watching log files
|
3
|
+
class CLI::TailWatcher
|
4
|
+
attr_accessor :tail_watchers
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@tail_mutex = Mutex.new
|
8
|
+
@tail_watchers = Hash.new { |h, k| h[k] = [] }
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](process_name)
|
12
|
+
@tail_mutex.synchronize { tail_watchers[process_name] }
|
13
|
+
end
|
14
|
+
|
15
|
+
def add(names, socket)
|
16
|
+
@tail_mutex.synchronize do
|
17
|
+
names.each { |name| tail_watchers[name] << socket }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove(name, socket)
|
22
|
+
@tail_mutex.synchronize do
|
23
|
+
client_list = tail_watchers[name]
|
24
|
+
client_list.delete(socket)
|
25
|
+
purge(name, socket) if client_list.empty?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def purge(name, socket)
|
30
|
+
tail_watchers.delete(name)
|
31
|
+
Invoker.close_socket(socket)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -24,11 +24,37 @@ module Invoker
|
|
24
24
|
|
25
25
|
# Print the lines received over the network
|
26
26
|
def receive_line(line)
|
27
|
-
Invoker
|
27
|
+
tail_watchers = Invoker.tail_watchers[@command_label]
|
28
|
+
color_line = "#{@command_label.color(color)} : #{line}"
|
29
|
+
if tail_watchers && !tail_watchers.empty?
|
30
|
+
json_encoded_tail_response = tail_response(color_line)
|
31
|
+
if json_encoded_tail_response
|
32
|
+
tail_watchers.each { |tail_socket| send_data(tail_socket, json_encoded_tail_response) }
|
33
|
+
end
|
34
|
+
else
|
35
|
+
Invoker::Logger.puts color_line
|
36
|
+
end
|
28
37
|
end
|
29
38
|
|
30
39
|
def to_h
|
31
|
-
{:command_label
|
40
|
+
{ command_label: command_label, pid: pid.to_s }
|
41
|
+
end
|
42
|
+
|
43
|
+
def send_data(socket, data)
|
44
|
+
socket.write(data)
|
45
|
+
rescue
|
46
|
+
Invoker::Logger.puts "Removing #{@command_label} watcher #{socket} from list"
|
47
|
+
Invoker.tail_watchers.remove(@command_label, socket)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Encode current line as json and send the response.
|
53
|
+
def tail_response(line)
|
54
|
+
tail_response = Invoker::IPC::Message::TailResponse.new(tail_line: line)
|
55
|
+
tail_response.encoded_message
|
56
|
+
rescue
|
57
|
+
nil
|
32
58
|
end
|
33
59
|
end
|
34
60
|
end
|
data/lib/invoker/commander.rb
CHANGED
@@ -2,129 +2,48 @@ require "io/console"
|
|
2
2
|
require 'pty'
|
3
3
|
require "json"
|
4
4
|
require "dotenv"
|
5
|
+
require "forwardable"
|
5
6
|
|
6
7
|
module Invoker
|
7
8
|
class Commander
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
attr_accessor :event_manager, :runnables
|
9
|
+
attr_accessor :reactor, :process_manager
|
10
|
+
attr_accessor :event_manager, :runnables, :thread_group
|
11
|
+
extend Forwardable
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
@open_pipes = {}
|
13
|
+
def_delegators :@process_manager, :start_process_by_name, :stop_process
|
14
|
+
def_delegators :@process_manager, :restart_process, :get_worker_from_fd, :process_list
|
16
15
|
|
17
|
-
|
18
|
-
|
16
|
+
def_delegators :@event_manager, :schedule_event, :trigger
|
17
|
+
def_delegator :@reactor, :watch_for_read
|
19
18
|
|
20
|
-
|
21
|
-
@
|
19
|
+
def initialize
|
20
|
+
@thread_group = ThreadGroup.new
|
21
|
+
@runnable_mutex = Mutex.new
|
22
22
|
|
23
|
-
@event_manager = Invoker::Event::Manager.new
|
23
|
+
@event_manager = Invoker::Event::Manager.new
|
24
24
|
@runnables = []
|
25
25
|
|
26
26
|
@reactor = Invoker::Reactor.new
|
27
|
+
@process_manager = Invoker::ProcessManager.new
|
27
28
|
Thread.abort_on_exception = true
|
28
29
|
end
|
29
30
|
|
30
31
|
# Start the invoker process supervisor. This method starts a unix server
|
31
32
|
# in separate thread that listens for incoming commands.
|
32
33
|
def start_manager
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
start_event_loop()
|
43
|
-
end
|
44
|
-
|
45
|
-
# Start given command and start a background thread to wait on the process
|
46
|
-
#
|
47
|
-
# @param process_info [OpenStruct(command, directory)]
|
48
|
-
def add_command(process_info)
|
49
|
-
m, s = PTY.open
|
50
|
-
s.raw! # disable newline conversion.
|
51
|
-
|
52
|
-
pid = run_command(process_info, s)
|
53
|
-
|
54
|
-
s.close()
|
55
|
-
|
56
|
-
worker = Invoker::CommandWorker.new(process_info.label, m, pid, select_color())
|
57
|
-
|
58
|
-
add_worker(worker)
|
59
|
-
wait_on_pid(process_info.label,pid)
|
60
|
-
end
|
61
|
-
|
62
|
-
# List currently running commands
|
63
|
-
def list_commands
|
64
|
-
Invoker::ProcessPrinter.to_json(workers)
|
65
|
-
end
|
66
|
-
|
67
|
-
# Start executing given command by their label name.
|
68
|
-
#
|
69
|
-
# @param command_label [String] Command label of process specified in config file.
|
70
|
-
def add_command_by_label(command_label)
|
71
|
-
if process_running?(command_label)
|
72
|
-
Invoker::Logger.puts "\nProcess '#{command_label}' is already running".color(:red)
|
73
|
-
return false
|
74
|
-
end
|
75
|
-
|
76
|
-
process_info = Invoker::CONFIG.process(command_label)
|
77
|
-
if process_info
|
78
|
-
add_command(process_info)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
# Reload a process given by command label
|
83
|
-
#
|
84
|
-
# @params command_label [String] Command label of process specified in config file.
|
85
|
-
def reload_command(command_label, rest_args)
|
86
|
-
if remove_command(command_label, rest_args)
|
87
|
-
event_manager.schedule_event(command_label, :worker_removed) {
|
88
|
-
add_command_by_label(command_label)
|
89
|
-
}
|
90
|
-
else
|
91
|
-
add_command_by_label(command_label)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
# Remove a process from list of processes managed by invoker supervisor.It also
|
96
|
-
# kills the process before removing it from the list.
|
97
|
-
#
|
98
|
-
# @param command_label [String] Command label of process specified in config file
|
99
|
-
# @param rest_args [Array] Additional option arguments, such as signal that can be used.
|
100
|
-
# @return [Boolean] if process existed and was removed else false
|
101
|
-
def remove_command(command_label, rest_args)
|
102
|
-
worker = workers[command_label]
|
103
|
-
return false unless worker
|
104
|
-
signal_to_use = rest_args ? Array(rest_args).first : 'INT'
|
105
|
-
|
106
|
-
Invoker::Logger.puts("Removing #{command_label} with signal #{signal_to_use}".color(:red))
|
107
|
-
kill_or_remove_process(worker.pid, signal_to_use, command_label)
|
108
|
-
end
|
109
|
-
|
110
|
-
# Given a file descriptor returns the worker object
|
111
|
-
#
|
112
|
-
# @param fd [IO] an IO object with valid file descriptor
|
113
|
-
# @return [Invoker::CommandWorker] The worker object which is associated with this fd
|
114
|
-
def get_worker_from_fd(fd)
|
115
|
-
open_pipes[fd.fileno]
|
116
|
-
end
|
117
|
-
|
118
|
-
# Given a command label returns the associated worker object
|
119
|
-
#
|
120
|
-
# @param label [String] Command label of the command
|
121
|
-
# @return [Invoker::CommandWorker] The worker object which is associated with this command
|
122
|
-
def get_worker_from_label(label)
|
123
|
-
workers[label]
|
34
|
+
verify_process_configuration
|
35
|
+
daemonize_app if Invoker.daemonize?
|
36
|
+
install_interrupt_handler
|
37
|
+
unix_server_thread = Thread.new { Invoker::IPC::Server.new }
|
38
|
+
@thread_group.add(unix_server_thread)
|
39
|
+
process_manager.run_power_server
|
40
|
+
Invoker.config.processes.each { |process_info| process_manager.start_process(process_info) }
|
41
|
+
at_exit { process_manager.kill_workers }
|
42
|
+
start_event_loop
|
124
43
|
end
|
125
44
|
|
126
45
|
def on_next_tick(*args, &block)
|
127
|
-
@
|
46
|
+
@runnable_mutex.synchronize do
|
128
47
|
@runnables << OpenStruct.new(:args => args, :block => block)
|
129
48
|
end
|
130
49
|
end
|
@@ -136,158 +55,37 @@ module Invoker
|
|
136
55
|
@runnables = []
|
137
56
|
end
|
138
57
|
|
139
|
-
|
140
|
-
return unless Invoker.can_run_balancer?(false)
|
141
|
-
|
142
|
-
powerup_id = Invoker::Power::Powerup.fork_and_start()
|
143
|
-
wait_on_pid("powerup_manager", powerup_id)
|
144
|
-
at_exit {
|
145
|
-
begin
|
146
|
-
Process.kill("INT", powerup_id)
|
147
|
-
rescue Errno::ESRCH; end
|
148
|
-
}
|
149
|
-
end
|
58
|
+
private
|
150
59
|
|
151
|
-
def
|
152
|
-
|
153
|
-
|
154
|
-
if File.exist?(default_env)
|
155
|
-
Dotenv::Environment.new(default_env)
|
156
|
-
else
|
157
|
-
{}
|
60
|
+
def verify_process_configuration
|
61
|
+
if !Invoker.config.processes || Invoker.config.processes.empty?
|
62
|
+
raise Invoker::Errors::InvalidConfig.new("No processes configured in config file")
|
158
63
|
end
|
159
64
|
end
|
160
65
|
|
161
|
-
private
|
162
66
|
def start_event_loop
|
163
67
|
loop do
|
164
|
-
reactor.
|
165
|
-
run_runnables
|
166
|
-
run_scheduled_events
|
68
|
+
reactor.monitor_for_fd_events
|
69
|
+
run_runnables
|
70
|
+
run_scheduled_events
|
167
71
|
end
|
168
72
|
end
|
169
73
|
|
170
74
|
def run_scheduled_events
|
171
75
|
event_manager.run_scheduled_events do |event|
|
172
|
-
event.block.call
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
def kill_or_remove_process(pid, signal_to_use, command_label)
|
177
|
-
process_kill(pid, signal_to_use)
|
178
|
-
true
|
179
|
-
rescue Errno::ESRCH
|
180
|
-
Invoker::Logger.puts("Killing process with #{pid} and name #{command_label} failed".color(:red))
|
181
|
-
remove_worker(command_label, false)
|
182
|
-
false
|
183
|
-
end
|
184
|
-
|
185
|
-
def process_kill(pid, signal_to_use)
|
186
|
-
if signal_to_use.to_i == 0
|
187
|
-
Process.kill(signal_to_use, -Process.getpgid(pid))
|
188
|
-
else
|
189
|
-
Process.kill(signal_to_use.to_i, -Process.getpgid(pid))
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
def select_color
|
194
|
-
selected_color = LABEL_COLORS.shift()
|
195
|
-
LABEL_COLORS.push(selected_color)
|
196
|
-
selected_color
|
197
|
-
end
|
198
|
-
|
199
|
-
# Remove worker from all collections
|
200
|
-
def remove_worker(command_label, trigger_event = true)
|
201
|
-
worker = @workers[command_label]
|
202
|
-
if worker
|
203
|
-
@open_pipes.delete(worker.pipe_end.fileno)
|
204
|
-
@workers.delete(command_label)
|
205
|
-
end
|
206
|
-
if trigger_event
|
207
|
-
event_manager.trigger(command_label, :worker_removed)
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
# add worker to global collections
|
212
|
-
def add_worker(worker)
|
213
|
-
@open_pipes[worker.pipe_end.fileno] = worker
|
214
|
-
@workers[worker.command_label] = worker
|
215
|
-
@reactor.add_to_monitor(worker.pipe_end)
|
216
|
-
end
|
217
|
-
|
218
|
-
def run_command(process_info, write_pipe)
|
219
|
-
command_label = process_info.label
|
220
|
-
|
221
|
-
event_manager.schedule_event(command_label, :exit) { remove_worker(command_label) }
|
222
|
-
|
223
|
-
env_options = load_env(process_info.dir)
|
224
|
-
|
225
|
-
spawn_options = {
|
226
|
-
:chdir => process_info.dir || ENV['PWD'], :out => write_pipe, :err => write_pipe,
|
227
|
-
:pgroup => true, :close_others => true, :in => :close
|
228
|
-
}
|
229
|
-
|
230
|
-
if defined?(Bundler)
|
231
|
-
Bundler.with_clean_env do
|
232
|
-
spawn(env_options, process_info.cmd, spawn_options)
|
233
|
-
end
|
234
|
-
else
|
235
|
-
spawn(env_options, process_info.cmd, spawn_options)
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
def wait_on_pid(command_label,pid)
|
240
|
-
raise Invoker::Errors::ToomanyOpenConnections if @thread_group.enclosed?
|
241
|
-
|
242
|
-
thread = Thread.new do
|
243
|
-
Process.wait(pid)
|
244
|
-
message = "Process with command #{command_label} exited with status #{$?.exitstatus}"
|
245
|
-
Invoker::Logger.puts("\n#{message}".color(:red))
|
246
|
-
notify_user(message)
|
247
|
-
event_manager.trigger(command_label, :exit)
|
248
|
-
end
|
249
|
-
@thread_group.add(thread)
|
250
|
-
end
|
251
|
-
|
252
|
-
def notify_user(message)
|
253
|
-
if defined?(Bundler)
|
254
|
-
Bundler.with_clean_env do
|
255
|
-
check_and_notify_with_terminal_notifier(message)
|
256
|
-
end
|
257
|
-
else
|
258
|
-
check_and_notify_with_terminal_notifier(message)
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
def check_and_notify_with_terminal_notifier(message)
|
263
|
-
return unless Invoker.darwin?
|
264
|
-
|
265
|
-
command_path = `which terminal-notifier`
|
266
|
-
if command_path && !command_path.empty?
|
267
|
-
system("terminal-notifier -message '#{message}' -title Invoker")
|
76
|
+
event.block.call
|
268
77
|
end
|
269
78
|
end
|
270
79
|
|
271
80
|
def install_interrupt_handler
|
272
81
|
Signal.trap("INT") do
|
273
|
-
kill_workers
|
82
|
+
process_manager.kill_workers
|
274
83
|
exit(0)
|
275
84
|
end
|
276
85
|
end
|
277
86
|
|
278
|
-
def
|
279
|
-
|
280
|
-
begin
|
281
|
-
Process.kill("INT", -Process.getpgid(worker.pid))
|
282
|
-
rescue Errno::ESRCH
|
283
|
-
puts "Error killing #{key}"
|
284
|
-
end
|
285
|
-
}
|
286
|
-
@workers = {}
|
287
|
-
end
|
288
|
-
|
289
|
-
def process_running?(command_label)
|
290
|
-
!!workers[command_label]
|
87
|
+
def daemonize_app
|
88
|
+
Invoker.daemon.start
|
291
89
|
end
|
292
90
|
end
|
293
91
|
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Invoker
|
2
|
+
# rip off from borg
|
3
|
+
# https://github.com/code-mancers/borg/blob/master/lib/borg/borg_daemon.rb
|
4
|
+
class Daemon
|
5
|
+
attr_reader :process_name
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@process_name = 'invoker'
|
9
|
+
end
|
10
|
+
|
11
|
+
def start
|
12
|
+
if running?
|
13
|
+
Invoker::Logger.puts "Invoker daemon is already running"
|
14
|
+
exit(0)
|
15
|
+
elsif dead?
|
16
|
+
File.delete(pid_file) if File.exists?(pid_file)
|
17
|
+
end
|
18
|
+
Invoker::Logger.puts "Running Invoker daemon"
|
19
|
+
daemonize
|
20
|
+
end
|
21
|
+
|
22
|
+
def stop
|
23
|
+
kill_process
|
24
|
+
end
|
25
|
+
|
26
|
+
def pid_file
|
27
|
+
File.join(Dir.home, ".invoker", "#{process_name}.pid")
|
28
|
+
end
|
29
|
+
|
30
|
+
def pid
|
31
|
+
File.read(pid_file).strip.to_i
|
32
|
+
end
|
33
|
+
|
34
|
+
def log_file
|
35
|
+
File.join(Dir.home, ".invoker", "#{process_name}.log")
|
36
|
+
end
|
37
|
+
|
38
|
+
def daemonize
|
39
|
+
if fork
|
40
|
+
sleep(2)
|
41
|
+
exit(0)
|
42
|
+
else
|
43
|
+
Process.setsid
|
44
|
+
File.open(pid_file, "w") do |file|
|
45
|
+
file.write(Process.pid.to_s)
|
46
|
+
end
|
47
|
+
Invoker::Logger.puts "Invoker daemon log is available at #{log_file}"
|
48
|
+
redirect_io(log_file)
|
49
|
+
$0 = process_name
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def kill_process
|
54
|
+
pgid = Process.getpgid(pid)
|
55
|
+
Process.kill('-TERM', pgid)
|
56
|
+
File.delete(pid_file) if File.exist?(pid_file)
|
57
|
+
Invoker::Logger.puts "Stopped Invoker daemon"
|
58
|
+
end
|
59
|
+
|
60
|
+
def process_running?
|
61
|
+
Process.kill(0, pid)
|
62
|
+
true
|
63
|
+
rescue Errno::ESRCH
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
67
|
+
def status
|
68
|
+
@status ||= check_process_status
|
69
|
+
end
|
70
|
+
|
71
|
+
def pidfile_exists?
|
72
|
+
File.exist?(pid_file)
|
73
|
+
end
|
74
|
+
|
75
|
+
def running?
|
76
|
+
status == 0
|
77
|
+
end
|
78
|
+
|
79
|
+
# pidfile exists but process isn't running
|
80
|
+
def dead?
|
81
|
+
status == 1
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def check_process_status
|
87
|
+
if pidfile_exists? && process_running?
|
88
|
+
0
|
89
|
+
elsif pidfile_exists? # but not process_running
|
90
|
+
1
|
91
|
+
else
|
92
|
+
3
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def redirect_io(logfile_name = nil)
|
97
|
+
redirect_file_to_target($stdin)
|
98
|
+
redirect_stdout(logfile_name)
|
99
|
+
redirect_stderr
|
100
|
+
end
|
101
|
+
|
102
|
+
def redirect_stderr
|
103
|
+
redirect_file_to_target($stderr, $stdout)
|
104
|
+
$stderr.sync = true
|
105
|
+
end
|
106
|
+
|
107
|
+
def redirect_stdout(logfile_name)
|
108
|
+
if logfile_name
|
109
|
+
begin
|
110
|
+
$stdout.reopen logfile_name, "a"
|
111
|
+
$stdout.sync = true
|
112
|
+
rescue StandardError
|
113
|
+
redirect_file_to_target($stdout)
|
114
|
+
end
|
115
|
+
else
|
116
|
+
redirect_file_to_target($stdout)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def redirect_file_to_target(file, target = "/dev/null")
|
121
|
+
begin
|
122
|
+
file.reopen(target)
|
123
|
+
rescue; end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|