Last active
April 19, 2016 15:49
-
-
Save FooBarWidget/7047475 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# This tool reads from STDIN and forwards all data to a file. | |
# It reopens the file upon receiving the SIGHUP signal. | |
require 'fcntl' | |
class Pipetool | |
def initialize(input, filename) | |
@input = input | |
@filename = filename | |
@event_pipe = IO.pipe | |
make_nonblock(@input) | |
make_nonblock(@event_pipe[0]) | |
make_nonblock(@event_pipe[1]) | |
@input.binmode | |
@event_pipe[1].sync = true | |
reopen_file | |
trap("HUP", &method(:on_sighup)) | |
debug "Event pipe on FDs #{@event_pipe[0].fileno}, #{@event_pipe[1].fileno}" | |
end | |
def main | |
debug "Pipetool started on PID #{Process.pid}" | |
done = false | |
while !done | |
ios = select([@input, @event_pipe[0]])[0] | |
if ios[0] == @event_pipe[0] | |
debug "Reopening #{@filename}" | |
drain_io(@event_pipe[0]) | |
reopen_file | |
else | |
done = forward_block | |
end | |
end | |
rescue SignalException => e | |
if !is_exit_signal?(e) | |
raise | |
end | |
end | |
private | |
def make_nonblock(io) | |
flags = io.fcntl(Fcntl::F_GETFL) | |
io.fcntl(Fcntl::F_SETFL, flags | Fcntl::O_NONBLOCK) | |
end | |
def on_sighup(signo) | |
debug "SIGHUP received" | |
@event_pipe[1].write('x') | |
end | |
def drain_io(io) | |
while true | |
begin | |
io.read_nonblock(1024 * 16) | |
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINTR | |
break | |
end | |
end | |
end | |
def reopen_file | |
@output.close if @output | |
@output = File.open(@filename, "ab") | |
@output.sync = true | |
end | |
def forward_block | |
begin | |
buf = @input.read_nonblock(1024 * 32) | |
rescue EOFError | |
return true | |
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::EINTR | |
return false | |
end | |
@output.write(buf) | |
false | |
end | |
def is_exit_signal?(e) | |
e.signo == Signal.list['TERM'] || e.signo == Signal.list['INT'] | |
end | |
def debug(message) | |
#STDERR.puts(message) | |
end | |
end | |
abort "Usage: pipetool <FILENAME>" if !ARGV[0] | |
Pipetool.new(STDIN, ARGV[0]).main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment