diff --git a/Bdd/features/steps/syslog_steps.py b/Bdd/features/steps/syslog_steps.py index 8be5a3fc..e7143677 100644 --- a/Bdd/features/steps/syslog_steps.py +++ b/Bdd/features/steps/syslog_steps.py @@ -408,7 +408,7 @@ def run_example(context, extra_args=None, expected_messages=1): (S24.05). The pre-S13.18 implementation wrote `send N\\nquit\\n` upfront via - process.communicate() and relied on NullBuffer's synchronous Send to + process.communicate() and relied on PassthroughBuffer's synchronous Send to guarantee delivery before exit. That assumption broke once the target became buffered — `quit` could land before the service thread had drained, losing the UDP packet. The prompt protocol diff --git a/Bdd/features/tcp_singletask.feature b/Bdd/features/tcp_singletask.feature index 35e0b180..e003111b 100644 --- a/Bdd/features/tcp_singletask.feature +++ b/Bdd/features/tcp_singletask.feature @@ -1,6 +1,6 @@ @tcp @windows_wip Feature: TCP message delivery (single-task example) - The Linux single-task example (NullBuffer, synchronous send) sends + The Linux single-task example (PassthroughBuffer, synchronous send) sends messages via TCP transport with RFC 6587 octet-counting framing. Linux-only post-S13.18 — Windows TCP coverage now comes from tcp_transport.feature via the BDD target. The single-task diff --git a/CLAUDE.md b/CLAUDE.md index 2f922a70..441ead56 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -336,7 +336,7 @@ live under `Core/Interface/`; platform-specific helpers (the `SolidSyslogPosix*` | `SolidSyslogSwitchingSender.h` | System setup code composing multiple inner senders | `SolidSyslogSwitchingSenderConfig` (senders, senderCount, selector), `SolidSyslogSwitchingSenderSelector`, `SolidSyslogSwitchingSender_Create`, `_Destroy` | | `SolidSyslogBuffer.h` | Library internals consuming a buffer (Service algorithm) | `SolidSyslogBuffer_Write`, `_Read` | | `SolidSyslogBufferDefinition.h` | Buffer implementors (extension point) | `SolidSyslogBuffer` vtable struct | -| `SolidSyslogNullBuffer.h` | System setup code (single-task, no buffering) | `SolidSyslogNullBuffer_Create`, `_Destroy` | +| `SolidSyslogPassthroughBuffer.h` | System setup code (single-task, no buffering) | `SolidSyslogPassthroughBuffer_Create`, `_Destroy` | | `SolidSyslogPosixMessageQueueBuffer.h` | System setup code using POSIX message queue buffer | `SolidSyslogPosixMessageQueueBuffer_Create`, `_Destroy` | | `SolidSyslogCircularBuffer.h` | System setup code using an in-memory ring buffer (bare-metal / RTOS / Windows) | `SOLIDSYSLOG_CIRCULAR_BUFFER_HEADER_BYTES`, `SOLIDSYSLOG_CIRCULAR_BUFFER_RING_BYTES(maxMessages)` (friendly: N max-sized messages → ring bytes), `SolidSyslogCircularBuffer_Create(mutex, ring, ringBytes)`, `_Destroy(buffer)` (uint16-length-prefixed records, drop-newest on overflow, no-split wrap, mutex-injected synchronization). Instance struct lives in a library-internal static pool (E11); caller supplies the ring memory only. | | `SolidSyslogMutex.h` | Any code holding a mutex handle | `SolidSyslogMutex_Lock`, `_Unlock` | @@ -353,7 +353,7 @@ live under `Core/Interface/`; platform-specific helpers (the `SolidSyslogPosix*` | `SolidSyslogBlockDevice.h` | Library internals consuming a block device (BlockSequence inside BlockStore) and integrator code addressing blocks directly | `SolidSyslogBlockDevice_Acquire`, `_Dispose`, `_Exists`, `_Read`, `_Append`, `_WriteAt`, `_Size` (block-indexed I/O; Acquire makes a block ready for fresh writes, Dispose releases it) | | `SolidSyslogBlockDeviceDefinition.h` | BlockDevice implementors (extension point) | `SolidSyslogBlockDevice` vtable struct (`Acquire`, `Dispose`, `Exists`, `Read`, `Append`, `WriteAt`, `Size`) | | `SolidSyslogFileBlockDevice.h` | System setup code wiring a file-backed block device | `SolidSyslogFileBlockDeviceStorage`, `SOLIDSYSLOG_FILEBLOCKDEVICE_STORAGE_SIZE`, `SolidSyslogFileBlockDevice_Create(storage, readFile, writeFile, pathPrefix)` (sequence-numbered filenames `.log`, two cached file handles), `_Destroy(device)` | -| `SolidSyslogBlockStore.h` | System setup code using a BlockDevice-backed store | `SolidSyslogBlockStoreStorage`, `SOLIDSYSLOG_BLOCKSTORE_STORAGE_SIZE`, `SolidSyslogBlockStoreConfig` (with `blockDevice`, `storeFullContext`, `getCapacityThreshold`, `onThresholdCrossed`, `thresholdContext`), `SolidSyslogBlockStore_Create(storage, config)`, `_Destroy(store)`, `SolidSyslogDiscardPolicy` (`SolidSyslogDiscardPolicy_Oldest` / `_Newest` / `_Halt`), `SolidSyslogStoreFullCallback(void* context)`, `SolidSyslogStoreThresholdFunction(void* context)` (returns threshold in bytes; 0 disables; queried each Write), `SolidSyslogStoreThresholdCallback(void* context)` (edge-triggered; NullBuffer recursion gotcha — see header) | +| `SolidSyslogBlockStore.h` | System setup code using a BlockDevice-backed store | `SolidSyslogBlockStoreStorage`, `SOLIDSYSLOG_BLOCKSTORE_STORAGE_SIZE`, `SolidSyslogBlockStoreConfig` (with `blockDevice`, `storeFullContext`, `getCapacityThreshold`, `onThresholdCrossed`, `thresholdContext`), `SolidSyslogBlockStore_Create(storage, config)`, `_Destroy(store)`, `SolidSyslogDiscardPolicy` (`SolidSyslogDiscardPolicy_Oldest` / `_Newest` / `_Halt`), `SolidSyslogStoreFullCallback(void* context)`, `SolidSyslogStoreThresholdFunction(void* context)` (returns threshold in bytes; 0 disables; queried each Write), `SolidSyslogStoreThresholdCallback(void* context)` (edge-triggered; PassthroughBuffer recursion gotcha — see header) | | `SolidSyslogSecurityPolicyDefinition.h` | SecurityPolicy implementors (extension point) | `SolidSyslogSecurityPolicy` vtable struct, `SOLIDSYSLOG_MAX_INTEGRITY_SIZE` | | `SolidSyslogNullSecurityPolicy.h` | System setup code (no integrity checking) | `SolidSyslogNullSecurityPolicy_Create`, `_Destroy` | | `SolidSyslogCrc16Policy.h` | System setup code using CRC-16 integrity | `SolidSyslogCrc16Policy_Create`, `_Destroy` | @@ -462,8 +462,8 @@ code should follow them; reviewers should call out drift. comparison operators.** Plain `&&` between bool-typed operands needs no extra parens — readability wins over MISRA pedantry there. - **No null-pointer checks where the type's null object exists.** Use - `SolidSyslogNullSecurityPolicy`, `SolidSyslogNullStore`, `SolidSyslogNullBuffer` - rather than `if (policy != NULL) policy->Compute(...)`. + `SolidSyslogNullSecurityPolicy`, `SolidSyslogNullStore` rather than + `if (policy != NULL) policy->Compute(...)`. ### Test code diff --git a/Core/Interface/SolidSyslogBlockStore.h b/Core/Interface/SolidSyslogBlockStore.h index a44521d0..8b2b45f0 100644 --- a/Core/Interface/SolidSyslogBlockStore.h +++ b/Core/Interface/SolidSyslogBlockStore.h @@ -27,7 +27,7 @@ EXTERN_C_BEGIN typedef size_t (*SolidSyslogStoreThresholdFunction)(void* context); /* Edge-triggered: fires once when used-bytes transitions from below threshold to at-or-above. - * NullBuffer note: SolidSyslog_Log is synchronous under SolidSyslogNullBuffer, so calling + * PassthroughBuffer note: SolidSyslog_Log is synchronous under SolidSyslogPassthroughBuffer, so calling * SolidSyslog_Log from this callback will recurse into Store_Write. Either gate the Log, * or use SolidSyslogPosixMessageQueueBuffer (which returns immediately). */ typedef void (*SolidSyslogStoreThresholdCallback)(void* context); diff --git a/Core/Interface/SolidSyslogNullBuffer.h b/Core/Interface/SolidSyslogNullBuffer.h deleted file mode 100644 index 00c19bd1..00000000 --- a/Core/Interface/SolidSyslogNullBuffer.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef SOLIDSYSLOGNULLBUFFER_H -#define SOLIDSYSLOGNULLBUFFER_H - -#include "ExternC.h" - -EXTERN_C_BEGIN - - struct SolidSyslogSender; - - struct SolidSyslogBuffer* SolidSyslogNullBuffer_Create(struct SolidSyslogSender * sender); - void SolidSyslogNullBuffer_Destroy(void); - -EXTERN_C_END - -#endif /* SOLIDSYSLOGNULLBUFFER_H */ diff --git a/Core/Interface/SolidSyslogPassthroughBuffer.h b/Core/Interface/SolidSyslogPassthroughBuffer.h new file mode 100644 index 00000000..c5efa7b8 --- /dev/null +++ b/Core/Interface/SolidSyslogPassthroughBuffer.h @@ -0,0 +1,15 @@ +#ifndef SOLIDSYSLOGPASSTHROUGHBUFFER_H +#define SOLIDSYSLOGPASSTHROUGHBUFFER_H + +#include "ExternC.h" + +EXTERN_C_BEGIN + + struct SolidSyslogSender; + + struct SolidSyslogBuffer* SolidSyslogPassthroughBuffer_Create(struct SolidSyslogSender * sender); + void SolidSyslogPassthroughBuffer_Destroy(void); + +EXTERN_C_END + +#endif /* SOLIDSYSLOGPASSTHROUGHBUFFER_H */ diff --git a/Core/Source/CMakeLists.txt b/Core/Source/CMakeLists.txt index 09dc424c..377305ff 100644 --- a/Core/Source/CMakeLists.txt +++ b/Core/Source/CMakeLists.txt @@ -6,7 +6,7 @@ set(SOURCES SolidSyslogBuffer.c SolidSyslogFormatter.c SolidSyslogMetaSd.c - SolidSyslogNullBuffer.c + SolidSyslogPassthroughBuffer.c SolidSyslogCircularBuffer.c SolidSyslogCircularBufferStatic.c SolidSyslogPoolAllocator.c diff --git a/Core/Source/SolidSyslog.c b/Core/Source/SolidSyslog.c index a191bdaf..25654148 100644 --- a/Core/Source/SolidSyslog.c +++ b/Core/Source/SolidSyslog.c @@ -570,9 +570,8 @@ static inline void SolidSyslog_FormatNilvalue(struct SolidSyslogFormatter* f) * — its absence is a valid integrator choice (matches SolidSyslogNullStore * semantics: drain proceeds, fall-through to direct send). * - * The public SolidSyslogNull* family is for integrator-chosen no-ops with - * different semantics (e.g. NullBuffer is a direct-send shim); these - * internal nils are crash-safe defaults only. + * The public SolidSyslogNull* family is for integrator-chosen no-ops; + * these internal nils are crash-safe defaults only. * ============================================================================ */ static bool nilBufferReportArmed = true; diff --git a/Core/Source/SolidSyslogNullBuffer.c b/Core/Source/SolidSyslogNullBuffer.c deleted file mode 100644 index cf71bc5a..00000000 --- a/Core/Source/SolidSyslogNullBuffer.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "SolidSyslogNullBuffer.h" - -#include -#include - -#include "SolidSyslogBufferDefinition.h" -#include "SolidSyslogSender.h" - -static bool NullBuffer_Read(struct SolidSyslogBuffer* base, void* data, size_t maxSize, size_t* bytesRead); -static void NullBuffer_Write(struct SolidSyslogBuffer* base, const void* data, size_t size); - -static inline struct SolidSyslogNullBuffer* NullBuffer_SelfFromBase(struct SolidSyslogBuffer* base); - -struct SolidSyslogNullBuffer -{ - struct SolidSyslogBuffer Base; - struct SolidSyslogSender* Sender; -}; - -static struct SolidSyslogNullBuffer NullBuffer_Instance; - -struct SolidSyslogBuffer* SolidSyslogNullBuffer_Create(struct SolidSyslogSender* sender) -{ - NullBuffer_Instance.Base.Write = NullBuffer_Write; - NullBuffer_Instance.Base.Read = NullBuffer_Read; - NullBuffer_Instance.Sender = sender; - return &NullBuffer_Instance.Base; -} - -void SolidSyslogNullBuffer_Destroy(void) -{ - NullBuffer_Instance.Base.Write = NULL; - NullBuffer_Instance.Base.Read = NULL; - NullBuffer_Instance.Sender = NULL; -} - -static bool NullBuffer_Read(struct SolidSyslogBuffer* base, void* data, size_t maxSize, size_t* bytesRead) -{ - (void) base; - (void) data; - (void) maxSize; - *bytesRead = 0; - return false; -} - -static void NullBuffer_Write(struct SolidSyslogBuffer* base, const void* data, size_t size) -{ - struct SolidSyslogNullBuffer* self = NullBuffer_SelfFromBase(base); - (void) SolidSyslogSender_Send(self->Sender, data, size); -} - -static inline struct SolidSyslogNullBuffer* NullBuffer_SelfFromBase(struct SolidSyslogBuffer* base) -{ - return (struct SolidSyslogNullBuffer*) base; -} diff --git a/Core/Source/SolidSyslogPassthroughBuffer.c b/Core/Source/SolidSyslogPassthroughBuffer.c new file mode 100644 index 00000000..28ca0471 --- /dev/null +++ b/Core/Source/SolidSyslogPassthroughBuffer.c @@ -0,0 +1,55 @@ +#include "SolidSyslogPassthroughBuffer.h" + +#include +#include + +#include "SolidSyslogBufferDefinition.h" +#include "SolidSyslogSender.h" + +static bool PassthroughBuffer_Read(struct SolidSyslogBuffer* base, void* data, size_t maxSize, size_t* bytesRead); +static void PassthroughBuffer_Write(struct SolidSyslogBuffer* base, const void* data, size_t size); + +static inline struct SolidSyslogPassthroughBuffer* PassthroughBuffer_SelfFromBase(struct SolidSyslogBuffer* base); + +struct SolidSyslogPassthroughBuffer +{ + struct SolidSyslogBuffer Base; + struct SolidSyslogSender* Sender; +}; + +static struct SolidSyslogPassthroughBuffer PassthroughBuffer_Instance; + +struct SolidSyslogBuffer* SolidSyslogPassthroughBuffer_Create(struct SolidSyslogSender* sender) +{ + PassthroughBuffer_Instance.Base.Write = PassthroughBuffer_Write; + PassthroughBuffer_Instance.Base.Read = PassthroughBuffer_Read; + PassthroughBuffer_Instance.Sender = sender; + return &PassthroughBuffer_Instance.Base; +} + +void SolidSyslogPassthroughBuffer_Destroy(void) +{ + PassthroughBuffer_Instance.Base.Write = NULL; + PassthroughBuffer_Instance.Base.Read = NULL; + PassthroughBuffer_Instance.Sender = NULL; +} + +static bool PassthroughBuffer_Read(struct SolidSyslogBuffer* base, void* data, size_t maxSize, size_t* bytesRead) +{ + (void) base; + (void) data; + (void) maxSize; + *bytesRead = 0; + return false; +} + +static void PassthroughBuffer_Write(struct SolidSyslogBuffer* base, const void* data, size_t size) +{ + struct SolidSyslogPassthroughBuffer* self = PassthroughBuffer_SelfFromBase(base); + (void) SolidSyslogSender_Send(self->Sender, data, size); +} + +static inline struct SolidSyslogPassthroughBuffer* PassthroughBuffer_SelfFromBase(struct SolidSyslogBuffer* base) +{ + return (struct SolidSyslogPassthroughBuffer*) base; +} diff --git a/DEVLOG.md b/DEVLOG.md index ea8089c5..ba5cc031 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -1,5 +1,99 @@ # Dev Log +## 2026-05-18 — S11.03: Rename NullBuffer to PassthroughBuffer (#397) + +Pure mechanical rename — the type is a passthrough that forwards +`Write` directly into an injected `SolidSyslogSender`, not a GoF Null +Object. Splitting it out from the Core sweep (S11.04) per the epic +sequencing keeps the cross-tree rename's review surface separate from +pool semantics. + +### Mechanical scope + +- File renames via `git mv`: `Core/Interface/SolidSyslogNullBuffer.h`, + `Core/Source/SolidSyslogNullBuffer.c`, + `Tests/SolidSyslogNullBufferTest.cpp` → corresponding + `PassthroughBuffer` paths. +- Tier 1 symbols: `SolidSyslogNullBuffer_Create`, `_Destroy`, `struct + SolidSyslogNullBuffer`, include guard `SOLIDSYSLOGNULLBUFFER_H`. +- Tier 2 statics: `NullBuffer_Read`, `_Write`, `_SelfFromBase`, + `_Instance` → `PassthroughBuffer_*` per the strip-only rule (file + basename minus the `SolidSyslog` namespace). +- Test group: `TEST_GROUP(SolidSyslogPassthroughBuffer)`. Individual + test names left alone — they describe behaviour, not the class. +- Call sites: `Tests/SolidSyslogTest.cpp` (include + two Create/Destroy + calls); two CMakeLists.txt source-list entries. +- Doc / comment sweep: `CLAUDE.md` (audience table row), `README.md` + (two prose), `SKILL.md` (one prose), `docs/iec62443.md` (three SR + rows), `Core/Interface/SolidSyslogBlockStore.h` (recursion-gotcha + doc comment), `Bdd/features/tcp_singletask.feature`, + `Bdd/features/steps/syslog_steps.py`. +- `misra_suppressions.txt`: 11.3 entry path updated (line number + unchanged — same file content under new name). + +### Doc honesty corrections (adjacent to the rename, not the rename itself) + +Three places held a "but NullBuffer doesn't actually fit this rule" +caveat — the rename removes the misleading example, so the caveat +can go too. Calling these out explicitly because they're substantive +edits in a story scoped as mechanical: + +- **`CLAUDE.md` Design Patterns null-object bullet** previously listed + `SolidSyslogNullBuffer` alongside `NullSecurityPolicy` and `NullStore` + as "the type's null object" sites where guard checks should be + skipped. `PassthroughBuffer` is not a null object. Removed from the + list (decided pre-raise). +- **`SKILL.md` Architecture line** previously read "Null object pattern + throughout (NullBuffer is the buffer null object)". The parenthetical + was wrong; struck. The remaining "Null object pattern throughout" is + still accurate (NullMutex / NullStore / NullSecurityPolicy genuinely + are no-ops). +- **`Core/Source/SolidSyslog.c` nil-collaborators comment block** + previously read "The public SolidSyslogNull* family is for + integrator-chosen no-ops with different semantics (e.g. NullBuffer + is a direct-send shim)". After the rename the family no longer + contains the "different semantics" example; the caveat now misleads + in the opposite direction, so dropped. The remaining sentence stands + unchanged. + +### Deliberate non-renames + +Three callouts so the next sed sweep doesn't catch them: + +- `Tests/SolidSyslogTest.cpp::TEST(SolidSyslogLifecycle, CreateWithNullBufferReportsError)` — + refers to NULL pointer (`config.Buffer = NULL`), not the class. +- `Tests/SolidSyslogUdpSenderTest.cpp::TEST(SolidSyslogUdpSenderBadSetup, + SendWithNullBufferReportsErrorAndDoesNotSend)` — same. +- `Bdd/Targets/FreeRtos/main.c:6` historical comment about S08.04 — + preserved as historical narrative; at the time the type really was + called NullBuffer. + +### Gates + +debug, clang-debug, sanitize, coverage, tidy, cppcheck, clang-format. +cppcheck-misra count **88 on the rename branch, 88 on main** — zero +new findings, zero entries against `SolidSyslogPassthroughBuffer.c`. +(Side-note: my S11.02 closing note recorded "60" as the baseline; that +appears to have been a non-deterministic cppcheck run or an +incomplete invocation — the actual main baseline at the time of this +session is 88, verified by running the identical command against main +in isolation. AC #3 in the story body inherits the same stale number; +the true invariant — "no new findings introduced by the rename" — is +met.) + +PassthroughBuffer.c at 100% line coverage post-rename. + +### Out of scope (carrying forward to S11.04) + +The `Instance` singleton, the `void _Destroy(void)` signature, the +existing 11.3 storage-cast on the `SelfFromBase` helper, and the +absence of a `SOLIDSYSLOG_PASSTHROUGHBUFFER_POOL_SIZE` tunable all +carry forward unchanged. S11.04 (Core sweep) migrates this class +onto `SolidSyslogPoolAllocator` and retires the cast in the same +move. + +--- + ## 2026-05-18 — S11.02: Extract SolidSyslogPoolAllocator helper (#395) S11.01 left every E11 class about to copy the same 13-function pool diff --git a/README.md b/README.md index be2e0949..e88bf992 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ is not a core dependency; Core has zero OpenSSL references. Designed for resource-constrained environments: - C99, no dynamic memory allocation required — allocator is caller-injected - Transport-agnostic — UDP, TCP, TLS, or bring your own -- Buffer-agnostic — NullBuffer (direct send), portable CircularBuffer (mutex-injected ring), POSIX message queue, or bring your own +- Buffer-agnostic — PassthroughBuffer (direct send), portable CircularBuffer (mutex-injected ring), POSIX message queue, or bring your own - No `#ifdef` feature flags — optional features composed at link time - MISRA C:2012 informed - Dependency injection throughout — fully testable without a network @@ -61,7 +61,7 @@ Public headers are split by audience (Interface Segregation Principle): - **`SolidSyslogError.h`** — install a handler to react to library-internal errors (NULL guards, send failures); default is silent. See `Bdd/Targets/Common/BddTargetStderrErrorHandler.c` for a reference implementation - **`SolidSyslogConfigLock.h`** — optional setup-time lock injection (`SolidSyslog_SetConfigLock(lockFn, unlockFn)`); wraps library-internal pool slot walks so multi-task setup is safe. Defaults are no-ops, so single-task systems can ignore it. Integrators on RTOS / multi-core wire `taskENTER_CRITICAL`, a static `pthread_mutex_t`, `EnterCriticalSection`, or a spinlock pair - **`SolidSyslogSenderDefinition.h`** / **`SolidSyslogBufferDefinition.h`** — extension points for custom senders and buffers -- **`SolidSyslogNullBuffer.h`** — direct-send buffer for single-task systems +- **`SolidSyslogPassthroughBuffer.h`** — direct-send buffer for single-task systems - **`SolidSyslogCircularBuffer.h`** — portable ring buffer with caller-allocated ring memory and an injected `SolidSyslogMutex` (`SolidSyslogPosixMutex` / `SolidSyslogWindowsMutex` / `SolidSyslogFreeRtosMutex` / `SolidSyslogNullMutex` / your own); the cross-platform threaded buffer. Instance bookkeeping lives in a library-internal static pool (E11) - **`SolidSyslogPosixMessageQueueBuffer.h`** — thread-safe POSIX message queue buffer - **`SolidSyslogUdpSender.h`** — UDP transport (RFC 5426) diff --git a/SKILL.md b/SKILL.md index 457bbbe3..63723e3d 100644 --- a/SKILL.md +++ b/SKILL.md @@ -69,11 +69,11 @@ Test progression follows ZOMBIES order. - OO-in-C: structs with function pointers (vtable pattern) - Dependency injection for transport, buffering, clock, hostname, allocator - Buffer abstraction decouples formatting from sending: `SolidSyslog_Log` writes to buffer, - `SolidSyslog_Service` reads from buffer and sends. Implementations: NullBuffer (direct send, + `SolidSyslog_Service` reads from buffer and sends. Implementations: PassthroughBuffer (direct send, single-task), CircularBuffer (portable ring with caller-allocated ring memory and an injected `SolidSyslogMutex` — Posix/Windows/Null mutex shipped, integrators can plug their own RTOS primitive), PosixMessageQueueBuffer (POSIX message queue, used by the Linux Threaded example) -- Null object pattern throughout (NullBuffer is the buffer null object) +- Null object pattern throughout - All fields use uniform field object pattern with format function pointer - Optional features composed at link time — no conditional compilation - C11 static assertions via compatibility shim diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 88ea219a..a427d719 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -23,7 +23,7 @@ set(TEST_SOURCES SolidSyslogMetaSdTest.cpp SolidSyslogTimeQualitySdTest.cpp SolidSyslogOriginSdTest.cpp - SolidSyslogNullBufferTest.cpp + SolidSyslogPassthroughBufferTest.cpp SolidSyslogCircularBufferTest.cpp SolidSyslogPoolAllocatorTest.cpp SolidSyslogNullMutexTest.cpp diff --git a/Tests/SolidSyslogNullBufferTest.cpp b/Tests/SolidSyslogPassthroughBufferTest.cpp similarity index 71% rename from Tests/SolidSyslogNullBufferTest.cpp rename to Tests/SolidSyslogPassthroughBufferTest.cpp index 573f0993..f3e00c6a 100644 --- a/Tests/SolidSyslogNullBufferTest.cpp +++ b/Tests/SolidSyslogPassthroughBufferTest.cpp @@ -6,14 +6,14 @@ using namespace CososoTesting; // NOLINT(google-build-using-namespace) -- test-file scope only; brings NEVER/ONCE/TWICE/THRICE into scope for the CALLED_* // macros #include "SolidSyslogBuffer.h" -#include "SolidSyslogNullBuffer.h" +#include "SolidSyslogPassthroughBuffer.h" #include "SenderFake.h" static const char* const TEST_MESSAGE = "hello"; static const size_t TEST_MESSAGE_LEN = 5; // clang-format off -TEST_GROUP(SolidSyslogNullBuffer) +TEST_GROUP(SolidSyslogPassthroughBuffer) { struct SolidSyslogSender* fakeSender = nullptr; struct SolidSyslogBuffer* buffer = nullptr; @@ -22,12 +22,12 @@ TEST_GROUP(SolidSyslogNullBuffer) { fakeSender = SenderFake_Create(); // cppcheck-suppress unreadVariable -- used across TEST_GROUP methods; cppcheck does not model CppUTest macros - buffer = SolidSyslogNullBuffer_Create(fakeSender); + buffer = SolidSyslogPassthroughBuffer_Create(fakeSender); } void teardown() override { - SolidSyslogNullBuffer_Destroy(); + SolidSyslogPassthroughBuffer_Destroy(); SenderFake_Destroy(fakeSender); } @@ -39,41 +39,41 @@ TEST_GROUP(SolidSyslogNullBuffer) // clang-format on -TEST(SolidSyslogNullBuffer, CreateDestroyWorksWithoutCrashing) +TEST(SolidSyslogPassthroughBuffer, CreateDestroyWorksWithoutCrashing) { } -TEST(SolidSyslogNullBuffer, WriteForwardsBufferToSender) +TEST(SolidSyslogPassthroughBuffer, WriteForwardsBufferToSender) { Write(); STRCMP_EQUAL(TEST_MESSAGE, SenderFake_LastBufferAsString(fakeSender)); } -TEST(SolidSyslogNullBuffer, WriteForwardsSizeToSender) +TEST(SolidSyslogPassthroughBuffer, WriteForwardsSizeToSender) { Write(); LONGS_EQUAL(TEST_MESSAGE_LEN, SenderFake_LastSize(fakeSender)); } -TEST(SolidSyslogNullBuffer, WriteResultsInOneSend) +TEST(SolidSyslogPassthroughBuffer, WriteResultsInOneSend) { Write(); CALLED_FAKE_ON(SenderFake_Send, fakeSender, ONCE); } -TEST(SolidSyslogNullBuffer, TwoWritesResultInTwoSends) +TEST(SolidSyslogPassthroughBuffer, TwoWritesResultInTwoSends) { Write(); Write(); CALLED_FAKE_ON(SenderFake_Send, fakeSender, TWICE); } -TEST(SolidSyslogNullBuffer, NoWritesResultInNoSends) +TEST(SolidSyslogPassthroughBuffer, NoWritesResultInNoSends) { CALLED_FAKE_ON(SenderFake_Send, fakeSender, NEVER); } -TEST(SolidSyslogNullBuffer, ReadReturnsNothingToSend) +TEST(SolidSyslogPassthroughBuffer, ReadReturnsNothingToSend) { char data[512]; size_t bytesRead = 0; @@ -81,7 +81,7 @@ TEST(SolidSyslogNullBuffer, ReadReturnsNothingToSend) CHECK_FALSE(sent); } -IGNORE_TEST(SolidSyslogNullBuffer, HappyPathOnly) +IGNORE_TEST(SolidSyslogPassthroughBuffer, HappyPathOnly) { // Error handling not yet implemented — see Epic #31 diff --git a/Tests/SolidSyslogTest.cpp b/Tests/SolidSyslogTest.cpp index db495a50..6cdc31ce 100644 --- a/Tests/SolidSyslogTest.cpp +++ b/Tests/SolidSyslogTest.cpp @@ -8,7 +8,7 @@ #include "SolidSyslogMetaSd.h" #include "SolidSyslogTimeQualitySd.h" #include "SolidSyslogCircularBuffer.h" -#include "SolidSyslogNullBuffer.h" +#include "SolidSyslogPassthroughBuffer.h" #include "SolidSyslogNullMutex.h" #include "SolidSyslogNullStore.h" #include "SolidSyslogFormatter.h" @@ -366,7 +366,7 @@ TEST_GROUP(SolidSyslog) { fakeSender = SenderFake_Create(); StringFake_Reset(); - buffer = SolidSyslogNullBuffer_Create(fakeSender); + buffer = SolidSyslogPassthroughBuffer_Create(fakeSender); store = SolidSyslogNullStore_Create(); config = {buffer, nullptr, nullptr, StringFake_GetHostname, StringFake_GetAppName, StringFake_GetProcessId, store, nullptr, 0}; SolidSyslog_Create(&config); @@ -378,7 +378,7 @@ TEST_GROUP(SolidSyslog) { SolidSyslog_Destroy(); SolidSyslogNullStore_Destroy(); - SolidSyslogNullBuffer_Destroy(); + SolidSyslogPassthroughBuffer_Destroy(); SenderFake_Destroy(fakeSender); } diff --git a/docs/iec62443.md b/docs/iec62443.md index 72f58c20..620771f2 100644 --- a/docs/iec62443.md +++ b/docs/iec62443.md @@ -37,7 +37,7 @@ at the end of this document for recommended starter component sets. | CR 2.11 | Timestamps | SL1+ | Caller-injected `SolidSyslogClockFunction` — `SolidSyslogPosixClock_GetTimestamp` (POSIX) or `SolidSyslogWindowsClock_GetTimestamp` (Windows). Quality metadata via `SolidSyslogTimeQualitySd` (`tzKnown` / `isSynced` / `syncAccuracy`) | None | | CR 2.12 | Non-repudiation | SL3+ | At the wire: `SolidSyslogTlsStream` mTLS cryptographically identifies the sender (SL4 deployments). End-to-end loss detection: `SolidSyslogMetaSd` sequenceId, gap detection at the SIEM. At rest: `SolidSyslogCrc16Policy` records integrity | At-rest CRC-16 is non-cryptographic — a local attacker can rewrite records and recompute the CRC. HMAC / AES-CMAC planned — [E17 #105](https://github.com/DavidCozens/solid-syslog/issues/105) | | CR 3.9 | Audit information protection | SL2+ | In transit: `SolidSyslogTlsStream` (TLS 1.2+, AEAD ciphers, hostname verification, optional mTLS). At rest: `SolidSyslogCrc16Policy` for tamper detection | At-rest cryptographic integrity / encryption planned — [E17 #105](https://github.com/DavidCozens/solid-syslog/issues/105) | -| SR 6.1 | Audit log accessibility | SL1+ | Two wirings, two cost models. **Single-task (`SolidSyslogNullBuffer`):** `SolidSyslog_Log` performs the transport `Send` synchronously on the caller's thread — bounded by `SO_SNDTIMEO` (5 s — see [`SolidSyslogPosixTcpStream.c`](../Platform/Posix/Source/SolidSyslogPosixTcpStream.c) and [`SolidSyslogWinsockTcpStream.c`](../Platform/Windows/Source/SolidSyslogWinsockTcpStream.c)) but otherwise blocks the application until the bytes leave the socket. Suitable when the application has no real-time deadline or the transport latency is known to be sub-millisecond. **Buffered (`SolidSyslogCircularBuffer` with an injected `SolidSyslogMutex` — Posix / Windows / FreeRTOS / Null / caller-supplied RTOS primitive — or `SolidSyslogPosixMessageQueueBuffer`):** `SolidSyslog_Log` is non-blocking — it formats and enqueues, returning before any I/O. `SolidSyslog_Service`, called on the integrator's chosen thread (typically a dedicated service thread), performs the blocking transport I/O bounded by the same `SO_SNDTIMEO`. Caller picks the wiring that fits the deployment's audit-loss / latency trade-off | None | +| SR 6.1 | Audit log accessibility | SL1+ | Two wirings, two cost models. **Single-task (`SolidSyslogPassthroughBuffer`):** `SolidSyslog_Log` performs the transport `Send` synchronously on the caller's thread — bounded by `SO_SNDTIMEO` (5 s — see [`SolidSyslogPosixTcpStream.c`](../Platform/Posix/Source/SolidSyslogPosixTcpStream.c) and [`SolidSyslogWinsockTcpStream.c`](../Platform/Windows/Source/SolidSyslogWinsockTcpStream.c)) but otherwise blocks the application until the bytes leave the socket. Suitable when the application has no real-time deadline or the transport latency is known to be sub-millisecond. **Buffered (`SolidSyslogCircularBuffer` with an injected `SolidSyslogMutex` — Posix / Windows / FreeRTOS / Null / caller-supplied RTOS primitive — or `SolidSyslogPosixMessageQueueBuffer`):** `SolidSyslog_Log` is non-blocking — it formats and enqueues, returning before any I/O. `SolidSyslog_Service`, called on the integrator's chosen thread (typically a dedicated service thread), performs the blocking transport I/O bounded by the same `SO_SNDTIMEO`. Caller picks the wiring that fits the deployment's audit-loss / latency trade-off | None | | SR 6.2 | Continuous monitoring | SL2+ | TCP / TLS delivery confirmation via `SolidSyslogStreamSender`. Replay across outages via `SolidSyslogBlockStore` store-and-forward. SIEM-side gap detection via `SolidSyslogMetaSd` sequenceId | None | CR 1.5 and CR 1.8 are listed only at SL4 because IEC 62443 requires them only @@ -125,7 +125,7 @@ linked. Dead code elimination removes unused components. For resource-constrained targets, SL1 or SL2 can operate with: - No heap allocation - No POSIX dependency (provide your own clock, hostname, and transport) -- Single-task operation with `SolidSyslogNullBuffer`, *or* multi-task on +- Single-task operation with `SolidSyslogPassthroughBuffer`, *or* multi-task on bare-metal / RTOS with `SolidSyslogCircularBuffer` driven by a `SolidSyslogMutex` you supply (the library ships `SolidSyslogPosixMutex`, `SolidSyslogWindowsMutex`, and `SolidSyslogNullMutex`) @@ -179,7 +179,7 @@ above for the canonical CR-to-component mapping. - `SolidSyslogFormatter` — bounded buffer formatting. - A clock — `SolidSyslogPosixClock_GetTimestamp` (POSIX) or `SolidSyslogWindowsClock_GetTimestamp` (Windows). - A sender — `SolidSyslogUdpSender` (RFC 5426 UDP) or `SolidSyslogStreamSender` over a TCP `Stream` (RFC 6587). -- A buffer — `SolidSyslogNullBuffer` (single-task), `SolidSyslogCircularBuffer` with a `SolidSyslogMutex` (cross-platform threaded — Posix / Windows / FreeRTOS / your RTOS primitive), or `SolidSyslogPosixMessageQueueBuffer` (POSIX `mqueue`). +- A buffer — `SolidSyslogPassthroughBuffer` (single-task), `SolidSyslogCircularBuffer` with a `SolidSyslogMutex` (cross-platform threaded — Posix / Windows / FreeRTOS / your RTOS primitive), or `SolidSyslogPosixMessageQueueBuffer` (POSIX `mqueue`). - A store — `SolidSyslogNullStore` is sufficient at SL1 (no persistence required). - A security policy — `SolidSyslogNullSecurityPolicy` is sufficient. diff --git a/misra_suppressions.txt b/misra_suppressions.txt index 863a18ed..bda6faeb 100644 --- a/misra_suppressions.txt +++ b/misra_suppressions.txt @@ -29,7 +29,7 @@ misra-c2012-11.3:Core/Source/SolidSyslogFileBlockDevice.c:118 misra-c2012-11.3:Core/Source/SolidSyslogFileBlockDevice.c:146 misra-c2012-11.3:Core/Source/SolidSyslogFormatter.c:93 misra-c2012-11.3:Core/Source/SolidSyslogMetaSd.c:83 -misra-c2012-11.3:Core/Source/SolidSyslogNullBuffer.c:54 +misra-c2012-11.3:Core/Source/SolidSyslogPassthroughBuffer.c:54 misra-c2012-11.3:Core/Source/SolidSyslogOriginSd.c:84 misra-c2012-11.3:Core/Source/SolidSyslogStreamSender.c:98 misra-c2012-11.3:Core/Source/SolidSyslogStreamSender.c:110