Skip to content

Commit

Permalink
Merge pull request #674 from fluent/windows
Browse files Browse the repository at this point in the history
Add Windows support
  • Loading branch information
repeatedly committed Oct 8, 2015
2 parents c5d22cf + d9ef11b commit f537195
Show file tree
Hide file tree
Showing 22 changed files with 499 additions and 75 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ test/config/tmp/*
make_dist.sh
Gemfile.local
.ruby-version
*.swp
coverage/*
25 changes: 25 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
version: '{build}'

install:
- SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
- "%devkit%\\devkitvars.bat"
- ruby --version
- gem --version
- bundle install
build: off
test_script:
- bundle exec rake test TESTOPTS=-v

environment:
matrix:
- ruby_version: "22-x64"
devkit: C:\Ruby21-x64\DevKit
- ruby_version: "22"
devkit: C:\Ruby21\DevKit
- ruby_version: "21-x64"
devkit: C:\Ruby21-x64\DevKit
- ruby_version: "21"
devkit: C:\Ruby21\DevKit
matrix:
allow_failures:
- ruby_version: "21"
10 changes: 8 additions & 2 deletions fluentd.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,25 @@ Gem::Specification.new do |gem|
gem.add_runtime_dependency("msgpack", [">= 0.5.11", "< 0.6.0"])
gem.add_runtime_dependency("json", [">= 1.4.3"])
gem.add_runtime_dependency("yajl-ruby", ["~> 1.0"])
gem.add_runtime_dependency("cool.io", [">= 1.2.2", "< 2.0.0"])
gem.add_runtime_dependency("cool.io", [">= 1.4.1", "< 2.0.0"])
gem.add_runtime_dependency("http_parser.rb", [">= 0.5.1", "< 0.7.0"])
gem.add_runtime_dependency("sigdump", ["~> 0.2.2"])
gem.add_runtime_dependency("tzinfo", [">= 1.0.0"])
gem.add_runtime_dependency("tzinfo-data", [">= 1.0.0"])
gem.add_runtime_dependency("string-scrub", [">= 0.0.3"])
if /mswin|mingw/ =~ RUBY_PLATFORM
gem.add_runtime_dependency("win32-service", ["~> 0.8.3"])
gem.add_runtime_dependency("win32-ipc", ["~> 0.6.1"])
gem.add_runtime_dependency("win32-event", ["~> 0.6.1"])
gem.add_runtime_dependency("windows-pr", ["~> 1.2.3"])
end

gem.add_development_dependency("rake", [">= 0.9.2"])
gem.add_development_dependency("flexmock", ["~> 1.3.3"])
gem.add_development_dependency("parallel_tests", [">= 0.15.3"])
gem.add_development_dependency("simplecov", ["~> 0.6.4"])
gem.add_development_dependency("rr", [">= 1.0.0"])
gem.add_development_dependency("timecop", [">= 0.3.0"])
gem.add_development_dependency("test-unit", ["~> 3.0.2"])
gem.add_development_dependency("test-unit", ["~> 3.1.4"])
gem.add_development_dependency("test-unit-rr", ["~> 1.0.3"])
end
66 changes: 66 additions & 0 deletions lib/fluent/command/fluentd.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@

require 'optparse'
require 'fluent/supervisor'
require 'fluent/log'
require 'fluent/env'
require 'fluent/version'
require 'windows/library'
include Windows::Library

$fluentdargv = Marshal.load(Marshal.dump(ARGV))

op = OptionParser.new
op.version = Fluent::VERSION
Expand Down Expand Up @@ -119,6 +126,21 @@
opts[:gem_install_path] = s
}

if Fluent.windows?
op.on('-x', '--signame INTSIGNAME', "an object name which is used for Windows Service signal (Windows only)") {|s|
opts[:signame] = s
}

op.on('--reg-winsvc MODE', "install/uninstall as Windows Service. (i: install, u: uninstall) (Windows only)") {|s|
opts[:regwinsvc] = s
}

op.on('--reg-winsvc-fluentdopt OPTION', "specify fluentd option paramters for Windows Service. (Windows only)") {|s|
opts[:fluentdopt] = s
}
end


(class<<self;self;end).module_eval do
define_method(:usage) do |msg|
puts op.to_s
Expand Down Expand Up @@ -168,4 +190,48 @@
exit 0
end

if winsvcinstmode = opts[:regwinsvc]
FLUENTD_WINSVC_NAME="fluentdwinsvc"
FLUENTD_WINSVC_DISPLAYNAME="Fluentd Windows Service"
FLUENTD_WINSVC_DESC="Fluentd is an event collector system."
require 'fileutils'
require "win32/service"
require "win32/registry"
include Win32

case winsvcinstmode
when 'i'
binary_path = File.join(File.dirname(__FILE__), "..")
ruby_path = "\0" * 256
GetModuleFileName.call(0,ruby_path,256)
ruby_path = ruby_path.rstrip.gsub(/\\/, '/')

Service.create(
:service_name => FLUENTD_WINSVC_NAME,
:host => nil,
:service_type => Service::WIN32_OWN_PROCESS,
:description => FLUENTD_WINSVC_DESC,
:start_type => Service::DEMAND_START,
:error_control => Service::ERROR_NORMAL,
:binary_path_name => ruby_path+" -C "+binary_path+" winsvc.rb",
:load_order_group => "",
:dependencies => [""],
:display_name => FLUENTD_WINSVC_DISPLAYNAME
)
when 'u'
Service.delete(FLUENTD_WINSVC_NAME)
else
# none
end
exit 0
end

if fluentdopt = opts[:fluentdopt]
Win32::Registry::HKEY_LOCAL_MACHINE.open("SYSTEM\\CurrentControlSet\\Services\\fluentdwinsvc", Win32::Registry::KEY_ALL_ACCESS) do |reg|
reg['fluentdopt', Win32::Registry::REG_SZ] = fluentdopt
end
exit 0
end

require 'fluent/supervisor'
Fluent::Supervisor.new(opts).start
6 changes: 6 additions & 0 deletions lib/fluent/env.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,10 @@ module Fluent
DEFAULT_LISTEN_PORT = 24224
DEFAULT_FILE_PERMISSION = 0644
DEFAULT_DIR_PERMISSION = 0755
IS_WINDOWS = /mswin|mingw/ === RUBY_PLATFORM
private_constant :IS_WINDOWS

def self.windows?
IS_WINDOWS
end
end
1 change: 1 addition & 0 deletions lib/fluent/load.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# ignore setup error on Win or similar platform which doesn't support signal
end
require 'cool.io'

require 'fluent/env'
require 'fluent/version'
require 'fluent/log'
Expand Down
2 changes: 2 additions & 0 deletions lib/fluent/output.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ def secondary_init(primary)
$log.warn "type of secondary output should be same as primary output", :primary=>primary.class.to_s, :secondary=>self.class.to_s
end
end

def inspect; "#<%s:%014x>" % [self.class.name, '0x%014x' % (__id__<<1)] end
end
Expand Down
17 changes: 15 additions & 2 deletions lib/fluent/plugin/buf_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def initialize(key, path, unique_id, mode="a+", symlink_path = nil)
@path = path
@unique_id = unique_id
@file = File.open(@path, mode, DEFAULT_FILE_PERMISSION)
@file.binmode
@file.sync = true
@size = @file.stat.size
FileUtils.ln_sf(@path, symlink_path) if symlink_path
Expand Down Expand Up @@ -65,8 +66,20 @@ def open(&block)
end

def mv(path)
File.rename(@path, path)
@path = path
if Fluent.windows?
pos = @file.pos
@file.close
File.rename(@path, path)
@path = path
@file = File.open(@path, 'rb', DEFAULT_FILE_PERMISSION)
@file.sync = true
@size = @file.size
@file.pos = pos
@size = @file.stat.size
else
File.rename(@path, path)
@path = path
end
end
end

Expand Down
120 changes: 120 additions & 0 deletions lib/fluent/plugin/file_wrapper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#
# Fluentd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

module Fluent
module FileWrapper
def self.open(*args)
io = WindowsFile.new(*args).io
if block_given?
v = yield io
io.close
v
else
io
end
end

def self.stat(path)
f = WindowsFile.new(path)
s = f.stat
f.close
s
end
end

module WindowsFileExtension
attr_reader :path

def stat
s = super
s.instance_variable_set :@ino, @ino
def s.ino; @ino; end
s
end
end

# To open and get stat with setting FILE_SHARE_DELETE
class WindowsFile
require 'windows/file'
require 'windows/error'
require 'windows/handle'
require 'windows/nio'

include Windows::Error
include Windows::File
include Windows::Handle
include Windows::NIO

def initialize(path, mode='r', sharemode=FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE)
@path = path
@file_handle = INVALID_HANDLE_VALUE
@mode = mode


access, creationdisposition, seektoend = case mode.delete('b')
when "r" ; [FILE_GENERIC_READ , OPEN_EXISTING, false]
when "r+"; [FILE_GENERIC_READ | FILE_GENERIC_WRITE, OPEN_ALWAYS , false]
when "w" ; [FILE_GENERIC_WRITE , CREATE_ALWAYS, false]
when "w+"; [FILE_GENERIC_READ | FILE_GENERIC_WRITE, CREATE_ALWAYS, false]
when "a" ; [FILE_GENERIC_WRITE , OPEN_ALWAYS , true]
when "a+"; [FILE_GENERIC_READ | FILE_GENERIC_WRITE, OPEN_ALWAYS , true]
else raise "unknown mode '#{mode}'"
end

@file_handle = CreateFile.call(@path, access, sharemode,
0, creationdisposition, FILE_ATTRIBUTE_NORMAL, 0)
if @file_handle == INVALID_HANDLE_VALUE
err = GetLastError.call
if err == ERROR_FILE_NOT_FOUND || err == ERROR_ACCESS_DENIED
raise SystemCallError.new(2)
end
raise SystemCallError.new(err)
end
end

def close
CloseHandle.call(@file_handle)
@file_handle = INVALID_HANDLE_VALUE
end

def io
fd = _open_osfhandle(@file_handle, 0)
raise Errno::ENOENT if fd == -1
io = File.for_fd(fd, @mode)
io.instance_variable_set :@ino, self.ino
io.instance_variable_set :@path, @path
io.extend WindowsFileExtension
io
end

def ino
by_handle_file_information = '\0'*(4+8+8+8+4+4+4+4+4+4) #72bytes

unless GetFileInformationByHandle.call(@file_handle, by_handle_file_information)
return 0
end

by_handle_file_information.unpack("I11Q1")[11] # fileindex
end

def stat
s = File.stat(@path)
s.instance_variable_set :@ino, self.ino
def s.ino; @ino; end
s
end
end
end if Fluent.windows?
Loading

0 comments on commit f537195

Please sign in to comment.