Skip to content

Commit

Permalink
Merge pull request #3131 from ashie/win32-event-alternative-to-unix-s…
Browse files Browse the repository at this point in the history
…ignal

Add win32 events alternative to unix signals
  • Loading branch information
repeatedly authored Dec 22, 2020
2 parents 7a45066 + f4a368a commit 94fa508
Show file tree
Hide file tree
Showing 6 changed files with 505 additions and 56 deletions.
7 changes: 7 additions & 0 deletions bin/fluent-ctl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env ruby

here = File.dirname(__FILE__)
$LOAD_PATH << File.expand_path(File.join(here, '..', 'lib'))
require 'fluent/command/ctl'

Fluent::Ctl.new.call
177 changes: 177 additions & 0 deletions lib/fluent/command/ctl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#
# 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.
#

require 'optparse'
require 'fluent/env'
require 'fluent/version'
if Fluent.windows?
require 'win32/event'
require 'win32/service'
end

module Fluent
class Ctl
DEFAULT_OPTIONS = {}

if Fluent.windows?
include Windows::ServiceConstants
include Windows::ServiceStructs
include Windows::ServiceFunctions

COMMAND_MAP = {
shutdown: "",
restart: "HUP",
flush: "USR1",
reload: "USR2",
}
WINSVC_CONTROL_CODE_MAP = {
shutdown: SERVICE_CONTROL_STOP,
# 128 - 255: user-defined control code
# See https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-controlservice
restart: 128,
flush: 129,
reload: SERVICE_CONTROL_PARAMCHANGE,
}
else
COMMAND_MAP = {
shutdown: :TERM,
restart: :HUP,
flush: :USR1,
reload: :USR2,
}
end

def initialize(argv = ARGV)
@argv = argv
@options = {}
@opt_parser = OptionParser.new
configure_option_parser
@options.merge!(DEFAULT_OPTIONS)
parse_options!
end

def help_text
text = "\n"
if Fluent.windows?
text << "Usage: #{$PROGRAM_NAME} COMMAND [PID_OR_SVCNAME]\n"
else
text << "Usage: #{$PROGRAM_NAME} COMMAND PID\n"
end
text << "\n"
text << "Commands: \n"
COMMAND_MAP.each do |key, value|
text << " #{key}\n"
end
text
end

def usage(msg = nil)
puts help_text
if msg
puts
puts "Error: #{msg}"
end
exit 1
end

def call
if Fluent.windows?
if @pid_or_svcname =~ /^[0-9]+$/
# Use as PID
return call_windows_event(@command, "fluentd_#{@pid_or_svcname}")
end

unless call_winsvc_control_code(@command, @pid_or_svcname)
puts "Cannot send control code to #{@pid_or_svcname} service, try to send an event with this name ..."
call_windows_event(@command, @pid_or_svcname)
end
else
call_signal(@command, @pid_or_svcname)
end
end

private

def call_signal(command, pid)
signal = COMMAND_MAP[command.to_sym]
Process.kill(signal, pid.to_i)
end

def call_winsvc_control_code(command, pid_or_svcname)
status = SERVICE_STATUS.new

begin
handle_scm = OpenSCManager(nil, nil, SC_MANAGER_CONNECT)
FFI.raise_windows_error('OpenSCManager') if handle_scm == 0

handle_scs = OpenService(handle_scm, "fluentdwinsvc", SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_USER_DEFINED_CONTROL)
FFI.raise_windows_error('OpenService') if handle_scs == 0

control_code = WINSVC_CONTROL_CODE_MAP[command.to_sym]

unless ControlService(handle_scs, control_code, status)
FFI.raise_windows_error('ControlService')
end
rescue => e
puts e
state = status[:dwCurrentState]
return state == SERVICE_STOPPED || state == SERVICE_STOP_PENDING
ensure
CloseServiceHandle(handle_scs)
CloseServiceHandle(handle_scm)
end

return true
end

def call_windows_event(command, pid_or_svcname)
prefix = pid_or_svcname
event_name = COMMAND_MAP[command.to_sym]
suffix = event_name.empty? ? "" : "_#{event_name}"

begin
event = Win32::Event.open("#{prefix}#{suffix}")
event.set
event.close
rescue Errno::ENOENT => e
puts "Error: Cannot find the fluentd process with the event name: \"#{prefix}\""
end
end

def configure_option_parser
@opt_parser.banner = help_text
@opt_parser.version = Fluent::VERSION
end

def parse_options!
@opt_parser.parse!(@argv)

@command = @argv[0]
@pid_or_svcname = @argv[1] || "fluentdwinsvc"

usage("Command isn't specified!") if @command.nil? || @command.empty?
usage("Unknown command: #{@command}") unless COMMAND_MAP.has_key?(@command.to_sym)

if Fluent.windows?
usage("PID or SVCNAME isn't specified!") if @pid_or_svcname.nil? || @pid_or_svcname.empty?
else
usage("PID isn't specified!") if @pid_or_svcname.nil? || @pid_or_svcname.empty?
usage("Invalid PID: #{pid}") unless @pid_or_svcname =~ /^[0-9]+$/
end
end
end
end

Loading

0 comments on commit 94fa508

Please sign in to comment.