diff --git a/Bdd/Targets/FreeRtos/main.c b/Bdd/Targets/FreeRtos/main.c index 10aed951..080f3907 100644 --- a/Bdd/Targets/FreeRtos/main.c +++ b/Bdd/Targets/FreeRtos/main.c @@ -250,6 +250,8 @@ static struct SolidSyslogResolver* resolver = NULL; static struct SolidSyslogDatagram* datagram = NULL; static struct SolidSyslogStream* tcpStream = NULL; static struct SolidSyslogSender* tcpSender = NULL; +static struct SolidSyslogSender* udpSender = NULL; +static struct SolidSyslogSender* switchingSender = NULL; static struct SolidSyslogBuffer* buffer = NULL; static struct SolidSyslogMutex* bufferMutex = NULL; @@ -631,10 +633,7 @@ static void DestroyCurrentStore(void) SolidSyslogCrc16Policy_Destroy(); SolidSyslogFatFsFile_Destroy(storeFile); } - else - { - SolidSyslogNullStore_Destroy(); - } + /* else: NullStore is shared and immutable — nothing to destroy. */ } /* Full teardown of every resource InteractiveTask allocated during Setup. @@ -651,9 +650,9 @@ static void TeardownAll(void) solidSyslogTeardown = true; solidSyslogReady = false; SolidSyslog_Destroy(); - SolidSyslogOriginSd_Destroy(); - SolidSyslogTimeQualitySd_Destroy(); - SolidSyslogMetaSd_Destroy(); + SolidSyslogOriginSd_Destroy(originSd); + SolidSyslogTimeQualitySd_Destroy(timeQualitySd); + SolidSyslogMetaSd_Destroy(metaSd); SolidSyslogStdAtomicCounter_Destroy(atomicCounter); DestroyCurrentStore(); if (fatfsMounted) @@ -674,10 +673,10 @@ static void TeardownAll(void) SolidSyslogFreeRtosMutex_Destroy(bufferMutex); SolidSyslogFreeRtosMutex_Destroy(lifecycleMutex); lifecycleMutex = NULL; - SolidSyslogSwitchingSender_Destroy(); + SolidSyslogSwitchingSender_Destroy(switchingSender); SolidSyslogStreamSender_Destroy(tcpSender); SolidSyslogFreeRtosTcpStream_Destroy(tcpStream); - SolidSyslogUdpSender_Destroy(); + SolidSyslogUdpSender_Destroy(udpSender); SolidSyslogFreeRtosDatagram_Destroy(datagram); SolidSyslogFreeRtosStaticResolver_Destroy(resolver); } @@ -788,7 +787,7 @@ static void InteractiveTask(void* argument) .Endpoint = GetEndpoint, .EndpointVersion = GetEndpointVersion, }; - struct SolidSyslogSender* udpSender = SolidSyslogUdpSender_Create(&udpConfig); + udpSender = SolidSyslogUdpSender_Create(&udpConfig); /* Plain TCP path via the new FreeRTOS Plus-TCP stream adapter. Shares the * UDP endpoint callbacks because the BDD oracle (syslog-ng) listens on the @@ -816,7 +815,8 @@ static void InteractiveTask(void* argument) .Selector = BddTargetSwitchConfig_Selector, }; BddTargetSwitchConfig_SetByName("udp"); - struct SolidSyslogSender* sender = SolidSyslogSwitchingSender_Create(&switchConfig); + switchingSender = SolidSyslogSwitchingSender_Create(&switchConfig); + struct SolidSyslogSender* sender = switchingSender; /* CircularBuffer drained by ServiceTask below, with a FreeRtosMutex * gating concurrent producers (interactive task today; multi-task @@ -832,7 +832,7 @@ static void InteractiveTask(void* argument) /* Default store is NullStore — flipped to FatFs/BlockStore by * `set store file` via RebuildWithFileStore(). */ - currentStore = SolidSyslogNullStore_Create(); + currentStore = SolidSyslogNullStore_Get(); currentStoreIsFile = false; atomicCounter = SolidSyslogStdAtomicCounter_Create(&atomicCounterStorage); diff --git a/Bdd/Targets/Linux/main.c b/Bdd/Targets/Linux/main.c index 81e998ab..235b5a1d 100644 --- a/Bdd/Targets/Linux/main.c +++ b/Bdd/Targets/Linux/main.c @@ -55,6 +55,8 @@ static SolidSyslogPosixTcpStreamStorage plainTcpStreamStorage; static struct SolidSyslogStream* plainTcpStream; static SolidSyslogStreamSenderStorage plainTcpSenderStorage; static struct SolidSyslogSender* plainTcpSender; +static struct SolidSyslogSender* udpSender; +static struct SolidSyslogSender* switchingSender; static void GetTimeQuality(struct SolidSyslogTimeQuality* timeQuality) { @@ -83,7 +85,7 @@ static struct SolidSyslogSender* CreateSender(const struct BddTargetOptions* opt udpConfig.Datagram = SolidSyslogPosixDatagram_Create(); udpConfig.Endpoint = BddTargetUdpConfig_GetEndpoint; udpConfig.EndpointVersion = BddTargetUdpConfig_GetEndpointVersion; - struct SolidSyslogSender* udpSender = SolidSyslogUdpSender_Create(&udpConfig); + udpSender = SolidSyslogUdpSender_Create(&udpConfig); plainTcpStream = SolidSyslogPosixTcpStream_Create(&plainTcpStreamStorage); static struct SolidSyslogStreamSenderConfig tcpConfig = {0}; @@ -106,7 +108,8 @@ static struct SolidSyslogSender* CreateSender(const struct BddTargetOptions* opt switchConfig.Selector = BddTargetSwitchConfig_Selector; BddTargetSwitchConfig_SetByName(options->Transport); - return SolidSyslogSwitchingSender_Create(&switchConfig); + switchingSender = SolidSyslogSwitchingSender_Create(&switchConfig); + return switchingSender; } static enum SolidSyslogDiscardPolicy MapDiscardPolicy(const char* policy) @@ -178,16 +181,16 @@ static struct SolidSyslogStore* CreateStore(const struct BddTargetOptions* optio return SolidSyslogBlockStore_Create(&storeStorage, &storeConfig); } - return SolidSyslogNullStore_Create(); + return SolidSyslogNullStore_Get(); } static void DestroySender(void) { - SolidSyslogSwitchingSender_Destroy(); + SolidSyslogSwitchingSender_Destroy(switchingSender); BddTargetTlsSender_Destroy(); SolidSyslogStreamSender_Destroy(plainTcpSender); SolidSyslogPosixTcpStream_Destroy(plainTcpStream); - SolidSyslogUdpSender_Destroy(); + SolidSyslogUdpSender_Destroy(udpSender); SolidSyslogPosixDatagram_Destroy(); SolidSyslogGetAddrInfoResolver_Destroy(); } @@ -203,10 +206,7 @@ static void DestroyStore(struct SolidSyslogStore* store, const struct BddTargetO SolidSyslogCrc16Policy_Destroy(); SolidSyslogPosixFile_Destroy(storeFile); } - else - { - SolidSyslogNullStore_Destroy(); - } + /* else: NullStore is shared and immutable — nothing to destroy. */ } int main(int argc, char* argv[]) @@ -287,9 +287,9 @@ int main(int argc, char* argv[]) pthread_join(serviceThread, NULL); SolidSyslog_Destroy(); - SolidSyslogOriginSd_Destroy(); - SolidSyslogTimeQualitySd_Destroy(); - SolidSyslogMetaSd_Destroy(); + SolidSyslogOriginSd_Destroy(originSd); + SolidSyslogTimeQualitySd_Destroy(timeQuality); + SolidSyslogMetaSd_Destroy(metaSd); SolidSyslogStdAtomicCounter_Destroy(counter); DestroyStore(store, &options); SolidSyslogPosixMessageQueueBuffer_Destroy(); diff --git a/Bdd/Targets/Windows/BddTargetWindows.c b/Bdd/Targets/Windows/BddTargetWindows.c index 682d1404..463702e8 100644 --- a/Bdd/Targets/Windows/BddTargetWindows.c +++ b/Bdd/Targets/Windows/BddTargetWindows.c @@ -76,6 +76,8 @@ static volatile bool shutdownFlag; static struct SolidSyslogStream* plainTcpStream; static struct SolidSyslogSender* plainTcpSender; static struct SolidSyslogDatagram* udpDatagram; +static struct SolidSyslogSender* udpSender; +static struct SolidSyslogSender* switchingSender; /* Block-store backing — created in CreateStore, released in DestroyStore. */ static struct SolidSyslogFile* storeFile; @@ -186,7 +188,7 @@ static struct SolidSyslogSender* CreateSender(const struct BddTargetWindowsOptio udpConfig.Datagram = udpDatagram; udpConfig.Endpoint = GetEndpoint; udpConfig.EndpointVersion = GetEndpointVersion; - struct SolidSyslogSender* udpSender = SolidSyslogUdpSender_Create(&udpConfig); + udpSender = SolidSyslogUdpSender_Create(&udpConfig); plainTcpStream = SolidSyslogWinsockTcpStream_Create(&tcpStreamStorage); static struct SolidSyslogStreamSenderConfig tcpConfig = {0}; @@ -209,16 +211,17 @@ static struct SolidSyslogSender* CreateSender(const struct BddTargetWindowsOptio switchConfig.Selector = BddTargetSwitchConfig_Selector; BddTargetSwitchConfig_SetByName(options->Transport); - return SolidSyslogSwitchingSender_Create(&switchConfig); + switchingSender = SolidSyslogSwitchingSender_Create(&switchConfig); + return switchingSender; } static void DestroySender(void) { - SolidSyslogSwitchingSender_Destroy(); + SolidSyslogSwitchingSender_Destroy(switchingSender); BddTargetTlsSender_Destroy(); SolidSyslogStreamSender_Destroy(plainTcpSender); SolidSyslogWinsockTcpStream_Destroy(plainTcpStream); - SolidSyslogUdpSender_Destroy(); + SolidSyslogUdpSender_Destroy(udpSender); SolidSyslogWinsockDatagram_Destroy(); SolidSyslogWinsockResolver_Destroy(); } @@ -252,7 +255,7 @@ static struct SolidSyslogStore* CreateStore(const struct BddTargetWindowsOptions return SolidSyslogBlockStore_Create(&storeStorage, &storeConfig); } - return SolidSyslogNullStore_Create(); + return SolidSyslogNullStore_Get(); } static void DestroyStore(struct SolidSyslogStore* store, const struct BddTargetWindowsOptions* options) @@ -266,10 +269,7 @@ static void DestroyStore(struct SolidSyslogStore* store, const struct BddTargetW SolidSyslogCrc16Policy_Destroy(); SolidSyslogWindowsFile_Destroy(storeFile); } - else - { - SolidSyslogNullStore_Destroy(); - } + /* else: NullStore is shared and immutable — nothing to destroy. */ } int BddTargetWindows_Run(int argc, char* argv[]) @@ -358,9 +358,9 @@ int BddTargetWindows_Run(int argc, char* argv[]) CloseHandle(serviceThread); SolidSyslog_Destroy(); - SolidSyslogOriginSd_Destroy(); - SolidSyslogTimeQualitySd_Destroy(); - SolidSyslogMetaSd_Destroy(); + SolidSyslogOriginSd_Destroy(originSd); + SolidSyslogTimeQualitySd_Destroy(timeQuality); + SolidSyslogMetaSd_Destroy(metaSd); SolidSyslogWindowsAtomicCounter_Destroy(counter); DestroyStore(store, &options); SolidSyslogCircularBuffer_Destroy(buffer); diff --git a/CLAUDE.md b/CLAUDE.md index 441ead56..4ace6498 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -321,6 +321,7 @@ live under `Core/Interface/`; platform-specific helpers (the `SolidSyslogPosix*` | `SolidSyslogEndpoint.h` | System setup code that supplies destination host/port (and version on changes) | `SolidSyslogEndpoint`, `SolidSyslogEndpointFunction`, `SolidSyslogEndpointVersionFunction`, `SOLIDSYSLOG_MAX_HOST_SIZE` | | `SolidSyslogSender.h` | Any code holding a sender handle | `SolidSyslogSender_Send`, `SolidSyslogSender_Disconnect` | | `SolidSyslogSenderDefinition.h` | Sender implementors (extension point) | `SolidSyslogSender` vtable struct (`Send`, `Disconnect`) | +| `SolidSyslogNullSender.h` | Any code installing a no-op sender slot (Send returns `true` to drop on the floor — keeps Store from filling with undeliverables; Disconnect is a no-op) | `SolidSyslogNullSender_Get` | | `SolidSyslogTransport.h` | Any code selecting a transport or needing default port constants | `SolidSyslogTransport` enum (`UDP`, `TCP`), `SOLIDSYSLOG_UDP_DEFAULT_PORT`, `SOLIDSYSLOG_TCP_DEFAULT_PORT` | | `SolidSyslogResolver.h` | Any code that needs to resolve a destination | `SolidSyslogResolver_Resolve(resolver, transport, host, port, *out)` | | `SolidSyslogResolverDefinition.h` | Resolver implementors (extension point) | `SolidSyslogResolver` vtable struct (`Resolve`) | @@ -331,12 +332,12 @@ live under `Core/Interface/`; platform-specific helpers (the `SolidSyslogPosix*` | `SolidSyslogStream.h` | Sender implementors using stream transport | `SolidSyslogStream_Open`, `_Send`, `_Read`, `_Close`, `SolidSyslogSsize` | | `SolidSyslogStreamDefinition.h` | Stream implementors (extension point) | `SolidSyslogStream` vtable struct (`Open`, `Send`, `Read`, `Close`) | | `SolidSyslogFreeRtosTcpStream.h` | System setup code on FreeRTOS targets using FreeRTOS-Plus-TCP for TCP | `SolidSyslogFreeRtosTcpStreamStorage`, `SOLIDSYSLOG_FREERTOSTCPSTREAM_SIZE`, `SolidSyslogFreeRtosTcpStream_Create(storage)`, `_Destroy(stream)` (wraps `FreeRTOS_socket` / `FreeRTOS_connect` / `FreeRTOS_send` / `FreeRTOS_recv` / `FreeRTOS_closesocket`; bounded blocking connect via `SO_SNDTIMEO=200ms`, cleared post-connect so Send/Read are non-blocking) | -| `SolidSyslogUdpSender.h` | System setup code using UDP transport | `SolidSyslogUdpSenderConfig` (resolver, datagram, endpoint, endpointVersion), `SolidSyslogUdpSender_Create`, `_Destroy` | +| `SolidSyslogUdpSender.h` | System setup code using UDP transport | `SolidSyslogUdpSenderConfig` (resolver, datagram, endpoint, endpointVersion), `SolidSyslogUdpSender_Create(config)`, `_Destroy(sender)`. Instance struct lives in a library-internal static pool (E11); `_Destroy` takes the handle returned by `_Create`. Pool-exhaustion and bad-config fallback is the shared `SolidSyslogNullSender`. | | `SolidSyslogStreamSender.h` | System setup code using octet-framed transport (RFC 6587) over any Stream — `SolidSyslogPosixTcpStream` (POSIX TCP), `SolidSyslogWinsockTcpStream` (Windows TCP), `SolidSyslogFreeRtosTcpStream` (FreeRTOS-Plus-TCP), `SolidSyslogTlsStream` (TLS; OpenSSL reference integration), or a caller-supplied Stream backend | `SolidSyslogStreamSenderConfig` (resolver, stream, endpoint, endpointVersion), `SolidSyslogStreamSender_Create`, `_Destroy` | -| `SolidSyslogSwitchingSender.h` | System setup code composing multiple inner senders | `SolidSyslogSwitchingSenderConfig` (senders, senderCount, selector), `SolidSyslogSwitchingSenderSelector`, `SolidSyslogSwitchingSender_Create`, `_Destroy` | +| `SolidSyslogSwitchingSender.h` | System setup code composing multiple inner senders | `SolidSyslogSwitchingSenderConfig` (senders, senderCount, selector), `SolidSyslogSwitchingSenderSelector`, `SolidSyslogSwitchingSender_Create(config)`, `_Destroy(sender)`. Instance struct lives in a library-internal static pool (E11); `_Destroy` takes the handle returned by `_Create`. Out-of-range selector and pool exhaustion both resolve to the shared `SolidSyslogNullSender`. | | `SolidSyslogBuffer.h` | Library internals consuming a buffer (Service algorithm) | `SolidSyslogBuffer_Write`, `_Read` | | `SolidSyslogBufferDefinition.h` | Buffer implementors (extension point) | `SolidSyslogBuffer` vtable struct | -| `SolidSyslogPassthroughBuffer.h` | System setup code (single-task, no buffering) | `SolidSyslogPassthroughBuffer_Create`, `_Destroy` | +| `SolidSyslogPassthroughBuffer.h` | System setup code (single-task, no buffering) | `SolidSyslogPassthroughBuffer_Create(sender)`, `_Destroy(buffer)`. Instance struct lives in a library-internal static pool (E11); `_Destroy` takes the handle returned by `_Create` to release the slot. | | `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` | @@ -347,7 +348,7 @@ live under `Core/Interface/`; platform-specific helpers (the `SolidSyslogPosix*` | `SolidSyslogFreeRtosMutex.h` | System setup code on FreeRTOS targets needing thread-safe buffering | `SolidSyslogFreeRtosMutexStorage`, `SOLIDSYSLOG_FREERTOSMUTEX_SIZE`, `SolidSyslogFreeRtosMutex_Create(storage)`, `_Destroy(mutex)` (wraps `xSemaphoreCreateMutexStatic` — caller's `StaticSemaphore_t` lives inside the injected storage; requires `configSUPPORT_STATIC_ALLOCATION=1`) | | `SolidSyslogStore.h` | Library internals consuming a store (Service algorithm) and integrator code querying capacity | `SolidSyslogStore_Write`, `_ReadNextUnsent`, `_MarkSent`, `_HasUnsent`, `_IsHalted`, `_GetTotalBytes`, `_GetUsedBytes` | | `SolidSyslogStoreDefinition.h` | Store implementors (extension point) | `SolidSyslogStore` vtable struct (`Write`, `ReadNextUnsent`, `MarkSent`, `HasUnsent`, `IsHalted`, `GetTotalBytes`, `GetUsedBytes`) | -| `SolidSyslogNullStore.h` | System setup code (no store-and-forward) | `SolidSyslogNullStore_Create`, `_Destroy` | +| `SolidSyslogNullStore.h` | System setup code (no store-and-forward) | `SolidSyslogNullStore_Get` | | `SolidSyslogFileDefinition.h` | File implementors (extension point) | `SolidSyslogFile` vtable struct | | `SolidSyslogFile.h` | Any code using the file abstraction | `SolidSyslogFile_Open`, `_Close`, `_IsOpen`, `_Read`, `_Write`, `_SeekTo`, `_Size`, `_Truncate` | | `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) | @@ -355,7 +356,7 @@ live under `Core/Interface/`; platform-specific helpers (the `SolidSyslogPosix*` | `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; 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` | +| `SolidSyslogNullSecurityPolicy.h` | System setup code (no integrity checking) | `SolidSyslogNullSecurityPolicy_Get` | | `SolidSyslogCrc16Policy.h` | System setup code using CRC-16 integrity | `SolidSyslogCrc16Policy_Create`, `_Destroy` | | `SolidSyslogCrc16.h` | Any code needing CRC-16 computation | `SolidSyslogCrc16_Compute` | | `SolidSyslogPosixFile.h` | System setup code using POSIX file I/O | `SolidSyslogPosixFile_Create`, `_Destroy` | @@ -375,14 +376,15 @@ live under `Core/Interface/`; platform-specific helpers (the `SolidSyslogPosix*` | `SolidSyslogSleep.h` | Any code passing or implementing a sleep callback | `SolidSyslogSleepFunction` typedef (used by `SolidSyslogTlsStreamConfig.sleep` for the bounded handshake retry) | | `SolidSyslogStructuredData.h` | Library internals (SD dispatch) | `SolidSyslogStructuredData_Format` (writes into `SolidSyslogFormatter*`) | | `SolidSyslogStructuredDataDefinition.h` | SD implementors (extension point) | `SolidSyslogStructuredData` vtable struct (Format takes `SolidSyslogFormatter*`) | -| `SolidSyslogMetaSd.h` | System setup code using meta SD (sequenceId, sysUpTime, language) | `SolidSyslogMetaSdConfig` (counter, getSysUpTime, getLanguage — each independently optional via NULL), `SolidSyslogSysUpTimeFunction`, `SolidSyslogMetaSd_Create`, `_Destroy` | +| `SolidSyslogNullSd.h` | Any code installing a no-op Structured Data slot in `SolidSyslogConfig.Sd[]` | `SolidSyslogNullSd_Get` | +| `SolidSyslogMetaSd.h` | System setup code using meta SD (sequenceId, sysUpTime, language) | `SolidSyslogMetaSdConfig` (counter, getSysUpTime, getLanguage — each independently optional via NULL), `SolidSyslogSysUpTimeFunction`, `SolidSyslogMetaSd_Create(config)`, `_Destroy(sd)`. Instance struct lives in a library-internal static pool (E11). Bad-config and pool exhaustion both resolve to the shared `SolidSyslogNullSd`. | | `SolidSyslogAtomicCounter.h` | Any code holding a counter handle | `SolidSyslogAtomicCounter_Increment(base)` — public vtable-dispatched call. Wrap-aware in [1, 2³¹ - 1] per RFC 5424 §7.3.1, never returns 0. | | `SolidSyslogAtomicCounterDefinition.h` | AtomicCounter implementors (extension point) | `SolidSyslogAtomicCounter` vtable struct (`Increment` function pointer) | | `SolidSyslogStdAtomicCounter.h` | System setup code on platforms with C11 `` | `SolidSyslogStdAtomicCounterStorage`, `SOLIDSYSLOG_STDATOMICCOUNTER_SIZE`, `SolidSyslogStdAtomicCounter_Create(storage)`, `_Destroy(base)`. Uses `_Atomic uint32_t` + `atomic_compare_exchange_strong_explicit` CAS loop. | | `SolidSyslogWindowsAtomicCounter.h` | System setup code on Windows targets without `` (legacy MSVC) | `SolidSyslogWindowsAtomicCounterStorage`, `SOLIDSYSLOG_WINDOWSATOMICCOUNTER_SIZE`, `SolidSyslogWindowsAtomicCounter_Create(storage)`, `_Destroy(base)`. Uses `volatile LONG` + `InterlockedCompareExchange` CAS loop. | | `SolidSyslogTimeQuality.h` | Any code providing time quality data | `SolidSyslogTimeQuality`, `SolidSyslogTimeQualityFunction`, `SOLIDSYSLOG_SYNC_ACCURACY_OMIT` | -| `SolidSyslogTimeQualitySd.h` | System setup code using timeQuality SD | `SolidSyslogTimeQualitySd_Create`, `_Destroy` | -| `SolidSyslogOriginSd.h` | System setup code using origin SD (software, swVersion, enterpriseId, ip) | `SolidSyslogOriginSdConfig` (software, swVersion, enterpriseId, getIpCount, getIpAt — each independently optional via NULL), `SolidSyslogOriginIpCountFunction`, `SolidSyslogOriginIpAtFunction`, `SolidSyslogOriginSd_Create`, `_Destroy` | +| `SolidSyslogTimeQualitySd.h` | System setup code using timeQuality SD | `SolidSyslogTimeQualitySd_Create(getTimeQuality)`, `_Destroy(sd)`. Instance struct lives in a library-internal static pool (E11). Pool exhaustion resolves to the shared `SolidSyslogNullSd`. | +| `SolidSyslogOriginSd.h` | System setup code using origin SD (software, swVersion, enterpriseId, ip) | `SolidSyslogOriginSdConfig` (software, swVersion, enterpriseId, getIpCount, getIpAt — each independently optional via NULL), `SolidSyslogOriginIpCountFunction`, `SolidSyslogOriginIpAtFunction`, `SolidSyslogOriginSd_Create(config)`, `_Destroy(sd)`. Instance struct lives in a library-internal static pool (E11); each slot carries the pre-formatted static-prefix Formatter storage. Pool exhaustion resolves to the shared `SolidSyslogNullSd`. | Most application code only needs `SolidSyslog.h` — it never sees allocators, senders, buffers, or config structs. diff --git a/Core/Interface/SolidSyslogMetaSd.h b/Core/Interface/SolidSyslogMetaSd.h index 2f690271..cd83dbea 100644 --- a/Core/Interface/SolidSyslogMetaSd.h +++ b/Core/Interface/SolidSyslogMetaSd.h @@ -21,7 +21,7 @@ EXTERN_C_BEGIN }; struct SolidSyslogStructuredData* SolidSyslogMetaSd_Create(const struct SolidSyslogMetaSdConfig* config); - void SolidSyslogMetaSd_Destroy(void); + void SolidSyslogMetaSd_Destroy(struct SolidSyslogStructuredData * base); EXTERN_C_END diff --git a/Core/Interface/SolidSyslogNullSd.h b/Core/Interface/SolidSyslogNullSd.h new file mode 100644 index 00000000..e2f59117 --- /dev/null +++ b/Core/Interface/SolidSyslogNullSd.h @@ -0,0 +1,12 @@ +#ifndef SOLIDSYSLOGNULLSD_H +#define SOLIDSYSLOGNULLSD_H + +#include "ExternC.h" + +EXTERN_C_BEGIN + + struct SolidSyslogStructuredData* SolidSyslogNullSd_Get(void); + +EXTERN_C_END + +#endif /* SOLIDSYSLOGNULLSD_H */ diff --git a/Core/Interface/SolidSyslogNullSecurityPolicy.h b/Core/Interface/SolidSyslogNullSecurityPolicy.h index 714b216d..554f85b8 100644 --- a/Core/Interface/SolidSyslogNullSecurityPolicy.h +++ b/Core/Interface/SolidSyslogNullSecurityPolicy.h @@ -5,8 +5,7 @@ EXTERN_C_BEGIN - struct SolidSyslogSecurityPolicy* SolidSyslogNullSecurityPolicy_Create(void); - void SolidSyslogNullSecurityPolicy_Destroy(void); + struct SolidSyslogSecurityPolicy* SolidSyslogNullSecurityPolicy_Get(void); EXTERN_C_END diff --git a/Core/Interface/SolidSyslogNullSender.h b/Core/Interface/SolidSyslogNullSender.h new file mode 100644 index 00000000..eb3e874c --- /dev/null +++ b/Core/Interface/SolidSyslogNullSender.h @@ -0,0 +1,12 @@ +#ifndef SOLIDSYSLOGNULLSENDER_H +#define SOLIDSYSLOGNULLSENDER_H + +#include "ExternC.h" + +EXTERN_C_BEGIN + + struct SolidSyslogSender* SolidSyslogNullSender_Get(void); + +EXTERN_C_END + +#endif /* SOLIDSYSLOGNULLSENDER_H */ diff --git a/Core/Interface/SolidSyslogNullStore.h b/Core/Interface/SolidSyslogNullStore.h index 7979c477..662cf365 100644 --- a/Core/Interface/SolidSyslogNullStore.h +++ b/Core/Interface/SolidSyslogNullStore.h @@ -5,8 +5,7 @@ EXTERN_C_BEGIN - struct SolidSyslogStore* SolidSyslogNullStore_Create(void); - void SolidSyslogNullStore_Destroy(void); + struct SolidSyslogStore* SolidSyslogNullStore_Get(void); EXTERN_C_END diff --git a/Core/Interface/SolidSyslogOriginSd.h b/Core/Interface/SolidSyslogOriginSd.h index daeb7971..8a170dfd 100644 --- a/Core/Interface/SolidSyslogOriginSd.h +++ b/Core/Interface/SolidSyslogOriginSd.h @@ -23,7 +23,7 @@ EXTERN_C_BEGIN }; struct SolidSyslogStructuredData* SolidSyslogOriginSd_Create(const struct SolidSyslogOriginSdConfig* config); - void SolidSyslogOriginSd_Destroy(void); + void SolidSyslogOriginSd_Destroy(struct SolidSyslogStructuredData * base); EXTERN_C_END diff --git a/Core/Interface/SolidSyslogPassthroughBuffer.h b/Core/Interface/SolidSyslogPassthroughBuffer.h index c5efa7b8..57803af3 100644 --- a/Core/Interface/SolidSyslogPassthroughBuffer.h +++ b/Core/Interface/SolidSyslogPassthroughBuffer.h @@ -6,9 +6,10 @@ EXTERN_C_BEGIN struct SolidSyslogSender; + struct SolidSyslogBuffer; struct SolidSyslogBuffer* SolidSyslogPassthroughBuffer_Create(struct SolidSyslogSender * sender); - void SolidSyslogPassthroughBuffer_Destroy(void); + void SolidSyslogPassthroughBuffer_Destroy(struct SolidSyslogBuffer * base); EXTERN_C_END diff --git a/Core/Interface/SolidSyslogSwitchingSender.h b/Core/Interface/SolidSyslogSwitchingSender.h index d2860d74..01ed8057 100644 --- a/Core/Interface/SolidSyslogSwitchingSender.h +++ b/Core/Interface/SolidSyslogSwitchingSender.h @@ -8,6 +8,8 @@ EXTERN_C_BEGIN + struct SolidSyslogSender; + typedef uint8_t (*SolidSyslogSwitchingSenderSelector)(void); // NOLINT(modernize-redundant-void-arg) -- C idiom struct SolidSyslogSwitchingSenderConfig @@ -18,7 +20,7 @@ EXTERN_C_BEGIN }; struct SolidSyslogSender* SolidSyslogSwitchingSender_Create(const struct SolidSyslogSwitchingSenderConfig* config); - void SolidSyslogSwitchingSender_Destroy(void); + void SolidSyslogSwitchingSender_Destroy(struct SolidSyslogSender * base); EXTERN_C_END diff --git a/Core/Interface/SolidSyslogTimeQualitySd.h b/Core/Interface/SolidSyslogTimeQualitySd.h index 97b032e6..2cedfd8c 100644 --- a/Core/Interface/SolidSyslogTimeQualitySd.h +++ b/Core/Interface/SolidSyslogTimeQualitySd.h @@ -9,7 +9,7 @@ EXTERN_C_BEGIN struct SolidSyslogStructuredData; struct SolidSyslogStructuredData* SolidSyslogTimeQualitySd_Create(SolidSyslogTimeQualityFunction getTimeQuality); - void SolidSyslogTimeQualitySd_Destroy(void); + void SolidSyslogTimeQualitySd_Destroy(struct SolidSyslogStructuredData * base); EXTERN_C_END diff --git a/Core/Interface/SolidSyslogTunablesDefaults.h b/Core/Interface/SolidSyslogTunablesDefaults.h index 930efa3e..ec75d165 100644 --- a/Core/Interface/SolidSyslogTunablesDefaults.h +++ b/Core/Interface/SolidSyslogTunablesDefaults.h @@ -50,4 +50,117 @@ #error "SOLIDSYSLOG_CIRCULAR_BUFFER_POOL_SIZE must be >= 1" #endif +/* + * Number of SolidSyslogPassthroughBuffer instances the library's + * internal static pool can simultaneously hold. Each instance is + * tiny (vtable + a Sender pointer). + * + * PassthroughBuffer is the single-task "direct-send, no buffering" + * configuration — every integrator typically creates one. Default 1. + * Bump via SOLIDSYSLOG_USER_TUNABLES_FILE if more than one process or + * task needs its own passthrough Buffer instance. + * + * Floor: 1. Sub-floor values rejected at compile time. + */ +#ifndef SOLIDSYSLOG_PASSTHROUGH_BUFFER_POOL_SIZE +/* NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -- macro form required for preprocessor visibility (floor #if) and C array-size const-expr. */ +#define SOLIDSYSLOG_PASSTHROUGH_BUFFER_POOL_SIZE 1U +#endif + +#if SOLIDSYSLOG_PASSTHROUGH_BUFFER_POOL_SIZE < 1 +#error "SOLIDSYSLOG_PASSTHROUGH_BUFFER_POOL_SIZE must be >= 1" +#endif + +/* + * Number of SolidSyslogUdpSender instances the library's internal + * static pool can simultaneously hold. Each instance carries its + * config (resolver/datagram/endpoint pointers), the resolved address + * storage, and connection state. + * + * Default 1 — almost all integrators wire a single UDP sender into + * either SolidSyslogConfig directly or as one branch of a + * SwitchingSender. Bump via SOLIDSYSLOG_USER_TUNABLES_FILE if more + * than one is genuinely needed. + * + * Floor: 1. Sub-floor values rejected at compile time. + */ +#ifndef SOLIDSYSLOG_UDP_SENDER_POOL_SIZE +/* NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -- macro form required for preprocessor visibility (floor #if) and C array-size const-expr. */ +#define SOLIDSYSLOG_UDP_SENDER_POOL_SIZE 1U +#endif + +#if SOLIDSYSLOG_UDP_SENDER_POOL_SIZE < 1 +#error "SOLIDSYSLOG_UDP_SENDER_POOL_SIZE must be >= 1" +#endif + +/* + * Number of SolidSyslogSwitchingSender instances the library's + * internal static pool can simultaneously hold. + * + * Default 1 — a SwitchingSender wraps several inner senders, so one + * per process is the typical pattern. Bump via SOLIDSYSLOG_USER_TUNABLES_FILE + * if more than one is genuinely needed. + * + * Floor: 1. Sub-floor values rejected at compile time. + */ +#ifndef SOLIDSYSLOG_SWITCHING_SENDER_POOL_SIZE +/* NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -- macro form required for preprocessor visibility (floor #if) and C array-size const-expr. */ +#define SOLIDSYSLOG_SWITCHING_SENDER_POOL_SIZE 1U +#endif + +#if SOLIDSYSLOG_SWITCHING_SENDER_POOL_SIZE < 1 +#error "SOLIDSYSLOG_SWITCHING_SENDER_POOL_SIZE must be >= 1" +#endif + +/* + * Number of SolidSyslogMetaSd instances the library's internal + * static pool can simultaneously hold. Default 1 — meta SD is typically + * wired into SolidSyslogConfig.Sd[] once per process. + * + * Floor: 1. Sub-floor values rejected at compile time. + */ +#ifndef SOLIDSYSLOG_META_SD_POOL_SIZE +/* NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -- macro form required for preprocessor visibility (floor #if) and C array-size const-expr. */ +#define SOLIDSYSLOG_META_SD_POOL_SIZE 1U +#endif + +#if SOLIDSYSLOG_META_SD_POOL_SIZE < 1 +#error "SOLIDSYSLOG_META_SD_POOL_SIZE must be >= 1" +#endif + +/* + * Number of SolidSyslogTimeQualitySd instances the library's internal + * static pool can simultaneously hold. Default 1. + * + * Floor: 1. Sub-floor values rejected at compile time. + */ +#ifndef SOLIDSYSLOG_TIME_QUALITY_SD_POOL_SIZE +/* NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -- macro form required for preprocessor visibility (floor #if) and C array-size const-expr. */ +#define SOLIDSYSLOG_TIME_QUALITY_SD_POOL_SIZE 1U +#endif + +#if SOLIDSYSLOG_TIME_QUALITY_SD_POOL_SIZE < 1 +#error "SOLIDSYSLOG_TIME_QUALITY_SD_POOL_SIZE must be >= 1" +#endif + +/* + * Number of SolidSyslogOriginSd instances the library's internal + * static pool can simultaneously hold. Each instance carries the + * pre-formatted static-prefix Formatter storage (software, swVersion, + * enterpriseId) so the per-message Format only splices in the IP + * params. Larger per-slot footprint than the other SDs. + * + * Default 1. + * + * Floor: 1. Sub-floor values rejected at compile time. + */ +#ifndef SOLIDSYSLOG_ORIGIN_SD_POOL_SIZE +/* NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -- macro form required for preprocessor visibility (floor #if) and C array-size const-expr. */ +#define SOLIDSYSLOG_ORIGIN_SD_POOL_SIZE 1U +#endif + +#if SOLIDSYSLOG_ORIGIN_SD_POOL_SIZE < 1 +#error "SOLIDSYSLOG_ORIGIN_SD_POOL_SIZE must be >= 1" +#endif + #endif /* SOLIDSYSLOG_TUNABLES_DEFAULTS_H */ diff --git a/Core/Interface/SolidSyslogUdpSender.h b/Core/Interface/SolidSyslogUdpSender.h index a6c800a9..c9520f62 100644 --- a/Core/Interface/SolidSyslogUdpSender.h +++ b/Core/Interface/SolidSyslogUdpSender.h @@ -6,6 +6,8 @@ EXTERN_C_BEGIN + struct SolidSyslogSender; + struct SolidSyslogUdpSenderConfig { struct SolidSyslogResolver* Resolver; @@ -15,7 +17,7 @@ EXTERN_C_BEGIN }; struct SolidSyslogSender* SolidSyslogUdpSender_Create(const struct SolidSyslogUdpSenderConfig* config); - void SolidSyslogUdpSender_Destroy(void); + void SolidSyslogUdpSender_Destroy(struct SolidSyslogSender * base); EXTERN_C_END diff --git a/Core/Source/CMakeLists.txt b/Core/Source/CMakeLists.txt index 377305ff..51643cec 100644 --- a/Core/Source/CMakeLists.txt +++ b/Core/Source/CMakeLists.txt @@ -3,17 +3,22 @@ set(SOURCES SolidSyslogError.c SolidSyslogConfigLock.c SolidSyslogSwitchingSender.c + SolidSyslogSwitchingSenderStatic.c SolidSyslogBuffer.c SolidSyslogFormatter.c SolidSyslogMetaSd.c + SolidSyslogMetaSdStatic.c SolidSyslogPassthroughBuffer.c + SolidSyslogPassthroughBufferStatic.c SolidSyslogCircularBuffer.c SolidSyslogCircularBufferStatic.c SolidSyslogPoolAllocator.c SolidSyslogMutex.c SolidSyslogNullMutex.c SolidSyslogSender.c + SolidSyslogNullSender.c SolidSyslogStore.c + SolidSyslogNullSd.c SolidSyslogNullStore.c SolidSyslogNullSecurityPolicy.c SolidSyslogCrc16.c @@ -21,7 +26,9 @@ set(SOURCES SolidSyslogStream.c SolidSyslogStructuredData.c SolidSyslogTimeQualitySd.c + SolidSyslogTimeQualitySdStatic.c SolidSyslogOriginSd.c + SolidSyslogOriginSdStatic.c SolidSyslogFile.c SolidSyslogBlockStore.c RecordStore.c @@ -45,6 +52,7 @@ if(SOLIDSYSLOG_POSIX OR SOLIDSYSLOG_WINSOCK OR DEFINED ENV{FREERTOS_KERNEL_PATH} SolidSyslogResolver.c SolidSyslogDatagram.c SolidSyslogUdpSender.c + SolidSyslogUdpSenderStatic.c SolidSyslogStreamSender.c ) endif() diff --git a/Core/Source/SolidSyslogBlockStore.c b/Core/Source/SolidSyslogBlockStore.c index fced2459..2c38f459 100644 --- a/Core/Source/SolidSyslogBlockStore.c +++ b/Core/Source/SolidSyslogBlockStore.c @@ -90,7 +90,7 @@ static inline struct SolidSyslogSecurityPolicy* BlockStore_ResolveSecurityPolicy if ((resolved == NULL) || (resolved->IntegritySize > SOLIDSYSLOG_MAX_INTEGRITY_SIZE)) { - resolved = SolidSyslogNullSecurityPolicy_Create(); + resolved = SolidSyslogNullSecurityPolicy_Get(); } return resolved; diff --git a/Core/Source/SolidSyslogCircularBufferStatic.c b/Core/Source/SolidSyslogCircularBufferStatic.c index b76b3fbf..3973e2c7 100644 --- a/Core/Source/SolidSyslogCircularBufferStatic.c +++ b/Core/Source/SolidSyslogCircularBufferStatic.c @@ -19,10 +19,13 @@ static void Fallback_Write(struct SolidSyslogBuffer* base, const void* data, siz static size_t CircularBuffer_IndexFromHandle(const struct SolidSyslogBuffer* base); static void CircularBuffer_CleanupAtIndex(size_t index, void* context); -static bool InUse[SOLIDSYSLOG_CIRCULAR_BUFFER_POOL_SIZE]; -static struct SolidSyslogCircularBuffer Pool[SOLIDSYSLOG_CIRCULAR_BUFFER_POOL_SIZE]; +static bool CircularBuffer_InUse[SOLIDSYSLOG_CIRCULAR_BUFFER_POOL_SIZE]; +static struct SolidSyslogCircularBuffer CircularBuffer_Pool[SOLIDSYSLOG_CIRCULAR_BUFFER_POOL_SIZE]; static struct SolidSyslogBuffer Fallback = {Fallback_Write, Fallback_Read}; -static struct SolidSyslogPoolAllocator Allocator = {InUse, SOLIDSYSLOG_CIRCULAR_BUFFER_POOL_SIZE}; +static struct SolidSyslogPoolAllocator CircularBuffer_Allocator = { + CircularBuffer_InUse, + SOLIDSYSLOG_CIRCULAR_BUFFER_POOL_SIZE +}; struct SolidSyslogBuffer* SolidSyslogCircularBuffer_Create( struct SolidSyslogMutex* mutex, @@ -30,12 +33,12 @@ struct SolidSyslogBuffer* SolidSyslogCircularBuffer_Create( size_t ringBytes ) { - size_t index = SolidSyslogPoolAllocator_AcquireFirstFree(&Allocator); + size_t index = SolidSyslogPoolAllocator_AcquireFirstFree(&CircularBuffer_Allocator); struct SolidSyslogBuffer* handle = &Fallback; - if (SolidSyslogPoolAllocator_IndexIsValid(&Allocator, index)) + if (SolidSyslogPoolAllocator_IndexIsValid(&CircularBuffer_Allocator, index)) { - CircularBuffer_Initialise(&Pool[index].Base, mutex, ring, ringBytes); - handle = &Pool[index].Base; + CircularBuffer_Initialise(&CircularBuffer_Pool[index].Base, mutex, ring, ringBytes); + handle = &CircularBuffer_Pool[index].Base; } else { @@ -47,8 +50,9 @@ struct SolidSyslogBuffer* SolidSyslogCircularBuffer_Create( void SolidSyslogCircularBuffer_Destroy(struct SolidSyslogBuffer* base) { size_t index = CircularBuffer_IndexFromHandle(base); - bool released = SolidSyslogPoolAllocator_IndexIsValid(&Allocator, index) && - SolidSyslogPoolAllocator_FreeIfInUse(&Allocator, index, CircularBuffer_CleanupAtIndex, NULL); + bool released = + SolidSyslogPoolAllocator_IndexIsValid(&CircularBuffer_Allocator, index) && + SolidSyslogPoolAllocator_FreeIfInUse(&CircularBuffer_Allocator, index, CircularBuffer_CleanupAtIndex, NULL); if (!released) { SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_WARNING, SOLIDSYSLOG_ERROR_MSG_CIRCULARBUFFER_UNKNOWN_DESTROY); @@ -60,7 +64,7 @@ static size_t CircularBuffer_IndexFromHandle(const struct SolidSyslogBuffer* bas size_t result = SOLIDSYSLOG_CIRCULAR_BUFFER_POOL_SIZE; for (size_t poolIndex = 0; poolIndex < SOLIDSYSLOG_CIRCULAR_BUFFER_POOL_SIZE; poolIndex++) { - if (base == &Pool[poolIndex].Base) + if (base == &CircularBuffer_Pool[poolIndex].Base) { result = poolIndex; break; @@ -72,7 +76,7 @@ static size_t CircularBuffer_IndexFromHandle(const struct SolidSyslogBuffer* bas static void CircularBuffer_CleanupAtIndex(size_t index, void* context) { (void) context; - CircularBuffer_Cleanup(&Pool[index].Base); + CircularBuffer_Cleanup(&CircularBuffer_Pool[index].Base); } static bool Fallback_Read(struct SolidSyslogBuffer* base, void* data, size_t maxSize, size_t* bytesRead) diff --git a/Core/Source/SolidSyslogErrorMessages.h b/Core/Source/SolidSyslogErrorMessages.h index e2529b20..2236492e 100644 --- a/Core/Source/SolidSyslogErrorMessages.h +++ b/Core/Source/SolidSyslogErrorMessages.h @@ -17,9 +17,39 @@ #define SOLIDSYSLOG_ERROR_MSG_UDPSENDER_CREATE_NULL_DATAGRAM "SolidSyslogUdpSender_Create config.Datagram is NULL" #define SOLIDSYSLOG_ERROR_MSG_UDPSENDER_CREATE_NULL_ENDPOINT "SolidSyslogUdpSender_Create config.Endpoint is NULL" #define SOLIDSYSLOG_ERROR_MSG_UDPSENDER_SEND_NULL_BUFFER "SolidSyslogUdpSender_Send called with NULL buffer" +#define SOLIDSYSLOG_ERROR_MSG_UDPSENDER_POOL_EXHAUSTED \ + "SolidSyslogUdpSender_Create pool exhausted; returning fallback sender" +#define SOLIDSYSLOG_ERROR_MSG_UDPSENDER_UNKNOWN_DESTROY \ + "SolidSyslogUdpSender_Destroy called with a handle not issued by this pool" +#define SOLIDSYSLOG_ERROR_MSG_SWITCHINGSENDER_CREATE_NULL_CONFIG \ + "SolidSyslogSwitchingSender_Create called with NULL config" +#define SOLIDSYSLOG_ERROR_MSG_SWITCHINGSENDER_CREATE_NULL_SENDERS \ + "SolidSyslogSwitchingSender_Create config.Senders is NULL" +#define SOLIDSYSLOG_ERROR_MSG_SWITCHINGSENDER_CREATE_NULL_SELECTOR \ + "SolidSyslogSwitchingSender_Create config.Selector is NULL" +#define SOLIDSYSLOG_ERROR_MSG_SWITCHINGSENDER_POOL_EXHAUSTED \ + "SolidSyslogSwitchingSender_Create pool exhausted; returning fallback sender" +#define SOLIDSYSLOG_ERROR_MSG_SWITCHINGSENDER_UNKNOWN_DESTROY \ + "SolidSyslogSwitchingSender_Destroy called with a handle not issued by this pool" +#define SOLIDSYSLOG_ERROR_MSG_METASD_POOL_EXHAUSTED "SolidSyslogMetaSd_Create pool exhausted; returning fallback SD" +#define SOLIDSYSLOG_ERROR_MSG_METASD_UNKNOWN_DESTROY \ + "SolidSyslogMetaSd_Destroy called with a handle not issued by this pool" +#define SOLIDSYSLOG_ERROR_MSG_TIMEQUALITYSD_CREATE_NULL_CALLBACK \ + "SolidSyslogTimeQualitySd_Create called with NULL getTimeQuality" +#define SOLIDSYSLOG_ERROR_MSG_TIMEQUALITYSD_POOL_EXHAUSTED \ + "SolidSyslogTimeQualitySd_Create pool exhausted; returning fallback SD" +#define SOLIDSYSLOG_ERROR_MSG_TIMEQUALITYSD_UNKNOWN_DESTROY \ + "SolidSyslogTimeQualitySd_Destroy called with a handle not issued by this pool" +#define SOLIDSYSLOG_ERROR_MSG_ORIGINSD_POOL_EXHAUSTED "SolidSyslogOriginSd_Create pool exhausted; returning fallback SD" +#define SOLIDSYSLOG_ERROR_MSG_ORIGINSD_UNKNOWN_DESTROY \ + "SolidSyslogOriginSd_Destroy called with a handle not issued by this pool" #define SOLIDSYSLOG_ERROR_MSG_CIRCULARBUFFER_POOL_EXHAUSTED \ "SolidSyslogCircularBuffer_Create pool exhausted; returning fallback buffer" #define SOLIDSYSLOG_ERROR_MSG_CIRCULARBUFFER_UNKNOWN_DESTROY \ "SolidSyslogCircularBuffer_Destroy called with a handle not issued by this pool" +#define SOLIDSYSLOG_ERROR_MSG_PASSTHROUGHBUFFER_POOL_EXHAUSTED \ + "SolidSyslogPassthroughBuffer_Create pool exhausted; returning fallback buffer" +#define SOLIDSYSLOG_ERROR_MSG_PASSTHROUGHBUFFER_UNKNOWN_DESTROY \ + "SolidSyslogPassthroughBuffer_Destroy called with a handle not issued by this pool" #endif /* SOLIDSYSLOGERRORMESSAGES_H */ diff --git a/Core/Source/SolidSyslogMetaSd.c b/Core/Source/SolidSyslogMetaSd.c index 19c63e82..0030ee98 100644 --- a/Core/Source/SolidSyslogMetaSd.c +++ b/Core/Source/SolidSyslogMetaSd.c @@ -3,67 +3,35 @@ #include #include "SolidSyslogAtomicCounter.h" -#include "SolidSyslogError.h" -#include "SolidSyslogErrorMessages.h" #include "SolidSyslogFormatter.h" -#include "SolidSyslogPrival.h" +#include "SolidSyslogMetaSdPrivate.h" #include "SolidSyslogStructuredDataDefinition.h" struct SolidSyslogFormatter; -struct SolidSyslogMetaSd -{ - struct SolidSyslogStructuredData Base; - struct SolidSyslogAtomicCounter* Counter; - SolidSyslogSysUpTimeFunction GetSysUpTime; - SolidSyslogStringFunction GetLanguage; -}; - static void MetaSd_Format(struct SolidSyslogStructuredData* base, struct SolidSyslogFormatter* formatter); -static void MetaSd_NilMetaSdFormat(struct SolidSyslogStructuredData* base, struct SolidSyslogFormatter* formatter); static inline struct SolidSyslogMetaSd* MetaSd_SelfFromBase(struct SolidSyslogStructuredData* base); static inline void MetaSd_EmitSequenceId(struct SolidSyslogMetaSd* self, struct SolidSyslogFormatter* formatter); static inline void MetaSd_EmitSysUpTime(struct SolidSyslogMetaSd* self, struct SolidSyslogFormatter* formatter); static inline void MetaSd_EmitLanguage(struct SolidSyslogMetaSd* self, struct SolidSyslogFormatter* formatter); -static struct SolidSyslogMetaSd MetaSd_Instance; - -struct SolidSyslogStructuredData* SolidSyslogMetaSd_Create(const struct SolidSyslogMetaSdConfig* config) -{ - static struct SolidSyslogStructuredData nilMetaSd = {.Format = MetaSd_NilMetaSdFormat}; - struct SolidSyslogStructuredData* result = &nilMetaSd; - if (config == NULL) - { - SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_WARNING, SOLIDSYSLOG_ERROR_MSG_METASD_CREATE_NULL_CONFIG); - } - else if (config->Counter == NULL) - { - SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_WARNING, SOLIDSYSLOG_ERROR_MSG_METASD_CREATE_NULL_COUNTER); - } - else - { - MetaSd_Instance.Base.Format = MetaSd_Format; - MetaSd_Instance.Counter = config->Counter; - MetaSd_Instance.GetSysUpTime = config->GetSysUpTime; - MetaSd_Instance.GetLanguage = config->GetLanguage; - result = &MetaSd_Instance.Base; - } - return result; -} - -void SolidSyslogMetaSd_Destroy(void) +void MetaSd_Initialise(struct SolidSyslogStructuredData* base, const struct SolidSyslogMetaSdConfig* config) { - MetaSd_Instance.Base.Format = NULL; - MetaSd_Instance.Counter = NULL; - MetaSd_Instance.GetSysUpTime = NULL; - MetaSd_Instance.GetLanguage = NULL; + struct SolidSyslogMetaSd* self = MetaSd_SelfFromBase(base); + self->Base.Format = MetaSd_Format; + self->Counter = config->Counter; + self->GetSysUpTime = config->GetSysUpTime; + self->GetLanguage = config->GetLanguage; } -static void MetaSd_NilMetaSdFormat(struct SolidSyslogStructuredData* base, struct SolidSyslogFormatter* formatter) +void MetaSd_Cleanup(struct SolidSyslogStructuredData* base) { - (void) base; - (void) formatter; + struct SolidSyslogMetaSd* self = MetaSd_SelfFromBase(base); + self->Base.Format = NULL; + self->Counter = NULL; + self->GetSysUpTime = NULL; + self->GetLanguage = NULL; } static void MetaSd_Format(struct SolidSyslogStructuredData* base, struct SolidSyslogFormatter* formatter) diff --git a/Core/Source/SolidSyslogMetaSdPrivate.h b/Core/Source/SolidSyslogMetaSdPrivate.h new file mode 100644 index 00000000..c7695997 --- /dev/null +++ b/Core/Source/SolidSyslogMetaSdPrivate.h @@ -0,0 +1,21 @@ +#ifndef SOLIDSYSLOGMETASDPRIVATE_H +#define SOLIDSYSLOGMETASDPRIVATE_H + +#include "SolidSyslogMetaSd.h" +#include "SolidSyslogStringFunction.h" +#include "SolidSyslogStructuredDataDefinition.h" + +struct SolidSyslogAtomicCounter; + +struct SolidSyslogMetaSd +{ + struct SolidSyslogStructuredData Base; + struct SolidSyslogAtomicCounter* Counter; + SolidSyslogSysUpTimeFunction GetSysUpTime; + SolidSyslogStringFunction GetLanguage; +}; + +void MetaSd_Initialise(struct SolidSyslogStructuredData* base, const struct SolidSyslogMetaSdConfig* config); +void MetaSd_Cleanup(struct SolidSyslogStructuredData* base); + +#endif /* SOLIDSYSLOGMETASDPRIVATE_H */ diff --git a/Core/Source/SolidSyslogMetaSdStatic.c b/Core/Source/SolidSyslogMetaSdStatic.c new file mode 100644 index 00000000..889c3697 --- /dev/null +++ b/Core/Source/SolidSyslogMetaSdStatic.c @@ -0,0 +1,90 @@ +#include "SolidSyslogMetaSd.h" + +#include +#include + +#include "SolidSyslogError.h" +#include "SolidSyslogErrorMessages.h" +#include "SolidSyslogMetaSdPrivate.h" +#include "SolidSyslogNullSd.h" +#include "SolidSyslogPoolAllocator.h" +#include "SolidSyslogPrival.h" +#include "SolidSyslogTunables.h" + +struct SolidSyslogStructuredData; + +static bool MetaSd_IsValidConfig(const struct SolidSyslogMetaSdConfig* config); +static size_t MetaSd_IndexFromHandle(const struct SolidSyslogStructuredData* base); +static void MetaSd_CleanupAtIndex(size_t index, void* context); + +static bool MetaSd_InUse[SOLIDSYSLOG_META_SD_POOL_SIZE]; +static struct SolidSyslogMetaSd MetaSd_Pool[SOLIDSYSLOG_META_SD_POOL_SIZE]; +static struct SolidSyslogPoolAllocator MetaSd_Allocator = {MetaSd_InUse, SOLIDSYSLOG_META_SD_POOL_SIZE}; + +struct SolidSyslogStructuredData* SolidSyslogMetaSd_Create(const struct SolidSyslogMetaSdConfig* config) +{ + struct SolidSyslogStructuredData* result = SolidSyslogNullSd_Get(); + if (MetaSd_IsValidConfig(config)) + { + size_t index = SolidSyslogPoolAllocator_AcquireFirstFree(&MetaSd_Allocator); + if (SolidSyslogPoolAllocator_IndexIsValid(&MetaSd_Allocator, index)) + { + MetaSd_Initialise(&MetaSd_Pool[index].Base, config); + result = &MetaSd_Pool[index].Base; + } + else + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_METASD_POOL_EXHAUSTED); + } + } + return result; +} + +void SolidSyslogMetaSd_Destroy(struct SolidSyslogStructuredData* base) +{ + size_t index = MetaSd_IndexFromHandle(base); + bool released = SolidSyslogPoolAllocator_IndexIsValid(&MetaSd_Allocator, index) && + SolidSyslogPoolAllocator_FreeIfInUse(&MetaSd_Allocator, index, MetaSd_CleanupAtIndex, NULL); + if (!released) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_WARNING, SOLIDSYSLOG_ERROR_MSG_METASD_UNKNOWN_DESTROY); + } +} + +static bool MetaSd_IsValidConfig(const struct SolidSyslogMetaSdConfig* config) +{ + bool valid = false; + if (config == NULL) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_WARNING, SOLIDSYSLOG_ERROR_MSG_METASD_CREATE_NULL_CONFIG); + } + else if (config->Counter == NULL) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_WARNING, SOLIDSYSLOG_ERROR_MSG_METASD_CREATE_NULL_COUNTER); + } + else + { + valid = true; + } + return valid; +} + +static size_t MetaSd_IndexFromHandle(const struct SolidSyslogStructuredData* base) +{ + size_t result = SOLIDSYSLOG_META_SD_POOL_SIZE; + for (size_t poolIndex = 0; poolIndex < SOLIDSYSLOG_META_SD_POOL_SIZE; poolIndex++) + { + if (base == &MetaSd_Pool[poolIndex].Base) + { + result = poolIndex; + break; + } + } + return result; +} + +static void MetaSd_CleanupAtIndex(size_t index, void* context) +{ + (void) context; + MetaSd_Cleanup(&MetaSd_Pool[index].Base); +} diff --git a/Core/Source/SolidSyslogNullSd.c b/Core/Source/SolidSyslogNullSd.c new file mode 100644 index 00000000..e9d72ff1 --- /dev/null +++ b/Core/Source/SolidSyslogNullSd.c @@ -0,0 +1,20 @@ +#include "SolidSyslogNullSd.h" + +#include "SolidSyslogStructuredDataDefinition.h" + +struct SolidSyslogFormatter; + +static void NullSd_Format(struct SolidSyslogStructuredData* base, struct SolidSyslogFormatter* formatter); + +static struct SolidSyslogStructuredData instance = {.Format = NullSd_Format}; + +struct SolidSyslogStructuredData* SolidSyslogNullSd_Get(void) +{ + return &instance; +} + +static void NullSd_Format(struct SolidSyslogStructuredData* base, struct SolidSyslogFormatter* formatter) +{ + (void) base; + (void) formatter; +} diff --git a/Core/Source/SolidSyslogNullSecurityPolicy.c b/Core/Source/SolidSyslogNullSecurityPolicy.c index a569da5a..a7529277 100644 --- a/Core/Source/SolidSyslogNullSecurityPolicy.c +++ b/Core/Source/SolidSyslogNullSecurityPolicy.c @@ -21,16 +21,13 @@ static bool NullSecurityPolicy_NullVerifyIntegrity(const uint8_t* data, uint16_t return true; } -struct SolidSyslogSecurityPolicy* SolidSyslogNullSecurityPolicy_Create(void) -{ - static struct SolidSyslogSecurityPolicy instance = { - 0, - NullSecurityPolicy_NullComputeIntegrity, - NullSecurityPolicy_NullVerifyIntegrity, - }; - return &instance; -} +static struct SolidSyslogSecurityPolicy instance = { + 0, + NullSecurityPolicy_NullComputeIntegrity, + NullSecurityPolicy_NullVerifyIntegrity, +}; -void SolidSyslogNullSecurityPolicy_Destroy(void) +struct SolidSyslogSecurityPolicy* SolidSyslogNullSecurityPolicy_Get(void) { + return &instance; } diff --git a/Core/Source/SolidSyslogNullSender.c b/Core/Source/SolidSyslogNullSender.c new file mode 100644 index 00000000..6efd9547 --- /dev/null +++ b/Core/Source/SolidSyslogNullSender.c @@ -0,0 +1,33 @@ +#include "SolidSyslogNullSender.h" + +#include +#include + +#include "SolidSyslogSenderDefinition.h" + +static bool NullSender_Send(struct SolidSyslogSender* base, const void* buffer, size_t size); +static void NullSender_Disconnect(struct SolidSyslogSender* base); + +static struct SolidSyslogSender instance = {.Send = NullSender_Send, .Disconnect = NullSender_Disconnect}; + +struct SolidSyslogSender* SolidSyslogNullSender_Get(void) +{ + return &instance; +} + +/* Send returns true ("delivered") so the Service algorithm drops the + * message rather than retaining it in the Store. A misconfigured Sender + * paired with a real Store would otherwise accumulate undeliverables + * forever; returning true at the null-object boundary contains that. */ +static bool NullSender_Send(struct SolidSyslogSender* base, const void* buffer, size_t size) +{ + (void) base; + (void) buffer; + (void) size; + return true; +} + +static void NullSender_Disconnect(struct SolidSyslogSender* base) +{ + (void) base; +} diff --git a/Core/Source/SolidSyslogNullStore.c b/Core/Source/SolidSyslogNullStore.c index 2ecbb303..f2789a8f 100644 --- a/Core/Source/SolidSyslogNullStore.c +++ b/Core/Source/SolidSyslogNullStore.c @@ -14,36 +14,20 @@ static size_t NullStore_GetTotalBytes(struct SolidSyslogStore* base); static size_t NullStore_GetUsedBytes(struct SolidSyslogStore* base); static bool NullStore_IsTransient(struct SolidSyslogStore* base); -struct SolidSyslogNullStore -{ - struct SolidSyslogStore Base; +static struct SolidSyslogStore instance = { + .Write = NullStore_Write, + .ReadNextUnsent = NullStore_ReadNextUnsent, + .MarkSent = NullStore_MarkSent, + .HasUnsent = NullStore_HasUnsent, + .IsHalted = NullStore_IsHalted, + .GetTotalBytes = NullStore_GetTotalBytes, + .GetUsedBytes = NullStore_GetUsedBytes, + .IsTransient = NullStore_IsTransient }; -static struct SolidSyslogNullStore instance; - -struct SolidSyslogStore* SolidSyslogNullStore_Create(void) -{ - instance.Base.Write = NullStore_Write; - instance.Base.ReadNextUnsent = NullStore_ReadNextUnsent; - instance.Base.MarkSent = NullStore_MarkSent; - instance.Base.HasUnsent = NullStore_HasUnsent; - instance.Base.IsHalted = NullStore_IsHalted; - instance.Base.GetTotalBytes = NullStore_GetTotalBytes; - instance.Base.GetUsedBytes = NullStore_GetUsedBytes; - instance.Base.IsTransient = NullStore_IsTransient; - return &instance.Base; -} - -void SolidSyslogNullStore_Destroy(void) +struct SolidSyslogStore* SolidSyslogNullStore_Get(void) { - instance.Base.Write = NULL; - instance.Base.ReadNextUnsent = NULL; - instance.Base.MarkSent = NULL; - instance.Base.HasUnsent = NULL; - instance.Base.IsHalted = NULL; - instance.Base.GetTotalBytes = NULL; - instance.Base.GetUsedBytes = NULL; - instance.Base.IsTransient = NULL; + return &instance; } /* NullStore never retains. Returns false to signal "not held by this store" diff --git a/Core/Source/SolidSyslogOriginSd.c b/Core/Source/SolidSyslogOriginSd.c index 1d4de1b8..4c89a661 100644 --- a/Core/Source/SolidSyslogOriginSd.c +++ b/Core/Source/SolidSyslogOriginSd.c @@ -1,33 +1,18 @@ #include "SolidSyslogOriginSd.h" + +#include + #include "SolidSyslogFormatter.h" +#include "SolidSyslogOriginSdPrivate.h" #include "SolidSyslogStructuredDataDefinition.h" -enum -{ - ORIGIN_SOFTWARE_MAX = 48, - ORIGIN_SWVERSION_MAX = 32, - ORIGIN_ENTERPRISE_ID_MAX = 64, - ORIGIN_IP_MAX = 64, - ORIGIN_LITERAL_BYTES = - 48, /* [origin software="" swVersion="" enterpriseId="" — closing ']' deferred to per-message OriginSd_Format */ - ORIGIN_CONTENT_MAX = ORIGIN_LITERAL_BYTES + SOLIDSYSLOG_ESCAPED_MAX_SIZE(ORIGIN_SOFTWARE_MAX) + - SOLIDSYSLOG_ESCAPED_MAX_SIZE(ORIGIN_SWVERSION_MAX) + - SOLIDSYSLOG_ESCAPED_MAX_SIZE(ORIGIN_ENTERPRISE_ID_MAX), - ORIGIN_FORMATTED_MAX = ORIGIN_CONTENT_MAX + 1 /* null terminator */ -}; - -struct SolidSyslogOriginSd -{ - struct SolidSyslogStructuredData Base; - SolidSyslogOriginIpCountFunction GetIpCount; - SolidSyslogOriginIpAtFunction GetIpAt; - SolidSyslogFormatterStorage FormattedStorage[SOLIDSYSLOG_FORMATTER_STORAGE_SIZE(ORIGIN_FORMATTED_MAX)]; -}; - static void OriginSd_Format(struct SolidSyslogStructuredData* base, struct SolidSyslogFormatter* formatter); static inline struct SolidSyslogOriginSd* OriginSd_SelfFromBase(struct SolidSyslogStructuredData* base); -static inline void OriginSd_PreFormatStaticPrefix(const struct SolidSyslogOriginSdConfig* config); +static inline void OriginSd_PreFormatStaticPrefix( + struct SolidSyslogOriginSd* self, + const struct SolidSyslogOriginSdConfig* config +); static inline void OriginSd_EmitSoftware( struct SolidSyslogFormatter* f, const struct SolidSyslogOriginSdConfig* config @@ -47,22 +32,21 @@ static inline void OriginSd_EmitIp( size_t index ); -static struct SolidSyslogOriginSd OriginSd_Instance; - -struct SolidSyslogStructuredData* SolidSyslogOriginSd_Create(const struct SolidSyslogOriginSdConfig* config) +void OriginSd_Initialise(struct SolidSyslogStructuredData* base, const struct SolidSyslogOriginSdConfig* config) { - OriginSd_Instance.Base.Format = OriginSd_Format; - OriginSd_Instance.GetIpCount = config->GetIpCount; - OriginSd_Instance.GetIpAt = config->GetIpAt; - OriginSd_PreFormatStaticPrefix(config); - return &OriginSd_Instance.Base; + struct SolidSyslogOriginSd* self = OriginSd_SelfFromBase(base); + self->Base.Format = OriginSd_Format; + self->GetIpCount = config->GetIpCount; + self->GetIpAt = config->GetIpAt; + OriginSd_PreFormatStaticPrefix(self, config); } -void SolidSyslogOriginSd_Destroy(void) +void OriginSd_Cleanup(struct SolidSyslogStructuredData* base) { - OriginSd_Instance.Base.Format = NULL; - OriginSd_Instance.GetIpCount = NULL; - OriginSd_Instance.GetIpAt = NULL; + struct SolidSyslogOriginSd* self = OriginSd_SelfFromBase(base); + self->Base.Format = NULL; + self->GetIpCount = NULL; + self->GetIpAt = NULL; } static void OriginSd_Format(struct SolidSyslogStructuredData* base, struct SolidSyslogFormatter* formatter) @@ -84,11 +68,13 @@ static inline struct SolidSyslogOriginSd* OriginSd_SelfFromBase(struct SolidSysl return (struct SolidSyslogOriginSd*) base; } -static inline void OriginSd_PreFormatStaticPrefix(const struct SolidSyslogOriginSdConfig* config) +static inline void OriginSd_PreFormatStaticPrefix( + struct SolidSyslogOriginSd* self, + const struct SolidSyslogOriginSdConfig* config +) { static const char sdPrefix[] = "[origin"; - struct SolidSyslogFormatter* f = - SolidSyslogFormatter_Create(OriginSd_Instance.FormattedStorage, ORIGIN_FORMATTED_MAX); + struct SolidSyslogFormatter* f = SolidSyslogFormatter_Create(self->FormattedStorage, ORIGIN_FORMATTED_MAX); SolidSyslogFormatter_BoundedString(f, sdPrefix, sizeof(sdPrefix) - 1U); OriginSd_EmitSoftware(f, config); diff --git a/Core/Source/SolidSyslogOriginSdPrivate.h b/Core/Source/SolidSyslogOriginSdPrivate.h new file mode 100644 index 00000000..be90f6dc --- /dev/null +++ b/Core/Source/SolidSyslogOriginSdPrivate.h @@ -0,0 +1,33 @@ +#ifndef SOLIDSYSLOGORIGINSDPRIVATE_H +#define SOLIDSYSLOGORIGINSDPRIVATE_H + +#include "SolidSyslogFormatter.h" +#include "SolidSyslogOriginSd.h" +#include "SolidSyslogStructuredDataDefinition.h" + +enum +{ + ORIGIN_SOFTWARE_MAX = 48, + ORIGIN_SWVERSION_MAX = 32, + ORIGIN_ENTERPRISE_ID_MAX = 64, + ORIGIN_IP_MAX = 64, + ORIGIN_LITERAL_BYTES = + 48, /* [origin software="" swVersion="" enterpriseId="" — closing ']' deferred to per-message OriginSd_Format */ + ORIGIN_CONTENT_MAX = ORIGIN_LITERAL_BYTES + SOLIDSYSLOG_ESCAPED_MAX_SIZE(ORIGIN_SOFTWARE_MAX) + + SOLIDSYSLOG_ESCAPED_MAX_SIZE(ORIGIN_SWVERSION_MAX) + + SOLIDSYSLOG_ESCAPED_MAX_SIZE(ORIGIN_ENTERPRISE_ID_MAX), + ORIGIN_FORMATTED_MAX = ORIGIN_CONTENT_MAX + 1 /* null terminator */ +}; + +struct SolidSyslogOriginSd +{ + struct SolidSyslogStructuredData Base; + SolidSyslogOriginIpCountFunction GetIpCount; + SolidSyslogOriginIpAtFunction GetIpAt; + SolidSyslogFormatterStorage FormattedStorage[SOLIDSYSLOG_FORMATTER_STORAGE_SIZE(ORIGIN_FORMATTED_MAX)]; +}; + +void OriginSd_Initialise(struct SolidSyslogStructuredData* base, const struct SolidSyslogOriginSdConfig* config); +void OriginSd_Cleanup(struct SolidSyslogStructuredData* base); + +#endif /* SOLIDSYSLOGORIGINSDPRIVATE_H */ diff --git a/Core/Source/SolidSyslogOriginSdStatic.c b/Core/Source/SolidSyslogOriginSdStatic.c new file mode 100644 index 00000000..4131b149 --- /dev/null +++ b/Core/Source/SolidSyslogOriginSdStatic.c @@ -0,0 +1,68 @@ +#include "SolidSyslogOriginSd.h" + +#include +#include + +#include "SolidSyslogError.h" +#include "SolidSyslogErrorMessages.h" +#include "SolidSyslogNullSd.h" +#include "SolidSyslogOriginSdPrivate.h" +#include "SolidSyslogPoolAllocator.h" +#include "SolidSyslogPrival.h" +#include "SolidSyslogTunables.h" + +struct SolidSyslogStructuredData; + +static size_t OriginSd_IndexFromHandle(const struct SolidSyslogStructuredData* base); +static void OriginSd_CleanupAtIndex(size_t index, void* context); + +static bool OriginSd_InUse[SOLIDSYSLOG_ORIGIN_SD_POOL_SIZE]; +static struct SolidSyslogOriginSd OriginSd_Pool[SOLIDSYSLOG_ORIGIN_SD_POOL_SIZE]; +static struct SolidSyslogPoolAllocator OriginSd_Allocator = {OriginSd_InUse, SOLIDSYSLOG_ORIGIN_SD_POOL_SIZE}; + +struct SolidSyslogStructuredData* SolidSyslogOriginSd_Create(const struct SolidSyslogOriginSdConfig* config) +{ + size_t index = SolidSyslogPoolAllocator_AcquireFirstFree(&OriginSd_Allocator); + struct SolidSyslogStructuredData* handle = SolidSyslogNullSd_Get(); + if (SolidSyslogPoolAllocator_IndexIsValid(&OriginSd_Allocator, index)) + { + OriginSd_Initialise(&OriginSd_Pool[index].Base, config); + handle = &OriginSd_Pool[index].Base; + } + else + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_ORIGINSD_POOL_EXHAUSTED); + } + return handle; +} + +void SolidSyslogOriginSd_Destroy(struct SolidSyslogStructuredData* base) +{ + size_t index = OriginSd_IndexFromHandle(base); + bool released = SolidSyslogPoolAllocator_IndexIsValid(&OriginSd_Allocator, index) && + SolidSyslogPoolAllocator_FreeIfInUse(&OriginSd_Allocator, index, OriginSd_CleanupAtIndex, NULL); + if (!released) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_WARNING, SOLIDSYSLOG_ERROR_MSG_ORIGINSD_UNKNOWN_DESTROY); + } +} + +static size_t OriginSd_IndexFromHandle(const struct SolidSyslogStructuredData* base) +{ + size_t result = SOLIDSYSLOG_ORIGIN_SD_POOL_SIZE; + for (size_t poolIndex = 0; poolIndex < SOLIDSYSLOG_ORIGIN_SD_POOL_SIZE; poolIndex++) + { + if (base == &OriginSd_Pool[poolIndex].Base) + { + result = poolIndex; + break; + } + } + return result; +} + +static void OriginSd_CleanupAtIndex(size_t index, void* context) +{ + (void) context; + OriginSd_Cleanup(&OriginSd_Pool[index].Base); +} diff --git a/Core/Source/SolidSyslogPassthroughBuffer.c b/Core/Source/SolidSyslogPassthroughBuffer.c index 28ca0471..2f48cde0 100644 --- a/Core/Source/SolidSyslogPassthroughBuffer.c +++ b/Core/Source/SolidSyslogPassthroughBuffer.c @@ -4,6 +4,7 @@ #include #include "SolidSyslogBufferDefinition.h" +#include "SolidSyslogPassthroughBufferPrivate.h" #include "SolidSyslogSender.h" static bool PassthroughBuffer_Read(struct SolidSyslogBuffer* base, void* data, size_t maxSize, size_t* bytesRead); @@ -11,27 +12,20 @@ static void PassthroughBuffer_Write(struct SolidSyslogBuffer* base, const void* static inline struct SolidSyslogPassthroughBuffer* PassthroughBuffer_SelfFromBase(struct SolidSyslogBuffer* base); -struct SolidSyslogPassthroughBuffer +void PassthroughBuffer_Initialise(struct SolidSyslogBuffer* base, struct SolidSyslogSender* sender) { - 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; + struct SolidSyslogPassthroughBuffer* self = PassthroughBuffer_SelfFromBase(base); + self->Base.Write = PassthroughBuffer_Write; + self->Base.Read = PassthroughBuffer_Read; + self->Sender = sender; } -void SolidSyslogPassthroughBuffer_Destroy(void) +void PassthroughBuffer_Cleanup(struct SolidSyslogBuffer* base) { - PassthroughBuffer_Instance.Base.Write = NULL; - PassthroughBuffer_Instance.Base.Read = NULL; - PassthroughBuffer_Instance.Sender = NULL; + struct SolidSyslogPassthroughBuffer* self = PassthroughBuffer_SelfFromBase(base); + self->Base.Write = NULL; + self->Base.Read = NULL; + self->Sender = NULL; } static bool PassthroughBuffer_Read(struct SolidSyslogBuffer* base, void* data, size_t maxSize, size_t* bytesRead) diff --git a/Core/Source/SolidSyslogPassthroughBufferPrivate.h b/Core/Source/SolidSyslogPassthroughBufferPrivate.h new file mode 100644 index 00000000..5cb8fdb7 --- /dev/null +++ b/Core/Source/SolidSyslogPassthroughBufferPrivate.h @@ -0,0 +1,17 @@ +#ifndef SOLIDSYSLOGPASSTHROUGHBUFFERPRIVATE_H +#define SOLIDSYSLOGPASSTHROUGHBUFFERPRIVATE_H + +#include "SolidSyslogBufferDefinition.h" + +struct SolidSyslogSender; + +struct SolidSyslogPassthroughBuffer +{ + struct SolidSyslogBuffer Base; + struct SolidSyslogSender* Sender; +}; + +void PassthroughBuffer_Initialise(struct SolidSyslogBuffer* base, struct SolidSyslogSender* sender); +void PassthroughBuffer_Cleanup(struct SolidSyslogBuffer* base); + +#endif /* SOLIDSYSLOGPASSTHROUGHBUFFERPRIVATE_H */ diff --git a/Core/Source/SolidSyslogPassthroughBufferStatic.c b/Core/Source/SolidSyslogPassthroughBufferStatic.c new file mode 100644 index 00000000..eb1d3fec --- /dev/null +++ b/Core/Source/SolidSyslogPassthroughBufferStatic.c @@ -0,0 +1,95 @@ +#include "SolidSyslogPassthroughBuffer.h" + +#include +#include + +#include "SolidSyslogBufferDefinition.h" +#include "SolidSyslogError.h" +#include "SolidSyslogErrorMessages.h" +#include "SolidSyslogPassthroughBufferPrivate.h" +#include "SolidSyslogPoolAllocator.h" +#include "SolidSyslogPrival.h" +#include "SolidSyslogTunables.h" + +struct SolidSyslogSender; + +static bool Fallback_Read(struct SolidSyslogBuffer* base, void* data, size_t maxSize, size_t* bytesRead); +static void Fallback_Write(struct SolidSyslogBuffer* base, const void* data, size_t size); +static size_t PassthroughBuffer_IndexFromHandle(const struct SolidSyslogBuffer* base); +static void PassthroughBuffer_CleanupAtIndex(size_t index, void* context); + +static bool PassthroughBuffer_InUse[SOLIDSYSLOG_PASSTHROUGH_BUFFER_POOL_SIZE]; +static struct SolidSyslogPassthroughBuffer PassthroughBuffer_Pool[SOLIDSYSLOG_PASSTHROUGH_BUFFER_POOL_SIZE]; +static struct SolidSyslogBuffer Fallback = {Fallback_Write, Fallback_Read}; +static struct SolidSyslogPoolAllocator PassthroughBuffer_Allocator = { + PassthroughBuffer_InUse, + SOLIDSYSLOG_PASSTHROUGH_BUFFER_POOL_SIZE +}; + +struct SolidSyslogBuffer* SolidSyslogPassthroughBuffer_Create(struct SolidSyslogSender* sender) +{ + size_t index = SolidSyslogPoolAllocator_AcquireFirstFree(&PassthroughBuffer_Allocator); + struct SolidSyslogBuffer* handle = &Fallback; + if (SolidSyslogPoolAllocator_IndexIsValid(&PassthroughBuffer_Allocator, index)) + { + PassthroughBuffer_Initialise(&PassthroughBuffer_Pool[index].Base, sender); + handle = &PassthroughBuffer_Pool[index].Base; + } + else + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_PASSTHROUGHBUFFER_POOL_EXHAUSTED); + } + return handle; +} + +void SolidSyslogPassthroughBuffer_Destroy(struct SolidSyslogBuffer* base) +{ + size_t index = PassthroughBuffer_IndexFromHandle(base); + bool released = SolidSyslogPoolAllocator_IndexIsValid(&PassthroughBuffer_Allocator, index) && + SolidSyslogPoolAllocator_FreeIfInUse( + &PassthroughBuffer_Allocator, + index, + PassthroughBuffer_CleanupAtIndex, + NULL + ); + if (!released) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_WARNING, SOLIDSYSLOG_ERROR_MSG_PASSTHROUGHBUFFER_UNKNOWN_DESTROY); + } +} + +static size_t PassthroughBuffer_IndexFromHandle(const struct SolidSyslogBuffer* base) +{ + size_t result = SOLIDSYSLOG_PASSTHROUGH_BUFFER_POOL_SIZE; + for (size_t poolIndex = 0; poolIndex < SOLIDSYSLOG_PASSTHROUGH_BUFFER_POOL_SIZE; poolIndex++) + { + if (base == &PassthroughBuffer_Pool[poolIndex].Base) + { + result = poolIndex; + break; + } + } + return result; +} + +static void PassthroughBuffer_CleanupAtIndex(size_t index, void* context) +{ + (void) context; + PassthroughBuffer_Cleanup(&PassthroughBuffer_Pool[index].Base); +} + +static bool Fallback_Read(struct SolidSyslogBuffer* base, void* data, size_t maxSize, size_t* bytesRead) +{ + (void) base; + (void) data; + (void) maxSize; + *bytesRead = 0; + return false; +} + +static void Fallback_Write(struct SolidSyslogBuffer* base, const void* data, size_t size) +{ + (void) base; + (void) data; + (void) size; +} diff --git a/Core/Source/SolidSyslogSwitchingSender.c b/Core/Source/SolidSyslogSwitchingSender.c index 8ac12b58..5a871675 100644 --- a/Core/Source/SolidSyslogSwitchingSender.c +++ b/Core/Source/SolidSyslogSwitchingSender.c @@ -2,16 +2,11 @@ #include #include +#include "SolidSyslogNullSender.h" +#include "SolidSyslogSender.h" #include "SolidSyslogSenderDefinition.h" #include "SolidSyslogSwitchingSender.h" -#include "SolidSyslogSender.h" - -struct SolidSyslogSwitchingSender -{ - struct SolidSyslogSender Base; - struct SolidSyslogSwitchingSenderConfig Config; - struct SolidSyslogSender* CurrentSender; -}; +#include "SolidSyslogSwitchingSenderPrivate.h" static bool SwitchingSender_Send(struct SolidSyslogSender* base, const void* buffer, size_t size); static void SwitchingSender_Disconnect(struct SolidSyslogSender* base); @@ -27,25 +22,25 @@ static inline void SwitchingSender_SwitchTo( struct SolidSyslogSender* newCurrent ); static inline struct SolidSyslogSender* SwitchingSender_RequestedSender(const struct SolidSyslogSwitchingSender* self); -static bool SwitchingSender_NilSend(struct SolidSyslogSender* base, const void* buffer, size_t size); -static void SwitchingSender_NilDisconnect(struct SolidSyslogSender* base); -static struct SolidSyslogSender NIL_SENDER = {SwitchingSender_NilSend, SwitchingSender_NilDisconnect}; -static const struct SolidSyslogSwitchingSender DEFAULT_INSTANCE = {.CurrentSender = &NIL_SENDER}; -static struct SolidSyslogSwitchingSender instance; - -struct SolidSyslogSender* SolidSyslogSwitchingSender_Create(const struct SolidSyslogSwitchingSenderConfig* config) +void SwitchingSender_Initialise(struct SolidSyslogSender* base, const struct SolidSyslogSwitchingSenderConfig* config) { - instance = DEFAULT_INSTANCE; - instance.Config = *config; - instance.Base.Send = SwitchingSender_Send; - instance.Base.Disconnect = SwitchingSender_Disconnect; - return &instance.Base; + struct SolidSyslogSwitchingSender* self = SwitchingSender_SelfFromBase(base); + self->Base.Send = SwitchingSender_Send; + self->Base.Disconnect = SwitchingSender_Disconnect; + self->Config = *config; + self->CurrentSender = SolidSyslogNullSender_Get(); } -void SolidSyslogSwitchingSender_Destroy(void) +void SwitchingSender_Cleanup(struct SolidSyslogSender* base) { - instance = DEFAULT_INSTANCE; + struct SolidSyslogSwitchingSender* self = SwitchingSender_SelfFromBase(base); + self->Base.Send = NULL; + self->Base.Disconnect = NULL; + self->Config.Senders = NULL; + self->Config.SenderCount = 0; + self->Config.Selector = NULL; + self->CurrentSender = NULL; } static bool SwitchingSender_Send(struct SolidSyslogSender* base, const void* buffer, size_t size) @@ -93,13 +88,14 @@ static inline void SwitchingSender_SwitchTo( self->CurrentSender = newCurrent; } -/* Falls back to the nil sender when the selector returns an out-of-range - * index (including the empty-array case). Contains the application contract - * violation of an invalid selector without corrupting memory or crashing. */ +/* Out-of-range selector index (including empty-array case) resolves to the + * shared NullSender. NullSender.Send returns true so the Service algorithm + * drops the message — a misconfigured selector must not retain messages in + * the Store. */ static inline struct SolidSyslogSender* SwitchingSender_RequestedSender(const struct SolidSyslogSwitchingSender* self) { + struct SolidSyslogSender* result = SolidSyslogNullSender_Get(); uint8_t index = self->Config.Selector(); - struct SolidSyslogSender* result = &NIL_SENDER; if (index < self->Config.SenderCount) { @@ -108,16 +104,3 @@ static inline struct SolidSyslogSender* SwitchingSender_RequestedSender(const st return result; } - -static bool SwitchingSender_NilSend(struct SolidSyslogSender* base, const void* buffer, size_t size) -{ - (void) base; - (void) buffer; - (void) size; - return false; -} - -static void SwitchingSender_NilDisconnect(struct SolidSyslogSender* base) -{ - (void) base; -} diff --git a/Core/Source/SolidSyslogSwitchingSenderPrivate.h b/Core/Source/SolidSyslogSwitchingSenderPrivate.h new file mode 100644 index 00000000..2319284d --- /dev/null +++ b/Core/Source/SolidSyslogSwitchingSenderPrivate.h @@ -0,0 +1,17 @@ +#ifndef SOLIDSYSLOGSWITCHINGSENDERPRIVATE_H +#define SOLIDSYSLOGSWITCHINGSENDERPRIVATE_H + +#include "SolidSyslogSenderDefinition.h" +#include "SolidSyslogSwitchingSender.h" + +struct SolidSyslogSwitchingSender +{ + struct SolidSyslogSender Base; + struct SolidSyslogSwitchingSenderConfig Config; + struct SolidSyslogSender* CurrentSender; +}; + +void SwitchingSender_Initialise(struct SolidSyslogSender* base, const struct SolidSyslogSwitchingSenderConfig* config); +void SwitchingSender_Cleanup(struct SolidSyslogSender* base); + +#endif /* SOLIDSYSLOGSWITCHINGSENDERPRIVATE_H */ diff --git a/Core/Source/SolidSyslogSwitchingSenderStatic.c b/Core/Source/SolidSyslogSwitchingSenderStatic.c new file mode 100644 index 00000000..9de9ecff --- /dev/null +++ b/Core/Source/SolidSyslogSwitchingSenderStatic.c @@ -0,0 +1,98 @@ +#include "SolidSyslogSwitchingSender.h" + +#include +#include + +#include "SolidSyslogError.h" +#include "SolidSyslogErrorMessages.h" +#include "SolidSyslogNullSender.h" +#include "SolidSyslogPoolAllocator.h" +#include "SolidSyslogPrival.h" +#include "SolidSyslogSwitchingSenderPrivate.h" +#include "SolidSyslogTunables.h" + +struct SolidSyslogSender; + +static bool SwitchingSender_IsValidConfig(const struct SolidSyslogSwitchingSenderConfig* config); +static size_t SwitchingSender_IndexFromHandle(const struct SolidSyslogSender* base); +static void SwitchingSender_CleanupAtIndex(size_t index, void* context); + +static bool SwitchingSender_InUse[SOLIDSYSLOG_SWITCHING_SENDER_POOL_SIZE]; +static struct SolidSyslogSwitchingSender SwitchingSender_Pool[SOLIDSYSLOG_SWITCHING_SENDER_POOL_SIZE]; +static struct SolidSyslogPoolAllocator SwitchingSender_Allocator = { + SwitchingSender_InUse, + SOLIDSYSLOG_SWITCHING_SENDER_POOL_SIZE +}; + +struct SolidSyslogSender* SolidSyslogSwitchingSender_Create(const struct SolidSyslogSwitchingSenderConfig* config) +{ + struct SolidSyslogSender* handle = SolidSyslogNullSender_Get(); + if (SwitchingSender_IsValidConfig(config)) + { + size_t index = SolidSyslogPoolAllocator_AcquireFirstFree(&SwitchingSender_Allocator); + if (SolidSyslogPoolAllocator_IndexIsValid(&SwitchingSender_Allocator, index)) + { + SwitchingSender_Initialise(&SwitchingSender_Pool[index].Base, config); + handle = &SwitchingSender_Pool[index].Base; + } + else + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_SWITCHINGSENDER_POOL_EXHAUSTED); + } + } + return handle; +} + +static bool SwitchingSender_IsValidConfig(const struct SolidSyslogSwitchingSenderConfig* config) +{ + bool valid = false; + if (config == NULL) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_SWITCHINGSENDER_CREATE_NULL_CONFIG); + } + else if (config->Senders == NULL) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_SWITCHINGSENDER_CREATE_NULL_SENDERS); + } + else if (config->Selector == NULL) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_SWITCHINGSENDER_CREATE_NULL_SELECTOR); + } + else + { + valid = true; + } + return valid; +} + +void SolidSyslogSwitchingSender_Destroy(struct SolidSyslogSender* base) +{ + size_t index = SwitchingSender_IndexFromHandle(base); + bool released = + SolidSyslogPoolAllocator_IndexIsValid(&SwitchingSender_Allocator, index) && + SolidSyslogPoolAllocator_FreeIfInUse(&SwitchingSender_Allocator, index, SwitchingSender_CleanupAtIndex, NULL); + if (!released) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_WARNING, SOLIDSYSLOG_ERROR_MSG_SWITCHINGSENDER_UNKNOWN_DESTROY); + } +} + +static size_t SwitchingSender_IndexFromHandle(const struct SolidSyslogSender* base) +{ + size_t result = SOLIDSYSLOG_SWITCHING_SENDER_POOL_SIZE; + for (size_t poolIndex = 0; poolIndex < SOLIDSYSLOG_SWITCHING_SENDER_POOL_SIZE; poolIndex++) + { + if (base == &SwitchingSender_Pool[poolIndex].Base) + { + result = poolIndex; + break; + } + } + return result; +} + +static void SwitchingSender_CleanupAtIndex(size_t index, void* context) +{ + (void) context; + SwitchingSender_Cleanup(&SwitchingSender_Pool[index].Base); +} diff --git a/Core/Source/SolidSyslogTimeQualitySd.c b/Core/Source/SolidSyslogTimeQualitySd.c index 65827dbc..820b5728 100644 --- a/Core/Source/SolidSyslogTimeQualitySd.c +++ b/Core/Source/SolidSyslogTimeQualitySd.c @@ -6,15 +6,10 @@ #include "SolidSyslogFormatter.h" #include "SolidSyslogStructuredDataDefinition.h" +#include "SolidSyslogTimeQualitySdPrivate.h" struct SolidSyslogFormatter; -struct SolidSyslogTimeQualitySd -{ - struct SolidSyslogStructuredData Base; - SolidSyslogTimeQualityFunction GetTimeQuality; -}; - static void TimeQualitySd_Format(struct SolidSyslogStructuredData* base, struct SolidSyslogFormatter* formatter); static inline struct SolidSyslogTimeQualitySd* TimeQualitySd_SelfFromBase(struct SolidSyslogStructuredData* base); @@ -26,19 +21,18 @@ static inline void TimeQualitySd_FormatBoolParam( ); static inline void TimeQualitySd_FormatSyncAccuracy(struct SolidSyslogFormatter* formatter, uint32_t value); -static struct SolidSyslogTimeQualitySd TimeQualitySd_Instance; - -struct SolidSyslogStructuredData* SolidSyslogTimeQualitySd_Create(SolidSyslogTimeQualityFunction getTimeQuality) +void TimeQualitySd_Initialise(struct SolidSyslogStructuredData* base, SolidSyslogTimeQualityFunction getTimeQuality) { - TimeQualitySd_Instance.Base.Format = TimeQualitySd_Format; - TimeQualitySd_Instance.GetTimeQuality = getTimeQuality; - return &TimeQualitySd_Instance.Base; + struct SolidSyslogTimeQualitySd* self = TimeQualitySd_SelfFromBase(base); + self->Base.Format = TimeQualitySd_Format; + self->GetTimeQuality = getTimeQuality; } -void SolidSyslogTimeQualitySd_Destroy(void) +void TimeQualitySd_Cleanup(struct SolidSyslogStructuredData* base) { - TimeQualitySd_Instance.Base.Format = NULL; - TimeQualitySd_Instance.GetTimeQuality = NULL; + struct SolidSyslogTimeQualitySd* self = TimeQualitySd_SelfFromBase(base); + self->Base.Format = NULL; + self->GetTimeQuality = NULL; } static void TimeQualitySd_Format(struct SolidSyslogStructuredData* base, struct SolidSyslogFormatter* formatter) diff --git a/Core/Source/SolidSyslogTimeQualitySdPrivate.h b/Core/Source/SolidSyslogTimeQualitySdPrivate.h new file mode 100644 index 00000000..5f318057 --- /dev/null +++ b/Core/Source/SolidSyslogTimeQualitySdPrivate.h @@ -0,0 +1,16 @@ +#ifndef SOLIDSYSLOGTIMEQUALITYSDPRIVATE_H +#define SOLIDSYSLOGTIMEQUALITYSDPRIVATE_H + +#include "SolidSyslogStructuredDataDefinition.h" +#include "SolidSyslogTimeQuality.h" + +struct SolidSyslogTimeQualitySd +{ + struct SolidSyslogStructuredData Base; + SolidSyslogTimeQualityFunction GetTimeQuality; +}; + +void TimeQualitySd_Initialise(struct SolidSyslogStructuredData* base, SolidSyslogTimeQualityFunction getTimeQuality); +void TimeQualitySd_Cleanup(struct SolidSyslogStructuredData* base); + +#endif /* SOLIDSYSLOGTIMEQUALITYSDPRIVATE_H */ diff --git a/Core/Source/SolidSyslogTimeQualitySdStatic.c b/Core/Source/SolidSyslogTimeQualitySdStatic.c new file mode 100644 index 00000000..e2ee8997 --- /dev/null +++ b/Core/Source/SolidSyslogTimeQualitySdStatic.c @@ -0,0 +1,80 @@ +#include "SolidSyslogTimeQualitySd.h" + +#include +#include + +#include "SolidSyslogError.h" +#include "SolidSyslogErrorMessages.h" +#include "SolidSyslogNullSd.h" +#include "SolidSyslogPoolAllocator.h" +#include "SolidSyslogPrival.h" +#include "SolidSyslogTimeQuality.h" +#include "SolidSyslogTimeQualitySdPrivate.h" +#include "SolidSyslogTunables.h" + +struct SolidSyslogStructuredData; + +static size_t TimeQualitySd_IndexFromHandle(const struct SolidSyslogStructuredData* base); +static void TimeQualitySd_CleanupAtIndex(size_t index, void* context); + +static bool TimeQualitySd_InUse[SOLIDSYSLOG_TIME_QUALITY_SD_POOL_SIZE]; +static struct SolidSyslogTimeQualitySd TimeQualitySd_Pool[SOLIDSYSLOG_TIME_QUALITY_SD_POOL_SIZE]; +static struct SolidSyslogPoolAllocator TimeQualitySd_Allocator = { + TimeQualitySd_InUse, + SOLIDSYSLOG_TIME_QUALITY_SD_POOL_SIZE +}; + +struct SolidSyslogStructuredData* SolidSyslogTimeQualitySd_Create(SolidSyslogTimeQualityFunction getTimeQuality) +{ + struct SolidSyslogStructuredData* handle = SolidSyslogNullSd_Get(); + if (getTimeQuality == NULL) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_TIMEQUALITYSD_CREATE_NULL_CALLBACK); + } + else + { + size_t index = SolidSyslogPoolAllocator_AcquireFirstFree(&TimeQualitySd_Allocator); + if (SolidSyslogPoolAllocator_IndexIsValid(&TimeQualitySd_Allocator, index)) + { + TimeQualitySd_Initialise(&TimeQualitySd_Pool[index].Base, getTimeQuality); + handle = &TimeQualitySd_Pool[index].Base; + } + else + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_TIMEQUALITYSD_POOL_EXHAUSTED); + } + } + return handle; +} + +void SolidSyslogTimeQualitySd_Destroy(struct SolidSyslogStructuredData* base) +{ + size_t index = TimeQualitySd_IndexFromHandle(base); + bool released = + SolidSyslogPoolAllocator_IndexIsValid(&TimeQualitySd_Allocator, index) && + SolidSyslogPoolAllocator_FreeIfInUse(&TimeQualitySd_Allocator, index, TimeQualitySd_CleanupAtIndex, NULL); + if (!released) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_WARNING, SOLIDSYSLOG_ERROR_MSG_TIMEQUALITYSD_UNKNOWN_DESTROY); + } +} + +static size_t TimeQualitySd_IndexFromHandle(const struct SolidSyslogStructuredData* base) +{ + size_t result = SOLIDSYSLOG_TIME_QUALITY_SD_POOL_SIZE; + for (size_t poolIndex = 0; poolIndex < SOLIDSYSLOG_TIME_QUALITY_SD_POOL_SIZE; poolIndex++) + { + if (base == &TimeQualitySd_Pool[poolIndex].Base) + { + result = poolIndex; + break; + } + } + return result; +} + +static void TimeQualitySd_CleanupAtIndex(size_t index, void* context) +{ + (void) context; + TimeQualitySd_Cleanup(&TimeQualitySd_Pool[index].Base); +} diff --git a/Core/Source/SolidSyslogUdpSender.c b/Core/Source/SolidSyslogUdpSender.c index b687b082..15a68b41 100644 --- a/Core/Source/SolidSyslogUdpSender.c +++ b/Core/Source/SolidSyslogUdpSender.c @@ -1,33 +1,23 @@ +#include #include #include -#include +#include "SolidSyslogAddress.h" +#include "SolidSyslogDatagram.h" #include "SolidSyslogEndpoint.h" #include "SolidSyslogError.h" #include "SolidSyslogErrorMessages.h" #include "SolidSyslogFormatter.h" #include "SolidSyslogPrival.h" -#include "SolidSyslogUdpPayload.h" -#include "SolidSyslogUdpSender.h" -#include "SolidSyslogSenderDefinition.h" -#include "SolidSyslogAddress.h" -#include "SolidSyslogDatagram.h" #include "SolidSyslogResolver.h" +#include "SolidSyslogSenderDefinition.h" #include "SolidSyslogTransport.h" +#include "SolidSyslogUdpPayload.h" +#include "SolidSyslogUdpSender.h" +#include "SolidSyslogUdpSenderPrivate.h" struct SolidSyslogFormatter; -struct SolidSyslogUdpSender -{ - struct SolidSyslogSender Base; - struct SolidSyslogUdpSenderConfig Config; - SolidSyslogAddressStorage AddrStorage; - bool Connected; - uint32_t LastEndpointVersion; -}; - -static bool UdpSender_IsValidConfig(const struct SolidSyslogUdpSenderConfig* config); -static void UdpSender_InstallConfig(const struct SolidSyslogUdpSenderConfig* config); static bool UdpSender_Send(struct SolidSyslogSender* base, const void* buffer, size_t size); static void UdpSender_Disconnect(struct SolidSyslogSender* base); @@ -52,71 +42,33 @@ static inline enum SolidSyslogDatagramSendResult UdpSender_RetryAfterOversize( size_t size ); static uint32_t UdpSender_NilEndpointVersion(void); -static bool UdpSender_NilUdpSenderSend(struct SolidSyslogSender* base, const void* buffer, size_t size); -static void UdpSender_NilUdpSenderDisconnect(struct SolidSyslogSender* base); - -static const struct SolidSyslogUdpSender DEFAULT_INSTANCE = { - .Config = {.EndpointVersion = UdpSender_NilEndpointVersion} -}; -static struct SolidSyslogUdpSender instance; -static struct SolidSyslogSender NilUdpSender = { - .Send = UdpSender_NilUdpSenderSend, - .Disconnect = UdpSender_NilUdpSenderDisconnect -}; -struct SolidSyslogSender* SolidSyslogUdpSender_Create(const struct SolidSyslogUdpSenderConfig* config) +void UdpSender_Initialise(struct SolidSyslogSender* base, const struct SolidSyslogUdpSenderConfig* config) { - struct SolidSyslogSender* result = &NilUdpSender; - if (UdpSender_IsValidConfig(config)) - { - UdpSender_InstallConfig(config); - result = &instance.Base; - } - return result; -} - -static bool UdpSender_IsValidConfig(const struct SolidSyslogUdpSenderConfig* config) -{ - bool valid = false; - if (config == NULL) - { - SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_UDPSENDER_CREATE_NULL_CONFIG); - } - else if (config->Resolver == NULL) - { - SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_UDPSENDER_CREATE_NULL_RESOLVER); - } - else if (config->Datagram == NULL) - { - SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_UDPSENDER_CREATE_NULL_DATAGRAM); - } - else if (config->Endpoint == NULL) - { - SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_UDPSENDER_CREATE_NULL_ENDPOINT); - } - else - { - valid = true; - } - return valid; -} - -static void UdpSender_InstallConfig(const struct SolidSyslogUdpSenderConfig* config) -{ - instance = DEFAULT_INSTANCE; - instance.Config = *config; - if (instance.Config.EndpointVersion == NULL) + struct SolidSyslogUdpSender* self = UdpSender_SelfFromBase(base); + self->Base.Send = UdpSender_Send; + self->Base.Disconnect = UdpSender_Disconnect; + self->Config = *config; + if (self->Config.EndpointVersion == NULL) { - instance.Config.EndpointVersion = UdpSender_NilEndpointVersion; + self->Config.EndpointVersion = UdpSender_NilEndpointVersion; } - instance.Base.Send = UdpSender_Send; - instance.Base.Disconnect = UdpSender_Disconnect; + self->Connected = false; + self->LastEndpointVersion = 0; } -void SolidSyslogUdpSender_Destroy(void) +void UdpSender_Cleanup(struct SolidSyslogSender* base) { - UdpSender_Disconnect(&instance.Base); - instance = DEFAULT_INSTANCE; + struct SolidSyslogUdpSender* self = UdpSender_SelfFromBase(base); + UdpSender_Disconnect(base); + self->Base.Send = NULL; + self->Base.Disconnect = NULL; + self->Config.Resolver = NULL; + self->Config.Datagram = NULL; + self->Config.Endpoint = NULL; + self->Config.EndpointVersion = NULL; + self->Connected = false; + self->LastEndpointVersion = 0; } static bool UdpSender_Send(struct SolidSyslogSender* base, const void* buffer, size_t size) @@ -271,16 +223,3 @@ static uint32_t UdpSender_NilEndpointVersion(void) { return 0; } - -static bool UdpSender_NilUdpSenderSend(struct SolidSyslogSender* base, const void* buffer, size_t size) -{ - (void) base; - (void) buffer; - (void) size; - return true; -} - -static void UdpSender_NilUdpSenderDisconnect(struct SolidSyslogSender* base) -{ - (void) base; -} diff --git a/Core/Source/SolidSyslogUdpSenderPrivate.h b/Core/Source/SolidSyslogUdpSenderPrivate.h new file mode 100644 index 00000000..892db5c2 --- /dev/null +++ b/Core/Source/SolidSyslogUdpSenderPrivate.h @@ -0,0 +1,23 @@ +#ifndef SOLIDSYSLOGUDPSENDERPRIVATE_H +#define SOLIDSYSLOGUDPSENDERPRIVATE_H + +#include +#include + +#include "SolidSyslogAddress.h" +#include "SolidSyslogSenderDefinition.h" +#include "SolidSyslogUdpSender.h" + +struct SolidSyslogUdpSender +{ + struct SolidSyslogSender Base; + struct SolidSyslogUdpSenderConfig Config; + SolidSyslogAddressStorage AddrStorage; + bool Connected; + uint32_t LastEndpointVersion; +}; + +void UdpSender_Initialise(struct SolidSyslogSender* base, const struct SolidSyslogUdpSenderConfig* config); +void UdpSender_Cleanup(struct SolidSyslogSender* base); + +#endif /* SOLIDSYSLOGUDPSENDERPRIVATE_H */ diff --git a/Core/Source/SolidSyslogUdpSenderStatic.c b/Core/Source/SolidSyslogUdpSenderStatic.c new file mode 100644 index 00000000..82fcbedc --- /dev/null +++ b/Core/Source/SolidSyslogUdpSenderStatic.c @@ -0,0 +1,98 @@ +#include "SolidSyslogUdpSender.h" + +#include +#include + +#include "SolidSyslogError.h" +#include "SolidSyslogErrorMessages.h" +#include "SolidSyslogNullSender.h" +#include "SolidSyslogPoolAllocator.h" +#include "SolidSyslogPrival.h" +#include "SolidSyslogTunables.h" +#include "SolidSyslogUdpSenderPrivate.h" + +struct SolidSyslogSender; + +static bool UdpSender_IsValidConfig(const struct SolidSyslogUdpSenderConfig* config); +static size_t UdpSender_IndexFromHandle(const struct SolidSyslogSender* base); +static void UdpSender_CleanupAtIndex(size_t index, void* context); + +static bool UdpSender_InUse[SOLIDSYSLOG_UDP_SENDER_POOL_SIZE]; +static struct SolidSyslogUdpSender UdpSender_Pool[SOLIDSYSLOG_UDP_SENDER_POOL_SIZE]; +static struct SolidSyslogPoolAllocator UdpSender_Allocator = {UdpSender_InUse, SOLIDSYSLOG_UDP_SENDER_POOL_SIZE}; + +struct SolidSyslogSender* SolidSyslogUdpSender_Create(const struct SolidSyslogUdpSenderConfig* config) +{ + struct SolidSyslogSender* result = SolidSyslogNullSender_Get(); + if (UdpSender_IsValidConfig(config)) + { + size_t index = SolidSyslogPoolAllocator_AcquireFirstFree(&UdpSender_Allocator); + if (SolidSyslogPoolAllocator_IndexIsValid(&UdpSender_Allocator, index)) + { + UdpSender_Initialise(&UdpSender_Pool[index].Base, config); + result = &UdpSender_Pool[index].Base; + } + else + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_UDPSENDER_POOL_EXHAUSTED); + } + } + return result; +} + +void SolidSyslogUdpSender_Destroy(struct SolidSyslogSender* base) +{ + size_t index = UdpSender_IndexFromHandle(base); + bool released = SolidSyslogPoolAllocator_IndexIsValid(&UdpSender_Allocator, index) && + SolidSyslogPoolAllocator_FreeIfInUse(&UdpSender_Allocator, index, UdpSender_CleanupAtIndex, NULL); + if (!released) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_WARNING, SOLIDSYSLOG_ERROR_MSG_UDPSENDER_UNKNOWN_DESTROY); + } +} + +static bool UdpSender_IsValidConfig(const struct SolidSyslogUdpSenderConfig* config) +{ + bool valid = false; + if (config == NULL) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_UDPSENDER_CREATE_NULL_CONFIG); + } + else if (config->Resolver == NULL) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_UDPSENDER_CREATE_NULL_RESOLVER); + } + else if (config->Datagram == NULL) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_UDPSENDER_CREATE_NULL_DATAGRAM); + } + else if (config->Endpoint == NULL) + { + SolidSyslog_Error(SOLIDSYSLOG_SEVERITY_ERROR, SOLIDSYSLOG_ERROR_MSG_UDPSENDER_CREATE_NULL_ENDPOINT); + } + else + { + valid = true; + } + return valid; +} + +static size_t UdpSender_IndexFromHandle(const struct SolidSyslogSender* base) +{ + size_t result = SOLIDSYSLOG_UDP_SENDER_POOL_SIZE; + for (size_t poolIndex = 0; poolIndex < SOLIDSYSLOG_UDP_SENDER_POOL_SIZE; poolIndex++) + { + if (base == &UdpSender_Pool[poolIndex].Base) + { + result = poolIndex; + break; + } + } + return result; +} + +static void UdpSender_CleanupAtIndex(size_t index, void* context) +{ + (void) context; + UdpSender_Cleanup(&UdpSender_Pool[index].Base); +} diff --git a/DEVLOG.md b/DEVLOG.md index ba5cc031..6bde8a9d 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -9001,3 +9001,89 @@ MISRA rule — different category, doesn't set precedent. ### Open questions - None. + +## 2026-05-18 — S11.04 Core singleton stateful classes onto PoolAllocator + +### Decisions + +- **Bundled scope, single PR.** The story body listed nine + commits (two new GoF nulls, six migrations, DEVLOG). The + session added two unplanned commits: flipping + `SolidSyslogNullStore` and `SolidSyslogNullSecurityPolicy` + from `_Create`/`_Destroy` to `_Get` so the four + null-object types now share one shape, and a naming + cleanup that put the public `_Destroy` parameter at `base` + per `docs/NAMING.md`. The signature change `_Destroy(void) + → _Destroy(base*)` is bundled into each per-class + migration commit; landed defaults from the three open + questions in the story body were confirmed by the + developer up front. +- **New GoF nulls keep state immutable.** `SolidSyslogNullSd` + and `SolidSyslogNullSender` ship as `_Get`-only — a single + static instance with the vtable wired at file-scope + initialisation. No wrapper struct, no `_Create`/`_Destroy` + pair. The retro-flip of the older two nulls follows the + same shape so any future Null* type slots in next to + these without inventing a new convention. +- **NullSender.Send returns true (drop on the floor).** The + story body said `false` ("didn't send"). That semantic + fills a real Store with undeliverables when the integrator + misconfigures the Sender. The old UdpSender NIL returned + true precisely to avoid that; the new shared NullSender + inherits it. `SwitchingSender`'s existing + out-of-range-selector behaviour (which returned false) + flips to match — same null-object boundary, same semantic. + Two SwitchingSender tests retitled accordingly. +- **UdpSender_Send NULL-buffer guard stays in UdpSender.c.** + The E11 three-TU split memo says `ClassStatic.c` is the + only TU that calls `SolidSyslog_Error` for the class — + intended for pool / bad-config errors. The Send NULL-buffer + guard is a runtime contract guard, not a pool error; it + belongs next to the check, in the vtable method. This is a + scoped exception, captured in the UdpSender commit message. +- **Pool-issue test pattern adopted everywhere.** Each per-class + migration adds one Pool `TEST_GROUP` with one test + (`FillingPoolThenOverflowReturnsDistinctFallback`) that + proves max-configured allocations succeed and one-more + returns a handle distinct from every pool slot. Generic + pool mechanics — lock counts, per-probe locking, stale- + handle warning — are covered by + `SolidSyslogPoolAllocatorTest.cpp` once, not duplicated + per class. PassthroughBuffer adds a second test for its + class-private fallback Write/Read no-ops because no shared + Null* class exercises those branches. +- **SwitchingSender test fixture defended against + double-Create.** Three SwitchingSender tests call + `CreateSwitchingSender(count)` to override the setup's + call. With singleton semantics the second call silently + overwrote the slot; with pooling the second call exhausts + the pool and returns the Fallback, leaving the first slot + orphaned. The helper now destroys any previously-allocated + sender before re-creating. Documented inline. +- **Validation per class.** Each migration was validated at + `SOLIDSYSLOG__POOL_SIZE=3` via the user-tunables + override mechanism. 100% line coverage on every new TU + pair (Class.c + ClassStatic.c) and on each new Null type. +- **Verified in `cpputest-freertos`.** Per + `feedback_verify_in_freertos_host_image.md`. With + `FREERTOS_KERNEL_PATH=/opt/freertos/kernel` set, the full + Core suite (1150 tests) plus seven FreeRTOS-specific + suites all green; no LayerGuard skip. + +### Deferred + +- **clang-tidy magic-numbers suppression revisit** — + `project_clang_tidy_magic_numbers.md` notes David's + preference to remove the per-rule disables under + `.clang-tidy`. The S11.04 migrations didn't introduce any + new magic-number suppressions; the cleanup is still its + own pass. +- **Pool-allocator unit test rename** — when the next sweep + story lands, consider renaming the macros in + `SolidSyslogCircularBufferTest.cpp` (the `CHECK_IS_FALLBACK` + reference) to be reusable across the per-class pool tests. + Currently inlined per-class to keep the macro file-local. + +### Open questions + +- None. diff --git a/Tests/Bdd/Targets/BddTargetServiceThreadTest.cpp b/Tests/Bdd/Targets/BddTargetServiceThreadTest.cpp index a283d01e..381cfefa 100644 --- a/Tests/Bdd/Targets/BddTargetServiceThreadTest.cpp +++ b/Tests/Bdd/Targets/BddTargetServiceThreadTest.cpp @@ -68,7 +68,7 @@ TEST_GROUP(BddTargetServiceThread) SolidSyslogUdpSenderConfig udpConfig = {SolidSyslogGetAddrInfoResolver_Create(), SolidSyslogPosixDatagram_Create(), BddTargetEndpoint, BddTargetEndpointVersion}; sender = SolidSyslogUdpSender_Create(&udpConfig); buffer = SolidSyslogPosixMessageQueueBuffer_Create(SOLIDSYSLOG_MAX_MESSAGE_SIZE, 10); - store = SolidSyslogNullStore_Create(); + store = SolidSyslogNullStore_Get(); SolidSyslogConfig config = {buffer, sender, nullptr, nullptr, nullptr, nullptr, store, nullptr, 0}; SolidSyslog_Create(&config); @@ -77,9 +77,8 @@ TEST_GROUP(BddTargetServiceThread) void teardown() override { SolidSyslog_Destroy(); - SolidSyslogNullStore_Destroy(); SolidSyslogPosixMessageQueueBuffer_Destroy(); - SolidSyslogUdpSender_Destroy(); + SolidSyslogUdpSender_Destroy(sender); SolidSyslogPosixDatagram_Destroy(); SolidSyslogGetAddrInfoResolver_Destroy(); } diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index a427d719..b2fb4ab2 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -27,6 +27,8 @@ set(TEST_SOURCES SolidSyslogCircularBufferTest.cpp SolidSyslogPoolAllocatorTest.cpp SolidSyslogNullMutexTest.cpp + SolidSyslogNullSdTest.cpp + SolidSyslogNullSenderTest.cpp SolidSyslogNullStoreTest.cpp SolidSyslogNullSecurityPolicyTest.cpp SolidSyslogCrc16Test.cpp diff --git a/Tests/SolidSyslogBlockStoreDrainOrderingTest.cpp b/Tests/SolidSyslogBlockStoreDrainOrderingTest.cpp index c4db0c67..7601d746 100644 --- a/Tests/SolidSyslogBlockStoreDrainOrderingTest.cpp +++ b/Tests/SolidSyslogBlockStoreDrainOrderingTest.cpp @@ -123,12 +123,11 @@ TEST_BASE(DrainTestFixtureBase) { file = FileFake_Create(&fileStorage); device = SolidSyslogFileBlockDevice_Create(&deviceStorage, file, TEST_PATH_PREFIX); - policy = SolidSyslogNullSecurityPolicy_Create(); + policy = SolidSyslogNullSecurityPolicy_Get(); } void teardownBlockDeviceAndPolicy() const { - SolidSyslogNullSecurityPolicy_Destroy(); SolidSyslogFileBlockDevice_Destroy(device); FileFake_Destroy(); } diff --git a/Tests/SolidSyslogMetaSdTest.cpp b/Tests/SolidSyslogMetaSdTest.cpp index ce9257cf..35730ddc 100644 --- a/Tests/SolidSyslogMetaSdTest.cpp +++ b/Tests/SolidSyslogMetaSdTest.cpp @@ -7,6 +7,7 @@ #include "SolidSyslogMetaSd.h" #include "SolidSyslogPrival.h" #include "SolidSyslogStructuredData.h" +#include "SolidSyslogTunables.h" #include "TestUtils.h" #include "CppUTest/TestHarness.h" @@ -92,14 +93,14 @@ TEST_GROUP(SolidSyslogMetaSd) void teardown() override { - SolidSyslogMetaSd_Destroy(); + SolidSyslogMetaSd_Destroy(sd); TestAtomicCounter_Destroy(counter); ErrorHandlerFake_Uninstall(); } void recreateWith(const SolidSyslogMetaSdConfig* configPtr) { - SolidSyslogMetaSd_Destroy(); + SolidSyslogMetaSd_Destroy(sd); sd = SolidSyslogMetaSd_Create(configPtr); } @@ -285,3 +286,67 @@ TEST(SolidSyslogMetaSd, CreateWithNullCounterReportsWarning) LONGS_EQUAL(SOLIDSYSLOG_SEVERITY_WARNING, ErrorHandlerFake_LastSeverity()); STRCMP_EQUAL("SolidSyslogMetaSd_Create config.Counter is NULL", ErrorHandlerFake_LastMessage()); } + +// Pool tests — prove SOLIDSYSLOG_META_SD_POOL_SIZE caps live instances +// and overflow falls back to the shared SolidSyslogNullSd. + +// clang-format off +TEST_GROUP(SolidSyslogMetaSdPool) +{ + TestAtomicCounterStorage counterStorage; + SolidSyslogAtomicCounter* counter = nullptr; + SolidSyslogMetaSdConfig config{}; + struct SolidSyslogStructuredData* pooled[SOLIDSYSLOG_META_SD_POOL_SIZE] = {}; + struct SolidSyslogStructuredData* overflow = nullptr; + + void setup() override + { + counter = TestAtomicCounter_Create(&counterStorage); + config.Counter = counter; + } + + void teardown() override + { + for (auto* handle : pooled) + { + if (handle != nullptr) + { + SolidSyslogMetaSd_Destroy(handle); + } + } + if (overflow != nullptr) + { + SolidSyslogMetaSd_Destroy(overflow); + } + TestAtomicCounter_Destroy(counter); + } + + struct SolidSyslogStructuredData* MakeSd() + { + return SolidSyslogMetaSd_Create(&config); + } + + void FillPool() + { + for (auto*& slot : pooled) + { + slot = MakeSd(); + } + } +}; + +// clang-format on + +TEST(SolidSyslogMetaSdPool, FillingPoolThenOverflowReturnsDistinctFallback) +{ + FillPool(); + + overflow = MakeSd(); + + CHECK_TEXT(overflow != nullptr, "Fallback handle was nullptr"); + for (auto* slot : pooled) + { + CHECK_TEXT(slot != nullptr, "pool slot was nullptr (FillPool failed?)"); + CHECK_TEXT(overflow != slot, "Fallback handle collided with a pool slot"); + } +} diff --git a/Tests/SolidSyslogNullSdTest.cpp b/Tests/SolidSyslogNullSdTest.cpp new file mode 100644 index 00000000..61ddc967 --- /dev/null +++ b/Tests/SolidSyslogNullSdTest.cpp @@ -0,0 +1,28 @@ +#include "CppUTest/TestHarness.h" +#include "SolidSyslogFormatter.h" +#include "SolidSyslogNullSd.h" +#include "SolidSyslogStructuredData.h" + +// clang-format off +TEST_GROUP(SolidSyslogNullSd) +{ + struct SolidSyslogStructuredData* sd = nullptr; + SolidSyslogFormatterStorage formatterStorage[SOLIDSYSLOG_FORMATTER_STORAGE_SIZE(32)]; + struct SolidSyslogFormatter* formatter = nullptr; + + void setup() override + { + // cppcheck-suppress unreadVariable -- used across TEST_GROUP methods; cppcheck does not model CppUTest macros + sd = SolidSyslogNullSd_Get(); + // cppcheck-suppress unreadVariable -- used across TEST_GROUP methods; cppcheck does not model CppUTest macros + formatter = SolidSyslogFormatter_Create(formatterStorage, 32); + } +}; + +// clang-format on + +TEST(SolidSyslogNullSd, FormatWritesNothing) +{ + SolidSyslogStructuredData_Format(sd, formatter); + LONGS_EQUAL(0, SolidSyslogFormatter_Length(formatter)); +} diff --git a/Tests/SolidSyslogNullSecurityPolicyTest.cpp b/Tests/SolidSyslogNullSecurityPolicyTest.cpp index 53b7cf90..fec6eb34 100644 --- a/Tests/SolidSyslogNullSecurityPolicyTest.cpp +++ b/Tests/SolidSyslogNullSecurityPolicyTest.cpp @@ -10,18 +10,13 @@ TEST_GROUP(SolidSyslogNullSecurityPolicy) void setup() override { // cppcheck-suppress unreadVariable -- used across TEST_GROUP methods; cppcheck does not model CppUTest macros - policy = SolidSyslogNullSecurityPolicy_Create(); - } - - void teardown() override - { - SolidSyslogNullSecurityPolicy_Destroy(); + policy = SolidSyslogNullSecurityPolicy_Get(); } }; // clang-format on -TEST(SolidSyslogNullSecurityPolicy, CreateReturnsNonNull) +TEST(SolidSyslogNullSecurityPolicy, GetReturnsNonNull) { CHECK_TRUE(policy != nullptr); } @@ -35,8 +30,3 @@ TEST(SolidSyslogNullSecurityPolicy, VerifyIntegrityReturnsTrue) { CHECK_TRUE(policy->VerifyIntegrity(nullptr, 0, nullptr)); } - -TEST(SolidSyslogNullSecurityPolicy, DestroyDoesNotCrash) -{ - SolidSyslogNullSecurityPolicy_Destroy(); -} diff --git a/Tests/SolidSyslogNullSenderTest.cpp b/Tests/SolidSyslogNullSenderTest.cpp new file mode 100644 index 00000000..2984d891 --- /dev/null +++ b/Tests/SolidSyslogNullSenderTest.cpp @@ -0,0 +1,27 @@ +#include "CppUTest/TestHarness.h" +#include "SolidSyslogNullSender.h" +#include "SolidSyslogSender.h" + +// clang-format off +TEST_GROUP(SolidSyslogNullSender) +{ + struct SolidSyslogSender* sender = nullptr; + + void setup() override + { + // cppcheck-suppress unreadVariable -- used across TEST_GROUP methods; cppcheck does not model CppUTest macros + sender = SolidSyslogNullSender_Get(); + } +}; + +// clang-format on + +TEST(SolidSyslogNullSender, SendReturnsTrueToDropOnTheFloor) +{ + CHECK_TRUE(SolidSyslogSender_Send(sender, "hello", 5)); +} + +TEST(SolidSyslogNullSender, DisconnectDoesNotCrash) +{ + SolidSyslogSender_Disconnect(sender); +} diff --git a/Tests/SolidSyslogNullStoreTest.cpp b/Tests/SolidSyslogNullStoreTest.cpp index bfa9b064..ac305b57 100644 --- a/Tests/SolidSyslogNullStoreTest.cpp +++ b/Tests/SolidSyslogNullStoreTest.cpp @@ -12,12 +12,7 @@ TEST_GROUP(SolidSyslogNullStore) void setup() override { // cppcheck-suppress unreadVariable -- used across TEST_GROUP methods; cppcheck does not model CppUTest macros - store = SolidSyslogNullStore_Create(); - } - - void teardown() override - { - SolidSyslogNullStore_Destroy(); + store = SolidSyslogNullStore_Get(); } }; diff --git a/Tests/SolidSyslogOriginSdTest.cpp b/Tests/SolidSyslogOriginSdTest.cpp index b89a0ed0..23f845f4 100644 --- a/Tests/SolidSyslogOriginSdTest.cpp +++ b/Tests/SolidSyslogOriginSdTest.cpp @@ -6,6 +6,7 @@ #include "SolidSyslogFormatter.h" #include "SolidSyslogOriginSd.h" #include "SolidSyslogStructuredData.h" +#include "SolidSyslogTunables.h" #include "CppUTest/TestHarness.h" class TEST_SolidSyslogOriginSd_EnterpriseIdContainingSpecialsIsEscaped_Test; @@ -95,13 +96,13 @@ TEST_GROUP(SolidSyslogOriginSd) void teardown() override { - SolidSyslogOriginSd_Destroy(); + SolidSyslogOriginSd_Destroy(sd); } // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) -- positional shorthand for the two existing config fields; tests assert exact output so a mix-up shows immediately void recreate(const char* software, const char* swVersion) { - SolidSyslogOriginSd_Destroy(); + SolidSyslogOriginSd_Destroy(sd); config.Software = software; config.SwVersion = swVersion; sd = SolidSyslogOriginSd_Create(&config); @@ -109,14 +110,14 @@ TEST_GROUP(SolidSyslogOriginSd) void useEnterpriseId(const char* enterpriseId) { - SolidSyslogOriginSd_Destroy(); + SolidSyslogOriginSd_Destroy(sd); config.EnterpriseId = enterpriseId; sd = SolidSyslogOriginSd_Create(&config); } void useIps(std::initializer_list ips) { - SolidSyslogOriginSd_Destroy(); + SolidSyslogOriginSd_Destroy(sd); fakeIpCount = ips.size(); size_t i = 0; for (const char* ip : ips) @@ -421,7 +422,7 @@ TEST(SolidSyslogOriginSd, IpContainingSpecialsIsEscaped) TEST(SolidSyslogOriginSd, GetIpCountSetButGetIpAtNullOmitsIpParams) { - SolidSyslogOriginSd_Destroy(); + SolidSyslogOriginSd_Destroy(sd); config.GetIpCount = FakeIpCount; config.GetIpAt = nullptr; sd = SolidSyslogOriginSd_Create(&config); @@ -435,7 +436,7 @@ TEST(SolidSyslogOriginSd, GetIpCountSetButGetIpAtNullOmitsIpParams) TEST(SolidSyslogOriginSd, GetIpAtSetButGetIpCountNullOmitsIpParams) { - SolidSyslogOriginSd_Destroy(); + SolidSyslogOriginSd_Destroy(sd); config.GetIpCount = nullptr; config.GetIpAt = FakeIpAt; sd = SolidSyslogOriginSd_Create(&config); @@ -492,3 +493,64 @@ TEST(SolidSyslogOriginSd, EnterpriseIdAndIpsNoSoftwareSwVersion) SolidSyslogFormatter_AsFormattedBuffer(formatter) ); } + +// Pool tests — prove SOLIDSYSLOG_ORIGIN_SD_POOL_SIZE caps live instances +// and overflow falls back to the shared SolidSyslogNullSd. + +// clang-format off +TEST_GROUP(SolidSyslogOriginSdPool) +{ + SolidSyslogOriginSdConfig config{}; + struct SolidSyslogStructuredData* pooled[SOLIDSYSLOG_ORIGIN_SD_POOL_SIZE] = {}; + struct SolidSyslogStructuredData* overflow = nullptr; + + void setup() override + { + config.Software = "TestSoftware"; + config.SwVersion = "1.0.0"; + } + + void teardown() override + { + for (auto* handle : pooled) + { + if (handle != nullptr) + { + SolidSyslogOriginSd_Destroy(handle); + } + } + if (overflow != nullptr) + { + SolidSyslogOriginSd_Destroy(overflow); + } + } + + struct SolidSyslogStructuredData* MakeSd() + { + return SolidSyslogOriginSd_Create(&config); + } + + void FillPool() + { + for (auto*& slot : pooled) + { + slot = MakeSd(); + } + } +}; + +// clang-format on + +TEST(SolidSyslogOriginSdPool, FillingPoolThenOverflowReturnsDistinctFallback) +{ + FillPool(); + + overflow = MakeSd(); + + CHECK_TEXT(overflow != nullptr, "Fallback handle was nullptr"); + for (auto* slot : pooled) + { + CHECK_TEXT(slot != nullptr, "pool slot was nullptr (FillPool failed?)"); + CHECK_TEXT(overflow != slot, "Fallback handle collided with a pool slot"); + } +} diff --git a/Tests/SolidSyslogPassthroughBufferTest.cpp b/Tests/SolidSyslogPassthroughBufferTest.cpp index f3e00c6a..2e4844f9 100644 --- a/Tests/SolidSyslogPassthroughBufferTest.cpp +++ b/Tests/SolidSyslogPassthroughBufferTest.cpp @@ -7,6 +7,7 @@ using namespace CososoTesting; // NOLINT(google-build-using-namespace) -- test-f // macros #include "SolidSyslogBuffer.h" #include "SolidSyslogPassthroughBuffer.h" +#include "SolidSyslogTunables.h" #include "SenderFake.h" static const char* const TEST_MESSAGE = "hello"; @@ -27,7 +28,7 @@ TEST_GROUP(SolidSyslogPassthroughBuffer) void teardown() override { - SolidSyslogPassthroughBuffer_Destroy(); + SolidSyslogPassthroughBuffer_Destroy(buffer); SenderFake_Destroy(fakeSender); } @@ -89,3 +90,81 @@ IGNORE_TEST(SolidSyslogPassthroughBuffer, HappyPathOnly) // Write with NULL buffer does not crash // Destroy with NULL buffer does not crash } + +// Pool tests — prove SOLIDSYSLOG_PASSTHROUGH_BUFFER_POOL_SIZE caps live +// instances and overflow falls back to a distinct no-op buffer. Generic +// pool mechanics (lock counts, per-probe locking, stale-handle warning) +// are covered by SolidSyslogPoolAllocatorTest.cpp. + +// clang-format off +TEST_GROUP(SolidSyslogPassthroughBufferPool) +{ + struct SolidSyslogSender* fakeSender = nullptr; + struct SolidSyslogBuffer* pooled[SOLIDSYSLOG_PASSTHROUGH_BUFFER_POOL_SIZE] = {}; + struct SolidSyslogBuffer* overflow = nullptr; + + void setup() override + { + // cppcheck-suppress unreadVariable -- used across TEST_GROUP methods; cppcheck does not model CppUTest macros + fakeSender = SenderFake_Create(); + } + + void teardown() override + { + for (auto* handle : pooled) + { + if (handle != nullptr) + { + SolidSyslogPassthroughBuffer_Destroy(handle); + } + } + if (overflow != nullptr) + { + SolidSyslogPassthroughBuffer_Destroy(overflow); + } + SenderFake_Destroy(fakeSender); + } + + [[nodiscard]] struct SolidSyslogBuffer* MakeBuffer() const + { + return SolidSyslogPassthroughBuffer_Create(fakeSender); + } + + void FillPool() + { + for (auto*& slot : pooled) + { + slot = MakeBuffer(); + } + } +}; + +// clang-format on + +TEST(SolidSyslogPassthroughBufferPool, FillingPoolThenOverflowReturnsDistinctFallback) +{ + FillPool(); + + overflow = MakeBuffer(); + + CHECK_TEXT(overflow != nullptr, "Fallback handle was nullptr"); + for (auto* slot : pooled) + { + CHECK_TEXT(slot != nullptr, "pool slot was nullptr (FillPool failed?)"); + CHECK_TEXT(overflow != slot, "Fallback handle collided with a pool slot"); + } +} + +TEST(SolidSyslogPassthroughBufferPool, FallbackWriteAndReadAreNoOps) +{ + FillPool(); + overflow = MakeBuffer(); + + SolidSyslogBuffer_Write(overflow, "hello", 5); + + char readBuffer[16] = {}; + size_t bytesRead = 99; + CHECK_FALSE(SolidSyslogBuffer_Read(overflow, readBuffer, sizeof(readBuffer), &bytesRead)); + LONGS_EQUAL(0, bytesRead); + CALLED_FAKE_ON(SenderFake_Send, fakeSender, NEVER); +} diff --git a/Tests/SolidSyslogPosixMessageQueueBufferTest.cpp b/Tests/SolidSyslogPosixMessageQueueBufferTest.cpp index 2afd53c6..e2484c12 100644 --- a/Tests/SolidSyslogPosixMessageQueueBufferTest.cpp +++ b/Tests/SolidSyslogPosixMessageQueueBufferTest.cpp @@ -101,7 +101,7 @@ TEST(SolidSyslogPosixMessageQueueBuffer, SecondReadAfterSingleWriteReturnsFalse) TEST(SolidSyslogPosixMessageQueueBuffer, ServiceSendsMessageWrittenViaLog) { struct SolidSyslogSender* fakeSender = SenderFake_Create(); - SolidSyslogStore* nullStore = SolidSyslogNullStore_Create(); + SolidSyslogStore* nullStore = SolidSyslogNullStore_Get(); SolidSyslogConfig config = {buffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullStore, nullptr, 0}; SolidSyslog_Create(&config); @@ -111,7 +111,6 @@ TEST(SolidSyslogPosixMessageQueueBuffer, ServiceSendsMessageWrittenViaLog) CALLED_FAKE_ON(SenderFake_Send, fakeSender, ONCE); SolidSyslog_Destroy(); - SolidSyslogNullStore_Destroy(); SenderFake_Destroy(fakeSender); } diff --git a/Tests/SolidSyslogSwitchingSenderTest.cpp b/Tests/SolidSyslogSwitchingSenderTest.cpp index 5bdde009..16beec5a 100644 --- a/Tests/SolidSyslogSwitchingSenderTest.cpp +++ b/Tests/SolidSyslogSwitchingSenderTest.cpp @@ -1,9 +1,11 @@ #include #include +#include "ErrorHandlerFake.h" #include "SenderFake.h" #include "SolidSyslogSender.h" #include "SolidSyslogSwitchingSender.h" +#include "SolidSyslogTunables.h" #include "TestUtils.h" #include "CppUTest/TestHarness.h" @@ -60,7 +62,7 @@ TEST_GROUP(SolidSyslogSwitchingSender) void teardown() override { - SolidSyslogSwitchingSender_Destroy(); + SolidSyslogSwitchingSender_Destroy(sender); SenderFake_Destroy(innerC); SenderFake_Destroy(innerB); SenderFake_Destroy(innerA); @@ -68,6 +70,13 @@ TEST_GROUP(SolidSyslogSwitchingSender) void CreateSwitchingSender(size_t count) { + /* Pool semantics: if setup already created one (slot-allocated), destroy + * it before creating again so this call reuses the same slot rather than + * exhausting the pool and getting the Fallback. */ + if (sender != nullptr) + { + SolidSyslogSwitchingSender_Destroy(sender); + } struct SolidSyslogSwitchingSenderConfig config = {inners, count, TestSelector}; sender = SolidSyslogSwitchingSender_Create(&config); } @@ -86,16 +95,28 @@ TEST(SolidSyslogSwitchingSender, CreateDestroyWorksWithoutCrashing) TEST(SolidSyslogSwitchingSender, DestroyDoesNotSendToInnerSenders) { - SolidSyslogSwitchingSender_Destroy(); + SolidSyslogSwitchingSender_Destroy(sender); + sender = nullptr; + CALLED_FAKE_ON(SenderFake_Send, innerA, NEVER); CALLED_FAKE_ON(SenderFake_Send, innerB, NEVER); + + // Re-create so teardown's Destroy(sender) targets a live handle — + // the destroy under test has already freed the original slot. + CreateSwitchingSender(2); } TEST(SolidSyslogSwitchingSender, DestroyDoesNotDisconnectInnerSenders) { - SolidSyslogSwitchingSender_Destroy(); + SolidSyslogSwitchingSender_Destroy(sender); + sender = nullptr; + CALLED_FAKE_ON(SenderFake_Disconnect, innerA, NEVER); CALLED_FAKE_ON(SenderFake_Disconnect, innerB, NEVER); + + // Re-create so teardown's Destroy(sender) targets a live handle — + // the destroy under test has already freed the original slot. + CreateSwitchingSender(2); } TEST(SolidSyslogSwitchingSender, SendDelegatesToSenderAtSelectedIndex) @@ -203,10 +224,13 @@ TEST(SolidSyslogSwitchingSender, SelectorAtLastValidIndexDelegatesToThatSender) CALLED_FAKE_ON(SenderFake_Send, innerC, ONCE); } -TEST(SolidSyslogSwitchingSender, ZeroSenderCountSendReturnsFalse) +TEST(SolidSyslogSwitchingSender, ZeroSenderCountSendReturnsTrueToDropOnTheFloor) { + /* Out-of-range selector (or zero-count) resolves to the shared + * NullSender, whose Send returns true so messages drop rather than + * accumulate in the Store. */ CreateSwitchingSender(0); - CHECK_FALSE(SolidSyslogSender_Send(sender, "x", 1)); + CHECK_TRUE(SolidSyslogSender_Send(sender, "x", 1)); } TEST(SolidSyslogSwitchingSender, ZeroSenderCountDisconnectDoesNotCrash) @@ -215,10 +239,12 @@ TEST(SolidSyslogSwitchingSender, ZeroSenderCountDisconnectDoesNotCrash) SolidSyslogSender_Disconnect(sender); } -TEST(SolidSyslogSwitchingSender, SelectorBeyondEndSendReturnsFalseAndDoesNotTouchInnerSenders) +TEST(SolidSyslogSwitchingSender, SelectorBeyondEndSendReturnsTrueAndDoesNotTouchInnerSenders) { + /* Out-of-range selector resolves to the shared NullSender, whose + * Send returns true so messages drop rather than accumulate. */ selectorReturn = BEYOND_END; - CHECK_FALSE(SolidSyslogSender_Send(sender, "x", 1)); + CHECK_TRUE(SolidSyslogSender_Send(sender, "x", 1)); CALLED_FAKE_ON(SenderFake_Send, innerA, NEVER); CALLED_FAKE_ON(SenderFake_Send, innerB, NEVER); } @@ -244,3 +270,124 @@ TEST(SolidSyslogSwitchingSender, SelectorBeyondEndDisconnectBeforeSendDoesNotTou CALLED_FAKE_ON(SenderFake_Disconnect, innerA, NEVER); CALLED_FAKE_ON(SenderFake_Disconnect, innerB, NEVER); } + +// Pool tests — prove SOLIDSYSLOG_SWITCHING_SENDER_POOL_SIZE caps live +// instances and overflow falls back to the shared SolidSyslogNullSender. + +// clang-format off +TEST_GROUP(SolidSyslogSwitchingSenderPool) +{ + struct SolidSyslogSender* innerA = nullptr; + struct SolidSyslogSender* inners[1] = {nullptr}; + struct SolidSyslogSender* pooled[SOLIDSYSLOG_SWITCHING_SENDER_POOL_SIZE] = {}; + struct SolidSyslogSender* overflow = nullptr; + SolidSyslogSwitchingSenderConfig config; + + void setup() override + { + innerA = SenderFake_Create(); + inners[0] = innerA; + config = {inners, 1, TestSelector}; + } + + void teardown() override + { + for (auto* handle : pooled) + { + if (handle != nullptr) + { + SolidSyslogSwitchingSender_Destroy(handle); + } + } + if (overflow != nullptr) + { + SolidSyslogSwitchingSender_Destroy(overflow); + } + SenderFake_Destroy(innerA); + } + + struct SolidSyslogSender* MakeSender() + { + return SolidSyslogSwitchingSender_Create(&config); + } + + void FillPool() + { + for (auto*& slot : pooled) + { + slot = MakeSender(); + } + } +}; + +// clang-format on + +TEST(SolidSyslogSwitchingSenderPool, FillingPoolThenOverflowReturnsDistinctFallback) +{ + FillPool(); + + overflow = MakeSender(); + + CHECK_TEXT(overflow != nullptr, "Fallback handle was nullptr"); + for (auto* slot : pooled) + { + CHECK_TEXT(slot != nullptr, "pool slot was nullptr (FillPool failed?)"); + CHECK_TEXT(overflow != slot, "Fallback handle collided with a pool slot"); + } +} + +// Bad-setup tests — _Create rejects malformed config and routes to NullSender. + +// clang-format off +TEST_GROUP(SolidSyslogSwitchingSenderBadSetup) +{ + struct SolidSyslogSender* innerA = nullptr; + struct SolidSyslogSender* inners[1] = {nullptr}; + int sentinel = 0; + SolidSyslogSwitchingSenderConfig config; + + void setup() override + { + innerA = SenderFake_Create(); + inners[0] = innerA; + // cppcheck-suppress unreadVariable -- used across TEST_GROUP methods; cppcheck does not model CppUTest macros + config = {inners, 1, TestSelector}; + ErrorHandlerFake_Install(&sentinel); + } + + void teardown() override + { + ErrorHandlerFake_Uninstall(); + SenderFake_Destroy(innerA); + } +}; + +// clang-format on + +TEST(SolidSyslogSwitchingSenderBadSetup, CreateWithNullConfigReportsError) +{ + SolidSyslogSwitchingSender_Create(nullptr); + CHECK_REPORTED_ERROR("SolidSyslogSwitchingSender_Create called with NULL config"); +} + +TEST(SolidSyslogSwitchingSenderBadSetup, CreateWithNullSendersReportsError) +{ + config.Senders = nullptr; + SolidSyslogSwitchingSender_Create(&config); + CHECK_REPORTED_ERROR("SolidSyslogSwitchingSender_Create config.Senders is NULL"); +} + +TEST(SolidSyslogSwitchingSenderBadSetup, CreateWithNullSelectorReportsError) +{ + config.Selector = nullptr; + SolidSyslogSwitchingSender_Create(&config); + CHECK_REPORTED_ERROR("SolidSyslogSwitchingSender_Create config.Selector is NULL"); +} + +TEST(SolidSyslogSwitchingSenderBadSetup, SendOnBadSetupSenderReturnsTrueAndDrops) +{ + /* Bad-config _Create returns NullSender (Send drops on the floor), + * so a misconfigured SwitchingSender doesn't fill the Store. */ + struct SolidSyslogSender* badSender = SolidSyslogSwitchingSender_Create(nullptr); + CHECK_TRUE(SolidSyslogSender_Send(badSender, "x", 1)); +} diff --git a/Tests/SolidSyslogTest.cpp b/Tests/SolidSyslogTest.cpp index 6cdc31ce..8955b92b 100644 --- a/Tests/SolidSyslogTest.cpp +++ b/Tests/SolidSyslogTest.cpp @@ -367,7 +367,7 @@ TEST_GROUP(SolidSyslog) fakeSender = SenderFake_Create(); StringFake_Reset(); buffer = SolidSyslogPassthroughBuffer_Create(fakeSender); - store = SolidSyslogNullStore_Create(); + store = SolidSyslogNullStore_Get(); config = {buffer, nullptr, nullptr, StringFake_GetHostname, StringFake_GetAppName, StringFake_GetProcessId, store, nullptr, 0}; SolidSyslog_Create(&config); // cppcheck-suppress unreadVariable -- read via Log() through &message; cppcheck does not model CppUTest macros @@ -377,8 +377,7 @@ TEST_GROUP(SolidSyslog) void teardown() override { SolidSyslog_Destroy(); - SolidSyslogNullStore_Destroy(); - SolidSyslogPassthroughBuffer_Destroy(); + SolidSyslogPassthroughBuffer_Destroy(buffer); SenderFake_Destroy(fakeSender); } @@ -644,7 +643,7 @@ TEST(SolidSyslog, MetaSdProducesSequenceIdInStructuredData) SolidSyslog_Create(&config); Log(); STRCMP_EQUAL("[meta sequenceId=\"1\"]", SyslogField(lastMessage(), SYSLOG_FIELD_SDATA).c_str()); - SolidSyslogMetaSd_Destroy(); + SolidSyslogMetaSd_Destroy(metaSd); TestAtomicCounter_Destroy(counter); } @@ -663,7 +662,7 @@ TEST(SolidSyslog, MetaSdSequenceIdIncrementsAcrossLogCalls) Log(); Log(); STRCMP_EQUAL("[meta sequenceId=\"2\"]", SyslogField(lastMessage(), SYSLOG_FIELD_SDATA).c_str()); - SolidSyslogMetaSd_Destroy(); + SolidSyslogMetaSd_Destroy(metaSd); TestAtomicCounter_Destroy(counter); } @@ -682,7 +681,7 @@ TEST(SolidSyslog, MsgFieldPreservedWithMetaSd) message.Msg = "hello world"; Log(); STRCMP_EQUAL("hello world", SyslogMsg(lastMessage()).c_str()); - SolidSyslogMetaSd_Destroy(); + SolidSyslogMetaSd_Destroy(metaSd); TestAtomicCounter_Destroy(counter); } @@ -748,8 +747,8 @@ TEST(SolidSyslog, MetaSdAndTimeQualitySdCoexistInSdArray) "[meta sequenceId=\"1\"][timeQuality tzKnown=\"1\" isSynced=\"1\"]", SyslogField(lastMessage(), SYSLOG_FIELD_SDATA).c_str() ); - SolidSyslogTimeQualitySd_Destroy(); - SolidSyslogMetaSd_Destroy(); + SolidSyslogTimeQualitySd_Destroy(timeQuality); + SolidSyslogMetaSd_Destroy(metaSd); TestAtomicCounter_Destroy(counter); } @@ -1315,7 +1314,7 @@ TEST(SolidSyslog, ServiceSendsMessageReadFromBuffer) TEST(SolidSyslog, ServiceSendsBufferedMessageWithNullStore) { SolidSyslogBuffer* fakeBuffer = BufferFake_Create(); - SolidSyslogStore* nullStore = SolidSyslogNullStore_Create(); + SolidSyslogStore* nullStore = SolidSyslogNullStore_Get(); SolidSyslogConfig serviceConfig = {fakeBuffer, fakeSender, nullptr, nullptr, nullptr, nullptr, nullStore, nullptr, 0}; @@ -1331,7 +1330,6 @@ TEST(SolidSyslog, ServiceSendsBufferedMessageWithNullStore) SolidSyslog_Destroy(); SolidSyslog_Create(&config); - SolidSyslogNullStore_Destroy(); BufferFake_Destroy(); } @@ -1646,14 +1644,13 @@ TEST_GROUP(SolidSyslogLifecycle) // cppcheck-suppress unreadVariable -- read via validConfig() in tests; cppcheck does not model CppUTest macros buffer = BufferFake_Create(); // cppcheck-suppress unreadVariable -- read via validConfig() in tests; cppcheck does not model CppUTest macros - store = SolidSyslogNullStore_Create(); + store = SolidSyslogNullStore_Get(); } void teardown() override { SolidSyslog_Destroy(); ErrorHandlerFake_Uninstall(); - SolidSyslogNullStore_Destroy(); BufferFake_Destroy(); SenderFake_Destroy(sender); } diff --git a/Tests/SolidSyslogTimeQualitySdTest.cpp b/Tests/SolidSyslogTimeQualitySdTest.cpp index e9edd35f..f5ffec4f 100644 --- a/Tests/SolidSyslogTimeQualitySdTest.cpp +++ b/Tests/SolidSyslogTimeQualitySdTest.cpp @@ -1,11 +1,16 @@ #include #include +#include "CppUTest/TestHarness.h" +#include "ErrorHandlerFake.h" #include "SolidSyslogFormatter.h" -#include "SolidSyslogTimeQualitySd.h" #include "SolidSyslogStructuredData.h" #include "SolidSyslogTimeQuality.h" -#include "CppUTest/TestHarness.h" +#include "SolidSyslogTimeQualitySd.h" +#include "SolidSyslogTunables.h" +#include "TestUtils.h" + +using namespace CososoTesting; // NOLINT(google-build-using-namespace) -- test-file scope only; brings ONCE/NEVER into scope for CALLED_FAKE struct SolidSyslogFormatter; struct SolidSyslogStructuredData; @@ -40,7 +45,7 @@ TEST_GROUP(SolidSyslogTimeQualitySd) void teardown() override { - SolidSyslogTimeQualitySd_Destroy(); + SolidSyslogTimeQualitySd_Destroy(sd); } void format() const @@ -142,3 +147,85 @@ TEST(SolidSyslogTimeQualitySd, DestroyDoesNotCrash) { // Covered by teardown — this test documents the intent } + +// Pool tests — prove SOLIDSYSLOG_TIME_QUALITY_SD_POOL_SIZE caps live +// instances and overflow falls back to the shared SolidSyslogNullSd. + +// clang-format off +TEST_GROUP(SolidSyslogTimeQualitySdPool) +{ + // cppcheck-suppress constVariable -- assigned in TEST() bodies; cppcheck does not model CppUTest macros + struct SolidSyslogStructuredData* pooled[SOLIDSYSLOG_TIME_QUALITY_SD_POOL_SIZE] = {}; + struct SolidSyslogStructuredData* overflow = nullptr; + + void teardown() override + { + for (auto* handle : pooled) + { + if (handle != nullptr) + { + SolidSyslogTimeQualitySd_Destroy(handle); + } + } + // cppcheck-suppress knownConditionTrueFalse -- assigned in TEST() bodies; cppcheck does not model CppUTest macros + if (overflow != nullptr) + { + SolidSyslogTimeQualitySd_Destroy(overflow); + } + } + + static struct SolidSyslogStructuredData* MakeSd() + { + return SolidSyslogTimeQualitySd_Create(StubGetTimeQuality); + } + + void FillPool() + { + for (auto*& slot : pooled) + { + slot = MakeSd(); + } + } +}; + +// clang-format on + +TEST(SolidSyslogTimeQualitySdPool, FillingPoolThenOverflowReturnsDistinctFallback) +{ + FillPool(); + + overflow = MakeSd(); + + CHECK_TEXT(overflow != nullptr, "Fallback handle was nullptr"); + for (auto* slot : pooled) + { + CHECK_TEXT(slot != nullptr, "pool slot was nullptr (FillPool failed?)"); + CHECK_TEXT(overflow != slot, "Fallback handle collided with a pool slot"); + } +} + +// Bad-setup test — _Create rejects NULL callback and routes to NullSd. + +// clang-format off +TEST_GROUP(SolidSyslogTimeQualitySdBadSetup) +{ + int sentinel = 0; + + void setup() override + { + ErrorHandlerFake_Install(&sentinel); + } + + void teardown() override + { + ErrorHandlerFake_Uninstall(); + } +}; + +// clang-format on + +TEST(SolidSyslogTimeQualitySdBadSetup, CreateWithNullCallbackReportsError) +{ + SolidSyslogTimeQualitySd_Create(nullptr); + CHECK_REPORTED_ERROR("SolidSyslogTimeQualitySd_Create called with NULL getTimeQuality"); +} diff --git a/Tests/SolidSyslogUdpSenderTest.cpp b/Tests/SolidSyslogUdpSenderTest.cpp index 9d7a936a..b5ad7d4b 100644 --- a/Tests/SolidSyslogUdpSenderTest.cpp +++ b/Tests/SolidSyslogUdpSenderTest.cpp @@ -163,7 +163,7 @@ TEST_GROUP_BASE(SolidSyslogUdpSender, UdpSenderTestBase) void teardown() override { - SolidSyslogUdpSender_Destroy(); + SolidSyslogUdpSender_Destroy(sender); teardownFakesWithPosixDatagram(); } }; @@ -353,8 +353,8 @@ TEST_GROUP_BASE(SolidSyslogUdpSenderDestroy, UdpSenderTestBase) void CreateAndDestroy() const { - SolidSyslogUdpSender_Create(&config); - SolidSyslogUdpSender_Destroy(); + struct SolidSyslogSender* localSender = SolidSyslogUdpSender_Create(&config); + SolidSyslogUdpSender_Destroy(localSender); } }; @@ -370,7 +370,7 @@ TEST(SolidSyslogUdpSenderDestroy, DestroyAfterSendClosesSocket) { sender = SolidSyslogUdpSender_Create(&config); Send(); - SolidSyslogUdpSender_Destroy(); + SolidSyslogUdpSender_Destroy(sender); CALLED_FAKE(SocketFake_Close, ONCE); LONGS_EQUAL(SocketFake_SocketFd(), SocketFake_LastClosedFd()); } @@ -380,7 +380,7 @@ TEST(SolidSyslogUdpSenderDestroy, DestroyAfterDisconnectDoesNotDoubleClose) sender = SolidSyslogUdpSender_Create(&config); Send(); SolidSyslogSender_Disconnect(sender); - SolidSyslogUdpSender_Destroy(); + SolidSyslogUdpSender_Destroy(sender); CALLED_FAKE(SocketFake_Close, ONCE); } @@ -388,7 +388,7 @@ TEST(SolidSyslogUdpSenderDestroy, SimpleScenario) { sender = SolidSyslogUdpSender_Create(&config); Send(); - SolidSyslogUdpSender_Destroy(); + SolidSyslogUdpSender_Destroy(sender); CALLED_FAKE(SocketFake_Socket, ONCE); LONGS_EQUAL(AF_INET, SocketFake_SocketDomain()); @@ -410,7 +410,7 @@ TEST_GROUP_BASE(SolidSyslogUdpSenderConfig, UdpSenderTestBase) void teardown() override { - SolidSyslogUdpSender_Destroy(); + SolidSyslogUdpSender_Destroy(sender); teardownFakesWithPosixDatagram(); } }; @@ -483,7 +483,7 @@ TEST_GROUP_BASE(SolidSyslogUdpSenderFailure, UdpSenderTestBase) void teardown() override { - SolidSyslogUdpSender_Destroy(); + SolidSyslogUdpSender_Destroy(sender); teardownFakesWithPosixDatagram(); } @@ -556,7 +556,7 @@ TEST_GROUP_BASE(SolidSyslogUdpSenderRetry, UdpSenderTestBase) void teardown() override { - SolidSyslogUdpSender_Destroy(); + SolidSyslogUdpSender_Destroy(sender); teardownFakesWithDatagramFake(); } @@ -719,7 +719,7 @@ TEST_GROUP_BASE(SolidSyslogUdpSenderBadSetup, UdpSenderTestBase) void teardown() override { - SolidSyslogUdpSender_Destroy(); + SolidSyslogUdpSender_Destroy(sender); teardownFakesWithPosixDatagram(); ErrorHandlerFake_Uninstall(); } @@ -781,3 +781,65 @@ TEST(SolidSyslogUdpSenderBadSetup, SendWithNullBufferReportsErrorAndDoesNotSend) CHECK_REPORTED_ERROR("SolidSyslogUdpSender_Send called with NULL buffer"); CALLED_FAKE(SocketFake_Sendto, NEVER); } + +// Pool tests — prove SOLIDSYSLOG_UDP_SENDER_POOL_SIZE caps live instances +// and overflow falls back to the shared SolidSyslogNullSender. Generic +// pool mechanics (lock counts, per-probe locking, stale-handle warning) +// are covered by SolidSyslogPoolAllocatorTest.cpp. + +// clang-format off +TEST_GROUP_BASE(SolidSyslogUdpSenderPool, UdpSenderTestBase) +{ + struct SolidSyslogSender* pooled[SOLIDSYSLOG_UDP_SENDER_POOL_SIZE] = {}; + struct SolidSyslogSender* overflow = nullptr; + + void setup() override + { + setupFakesWithPosixDatagram(); + } + + void teardown() override + { + for (auto* handle : pooled) + { + if (handle != nullptr) + { + SolidSyslogUdpSender_Destroy(handle); + } + } + if (overflow != nullptr) + { + SolidSyslogUdpSender_Destroy(overflow); + } + teardownFakesWithPosixDatagram(); + } + + struct SolidSyslogSender* MakeSender() + { + return SolidSyslogUdpSender_Create(&config); + } + + void FillPool() + { + for (auto*& slot : pooled) + { + slot = MakeSender(); + } + } +}; + +// clang-format on + +TEST(SolidSyslogUdpSenderPool, FillingPoolThenOverflowReturnsDistinctFallback) +{ + FillPool(); + + overflow = MakeSender(); + + CHECK_TEXT(overflow != nullptr, "Fallback handle was nullptr"); + for (auto* slot : pooled) + { + CHECK_TEXT(slot != nullptr, "pool slot was nullptr (FillPool failed?)"); + CHECK_TEXT(overflow != slot, "Fallback handle collided with a pool slot"); + } +}