From ac5fb4da207a2a459b8431be40df68f3eee07637 Mon Sep 17 00:00:00 2001 From: Kentaro Hayashi Date: Tue, 6 Feb 2024 14:49:03 +0900 Subject: [PATCH] Guard launching duplicated fluentd with same configuration Before: If you launch multiple Fluentd instance with same configuration file, it causes a disaster with inconsistency processed buffer or pos file. After: Detect fluentd service's main process and fetch FLUENT_CONF. if configuration is same as spawned process, abort it. It can block the following conditions are met: * fluentd is launched via systemd (fluent-package) configuration file is specified via FLUENT_CONF. * manually try to launch fluentd with same configuration file as fluentd user with -c option. Thus running fluentd service and manually try to launch normal user case can't be detected. NOTE: Windows is out of scope in this PR. Signed-off-by: Kentaro Hayashi --- lib/fluent/command/fluentd.rb | 13 +++++++++++++ lib/fluent/process.rb | 24 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/lib/fluent/command/fluentd.rb b/lib/fluent/command/fluentd.rb index afa26553e5..0d0c2eb6c7 100644 --- a/lib/fluent/command/fluentd.rb +++ b/lib/fluent/command/fluentd.rb @@ -20,6 +20,7 @@ require 'fluent/log' require 'fluent/env' require 'fluent/version' +require 'fluent/process' $fluentdargv = Marshal.load(Marshal.dump(ARGV)) @@ -340,6 +341,18 @@ early_exit = true end +begin + running_fluentd_conf = Fluent::ProcessDetector.running_fluentd_conf + if opts[:config_path] and opts[:config_path] == running_fluentd_conf + puts "Error: can't start duplicate Fluentd instance with same #{opts[:config_path]}" + exit 2 + end +rescue Errno::EACCES + # e.g. unprivileged access error, can't detect duplicated instance from command line parameter. +rescue Errno::ENOENT + # e.g. can't detect duplicated instance from ps. +end + if start_service Service.start(opts[:winsvc_name]) end diff --git a/lib/fluent/process.rb b/lib/fluent/process.rb index f9d40fb41a..059de25510 100644 --- a/lib/fluent/process.rb +++ b/lib/fluent/process.rb @@ -15,8 +15,32 @@ # require 'fluent/compat/detach_process_mixin' +require 'fluent/env' module Fluent DetachProcessMixin = Fluent::Compat::DetachProcessMixin DetachMultiProcessMixin = Fluent::Compat::DetachMultiProcessMixin + + class ProcessDetector + def self.running_fluentd_conf + # Detect if same configuration is used + unless Fluent.windows? + IO.popen(["/usr/bin/ps", "-e", "-o", "uid,pid,ppid,cmd"]) do |_io| + _io.readlines.each do |line| + uid, pid, ppid, cmd = line.split(' ', 4) + # skip self and supervisor process + next if Process.pid == pid.to_i or Process.pid == ppid.to_i + if cmd and cmd.chomp.include?("fluentd") and ppid.to_i == 1 + # under systemd control + File.open("/proc/#{pid.to_i}/environ") do |file| + conf = file.read.split("\u0000").select { |entry| entry.include?("FLUENT_CONF") } + return conf.first.split('=').last unless conf.empty? + end + end + end + end + end + return nil + end + end end