Skip to content

Commit e60cbd0

Browse files
committed
fix clean-processed-folder.py incorrectly merged
1 parent d766527 commit e60cbd0

1 file changed

Lines changed: 74 additions & 33 deletions

File tree

filebeat/scripts/clean-processed-folder.py

Lines changed: 74 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
#!/usr/bin/env python3
22

3-
# Copyright (c) 2025 Battelle Energy Alliance, LLC. All rights reserved.
3+
# Copyright (c) 2026 Battelle Energy Alliance, LLC. All rights reserved.
44

55
import fcntl
66
import logging
77
import magic
88
import os
9+
import re
910
import subprocess
1011
import sys
1112
import time
@@ -21,9 +22,10 @@
2122

2223
lock_filename = os.path.join(gettempdir(), f'{os.path.basename(__file__)}.lock')
2324

24-
filebeat_registry_filename = os.getenv(
25-
'FILEBEAT_REGISTRY_FILE', "/usr/share/filebeat-logs/data/registry/filebeat/log.json"
26-
)
25+
filebeat_registry_filenames = [
26+
os.getenv('FILEBEAT_REGISTRY_FILE', "/usr/share/filebeat-logs/data/registry/filebeat/log.json"),
27+
os.getenv('FILEBEAT_REDIS_REGISTRY_FILE', "/usr/share/filebeat-zeek-files-logs/data/registry/filebeat/log.json"),
28+
]
2729

2830
zeek_dir = os.path.join(os.getenv('FILEBEAT_ZEEK_DIR', "/zeek/"), '')
2931
zeek_live_dir = os.path.join(zeek_dir, "live/logs/")
@@ -32,6 +34,8 @@
3234

3335
suricata_dir = os.path.join(os.getenv('FILEBEAT_SURICATA_LOG_PATH', "/suricata/"), '')
3436

37+
filescan_dir = os.path.join(os.getenv('FILEBEAT_FILESCAN_LOG_PATH', "/filescan/"), '')
38+
3539
# We're only able to do this pruning because we're forwarding the logs along to Logstash
3640
# so they're not needed here anymore. If we're *not* forwarding, we can't delete them
3741
# based on age like that.
@@ -47,6 +51,7 @@
4751
_LOG_MIME_TYPES = (
4852
"application/json",
4953
"application/x-ndjson",
54+
"text/html",
5055
"text/plain",
5156
"text/x-file",
5257
)
@@ -66,6 +71,15 @@
6671
"application/zip",
6772
)
6873

