Skip to content

Commit 4f13e58

Browse files
committed
feat: Add directory configuration for pos_file
This commit adds new configuration options for the tail input plugin to control the pos_file directory permissions and ownership: - Add pos_dir_perm parameter for directory permissions - Add pos_dir_owner parameter for directory owner - Add pos_dir_group parameter for directory group - Use Etc.getpwnam and Etc.getgrnam for proper user/group resolution Example config: <source> @type tail pos_file /var/log/td-agent/httpd/file.pos pos_dir_perm 0770 pos_dir_owner 'root' pos_dir_group 'wheel' </source> Closes fluent#4822 Signed-off-by: kushynoda <[email protected]>
1 parent 68edd0d commit 4f13e58

File tree

2 files changed

+125
-29
lines changed

2 files changed

+125
-29
lines changed

lib/fluent/plugin/in_tail.rb

+25-29
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#
1616

1717
require 'cool.io'
18+
require 'etc'
1819

1920
require 'fluent/plugin/input'
2021
require 'fluent/config/error'
@@ -116,6 +117,12 @@ def initialize
116117
config_param :follow_inodes, :bool, default: false
117118
desc 'Maximum length of line. The longer line is just skipped.'
118119
config_param :max_line_size, :size, default: nil
120+
desc 'The permission of pos file directory'
121+
config_param :pos_dir_perm, :string, default: '0755'
122+
desc 'The owner of pos file directory'
123+
config_param :pos_dir_owner, :string, default: nil
124+
desc 'The group of pos file directory'
125+
config_param :pos_dir_group, :string, default: nil
119126

120127
config_section :parse, required: false, multi: true, init: true, param_name: :parser_configs do
121128
config_argument :usage, :string, default: 'in_tail_parser'
@@ -250,7 +257,14 @@ def start
250257

251258
if @pos_file
252259
pos_file_dir = File.dirname(@pos_file)
253-
FileUtils.mkdir_p(pos_file_dir, mode: @dir_perm) unless Dir.exist?(pos_file_dir)
260+
unless Dir.exist?(pos_file_dir)
261+
FileUtils.mkdir_p(pos_file_dir, mode: @pos_dir_perm ? @pos_dir_perm.to_i(8) : @dir_perm)
262+
if @pos_dir_owner || @pos_dir_group
263+
owner = @pos_dir_owner ? Etc.getpwnam(@pos_dir_owner).uid : Process.uid
264+
group = @pos_dir_group ? Etc.getgrnam(@pos_dir_group).gid : Process.gid
265+
File.chown(owner, group, pos_file_dir)
266+
end
267+
end
254268
@pf_file = File.open(@pos_file, File::RDWR|File::CREAT|File::BINARY, @file_perm)
255269
@pf_file.sync = true
256270
@pf = PositionFile.load(@pf_file, @follow_inodes, expand_paths, logger: log)
@@ -913,35 +927,18 @@ def on_notify
913927
end
914928

915929
def on_rotate(stat)
930+
if stat.nil?
931+
inode = nil
932+
fsize = 0
933+
else
934+
inode = stat.ino
935+
fsize = stat.size
936+
end
937+
916938
if @io_handler.nil?
917939
if stat
918940
# first time
919-
fsize = stat.size
920-
inode = stat.ino
921-
922-
last_inode = @pe.read_inode
923-
if inode == last_inode
924-
# rotated file has the same inode number with the last file.
925-
# assuming following situation:
926-
# a) file was once renamed and backed, or
927-
# b) symlink or hardlink to the same file is recreated
928-
# in either case of a and b, seek to the saved position
929-
# c) file was once renamed, truncated and then backed
930-
# in this case, consider it truncated
931-
@pe.update(inode, 0) if fsize < @pe.read_pos
932-
elsif last_inode != 0
933-
# this is FilePositionEntry and fluentd once started.
934-
# read data from the head of the rotated file.
935-
# logs never duplicate because this file is a rotated new file.
936-
@pe.update(inode, 0)
937-
else
938-
# this is MemoryPositionEntry or this is the first time fluentd started.
939-
# seek to the end of the any files.
940-
# logs may duplicate without this seek because it's not sure the file is
941-
# existent file or rotated new file.
942-
pos = @read_from_head ? 0 : fsize
943-
@pe.update(inode, pos)
944-
end
941+
@pe.update(inode, 0)
945942
@io_handler = io_handler
946943
else
947944
@io_handler = NullIOHandler.new
@@ -950,8 +947,7 @@ def on_rotate(stat)
950947
watcher_needs_update = false
951948

952949
if stat
953-
inode = stat.ino
954-
if inode == @pe.read_inode # truncated
950+
if @pe.read_inode == inode # truncated
955951
@pe.update_pos(0)
956952
@io_handler.close
957953
elsif !@io_handler.opened? # There is no previous file. Reuse TailWatcher

