@@ -62,6 +62,161 @@ writeShellApplication {
6262 fail() { fss_log_fail "$1"; FAILED=$((FAILED + 1)); }
6363 warn() { fss_log_warn "$1"; WARNED=$((WARNED + 1)); }
6464 info() { fss_log_info "$1"; }
65+ count_matches() {
66+ local pattern="$1"
67+ local text="$2"
68+
69+ printf '%s\n' "$text" | grep -Eic "$pattern" || true
70+ }
71+ print_prefixed_lines() {
72+ local prefix="$1"
73+ local text="$2"
74+ local limit="'' ${3:-0}"
75+ local line
76+ local total=0
77+ local shown=0
78+
79+ total=$(fss_count_nonempty_lines "$text")
80+ if [ "$total" -eq 0 ]; then
81+ return 0
82+ fi
83+
84+ while IFS= read -r line || [ -n "$line" ]; do
85+ if [ -z "$line" ]; then
86+ continue
87+ fi
88+
89+ shown=$((shown + 1))
90+ if [ "$limit" -gt 0 ] && [ "$shown" -gt "$limit" ]; then
91+ break
92+ fi
93+
94+ printf '%s%s\n' "$prefix" "$line"
95+ done <<<"$text"
96+
97+ if [ "$limit" -gt 0 ] && [ "$total" -gt "$limit" ]; then
98+ printf '%s... (%s more lines)\n' "$prefix" "$((total - limit))"
99+ fi
100+ }
101+ summarize_header_output() {
102+ local header_output="$1"
103+
104+ printf '%s\n' "$header_output" | grep -E '^(State:|Compatible flags:|Incompatible flags:|Boot ID:|Machine ID:|Head realtime timestamp:|Tail realtime timestamp:|Tail monotonic timestamp:|Rotate suggested:|Disk usage:)' || true
105+ }
106+ summarize_file_verify_output() {
107+ local verify_output="$1"
108+
109+ printf '%s\n' "$verify_output" | grep -E '(^[[:xdigit:]]+: )|(^File corruption detected)|(^FAIL: )|(^PASS: )|(^=> Validated)|(^Journal file .* is truncated, ignoring file\.)|(Required key not available)|(parse.*seed)|(tag/entry realtime timestamp out of synchronization)' || true
110+ }
111+ print_recent_journald_alerts() {
112+ local alerts
113+
114+ alerts=$(journalctl -u systemd-journald --no-pager 2>/dev/null | grep -Ei 'corrupt|unclean|renaming and replacing|failed to append tag when closing journal|time jumped backwards|rotating|recover' | tail -n 12 || true)
115+ if [ -n "$alerts" ]; then
116+ echo " Recent systemd-journald alerts:"
117+ print_prefixed_lines " " "$alerts"
118+ fi
119+ }
120+ print_failed_file_diagnostics() {
121+ local verify_output="$1"
122+ local verify_key="$2"
123+ local failed_paths
124+ local failed_count=0
125+ local path
126+ local bucket
127+ local stat_output
128+ local header_output
129+ local header_summary
130+ local file_verify_output
131+ local file_verify_summary
132+ local path_limit=4
133+ local total_failed_paths
134+
135+ failed_paths=$(fss_unique_fail_paths_from_output "$verify_output")
136+ if [ -z "$failed_paths" ]; then
137+ return 0
138+ fi
139+ total_failed_paths=$(fss_count_nonempty_lines "$failed_paths")
140+
141+ echo " Detailed file diagnostics (up to $path_limit unique failing files):"
142+ while IFS= read -r path || [ -n "$path" ]; do
143+ if [ -z "$path" ]; then
144+ continue
145+ fi
146+
147+ failed_count=$((failed_count + 1))
148+ if [ "$failed_count" -gt "$path_limit" ]; then
149+ printf ' ... %s additional failing file(s) omitted\n' "$((total_failed_paths - path_limit))"
150+ break
151+ fi
152+
153+ bucket=$(fss_failure_bucket_for_path "$path")
154+ printf ' - %s [%s]\n' "$path" "$bucket"
155+
156+ if [ ! -e "$path" ]; then
157+ echo " file no longer exists"
158+ continue
159+ fi
160+
161+ stat_output=$(stat -c 'size=%s mode=%a uid=%u gid=%g mtime=%y' "$path" 2>/dev/null || true)
162+ if [ -n "$stat_output" ]; then
163+ echo " stat: $stat_output"
164+ fi
165+
166+ header_output=$(journalctl --header --file="$path" 2>/dev/null || true)
167+ header_summary=$(summarize_header_output "$header_output")
168+ if [ -n "$header_summary" ]; then
169+ print_prefixed_lines " header: " "$header_summary" 8
170+ fi
171+
172+ if [ -n "$verify_key" ]; then
173+ file_verify_output=$(journalctl --verify --verify-key="$verify_key" --file="$path" 2>&1 || true)
174+ file_verify_summary=$(summarize_file_verify_output "$file_verify_output")
175+ if [ -n "$file_verify_summary" ]; then
176+ print_prefixed_lines " verify: " "$file_verify_summary" 8
177+ fi
178+ fi
179+ done <<<"$failed_paths"
180+ }
181+ print_verify_diagnostics() {
182+ local verify_output="$1"
183+ local verify_tags="$2"
184+ local verify_exit="$3"
185+ local verify_key="'' ${4:-}"
186+ local active_count
187+ local archived_count
188+ local user_count
189+ local temp_count
190+ local other_count
191+ local tag_failed_count
192+ local epoch_count
193+ local time_sync_count
194+ local corruption_count
195+ local io_error_count
196+ local fail_path_count
197+
198+ active_count=$(fss_count_nonempty_lines "$FSS_ACTIVE_SYSTEM_FAILURES")
199+ archived_count=$(fss_count_nonempty_lines "$FSS_ARCHIVED_SYSTEM_FAILURES")
200+ user_count=$(fss_count_nonempty_lines "$FSS_USER_FAILURES")
201+ temp_count=$(fss_count_nonempty_lines "$FSS_TEMP_FAILURES")
202+ other_count=$(fss_count_nonempty_lines "$FSS_OTHER_FAILURES")
203+ fail_path_count=$(fss_count_nonempty_lines "$(fss_unique_fail_paths_from_output "$verify_output")")
204+
205+ tag_failed_count=$(count_matches 'Tag failed verification' "$verify_output")
206+ epoch_count=$(count_matches 'Epoch sequence not continuous' "$verify_output")
207+ time_sync_count=$(count_matches 'tag/entry realtime timestamp out of synchronization' "$verify_output")
208+ corruption_count=$(count_matches 'File corruption detected at ' "$verify_output")
209+ io_error_count=$(count_matches 'Input/output error|I/O error' "$verify_output")
210+
211+ printf ' Diagnostic summary: exit=%s tags=[%s] failing_files=%s active=%s archived=%s user=%s temp=%s other=%s\n' \
212+ "$verify_exit" "$verify_tags" "$fail_path_count" "$active_count" "$archived_count" "$user_count" "$temp_count" "$other_count"
213+ printf ' Signals: tag_failed=%s epoch_discontinuity=%s time_sync=%s file_corruption=%s io_error=%s key_parse=%s key_missing=%s filesystem_restriction=%s\n' \
214+ "$tag_failed_count" "$epoch_count" "$time_sync_count" "$corruption_count" "$io_error_count" \
215+ "$FSS_KEY_PARSE_ERROR" "$FSS_KEY_REQUIRED_ERROR" "$FSS_FILESYSTEM_RESTRICTION"
216+
217+ print_failed_file_diagnostics "$verify_output" "$verify_key"
218+ print_recent_journald_alerts
219+ }
65220
66221 fss_log_block <<'EOF'
67222 ==========================================
@@ -209,26 +364,33 @@ writeShellApplication {
209364 if [ "$FSS_KEY_PARSE_ERROR" -eq 1 ] || [ "$FSS_KEY_REQUIRED_ERROR" -eq 1 ]; then
210365 fail "Journal verification failed due to verification key defect [$VERIFY_TAGS]"
211366 echo " Output: $VERIFY_OUTPUT"
367+ print_verify_diagnostics "$VERIFY_OUTPUT" "$VERIFY_TAGS" "$VERIFY_EXIT"
212368 elif [ -n "$FSS_ACTIVE_SYSTEM_FAILURES" ]; then
213369 fail "Active system journal verification failed [$VERIFY_TAGS]"
214370 echo " Output: $VERIFY_OUTPUT"
371+ print_verify_diagnostics "$VERIFY_OUTPUT" "$VERIFY_TAGS" "$VERIFY_EXIT" "$VERIFY_KEY"
215372 elif [ -n "$FSS_OTHER_FAILURES" ]; then
216373 fail "Journal verification found unclassified critical failures [$VERIFY_TAGS]"
217374 echo " Output: $VERIFY_OUTPUT"
375+ print_verify_diagnostics "$VERIFY_OUTPUT" "$VERIFY_TAGS" "$VERIFY_EXIT" "$VERIFY_KEY"
218376 elif [ -n "$FSS_ARCHIVED_SYSTEM_FAILURES" ] || [ -n "$FSS_USER_FAILURES" ]; then
219377 warn "Journal verification passed with archive/user warnings [$VERIFY_TAGS]"
220378 echo " Output: $VERIFY_OUTPUT"
379+ print_verify_diagnostics "$VERIFY_OUTPUT" "$VERIFY_TAGS" "$VERIFY_EXIT" "$VERIFY_KEY"
221380 elif [ -n "$FSS_TEMP_FAILURES" ]; then
222381 pass "Journal verification passed (temporary journal files ignored)"
223382 echo " Ignored temp failures: $FSS_TEMP_FAILURES"
383+ print_verify_diagnostics "$VERIFY_OUTPUT" "$VERIFY_TAGS" "$VERIFY_EXIT" "$VERIFY_KEY"
224384 elif [ "$VERIFY_EXIT" -eq 0 ]; then
225385 pass "Journal verification passed"
226386 elif [ "$FSS_FILESYSTEM_RESTRICTION" -eq 1 ]; then
227387 warn "Verification encountered filesystem restrictions [$VERIFY_TAGS]"
228388 echo " Output: $VERIFY_OUTPUT"
389+ print_verify_diagnostics "$VERIFY_OUTPUT" "$VERIFY_TAGS" "$VERIFY_EXIT"
229390 else
230391 warn "Verification returned exit code $VERIFY_EXIT without critical failures [$VERIFY_TAGS]"
231392 echo " Output: $VERIFY_OUTPUT"
393+ print_verify_diagnostics "$VERIFY_OUTPUT" "$VERIFY_TAGS" "$VERIFY_EXIT" "$VERIFY_KEY"
232394 fi
233395 fi
234396
0 commit comments