Branch data Line data Source code
1 : : /* SPDX-FileCopyrightText: Copyright 2026 Cozens Software Solutions Limited
2 : : * SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 OR LicenseRef-PolyForm-Internal-Use-1.0.0 OR LicenseRef-COSOSO-Commercial
3 : : */
4 : :
5 : : #include "SolidSyslog.h"
6 : :
7 : : #include <stdbool.h>
8 : : #include <stddef.h>
9 : : #include <stdint.h>
10 : :
11 : : #include "SolidSyslogBuffer.h"
12 : : #include "SolidSyslogBufferCategories.h"
13 : : #include "SolidSyslogConfig.h"
14 : : #include "SolidSyslogDiscardPolicy.h"
15 : : #include "SolidSyslogError.h"
16 : : #include "SolidSyslogErrorCategory.h"
17 : : #include "SolidSyslogErrors.h"
18 : : #include "SolidSyslogEventSd.h"
19 : : #include "SolidSyslogFormatter.h"
20 : : #include "SolidSyslogLoggerCategories.h"
21 : : #include "SolidSyslogMessageFormatter.h"
22 : : #include "SolidSyslogNullBuffer.h"
23 : : #include "SolidSyslogNullSender.h"
24 : : #include "SolidSyslogNullStore.h"
25 : : #include "SolidSyslogPendingEvents.h"
26 : : #include "SolidSyslogPrivate.h"
27 : : #include "SolidSyslogHeaderFieldFunction.h"
28 : : #include "SolidSyslogScratch.h"
29 : : #include "SolidSyslogSender.h"
30 : : #include "SolidSyslogStackScratch.h"
31 : : #include "SolidSyslogStore.h"
32 : : #include "SolidSyslogStoreDefinition.h"
33 : : #include "SolidSyslogTimestamp.h"
34 : : #include "SolidSyslogTunables.h"
35 : :
36 : : const struct SolidSyslogErrorSource SolidSyslogErrorSource = {"SolidSyslog", SolidSyslogErrors_Name};
37 : :
38 : : struct SolidSyslogBuffer;
39 : : struct SolidSyslogHeaderField;
40 : : struct SolidSyslogScratch;
41 : : struct SolidSyslogSender;
42 : : struct SolidSyslogStore;
43 : : struct SolidSyslogStructuredData;
44 : :
45 : : /* What Log carries into the scratch: the continuation formats from it. */
46 : : struct LogRequest
47 : : {
48 : : struct SolidSyslog* Handle;
49 : : const struct SolidSyslogMessage* Message;
50 : : struct SolidSyslogStructuredData** Sd;
51 : : size_t SdCount;
52 : : };
53 : :
54 : : static inline bool SolidSyslog_DrainBufferIntoStore(struct SolidSyslog* self);
55 : : static inline void SolidSyslog_ApplyPolicyToHeldRecordIfBufferFull(struct SolidSyslog* self);
56 : : static inline void SolidSyslog_DropHeldRecord(struct SolidSyslog* self);
57 : : static bool SolidSyslog_HoldNextRecord(struct SolidSyslog* self);
58 : : static inline bool SolidSyslog_ReportPendingEvents(struct SolidSyslog* self);
59 : : static inline bool SolidSyslog_HasRecordSlot(const struct SolidSyslog* self);
60 : : static inline bool SolidSyslog_ReportIfPending(struct SolidSyslog* self, size_t index);
61 : : static inline void SolidSyslog_HoldPendingReport(struct SolidSyslog* self, size_t index);
62 : : static inline void SolidSyslog_FormatReport(struct SolidSyslog* self, const struct SolidSyslogErrorEvent* event);
63 : : static bool SolidSyslog_DealWithHeldRecord(struct SolidSyslog* self);
64 : : static inline bool SolidSyslog_IsBufferRecordDealtWith(const struct SolidSyslog* self);
65 : : static inline bool SolidSyslog_StoreOrSenderTakesHeldRecord(struct SolidSyslog* self);
66 : : static inline bool SolidSyslog_StoreDealsWithHeldRecord(struct SolidSyslog* self);
67 : : static inline void SolidSyslog_HaltIfPolicyHalts(struct SolidSyslog* self);
68 : : static inline void SolidSyslog_Halt(struct SolidSyslog* self, enum SolidSyslogErrors haltedAt);
69 : : static inline bool SolidSyslog_SendOneFromStore(struct SolidSyslog* self);
70 : : static inline bool SolidSyslog_TakeNextFromStore(struct SolidSyslog* self);
71 : : static inline bool SolidSyslog_IsBlocked(const struct SolidSyslog* self, bool storeBlocked, bool drained);
72 : : static inline bool SolidSyslog_IsReportWaiting(const struct SolidSyslog* self);
73 : : static void SolidSyslog_DoLog(
74 : : struct SolidSyslog* handle,
75 : : const struct SolidSyslogMessage* message,
76 : : struct SolidSyslogStructuredData** sd,
77 : : size_t sdCount
78 : : );
79 : : static void SolidSyslog_FormatIntoScratch(SolidSyslogFormatterStorage* storage, size_t messageBytes, void* context);
80 : : static void SolidSyslog_InstallScratch(struct SolidSyslog* self, struct SolidSyslogScratch* configured);
81 : : static void SolidSyslog_InstallAppName(
82 : : struct SolidSyslog* self,
83 : : SolidSyslogHeaderFieldFunction configured,
84 : : void* context
85 : : );
86 : : static void SolidSyslog_InstallBuffer(struct SolidSyslog* self, struct SolidSyslogBuffer* configured);
87 : : static void SolidSyslog_InstallClock(struct SolidSyslog* self, SolidSyslogClockFunction configured);
88 : : static void SolidSyslog_InstallDiscardPolicy(
89 : : struct SolidSyslog* self,
90 : : SolidSyslogDiscardPolicyFunction configured,
91 : : void* context
92 : : );
93 : : static void SolidSyslog_InstallHostname(
94 : : struct SolidSyslog* self,
95 : : SolidSyslogHeaderFieldFunction configured,
96 : : void* context
97 : : );
98 : : static void SolidSyslog_InstallProcessId(
99 : : struct SolidSyslog* self,
100 : : SolidSyslogHeaderFieldFunction configured,
101 : : void* context
102 : : );
103 : : static void SolidSyslog_InstallSender(struct SolidSyslog* self, struct SolidSyslogSender* configured);
104 : : static void SolidSyslog_InstallStore(struct SolidSyslog* self, struct SolidSyslogStore* configured);
105 : : static void SolidSyslog_InstallStructuredData(
106 : : struct SolidSyslog* self,
107 : : struct SolidSyslogStructuredData** configured,
108 : : size_t count
109 : : );
110 : : static inline void SolidSyslog_ResumeIfPolicyChanged(struct SolidSyslog* self);
111 : : static inline bool SolidSyslog_IsServiceEnabled(const struct SolidSyslog* self);
112 : : static inline bool SolidSyslog_PolicyHalts(struct SolidSyslog* self);
113 : : static void SolidSyslog_NullClock(struct SolidSyslogTimestamp* ts);
114 : : static void SolidSyslog_NullHeaderField(struct SolidSyslogHeaderField* field, void* context);
115 : : static void SolidSyslog_NullHalt(void* context);
116 : : static void SolidSyslog_InstallHalt(struct SolidSyslog* self, SolidSyslogHaltCallback configured, void* context);
117 : : static bool SolidSyslog_ReportingAlwaysOn(void* context);
118 : : static void SolidSyslog_InstallEventReporting(
119 : : struct SolidSyslog* self,
120 : : SolidSyslogEventReportingFunction configured,
121 : : void* context
122 : : );
123 : : static enum SolidSyslogServiceStatus SolidSyslog_ProcessMessages(struct SolidSyslog* self);
124 : : static void SolidSyslog_ResetToDefaults(struct SolidSyslog* self);
125 : :
126 : 451 : void SolidSyslog_Initialise(
127 : : struct SolidSyslog* self,
128 : : const struct SolidSyslogConfig* config,
129 : : SolidSyslogFormatterStorage* recordStorage,
130 : : size_t recordCapacity
131 : : )
132 : : {
133 : 451 : SolidSyslog_ResetToDefaults(self);
134 : 451 : self->RecordStorage = recordStorage;
135 : 451 : self->Record = SolidSyslogFormatter_Bytes(SolidSyslogFormatter_Create(recordStorage, recordCapacity));
136 : 451 : self->RecordCapacity = recordCapacity;
137 : 451 : SolidSyslog_InstallBuffer(self, config->Buffer);
138 : 451 : SolidSyslog_InstallSender(self, config->Sender);
139 : 451 : SolidSyslog_InstallStore(self, config->Store);
140 : 451 : SolidSyslog_InstallClock(self, config->Clock);
141 : 451 : SolidSyslog_InstallHostname(self, config->GetHostname, config->GetHostnameContext);
142 : 451 : SolidSyslog_InstallAppName(self, config->GetAppName, config->GetAppNameContext);
143 : 451 : SolidSyslog_InstallProcessId(self, config->GetProcessId, config->GetProcessIdContext);
144 : 451 : SolidSyslog_InstallStructuredData(self, config->Sd, config->SdCount);
145 : 451 : SolidSyslog_InstallDiscardPolicy(self, config->GetDiscardPolicy, config->DiscardPolicyContext);
146 : 451 : SolidSyslog_InstallHalt(self, config->OnHalt, config->HaltContext);
147 : 451 : SolidSyslog_InstallScratch(self, config->Scratch);
148 : 451 : SolidSyslog_InstallEventReporting(self, config->IsEventReportingOn, config->EventReportingContext);
149 : 451 : }
150 : :
151 : 454 : void SolidSyslog_Cleanup(struct SolidSyslog* self)
152 : : {
153 : : /* Reset to safe defaults so a stale-handle Log/Service after Destroy is a
154 : : * silent no-op rather than a NULL-fn-pointer crash. The slot is then
155 : : * re-claimable by the next SolidSyslog_Create. */
156 : 454 : SolidSyslog_ResetToDefaults(self);
157 : 454 : }
158 : :
159 : 905 : static void SolidSyslog_ResetToDefaults(struct SolidSyslog* self)
160 : : {
161 : 905 : self->Buffer = SolidSyslogNullBuffer_Get();
162 : 905 : self->Sender = SolidSyslogNullSender_Get();
163 : 905 : self->Store = SolidSyslogNullStore_Get();
164 : 905 : self->Scratch = SolidSyslogStackScratch_Get();
165 : 905 : self->Format.Clock = SolidSyslog_NullClock;
166 : 905 : self->Format.GetHostname = SolidSyslog_NullHeaderField;
167 : 905 : self->Format.GetHostnameContext = NULL;
168 : 905 : self->Format.GetAppName = SolidSyslog_NullHeaderField;
169 : 905 : self->Format.GetAppNameContext = NULL;
170 : 905 : self->Format.GetProcessId = SolidSyslog_NullHeaderField;
171 : 905 : self->Format.GetProcessIdContext = NULL;
172 : 905 : self->Format.Sd = NULL;
173 : 905 : self->Format.SdCount = 0;
174 : 905 : self->RecordStorage = NULL;
175 : 905 : self->Record = NULL;
176 : 905 : self->RecordCapacity = 0U;
177 : 905 : self->RecordLength = 0U;
178 : 905 : self->HoldsRecord = false;
179 : 905 : self->HeldRecordIsReport = false;
180 : 905 : self->HeldRecordLossReported = false;
181 : 905 : self->GetDiscardPolicy = SolidSyslogDiscardPolicy_Default;
182 : 905 : self->DiscardPolicyContext = NULL;
183 : 905 : self->OnHalt = SolidSyslog_NullHalt;
184 : 905 : self->HaltContext = NULL;
185 : 905 : self->IsEventReportingOn = SolidSyslog_ReportingAlwaysOn;
186 : 905 : self->EventReportingContext = NULL;
187 : 905 : self->ReportTurnedDown = false;
188 : 905 : self->Halted = false;
189 : 905 : }
190 : :
191 : : /* No-op defaults for the header-field callbacks. No public Null equivalent
192 : : * exists for the function-pointer typedefs. */
193 : 6544 : static void SolidSyslog_NullClock(struct SolidSyslogTimestamp* ts)
194 : : {
195 : : (void) ts;
196 : 6544 : }
197 : :
198 : 19477 : static void SolidSyslog_NullHeaderField(struct SolidSyslogHeaderField* field, void* context)
199 : : {
200 : : (void) field;
201 : : (void) context;
202 : 19477 : }
203 : :
204 : 8 : static void SolidSyslog_NullHalt(void* context)
205 : : {
206 : : (void) context;
207 : 8 : }
208 : :
209 : 78 : static bool SolidSyslog_ReportingAlwaysOn(void* context)
210 : : {
211 : : (void) context;
212 : 78 : return true;
213 : : }
214 : :
215 : 451 : static void SolidSyslog_InstallBuffer(struct SolidSyslog* self, struct SolidSyslogBuffer* configured)
216 : : {
217 [ + + ]: 451 : if (configured == NULL)
218 : : {
219 : 52 : SolidSyslog_Report(
220 : : SOLIDSYSLOG_BAD_CONFIG_FATAL_SEVERITY,
221 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
222 : : SOLIDSYSLOG_ERROR_CREATE_NULL_BUFFER
223 : : );
224 : : }
225 : : else
226 : : {
227 : 399 : self->Buffer = configured;
228 : : }
229 : 451 : }
230 : :
231 : 451 : static void SolidSyslog_InstallSender(struct SolidSyslog* self, struct SolidSyslogSender* configured)
232 : : {
233 [ + + ]: 451 : if (configured == NULL)
234 : : {
235 : 144 : SolidSyslog_Report(
236 : : SOLIDSYSLOG_BAD_CONFIG_FATAL_SEVERITY,
237 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
238 : : SOLIDSYSLOG_ERROR_CREATE_NULL_SENDER
239 : : );
240 : : }
241 : : else
242 : : {
243 : 307 : self->Sender = configured;
244 : : }
245 : 451 : }
246 : :
247 : 451 : static void SolidSyslog_InstallStore(struct SolidSyslog* self, struct SolidSyslogStore* configured)
248 : : {
249 [ + + ]: 451 : if (configured == NULL)
250 : : {
251 : 64 : SolidSyslog_Report(
252 : : SOLIDSYSLOG_BAD_CONFIG_FATAL_SEVERITY,
253 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
254 : : SOLIDSYSLOG_ERROR_CREATE_NULL_STORE
255 : : );
256 : : }
257 : : else
258 : : {
259 : 387 : self->Store = configured;
260 : : }
261 : 451 : }
262 : :
263 : 451 : static void SolidSyslog_InstallClock(struct SolidSyslog* self, SolidSyslogClockFunction configured)
264 : : {
265 [ + + ]: 451 : if (configured != NULL)
266 : : {
267 : 66 : self->Format.Clock = configured;
268 : : }
269 : 451 : }
270 : :
271 : 451 : static void SolidSyslog_InstallHostname(
272 : : struct SolidSyslog* self,
273 : : SolidSyslogHeaderFieldFunction configured,
274 : : void* context
275 : : )
276 : : {
277 [ + + ]: 451 : if (configured != NULL)
278 : : {
279 : 191 : self->Format.GetHostname = configured;
280 : 191 : self->Format.GetHostnameContext = context;
281 : : }
282 : 451 : }
283 : :
284 : 451 : static void SolidSyslog_InstallAppName(
285 : : struct SolidSyslog* self,
286 : : SolidSyslogHeaderFieldFunction configured,
287 : : void* context
288 : : )
289 : : {
290 [ + + ]: 451 : if (configured != NULL)
291 : : {
292 : 191 : self->Format.GetAppName = configured;
293 : 191 : self->Format.GetAppNameContext = context;
294 : : }
295 : 451 : }
296 : :
297 : 451 : static void SolidSyslog_InstallProcessId(
298 : : struct SolidSyslog* self,
299 : : SolidSyslogHeaderFieldFunction configured,
300 : : void* context
301 : : )
302 : : {
303 [ + + ]: 451 : if (configured != NULL)
304 : : {
305 : 127 : self->Format.GetProcessId = configured;
306 : 127 : self->Format.GetProcessIdContext = context;
307 : : }
308 : 451 : }
309 : :
310 : 451 : static void SolidSyslog_InstallDiscardPolicy(
311 : : struct SolidSyslog* self,
312 : : SolidSyslogDiscardPolicyFunction configured,
313 : : void* context
314 : : )
315 : : {
316 [ + + ]: 451 : if (configured != NULL)
317 : : {
318 : 198 : self->GetDiscardPolicy = configured;
319 : 198 : self->DiscardPolicyContext = context;
320 : : }
321 : 451 : }
322 : :
323 : 451 : static void SolidSyslog_InstallHalt(struct SolidSyslog* self, SolidSyslogHaltCallback configured, void* context)
324 : : {
325 [ + + ]: 451 : if (configured != NULL)
326 : : {
327 : 180 : self->OnHalt = configured;
328 : 180 : self->HaltContext = context;
329 : : }
330 : 451 : }
331 : :
332 : : /* NULL means the stack scratch, which is what every logger had before the
333 : : * field existed; nothing to report. */
334 : 451 : static void SolidSyslog_InstallScratch(struct SolidSyslog* self, struct SolidSyslogScratch* configured)
335 : : {
336 [ + + ]: 451 : if (configured != NULL)
337 : : {
338 : 8 : self->Scratch = configured;
339 : : }
340 : 451 : }
341 : :
342 : 451 : static void SolidSyslog_InstallEventReporting(
343 : : struct SolidSyslog* self,
344 : : SolidSyslogEventReportingFunction configured,
345 : : void* context
346 : : )
347 : : {
348 [ + + ]: 451 : if (configured != NULL)
349 : : {
350 : 168 : self->IsEventReportingOn = configured;
351 : 168 : self->EventReportingContext = context;
352 : : }
353 : 451 : }
354 : :
355 : 451 : static void SolidSyslog_InstallStructuredData(
356 : : struct SolidSyslog* self,
357 : : struct SolidSyslogStructuredData** configured,
358 : : size_t count
359 : : )
360 : : {
361 [ + + + + ]: 451 : if ((configured == NULL) && (count > 0U))
362 : : {
363 : : /* Inconsistent pairing - the formatter would dereference Sd[i] for
364 : : * i < SdCount against a NULL array. Report and leave the reset
365 : : * defaults (no SD) in place so Log() degrades safely. */
366 : 2 : SolidSyslog_Report(
367 : : SOLIDSYSLOG_BAD_CONFIG_FATAL_SEVERITY,
368 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
369 : : SOLIDSYSLOG_ERROR_CREATE_INCONSISTENT_SD
370 : : );
371 : : }
372 : : else
373 : : {
374 : 449 : self->Format.Sd = configured;
375 : 449 : self->Format.SdCount = count;
376 : : }
377 : 451 : }
378 : :
379 : 4579 : enum SolidSyslogServiceStatus SolidSyslog_Service(struct SolidSyslog* handle)
380 : : {
381 : 4579 : enum SolidSyslogServiceStatus status = SOLIDSYSLOG_SERVICE_IDLE;
382 : :
383 [ + + ]: 4579 : if (handle == NULL)
384 : : {
385 : 1 : SolidSyslog_Report(
386 : : SOLIDSYSLOG_BAD_ARGUMENT_SEVERITY,
387 : : SOLIDSYSLOG_CAT_BAD_ARGUMENT,
388 : : SOLIDSYSLOG_ERROR_SERVICE_NULL_HANDLE
389 : : );
390 : : }
391 : : else
392 : : {
393 : 4578 : SolidSyslog_ResumeIfPolicyChanged(handle);
394 : :
395 [ + + ]: 4578 : if (SolidSyslog_IsServiceEnabled(handle))
396 : : {
397 : 4385 : status = SolidSyslog_ProcessMessages(handle);
398 : : }
399 : : else
400 : : {
401 : : /* Halted - nothing moves until the policy answers otherwise. */
402 : 193 : status = SOLIDSYSLOG_SERVICE_HALTED;
403 : : }
404 : : }
405 : :
406 : 4579 : return status;
407 : : }
408 : :
409 : : /* A halt stands while the policy still answers Halt; a device whose policy
410 : : * changes resumes from where it stopped, with everything it held still there. */
411 : 4578 : static inline void SolidSyslog_ResumeIfPolicyChanged(struct SolidSyslog* self)
412 : : {
413 [ + + + + ]: 4578 : self->Halted = self->Halted && SolidSyslog_PolicyHalts(self);
414 : 4578 : }
415 : :
416 : 4578 : static inline bool SolidSyslog_IsServiceEnabled(const struct SolidSyslog* self)
417 : : {
418 : 4578 : return !self->Halted;
419 : : }
420 : :
421 : 460 : static inline bool SolidSyslog_PolicyHalts(struct SolidSyslog* self)
422 : : {
423 : 460 : return self->GetDiscardPolicy(self->DiscardPolicyContext) == SOLIDSYSLOG_DISCARD_POLICY_HALT;
424 : : }
425 : :
426 : : /* A pass that halts says so at once, and sends nothing after the refusal
427 : : * that halted it. */
428 : 4385 : static enum SolidSyslogServiceStatus SolidSyslog_ProcessMessages(struct SolidSyslog* self)
429 : : {
430 : 4385 : self->ReportTurnedDown = false;
431 : 4385 : bool drained = SolidSyslog_DrainBufferIntoStore(self);
432 [ + + + + ]: 4385 : bool storeBlocked = !self->Halted && SolidSyslog_SendOneFromStore(self);
433 : 4385 : enum SolidSyslogServiceStatus status = SOLIDSYSLOG_SERVICE_IDLE;
434 : :
435 [ + + ]: 4385 : if (self->Halted)
436 : : {
437 : 35 : status = SOLIDSYSLOG_SERVICE_HALTED;
438 : : }
439 [ + + ]: 4350 : else if (SolidSyslog_IsBlocked(self, storeBlocked, drained))
440 : : {
441 : 904 : status = SOLIDSYSLOG_SERVICE_BLOCKED;
442 : : }
443 [ + + + + : 3446 : else if (drained || SolidSyslogStore_HasUnsent(self->Store) || SolidSyslog_IsReportWaiting(self))
+ + ]
444 : : {
445 : 2650 : status = SOLIDSYSLOG_SERVICE_READY;
446 : : }
447 : : else
448 : : {
449 : : /* Buffer idle, nothing stored, no failed send - IDLE (initial value). */
450 : : }
451 : :
452 : 4385 : return status;
453 : : }
454 : :
455 : : /* Eagerly drain the buffer so the producer-side shock absorber stays small while
456 : : * the sender is slow or down - a store that retains records then takes the
457 : : * backlog, and the buffer applies the discard policy only to what it cannot
458 : : * hand on. A pass deals with the record held from the last one before it reads
459 : : * the buffer, and stops at a record it cannot deal with, so nothing overtakes a
460 : : * record waiting on the sender, or at the refusal that halts the logger. Says
461 : : * whether anything was taken from the buffer. */
462 : 4385 : static inline bool SolidSyslog_DrainBufferIntoStore(struct SolidSyslog* self)
463 : : {
464 : 4385 : bool drained = false;
465 : 4385 : SolidSyslog_ApplyPolicyToHeldRecordIfBufferFull(self);
466 [ + + + + ]: 4385 : bool dealtWith = !self->Halted && SolidSyslog_DealWithHeldRecord(self);
467 : :
468 [ + + + - ]: 4385 : if (dealtWith && !self->Halted)
469 : : {
470 : 3852 : dealtWith = SolidSyslog_ReportPendingEvents(self);
471 : : }
472 : :
473 [ + + + + : 8874 : while (dealtWith && !self->Halted && SolidSyslog_HoldNextRecord(self))
+ + ]
474 : : {
475 : 4489 : drained = true;
476 : 4489 : dealtWith = SolidSyslog_DealWithHeldRecord(self);
477 : : }
478 : :
479 : 4385 : return drained;
480 : : }
481 : :
482 : : /* A record held from the last pass is the head of the whole queue, with the
483 : : * buffer behind it. A buffer that has filled meanwhile has lost records under
484 : : * its policy already, and the same policy decides the head: under Oldest it is
485 : : * the next to go, so the buffer's head is taken in its place; under Halt the
486 : : * logger stops, as it does when the store refuses a record; under Newest the
487 : : * held record is kept and retried. A report is kept under Oldest too: it is
488 : : * the account of a loss rather than a record the buffer took, and dropping it
489 : : * would lose the account and start a loss of its own. */
490 : 4385 : static inline void SolidSyslog_ApplyPolicyToHeldRecordIfBufferFull(struct SolidSyslog* self)
491 : : {
492 [ + + + + ]: 4385 : if (self->HoldsRecord && SolidSyslogBuffer_IsFull(self->Buffer))
493 : : {
494 : 152 : enum SolidSyslogDiscardPolicy policy = self->GetDiscardPolicy(self->DiscardPolicyContext);
495 : :
496 [ + + + + ]: 152 : if ((policy == SOLIDSYSLOG_DISCARD_POLICY_OLDEST) && !self->HeldRecordIsReport)
497 : : {
498 : 10 : SolidSyslog_DropHeldRecord(self);
499 : : }
500 [ + + ]: 142 : else if (policy == SOLIDSYSLOG_DISCARD_POLICY_HALT)
501 : : {
502 : 10 : SolidSyslog_Halt(self, SOLIDSYSLOG_ERROR_HALTED_BUFFER_FULL);
503 : : }
504 : : else
505 : : {
506 : : /* Newest, or a report under Oldest: the held record keeps its place. */
507 : : }
508 : : }
509 : 4385 : }
510 : :
511 : : /* The run is marked before the report, so a handler that logs cannot repeat
512 : : * it; it ends when a held record from the buffer is dealt with. */
513 : 10 : static inline void SolidSyslog_DropHeldRecord(struct SolidSyslog* self)
514 : : {
515 : 10 : bool runStarted = !self->HeldRecordLossReported;
516 : 10 : self->HoldsRecord = false;
517 : 10 : self->HeldRecordLossReported = true;
518 : :
519 [ + + ]: 10 : if (runStarted)
520 : : {
521 : 9 : SolidSyslog_Report(
522 : : SOLIDSYSLOG_SEVERITY_WARNING,
523 : : SOLIDSYSLOG_CAT_BUFFER_RECORDS_LOST,
524 : : SOLIDSYSLOG_ERROR_HELD_RECORD_DROPPED
525 : : );
526 : : }
527 : 10 : }
528 : :
529 : : /* Takes the next record from the buffer into the slot. Says whether one was
530 : : * there to take. */
531 : 8222 : static bool SolidSyslog_HoldNextRecord(struct SolidSyslog* self)
532 : : {
533 : 8222 : self->HoldsRecord = SolidSyslogBuffer_Read(self->Buffer, self->Record, self->RecordCapacity, &self->RecordLength);
534 : 8222 : self->HeldRecordIsReport = false;
535 : :
536 : 8222 : return self->HoldsRecord;
537 : : }
538 : :
539 : : /* Deals with the held record, if there is one. Says whether the slot is free
540 : : * again; a record the sender refused stays held for the next pass. */
541 : 9322 : static bool SolidSyslog_DealWithHeldRecord(struct SolidSyslog* self)
542 : : {
543 [ + + ]: 9322 : if (self->HoldsRecord)
544 : : {
545 : 5543 : self->HoldsRecord = !SolidSyslog_StoreOrSenderTakesHeldRecord(self);
546 [ + + + + ]: 5543 : self->HeldRecordLossReported = self->HeldRecordLossReported && !SolidSyslog_IsBufferRecordDealtWith(self);
547 : : }
548 : :
549 : 9322 : return !self->HoldsRecord;
550 : : }
551 : :
552 : : /* Only a record from the buffer ends a run of drops. The report of a drop is
553 : : * made in the pass that dropped it, so a report that ended the run would have
554 : : * every drop in it reported again. */
555 : 50 : static inline bool SolidSyslog_IsBufferRecordDealtWith(const struct SolidSyslog* self)
556 : : {
557 [ + + + + ]: 50 : return !self->HoldsRecord && !self->HeldRecordIsReport;
558 : : }
559 : :
560 : : /* Offers the held record to the store, and to the sender only when the store
561 : : * has not dealt with it - unless the store's refusal halted the logger, which
562 : : * sends nothing after it. */
563 : 5543 : static inline bool SolidSyslog_StoreOrSenderTakesHeldRecord(struct SolidSyslog* self)
564 : : {
565 [ + + ]: 7743 : return SolidSyslog_StoreDealsWithHeldRecord(self) ||
566 [ + + + + ]: 2200 : (!self->Halted && SolidSyslogSender_Send(self->Sender, self->Record, self->RecordLength));
567 : : }
568 : :
569 : : /* Offers the held record to the store. A store that retains records has dealt
570 : : * with a record from the buffer either way: it kept it, it lost it and reported
571 : : * why, or its discard policy refused it and reported the loss - letting a
572 : : * refused record escape to the sender would put it ahead of older stored ones
573 : : * once the sender recovered. A report is the account of a loss, and losing it
574 : : * would lose the account, so the store has dealt with one only by keeping it:
575 : : * one it lost or refused goes to the sender, ahead of the stored records, and
576 : : * is held while the sender refuses it, or while the refusal has the logger
577 : : * halted. A store that keeps nothing never had either, so it is still to be
578 : : * sent. */
579 : 5543 : static inline bool SolidSyslog_StoreDealsWithHeldRecord(struct SolidSyslog* self)
580 : : {
581 : 5543 : enum SolidSyslogStoreWriteResult result = SolidSyslogStore_Write(self->Store, self->Record, self->RecordLength);
582 [ + + + + ]: 5543 : bool refused = (result == SOLIDSYSLOG_STORE_WRITE_NOT_RETAINED) && !SolidSyslogStore_IsTransient(self->Store);
583 [ + + + + ]: 5543 : bool notKept = refused || (result == SOLIDSYSLOG_STORE_WRITE_LOST);
584 : :
585 [ + + ]: 5543 : if (refused)
586 : : {
587 : 250 : SolidSyslog_HaltIfPolicyHalts(self);
588 : : }
589 : :
590 [ + + + + : 5543 : return (result == SOLIDSYSLOG_STORE_WRITE_KEPT) || (notKept && !self->HeldRecordIsReport);
+ + ]
591 : : }
592 : :
593 : : /* A refusal is the discard policy at work; under Halt it is also where the
594 : : * logger stops. The record refused is gone either way, so the halt is asked
595 : : * about after the store has reported it, and the callback hears of it after
596 : : * that. */
597 : 250 : static inline void SolidSyslog_HaltIfPolicyHalts(struct SolidSyslog* self)
598 : : {
599 [ + + ]: 250 : if (SolidSyslog_PolicyHalts(self))
600 : : {
601 : 25 : SolidSyslog_Halt(self, SOLIDSYSLOG_ERROR_HALTED_STORE_REFUSED);
602 : : }
603 : 250 : }
604 : :
605 : : /* The halt is raised before the callback hears of it, since an OnHalt that
606 : : * stops the device never returns. */
607 : 35 : static inline void SolidSyslog_Halt(struct SolidSyslog* self, enum SolidSyslogErrors haltedAt)
608 : : {
609 : 35 : self->Halted = true;
610 : 35 : SolidSyslog_Report(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_CAT_LOGGER_HALTED, haltedAt);
611 : 35 : self->OnHalt(self->HaltContext);
612 : 35 : }
613 : :
614 : : /* Walks the table once, in its order, so a cause is reported ahead of what it
615 : : * caused. Each report is made in the free slot and dealt with before the next
616 : : * is made, and a kind raised meanwhile at a slot already walked waits for the
617 : : * next pass. The walk ends at a report the store and sender did not take, at a
618 : : * halt, or at the switch's first off, leaving every kind not yet reported
619 : : * waiting. Says whether the slot is free. */
620 : 3852 : static inline bool SolidSyslog_ReportPendingEvents(struct SolidSyslog* self)
621 : : {
622 : 3852 : bool walking = SolidSyslog_HasRecordSlot(self);
623 : :
624 [ + + + + ]: 62500 : for (size_t index = 0U; (index < SOLIDSYSLOG_PENDING_EVENT_KIND_COUNT) && walking; index++)
625 : : {
626 : 58648 : walking = SolidSyslog_ReportIfPending(self, index);
627 : : }
628 : :
629 : 3852 : return !self->HoldsRecord;
630 : : }
631 : :
632 : : /* The fallback SolidSyslog_Create returns carries no slot, so it takes no
633 : : * event and leaves each one for a logger that can report it. */
634 : 4657 : static inline bool SolidSyslog_HasRecordSlot(const struct SolidSyslog* self)
635 : : {
636 : 4657 : return self->RecordStorage != NULL;
637 : : }
638 : :
639 : : /* Only a kind with an event waiting asks the integrator's switch, and an event
640 : : * it turns down stays waiting for a pass when the switch is on. Says whether
641 : : * the walk goes on. */
642 : 58648 : static inline bool SolidSyslog_ReportIfPending(struct SolidSyslog* self, size_t index)
643 : : {
644 : 58648 : bool goesOn = true;
645 : :
646 [ + + ]: 58648 : if (SolidSyslogPendingEvents_IsPending(index))
647 : : {
648 : 784 : goesOn = self->IsEventReportingOn(self->EventReportingContext);
649 : 784 : self->ReportTurnedDown = !goesOn;
650 : :
651 [ + + ]: 784 : if (goesOn)
652 : : {
653 : 458 : SolidSyslog_HoldPendingReport(self, index);
654 [ + + + - ]: 458 : goesOn = SolidSyslog_DealWithHeldRecord(self) && !self->Halted;
655 : : }
656 : : }
657 : :
658 : 58648 : return goesOn;
659 : : }
660 : :
661 : : /* Takes the kind's event into the free slot as a report, formatted as a record
662 : : * of the logger's own. The switch is integrator code, asked after the kind was
663 : : * seen pending: should another logger's pass take the event meanwhile, this
664 : : * take finds nothing and the slot stays free, and what was formatted into it
665 : : * is never sent. */
666 : 458 : static inline void SolidSyslog_HoldPendingReport(struct SolidSyslog* self, size_t index)
667 : : {
668 : : struct SolidSyslogErrorEvent event;
669 : 458 : self->HoldsRecord = SolidSyslogPendingEvents_Take(index, &event);
670 : 458 : self->HeldRecordIsReport = self->HoldsRecord;
671 : 458 : SolidSyslog_FormatReport(self, &event);
672 : 458 : }
673 : :
674 : 458 : static inline void SolidSyslog_FormatReport(struct SolidSyslog* self, const struct SolidSyslogErrorEvent* event)
675 : : {
676 : : /* The MSG of every report; its MSGID and the library's SD element say
677 : : * which event it was. */
678 : : static const char reportMsg[] = "SolidSyslog event";
679 : 916 : struct SolidSyslogMessage message =
680 : 458 : {SOLIDSYSLOG_FACILITY_SYSLOG, event->Severity, SolidSyslogErrorCategory_Name(event->Category), reportMsg};
681 : : struct SolidSyslogEventSd eventSd;
682 : 458 : struct SolidSyslogStructuredData* sd = SolidSyslogEventSd_Initialise(&eventSd, event);
683 : 458 : struct SolidSyslogFormatter* formatter = SolidSyslogFormatter_Create(self->RecordStorage, self->RecordCapacity);
684 : :
685 : 458 : SolidSyslogMessageFormatter_Format(formatter, &message, &self->Format, &sd, 1U);
686 : : /* Trims a truncated multi-byte tail in place; Record already points here. */
687 : 458 : (void) SolidSyslogFormatter_AsFormattedBuffer(formatter);
688 : 458 : self->RecordLength = SolidSyslogFormatter_Length(formatter);
689 : 458 : }
690 : :
691 : : /* Sends one record the store hands out. The slot is the staging buffer; the
692 : : * record stays the store's, re-read next pass if the sender refuses it. */
693 : 4350 : static inline bool SolidSyslog_SendOneFromStore(struct SolidSyslog* self)
694 : : {
695 : 4350 : bool blocked = false;
696 : :
697 [ + + ]: 4350 : if (SolidSyslog_TakeNextFromStore(self))
698 : : {
699 [ + + ]: 2079 : if (SolidSyslogSender_Send(self->Sender, self->Record, self->RecordLength))
700 : : {
701 : 1198 : SolidSyslogStore_MarkSent(self->Store);
702 : : }
703 : : else
704 : : {
705 : 881 : blocked = true;
706 : : }
707 : : }
708 : : else
709 : : {
710 : : /* Records the store holds but cannot hand out yet. */
711 : 2271 : blocked = SolidSyslogStore_HasUnsent(self->Store);
712 : : }
713 : :
714 : 4350 : return blocked;
715 : : }
716 : :
717 : : /* Reads the store's oldest unsent record into the slot. Nothing is asked of the
718 : : * store while Service holds a record of its own: the slot is taken, and a store
719 : : * that keeps nothing has nothing to hand out anyway. */
720 : 4350 : static inline bool SolidSyslog_TakeNextFromStore(struct SolidSyslog* self)
721 : : {
722 [ + + + + ]: 8083 : return !self->HoldsRecord &&
723 : 3733 : SolidSyslogStore_ReadNextUnsent(self->Store, self->Record, self->RecordCapacity, &self->RecordLength);
724 : : }
725 : :
726 : : /* A record held for a failed direct send blocks the pass outright: the buffer
727 : : * waits behind it. A store that could not move a record blocks it only when the
728 : : * buffer stayed idle - buffer activity out-ranks a blocked store, so a down
729 : : * sender never demotes the loop out of "keep draining" while there is a store to
730 : : * drain into. */
731 : 4350 : static inline bool SolidSyslog_IsBlocked(const struct SolidSyslog* self, bool storeBlocked, bool drained)
732 : : {
733 [ + + + + : 4350 : return self->HoldsRecord || (storeBlocked && !drained);
+ + ]
734 : : }
735 : :
736 : : /* An event raised after the walk passed its slot - by the reports themselves,
737 : : * the buffer or the store - waits for the next pass, so it keeps the loop
738 : : * going. One the switch turned down this pass does not: the switch is asked
739 : : * again only on a pass, so a loop told to keep going would spin until it
740 : : * answered otherwise. The fallback has no slot to report in. */
741 : 805 : static inline bool SolidSyslog_IsReportWaiting(const struct SolidSyslog* self)
742 : : {
743 [ + + + + : 805 : return SolidSyslog_HasRecordSlot(self) && !self->ReportTurnedDown && SolidSyslogPendingEvents_AnyPending();
+ + ]
744 : : }
745 : :
746 : 6126 : void SolidSyslog_Log(struct SolidSyslog* handle, const struct SolidSyslogMessage* message)
747 : : {
748 : 6126 : SolidSyslog_DoLog(handle, message, NULL, 0);
749 : 6126 : }
750 : :
751 : 7 : void SolidSyslog_LogWithSd(
752 : : struct SolidSyslog* handle,
753 : : const struct SolidSyslogMessage* message,
754 : : struct SolidSyslogStructuredData** sd,
755 : : size_t sdCount
756 : : )
757 : : {
758 : 7 : SolidSyslog_DoLog(handle, message, sd, sdCount);
759 : 7 : }
760 : :
761 : 6133 : static void SolidSyslog_DoLog(
762 : : struct SolidSyslog* handle,
763 : : const struct SolidSyslogMessage* message,
764 : : struct SolidSyslogStructuredData** sd,
765 : : size_t sdCount
766 : : )
767 : : {
768 [ + + ]: 6133 : if (handle == NULL)
769 : : {
770 : 1 : SolidSyslog_Report(
771 : : SOLIDSYSLOG_BAD_ARGUMENT_SEVERITY,
772 : : SOLIDSYSLOG_CAT_BAD_ARGUMENT,
773 : : SOLIDSYSLOG_ERROR_LOG_NULL_HANDLE
774 : : );
775 : : }
776 [ + + ]: 6132 : else if (message == NULL)
777 : : {
778 : 2 : SolidSyslog_Report(
779 : : SOLIDSYSLOG_BAD_ARGUMENT_SEVERITY,
780 : : SOLIDSYSLOG_CAT_BAD_ARGUMENT,
781 : : SOLIDSYSLOG_ERROR_LOG_NULL_MESSAGE
782 : : );
783 : : }
784 : : else
785 : : {
786 : : /* Inconsistent pairing - the formatter would dereference sd[i] for
787 : : i < sdCount against a NULL array. Report and drop to no per-message
788 : : SD so the message still logs (degrade safely), mirroring the
789 : : Create-time guard in SolidSyslog_InstallStructuredData. */
790 : 6130 : size_t safeSdCount = sdCount;
791 [ + + + + ]: 6130 : if ((sd == NULL) && (sdCount > 0U))
792 : : {
793 : 2 : SolidSyslog_Report(
794 : : SOLIDSYSLOG_BAD_ARGUMENT_SEVERITY,
795 : : SOLIDSYSLOG_CAT_BAD_ARGUMENT,
796 : : SOLIDSYSLOG_ERROR_LOG_INCONSISTENT_SD
797 : : );
798 : 2 : safeSdCount = 0U;
799 : : }
800 : :
801 : 6130 : struct LogRequest request = {handle, message, sd, safeSdCount};
802 : 6130 : handle->Scratch->WithScratch(handle->Scratch, SolidSyslog_FormatIntoScratch, &request);
803 : : }
804 : 6133 : }
805 : :
806 : 6130 : static void SolidSyslog_FormatIntoScratch(SolidSyslogFormatterStorage* storage, size_t messageBytes, void* context)
807 : : {
808 : 6130 : struct LogRequest* request = (struct LogRequest*) context;
809 : 6130 : struct SolidSyslogFormatter* f = SolidSyslogFormatter_Create(storage, messageBytes);
810 : :
811 : 6130 : SolidSyslogMessageFormatter_Format(f, request->Message, &request->Handle->Format, request->Sd, request->SdCount);
812 : 6130 : SolidSyslogBuffer_Write(
813 : 6130 : request->Handle->Buffer,
814 : 6130 : SolidSyslogFormatter_AsFormattedBuffer(f),
815 : : SolidSyslogFormatter_Length(f)
816 : : );
817 : 6130 : }
|