74+
_LOG_FILE_TYPE_PATTERNS = [
75+
re.compile(r'^MS\s+Windows.*Event\s+Log', re.IGNORECASE),
76+
re.compile(r'\b(JSON|ASCII)\s+text\b', re.IGNORECASE),
77+
]
78+
79+
_ARCHIVE_FILE_TYPE_PATTERNS = [
80+
re.compile(r'\b(archive|compressed)\s+data\b', re.IGNORECASE),
81+
]
82+
6983
# --------------------------------------------------------------------
7084
# Helper functions
7185
# --------------------------------------------------------------------
@@ -109,20 +123,34 @@ def check_file(
109123
return
110124

111125
# get the file type (treat zero-length files as log files)
112-
file_type = magic.from_file(filename, mime=True)
113-
if check_logs and clean_log_seconds > 0 and (file_stat.st_size == 0 or file_type in _LOG_MIME_TYPES):
126+
clean_seconds = 0
127+
do_log_check = check_logs and clean_log_seconds > 0
128+
do_archive_check = check_archives and clean_zip_seconds > 0
129+
file_mime_type = magic.from_file(filename, mime=True)
130+
file_type = None
131+
if do_log_check and ((file_stat.st_size == 0) or (file_mime_type in _LOG_MIME_TYPES)):
114132
clean_seconds = clean_log_seconds
115-
elif check_archives and clean_zip_seconds > 0 and file_type in _ARCHIVE_MIME_TYPES:
133+
elif do_archive_check and (file_mime_type in _ARCHIVE_MIME_TYPES):
116134
clean_seconds = clean_zip_seconds
117135
else:
118-
# not a file we're going to be messing with
119-
logging.debug(f"Ignoring {filename} due to {file_type=}")
120-
return
121-
122-
if clean_seconds > 0 and last_use_time >= clean_seconds:
136+
# mime type didn't match, fall back to the non-MIME file magic description
137+
# e.g., "application/octet-stream" vs "MS Windows Vista Event Log""
138+
file_type = magic.from_file(filename, mime=False)
139+
if do_log_check and any(p.search(file_type) for p in _LOG_FILE_TYPE_PATTERNS):
140+
clean_seconds = clean_log_seconds
141+
elif do_archive_check and any(p.search(file_type) for p in _ARCHIVE_FILE_TYPE_PATTERNS):
142+
clean_seconds = clean_zip_seconds
143+
else:
144+
# not a file we're going to be messing with
145+
logging.debug(f"Ignoring {filename} of type {file_type} ({file_mime_type})")
146+
return
147+
148+
if (clean_seconds > 0) and (last_use_time >= clean_seconds):
123149
# this is a closed file that is old, so delete it
124150
silent_remove(filename)
125-
logging.info(f'Removed old file "{filename}" ({file_type}, used {last_use_time:.0f} seconds ago)')
151+
logging.info(
152+
f'Removed old file "{filename}" ({file_type or file_mime_type}, used {last_use_time:.0f} seconds ago)'
153+
)
126154

127155
except FileNotFoundError:
128156
# file's already gone, oh well
@@ -132,30 +160,35 @@ def check_file(
132160
logging.error(f"{type(e).__name__} for '{filename}': {e}")
133161

134162

135-
def list_files_in_dir(base_dir: str) -> List[str]:
163+
def list_files_in_dir(base_dir: str, sort_by_age: bool = False) -> List[str]:
136164
"""Recursively list all files in a directory."""
137165
if not os.path.isdir(base_dir):
138166
return []
139-
return [os.path.join(root, f) for root, _, files in os.walk(base_dir) for f in files]
140-
167+
files = [os.path.join(root, f) for root, _, filenames in os.walk(base_dir) for f in filenames]
168+
return sorted(files, key=os.path.getmtime) if sort_by_age else files
141169

142-
def load_filebeat_registry(registry_path: str) -> List[Tuple[int, int]]:
143-
"""Load the filebeat registry file and extract (device, inode) tuples."""
144-
if not os.path.isfile(registry_path):
145-
return []
146-
try:
147-
with open(registry_path) as f:
148-
fb_reg = LoadFileIfJson(f, attemptLines=True)
149-
except Exception as e:
150-
logging.error(f"Failed to load filebeat registry: {e}")
151-
return []
152170

171+
def load_filebeat_registries(registry_paths: List[str]) -> List[Tuple[int, int]]:
172+
"""Load the filebeat registry file(s) and extract (device, inode) tuples."""
153173
fb_files = []
154-
for entry in fb_reg:
155-
device = deep_get(entry, ['v', 'FileStateOS', 'device'])
156-
inode = deep_get(entry, ['v', 'FileStateOS', 'inode'])
157-
if device is not None and inode is not None:
158-
fb_files.append((int(device), int(inode)))
174+
175+
for registry_path in registry_paths:
176+
if not os.path.isfile(registry_path):
177+
continue
178+
try:
179+
with open(registry_path) as f:
180+
fb_reg = LoadFileIfJson(f, attemptLines=True)
181+
except Exception as e:
182+
logging.error(f"Failed to load filebeat registry: {e}")
183+
continue
184+
185+
if fb_reg:
186+
for entry in fb_reg:
187+
device = deep_get(entry, ['v', 'FileStateOS', 'device'])
188+
inode = deep_get(entry, ['v', 'FileStateOS', 'inode'])
189+
if device is not None and inode is not None:
190+
fb_files.append((int(device), int(inode)))
191+
159192
return fb_files
160193

161194

@@ -219,7 +252,7 @@ def prune_files() -> None:
219252
if (clean_log_seconds <= 0) and (clean_zip_seconds <= 0):
220253
return
221254

222-
fb_files = load_filebeat_registry(filebeat_registry_filename)
255+
fb_files = load_filebeat_registries(filebeat_registry_filenames)
223256

224257
# look for regular Zeek files in the processed/ directory
225258
zeek_found = list_files_in_dir(zeek_processed_dir)
@@ -240,9 +273,17 @@ def prune_files() -> None:
240273
logging.debug(f"Found {len(suricata_files)} Suricata files to consider.")
241274
process_files(suricata_files, fb_files, check_logs=True, check_archives=False, label="Suricata")
242275

276+
# check the filescan logs
277+
filescan_files = list_files_in_dir(filescan_dir, sort_by_age=True)
278+
if filescan_files:
279+
# filescan_files is sorted sorted oldest to newest; don't consider the newest file for deletion
280+
filescan_files.pop()
281+
logging.debug(f"Found {len(filescan_files)} filescan files to consider.")
282+
process_files(filescan_files, fb_files, check_logs=True, check_archives=False, label="Filescan")
283+
243284
# clean up any old and empty directories in Zeek processed/ and suricata non-live directories
244285
clean_dir_seconds = min(i for i in (clean_log_seconds, clean_zip_seconds) if i > 0)
245-
cleanup_empty_dirs([zeek_processed_dir, suricata_dir], clean_dir_seconds)
286+
cleanup_empty_dirs([zeek_processed_dir, filescan_dir, suricata_dir], clean_dir_seconds)
246287

247288
logging.debug("Finished pruning files.")
248289

0 commit comments

Comments
 (0)