Skip to content

Commit ecb99ac

Browse files
FIX: Possible race condition in file collector
Locking the file with python fcntl to aquire a file lock, it may not support windows installations :/ Fixes #2128 Fixes #1631 Signed-off-by: Sebastian Waldbauer <waldbauer@cert.at>
1 parent 1dc5364 commit ecb99ac

File tree

2 files changed

+34
-13
lines changed

2 files changed

+34
-13
lines changed

intelmq/bots/collectors/file/collector_file.py

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import fnmatch
2626
import os
27+
import fcntl
2728

2829
import intelmq.lib.exceptions as exceptions
2930
from intelmq.lib.bot import CollectorBot
@@ -58,32 +59,41 @@ def process(self):
5859
self.logger.debug("Started looking for files.")
5960

6061
if os.path.isdir(self.path):
61-
p = os.path.abspath(self.path)
62+
path = os.path.abspath(self.path)
6263

6364
# iterate over all files in dir
64-
for f in os.listdir(p):
65-
filename = os.path.join(p, f)
65+
for file in os.listdir(path):
66+
filename = os.path.join(path, file)
6667
if os.path.isfile(filename):
67-
if fnmatch.fnmatch(f, '*' + self.postfix):
68+
if fnmatch.fnmatch(file, '*' + self.postfix):
6869
self.logger.info("Processing file %r.", filename)
6970

7071
template = self.new_report()
71-
template.add("feed.url", "file://localhost%s" % filename)
72-
template.add("extra.file_name", f)
73-
74-
with open(filename, 'rb') as fh:
75-
for report in generate_reports(template, fh, self.chunk_size,
76-
self.chunk_replicate_header):
77-
self.send_message(report)
72+
template.add("feed.url", f"file://localhost{filename}")
73+
template.add("extra.file_name", file)
74+
75+
try:
76+
with open(filename, 'rb') as file_handle:
77+
fcntl.flock(file_handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
78+
for report in generate_reports(template, file_handle,
79+
self.chunk_size,
80+
self.chunk_replicate_header):
81+
self.send_message(report)
82+
fcntl.flock(file_handle, fcntl.LOCK_UN)
83+
except BlockingIOError:
84+
self.logger.info("File is already being used by another"
85+
" process, skipping.")
7886

7987
if self.delete_file:
8088
try:
8189
os.remove(filename)
8290
self.logger.debug("Deleted file: %r.", filename)
8391
except PermissionError:
8492
self.logger.error("Could not delete file %r.", filename)
85-
self.logger.info("Maybe I don't have sufficient rights on that file?")
86-
self.logger.error("Stopping now, to prevent reading this file again.")
93+
self.logger.info("Maybe I don't have sufficient rights"
94+
" on that file?")
95+
self.logger.error("Stopping now, to prevent reading this"
96+
" file again.")
8797
self.stop()
8898

8999

intelmq/tests/bots/collectors/file/test_collector.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"""
99
import os
1010
import unittest
11+
import fcntl
1112

1213
import intelmq.lib.test as test
1314
import intelmq.lib.utils as utils
@@ -49,6 +50,16 @@ def test_events(self):
4950

5051
self.assertMessageEqual(0, OUTPUT)
5152

53+
def test_file_lock(self):
54+
f = open(PATH, 'rb')
55+
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
56+
self.run_bot(iterations=1)
57+
self.assertLogMatches('File is already being used by another process, skipping.', levelname="INFO")
58+
fcntl.flock(f, fcntl.LOCK_UN)
59+
f.close()
60+
self.run_bot(iterations=1)
61+
self.assertMessageEqual(0, OUTPUT)
62+
5263

5364
if __name__ == '__main__': # pragma: no cover
5465
unittest.main()

0 commit comments

Comments
 (0)