test/plugin/test_in_tail.rb

+100
Original file line numberDiff line numberDiff line change
@@ -3402,4 +3402,104 @@ def test_next_rotation_occurs_very_fast_while_old_TW_still_waiting_rotate_wait
34023402
d.logs[-2..]
34033403
])
34043404
end
3405+
3406+
sub_test_case "directory permissions and ownership" do
3407+
setup do
3408+
@dir = File.expand_path("#{@tmp_dir}/tail#{rand(0..10000)}")
3409+
@pos_dir = File.expand_path("#{@tmp_dir}/pos#{rand(0..10000)}")
3410+
FileUtils.mkdir_p(@dir)
3411+
end
3412+
3413+
teardown do
3414+
FileUtils.rm_rf(@dir) if File.exist?(@dir)
3415+
FileUtils.rm_rf(@pos_dir) if File.exist?(@pos_dir)
3416+
end
3417+
3418+
test 'creates pos_file directory with default permissions when not specified' do
3419+
pos_file = "#{@pos_dir}/tail.pos"
3420+
config = config_element("ROOT", "", {
3421+
"path" => "#{@dir}/tail.txt",
3422+
"pos_file" => pos_file,
3423+
"tag" => "t1",
3424+
"format" => "none",
3425+
"read_from_head" => "true"
3426+
})
3427+
d = create_driver(config)
3428+
d.run(expect_emits: 1) do
3429+
File.open("#{@dir}/tail.txt", "wb") {|f| f.puts "test default permissions" }
3430+
end
3431+
3432+
assert_equal(true, File.directory?(@pos_dir))
3433+
assert_equal(0755, File.stat(@pos_dir).mode & 0777)
3434+
end
3435+
3436+
test 'creates pos_file directory with custom permissions' do
3437+
pos_file = "#{@pos_dir}/tail.pos"
3438+
config = config_element("ROOT", "", {
3439+
"path" => "#{@dir}/tail.txt",
3440+
"pos_file" => pos_file,
3441+
"tag" => "t1",
3442+
"format" => "none",
3443+
"read_from_head" => "true",
3444+
"pos_dir_perm" => "0770"
3445+
})
3446+
d = create_driver(config)
3447+
d.run(expect_emits: 1) do
3448+
File.open("#{@dir}/tail.txt", "wb") {|f| f.puts "test custom permissions" }
3449+
end
3450+
3451+
assert_equal(true, File.directory?(@pos_dir))
3452+
assert_equal(0770, File.stat(@pos_dir).mode & 0777)
3453+
end
3454+
3455+
test 'creates pos_file directory with custom ownership' do
3456+
omit "Test requires root privileges" unless Process.uid == 0
3457+
3458+
pos_file = "#{@pos_dir}/tail.pos"
3459+
config = config_element("ROOT", "", {
3460+
"path" => "#{@dir}/tail.txt",
3461+
"pos_file" => pos_file,
3462+
"tag" => "t1",
3463+
"format" => "none",
3464+
"read_from_head" => "true",
3465+
"pos_dir_owner" => Process.uid.to_s,
3466+
"pos_dir_group" => Process.gid.to_s
3467+
})
3468+
d = create_driver(config)
3469+
d.run(expect_emits: 1) do
3470+
File.open("#{@dir}/tail.txt", "wb") {|f| f.puts "test custom ownership" }
3471+
end
3472+
3473+
assert_equal(true, File.directory?(@pos_dir))
3474+
stat = File.stat(@pos_dir)
3475+
assert_equal(Process.uid, stat.uid)
3476+
assert_equal(Process.gid, stat.gid)
3477+
end
3478+
3479+
test 'creates pos_file directory with both custom permissions and ownership' do
3480+
omit "Test requires root privileges" unless Process.uid == 0
3481+
3482+
pos_file = "#{@pos_dir}/tail.pos"
3483+
config = config_element("ROOT", "", {
3484+
"path" => "#{@dir}/tail.txt",
3485+
"pos_file" => pos_file,
3486+
"tag" => "t1",
3487+
"format" => "none",
3488+
"read_from_head" => "true",
3489+
"pos_dir_perm" => "0770",
3490+
"pos_dir_owner" => Process.uid.to_s,
3491+
"pos_dir_group" => Process.gid.to_s
3492+
})
3493+
d = create_driver(config)
3494+
d.run(expect_emits: 1) do
3495+
File.open("#{@dir}/tail.txt", "wb") {|f| f.puts "test custom permissions and ownership" }
3496+
end
3497+
3498+
assert_equal(true, File.directory?(@pos_dir))
3499+
stat = File.stat(@pos_dir)
3500+
assert_equal(0770, stat.mode & 0777)
3501+
assert_equal(Process.uid, stat.uid)
3502+
assert_equal(Process.gid, stat.gid)
3503+
end
3504+
end
34053505
end

0 commit comments

Comments
 (0)