diff --git a/lib/fluent/plugin/in_tail.rb b/lib/fluent/plugin/in_tail.rb index 8185df0f5e..68374d608b 100644 --- a/lib/fluent/plugin/in_tail.rb +++ b/lib/fluent/plugin/in_tail.rb @@ -165,6 +165,7 @@ def configure(conf) @path_formatters = @paths.map{|path| [path, Fluent::Timezone.formatter(@path_timezone, path)]}.to_h @exclude_path_formatters = @exclude_path.map{|path| [path, Fluent::Timezone.formatter(@path_timezone, path)]}.to_h end + check_dir_permission unless Fluent.windows? # TODO: Use plugin_root_dir and storage plugin to store positions if available if @pos_file @@ -212,6 +213,20 @@ def configure(conf) @metrics = MetricsInfo.new(opened_file_metrics, closed_file_metrics, rotated_file_metrics, throttling_metrics) end + def check_dir_permission + expand_paths_raw.select { |path| + not File.exist?(path) + }.each { |path| + inaccessible_dir = Pathname.new(File.expand_path(path)) + .ascend + .reverse_each + .find { |p| p.directory? && !p.executable? } + if inaccessible_dir + log.warn "Skip #{path} because '#{inaccessible_dir}' lacks execute permission." + end + } + end + def configure_tag if @tag.index('*') @tag_prefix, @tag_suffix = @tag.split('*') @@ -321,7 +336,7 @@ def use_glob?(path) end end - def expand_paths + def expand_paths_raw date = Fluent::EventTime.now paths = [] @paths.each { |path| @@ -367,10 +382,14 @@ def expand_paths end use_glob?(path) ? Dir.glob(path) : path }.flatten.uniq + paths - excluded + end + + def expand_paths # filter out non existing files, so in case pattern is without '*' we don't do unnecessary work hash = {} - (paths - excluded).select { |path| - FileTest.exist?(path) + expand_paths_raw.select { |path| + File.exist?(path) }.each { |path| # Even we just checked for existence, there is a race condition here as # of which stat() might fail with ENOENT. See #3224. diff --git a/test/plugin/test_in_tail.rb b/test/plugin/test_in_tail.rb index 7b1405999a..0ce47bf023 100644 --- a/test/plugin/test_in_tail.rb +++ b/test/plugin/test_in_tail.rb @@ -2591,6 +2591,46 @@ def test_EACCES d.instance_shutdown if d && d.instance end + def test_warn_without_directory_permission + omit "Cannot test with root user" if Process::UID.eid == 0 + omit "NTFS doesn't support UNIX like permissions" if Fluent.windows? + path = "#{@tmp_dir}/noaccess/tail.txt" + begin + FileUtils.mkdir_p("#{@tmp_dir}/noaccess") + FileUtils.touch(path) + FileUtils.chmod(0400, path) + FileUtils.chmod(0600, "#{@tmp_dir}/noaccess") + config = config_element('', '', { + 'tag' => "tail", + 'path' => path, + 'format' => 'none', + "pos_file" => "#{@tmp_dir}/tail.pos", + }) + d = create_driver(config, false) + fname = File.expand_path("#{@tmp_dir}/noaccess") + assert(d.logs.any?{|log| log.include?("Skip #{path} because '#{fname}' lacks execute permission.\n") }) + end + end + + def test_no_warn_with_directory_permission + omit "Cannot test with root user" if Process::UID.eid == 0 + omit "NTFS doesn't support UNIX like permissions" if Fluent.windows? + path = "#{@tmp_dir}/noaccess/tail.txt" + begin + FileUtils.mkdir_p("#{@tmp_dir}/noaccess") + FileUtils.chmod(0100, "#{@tmp_dir}/noaccess") + config = config_element('', '', { + 'tag' => "tail", + 'path' => path, + 'format' => 'none', + "pos_file" => "#{@tmp_dir}/tail.pos", + }) + d = create_driver(config, false) + fname = File.expand_path("#{@tmp_dir}/noaccess") + assert(d.logs.all?{|log| !log.include?("Skip #{path} because '#{fname}' lacks execute permission.\n") }) + end + end + def test_shutdown_timeout Fluent::FileWrapper.open("#{@tmp_dir}/tail.txt", "wb") do |f| # Should be large enough to take too long time to consume