Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Bdd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,19 @@ for the compose layout.

## Tags

Authoritative reference: [`docs/bdd.md`](../docs/bdd.md#feature-tags). Quick recap below.

| Tag | Meaning |
|---|---|
| `@udp` / `@tcp` / `@tls` / `@mtls` | Transport-specific scenario; CI jobs filter by these. |
| `@buffered` | Needs a buffered wiring (CircularBuffer / PosixMessageQueueBuffer / service thread) beyond single-task PassthroughBuffer. Carried by all three targets. |
| `@store` | Needs file-backed `SolidSyslogBlockStore` capability (write blocks, replay, threshold callbacks). Carried by all three targets. |
| `@requires_message_size_1500` | Needs `SOLIDSYSLOG_MAX_MESSAGE_SIZE >= 1500` — only the UDP path-MTU clipping feature. FreeRTOS keeps the default trimmed, so this scenario is also `@freertoswip`. |
| `@windows_wip` | Skipped on the Windows runner (typically OS-specific behaviour the OTel oracle doesn't model). |
| `@freertoswip` | Skipped on the FreeRTOS-on-QEMU runner. Per-scenario follow-up tag for capability gaps; removed scenario-by-scenario as each gap closes. The early bring-up reasons (hardcoded `TEST_*` values, missing SD wiring, getopt-only args) have all been closed out by S08.03 + S08.04 slices. |
| `@rtc` | Scenario assumes the device has an RTC and synchronised wall-clock time. Run on Linux/Windows (which have both). Skipped on FreeRTOS, which models a no-RTC product per RFC 5424 §6.2.3.1. |
| `@no_rtc` | Scenario asserts the no-RTC product behaviour over the wire (`tzKnown="0"`, `isSynced="0"`). Run on FreeRTOS. Skipped on Linux/Windows. The NILVALUE TIMESTAMP itself is not asserted via the oracle — syslog-ng silently substitutes receipt time for `${ISODATE}` / `${S_ISODATE}` when the wire timestamp is NILVALUE — so that case is covered by formatter unit tests. |
| `@wip` | Globally skipped on every runner. |
| `@wip` | Globally skipped on every runner. Not currently in use on any scenario. |

## Running locally

Expand All @@ -51,7 +56,7 @@ once and run Behave from inside the container:
```bash
cmake --preset freertos-cross
cmake --build --preset freertos-cross --target SolidSyslogBddTarget
behave --tags='not @wip and not @freertoswip and not @rtc and not @windows_wip and (@udp or @tcp)' Bdd/features/
behave --tags='not @wip and not @freertoswip and not @rtc and not @windows_wip and (@udp or @tcp or @tls or @mtls)' Bdd/features/
```

`BDD_TARGET=freertos` and `EXAMPLE_BINARY=build/freertos-cross/...` are
Expand Down
28 changes: 15 additions & 13 deletions Bdd/Targets/FreeRtos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ QEMU's stdio UART.

SolidSyslog runs here with the portable `SolidSyslogCircularBuffer` +
`SolidSyslogFreeRtosMutex` drained by a dedicated FreeRTOS Service task,
a `SolidSyslogSwitchingSender` wrapping a UDP sender (over
`SolidSyslogFreeRtosDatagram`) and a TCP sender (over
`SolidSyslogFreeRtosTcpStream` + `SolidSyslogStreamSender`), the
`SolidSyslogFreeRtosResolver` pinned to the QEMU slirp gateway
(`10.0.2.2`), and the `Bdd/Targets/Common/BddTargetInteractive` runner.
Drive identity / endpoint / PRIVAL fields over the UART command channel
with `set NAME VALUE`, flip the active transport at runtime with
`switch udp` / `switch tcp` (routed through `BddTargetSwitchConfig`),
emit N RFC 5424 messages with `send N`, exit cleanly with `quit`. See
a `SolidSyslogSwitchingSender` wrapping four inner senders — UDP over
`SolidSyslogFreeRtosDatagram`, TCP over `SolidSyslogFreeRtosTcpStream` +
`SolidSyslogStreamSender`, and TLS + mTLS via `SolidSyslogMbedTlsStream`
layered over the same `SolidSyslogFreeRtosTcpStream` byte transport.
`SolidSyslogFreeRtosResolver` is pinned to the QEMU slirp gateway
(`10.0.2.2`), and the `Bdd/Targets/Common/BddTargetInteractive` runner
drives the target. Drive identity / endpoint / PRIVAL fields over the
UART command channel with `set NAME VALUE`, flip the active transport at
runtime with `switch udp` / `switch tcp` / `switch tls` / `switch mtls`
(routed through `BddTargetSwitchConfig`), emit N RFC 5424 messages with
`send N`, exit cleanly with `quit`. See
[`Bdd/README.md`](../../Bdd/README.md) for how Behave pipes these
commands through `qemu-system-arm`'s stdio UART.

Expand Down Expand Up @@ -163,10 +165,10 @@ static IPv4 of `10.0.2.15` on the QEMU slirp network, and starts the
scheduler. On link-up the IP-task event hook spawns the interactive task
(running `BddTargetInteractive_Run`) and the Service task (draining the
`SolidSyslogCircularBuffer` and pushing through the
`SolidSyslogSwitchingSender`'s active inner — UDP by default, TCP after
`switch tcp`). Each `send N` line over the UART emits N RFC 5424
messages to `{10.0.2.2, port=g_port}` over whichever transport is
currently selected.
`SolidSyslogSwitchingSender`'s active inner — UDP by default; TCP, TLS,
or mTLS after the matching `switch` command). Each `send N` line over
the UART emits N RFC 5424 messages to `{10.0.2.2, port=g_port}` over
whichever transport is currently selected.

The FreeRTOS GCC ARM_CM3 port supplies `vPortSVCHandler`,
`xPortPendSVHandler`, and `xPortSysTickHandler`.
Expand Down
31 changes: 17 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,23 @@ block store-and-forward with CRC-16 integrity, and the full
[IEC 62443 SL1–SL4 component set](docs/iec62443.md).

FreeRTOS support is in active development on Cortex-M3 (mps2-an385 under QEMU):
UDP and TCP transports via FreeRTOS-Plus-TCP, persistent store-and-forward via
ChaN FatFs (semihosting-backed disk image in BDD; integrator-supplied
`diskio.c` in production), host-TDD'd adapters, an interactive BDD target
wired with the portable CircularBuffer + FreeRtosMutex behind a Service
task, and BDD scenarios driven through QEMU's UART
UDP, TCP, TLS, and mTLS transports — TCP and UDP via FreeRTOS-Plus-TCP, TLS
and mTLS via `SolidSyslogMbedTlsStream` (Mbed TLS) layered over the same
FreeRTOS-Plus-TCP byte stream. Persistent store-and-forward via ChaN FatFs
(semihosting-backed disk image in BDD; integrator-supplied `diskio.c` in
production), host-TDD'd adapters, an interactive BDD target wired with the
portable CircularBuffer + FreeRtosMutex behind a Service task, and BDD
scenarios driven through QEMU's UART
([epic E08 #10](https://github.com/DavidCozens/solid-syslog/issues/10)).

**Not yet production-ready**, and no API stability guarantee yet. Known gaps:

- Public API may evolve — sender implementations currently use static
singleton state (`SolidSyslogUdpSender`, `SolidSyslogStreamSender`,
`SolidSyslogSwitchingSender`), so multiple concurrent instances per
process aren't yet supported. Additional platform backends (RTOS,
custom) are still to land.
- Public API may evolve. Multiple concurrent sender instances are now
pool-backed (E11) — `SOLIDSYSLOG_UDP_SENDER_POOL_SIZE`,
`SOLIDSYSLOG_STREAM_SENDER_POOL_SIZE`,
`SOLIDSYSLOG_SWITCHING_SENDER_POOL_SIZE` in
`Core/Interface/SolidSyslogTunablesDefaults.h` set the slot counts; the
matching pools live in `Core/Source/SolidSyslog*SenderStatic.c`.
- At-rest integrity is CRC-16 only; HMAC + AES-at-rest are planned for SL4
([E17 #105](https://github.com/DavidCozens/solid-syslog/issues/105)).
- TLS revocation (CRL / OCSP) is deferred to the OS trust store; the library
Expand Down Expand Up @@ -85,16 +88,16 @@ Public headers are split by audience (Interface Segregation Principle):
- **`SolidSyslogTimeQualitySd.h`** — timeQuality structured data (RFC 5424 §7.1): tzKnown, isSynced, syncAccuracy
- **`SolidSyslogOriginSd.h`** — origin structured data (RFC 5424 §7.2): software, swVersion, enterpriseId, ip
- **`SolidSyslogPosixClock.h`** / **`SolidSyslogPosixHostname.h`** / **`SolidSyslogPosixProcessId.h`** / **`SolidSyslogPosixSysUpTime.h`** — POSIX helpers
- **`SolidSyslogFreeRtosDatagram.h`** / **`SolidSyslogFreeRtosTcpStream.h`** / **`SolidSyslogFreeRtosResolver.h`** / **`SolidSyslogFreeRtosMutex.h`** / **`SolidSyslogFreeRtosSysUpTime.h`** — FreeRTOS adapters: FreeRTOS-Plus-TCP UDP datagram and TCP stream (with ARP-prime on cold connect and bounded `SO_RCVTIMEO` connect), hardcoded-IPv4 resolver, `xSemaphoreCreateMutexStatic`-backed mutex for CircularBuffer, and a kernel-tick sysUpTime source
- **`SolidSyslogFreeRtosDatagram.h`** / **`SolidSyslogFreeRtosTcpStream.h`** / **`SolidSyslogFreeRtosResolver.h`** / **`SolidSyslogFreeRtosAddress.h`** / **`SolidSyslogFreeRtosMutex.h`** / **`SolidSyslogFreeRtosSysUpTime.h`** — FreeRTOS adapters: FreeRTOS-Plus-TCP UDP datagram and TCP stream (with ARP-prime on cold connect and bounded `SO_RCVTIMEO` connect), hardcoded-IPv4 resolver, `struct freertos_sockaddr` address adapter, `xSemaphoreCreateMutexStatic`-backed mutex for CircularBuffer, and a kernel-tick sysUpTime source
- **`SolidSyslogFatFsFile.h`** — ChaN FatFs file adapter for the `SolidSyslogFile` extension point; `f_sync` per write for crash-safe store-and-forward. RTOS-agnostic; runs on bare-metal, FreeRTOS, Zephyr, NuttX

Three BDD-driven target binaries exercise the library on each supported
platform. They live under [`Bdd/Targets/`](Bdd/Targets/) — one binary
per platform, all named `SolidSyslogBddTarget`:

- **`Bdd/Targets/Linux/`** — POSIX, PosixMessageQueueBuffer, two pthreads (logger + service), SwitchingSender over UDP + TCP + TLS + mTLS (TLS build required for the last two); `--transport` sets the initial transport, `switch <name>` flips it at runtime
- **`Bdd/Targets/Windows/`** — Windows, CircularBuffer + WindowsMutex, Win32 service thread (`_beginthreadex`) draining the buffer, Winsock UDP / TCP, with the Windows clock / hostname / process-id / sysUpTime helpers
- **`Bdd/Targets/FreeRtos/`** — FreeRTOS-on-QEMU (Cortex-M3, mps2-an385), CircularBuffer + FreeRtosMutex drained by a dedicated Service task, UDP + TCP via FreeRTOS-Plus-TCP, persistent store-and-forward via ChaN FatFs over a semihosting-backed disk image, interactive `set NAME VALUE` / `send N` / `set store file` / `set shutdown 1` / `quit` command channel over the CMSDK UART; BDD-driven against syslog-ng including `store_and_forward`, `power_cycle_replay`, and `store_capacity` scenarios. See [`Bdd/Targets/FreeRtos/README.md`](Bdd/Targets/FreeRtos/README.md)
- **`Bdd/Targets/Linux/`** — POSIX, PosixMessageQueueBuffer, two pthreads (logger + service), SwitchingSender over UDP + TCP + TLS + mTLS (OpenSSL); `--transport` sets the initial transport, `switch <name>` flips it at runtime
- **`Bdd/Targets/Windows/`** — Windows, CircularBuffer + WindowsMutex, Win32 service thread (`_beginthreadex`) draining the buffer, SwitchingSender over Winsock UDP / TCP + OpenSSL TLS / mTLS over Winsock TCP, file-backed `SolidSyslogBlockStore` over `SolidSyslogWindowsFile`, with the Windows clock / hostname / process-id / sysUpTime helpers
- **`Bdd/Targets/FreeRtos/`** — FreeRTOS-on-QEMU (Cortex-M3, mps2-an385), CircularBuffer + FreeRtosMutex drained by a dedicated Service task, SwitchingSender over UDP + TCP via FreeRTOS-Plus-TCP plus TLS + mTLS via `SolidSyslogMbedTlsStream` (Mbed TLS) layered over the same FreeRTOS-Plus-TCP byte stream, persistent store-and-forward via ChaN FatFs over a semihosting-backed disk image, interactive `set NAME VALUE` / `send N` / `set store file` / `set shutdown 1` / `quit` command channel over the CMSDK UART; BDD-driven against syslog-ng including `store_and_forward`, `power_cycle_replay`, `store_capacity`, `tls_transport`, and `mtls_transport` scenarios. See [`Bdd/Targets/FreeRtos/README.md`](Bdd/Targets/FreeRtos/README.md)

## Compliance

Expand Down
34 changes: 19 additions & 15 deletions docs/bdd.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ fail here.
│ runs │ reads
▼ │
┌──────────────┐│
Example ││
binary ││
BDD target ││
binary ││
└──────┬───────┘│
│ │
UDP 5514 / TCP 5514│
Expand Down Expand Up @@ -50,7 +50,7 @@ fault-finding tips.
| FreeRTOS | inside `freertos-target` (cross image carries QEMU + Behave) | `syslog-ng-freertos` (shared netns; QEMU slirp `10.0.2.2` reaches it on loopback) |
| Windows | `behave` on the runner | `otelcol-contrib` (no compose; runner-direct) |

The Linux example binary is built in the `gcc` container but executed by Behave via
The Linux BDD target binary is built in the `gcc` container but executed by Behave via
`subprocess.run`. Both services share the workspace mount, so `Bdd/output/received.log`
is visible to both syslog-ng (writer) and Behave (reader) without any network file
transfer. The FreeRTOS ELF is built in the cross image and run under
Expand Down Expand Up @@ -132,23 +132,27 @@ etc.) without rewriting feature tags.
| `@tcp` | Needs TCP transport (RFC 6587 framing) |
| `@tls` | Needs TLS transport (RFC 5425, server-auth) |
| `@mtls` | Needs mutual TLS (client cert + key) |
| `@buffered` | Needs a Linux-only buffered example capability beyond a basic ring buffer + service thread — file-backed block store, switching sender between transports, or syslog-ng reload via the UNIX control socket. The cross-platform "single message via UDP/TCP through a real buffer" path is *not* `@buffered` post-S13.18; TLS and mTLS dropped `@buffered` in S13.19 once the OTel oracle gained TLS receivers (Windows otelcol-contrib listens on 6514 / 6515 with `client_ca_file` for mTLS). |
| `@buffered` | Needs a buffered wiring (CircularBuffer + service thread, PosixMessageQueueBuffer, etc.) beyond the single-task PassthroughBuffer path — e.g. file-backed block store, switching sender between transports, syslog-ng reload via the UNIX control socket. All three target binaries (Linux, Windows, FreeRTOS) carry a buffered wiring as of S13/S26, so `@buffered` is no longer excluded by any runner. TLS and mTLS dropped `@buffered` in S13.19 once the OTel oracle gained TLS receivers (Windows otelcol-contrib listens on 6514 / 6515 with `client_ca_file` for mTLS). |
| `@store` | Needs file-backed `SolidSyslogBlockStore` capability in the target (write blocks to disk, replay across restart, threshold callbacks). Carried alongside `@tcp @buffered` on the store-and-forward / capacity / power-cycle / block-lifecycle features. Run on all three targets — Linux + Windows over `SolidSyslogPosixFile` / `SolidSyslogWindowsFile`; FreeRTOS over `SolidSyslogFatFsFile`. |
| `@rtc` | Scenario assumes a real-time clock with synchronised wall-clock time — asserts a known absolute TIMESTAMP. Run on Linux and Windows; excluded on FreeRTOS, which models a no-RTC product per RFC 5424 §6.2.3.1. |
| `@no_rtc` | Scenario asserts the no-RTC product behaviour over the wire (`tzKnown="0"`, `isSynced="0"`). Run on FreeRTOS; excluded on Linux and Windows. The complementary pair of `@rtc`. |
| `@requires_message_size_1500` | Scenario requires `SOLIDSYSLOG_MAX_MESSAGE_SIZE` to be at least 1500 bytes — used by the UDP path-MTU clipping feature, which has to drive an oversized payload through `EMSGSIZE`. The Linux and Windows targets are built with the default 2048 max; FreeRTOS keeps the default trimmed to fit the embedded memory budget, so this scenario is also gated `@freertoswip` until the FreeRTOS target opts in. |

Three rollout markers are also used (temporary; remove once the scenario passes):

| Tag | Meaning |
| --- | --- |
| `@wip` | Skip everywhere — work in progress |
| `@windows_wip` | Skip on Windows only — should work but not yet verified |
| `@freertoswip` | Skip on FreeRTOS only — scenario currently fails or errors on the FreeRTOS-on-QEMU target and is gated until the relevant capability lands. Each tagged scenario is a follow-up tied to a specific gap; the tag is removed scenario-by-scenario as the gap closes. The early bring-up reasons (hardcoded `TEST_*` values, missing SD elements, cmdline-only args) have all been closed out by S08.03 + S08.04 slices. |
| `@wip` | Skip everywhere — work in progress. Not currently in use on any scenario. |
| `@windows_wip` | Skip on Windows only — should work but not yet verified. Currently on `tcp_singletask.feature` (whole feature) and one scenario in `udp_mtu.feature`. |
| `@freertoswip` | Skip on FreeRTOS only — scenario currently fails or errors on the FreeRTOS-on-QEMU target and is gated until the relevant capability lands. Each tagged scenario is a follow-up tied to a specific gap; the tag is removed scenario-by-scenario as the gap closes. Currently on one scenario in `udp_mtu.feature` (paired with `@requires_message_size_1500`). |

Runner tag filters:
Runner tag filters (canonical source: `ci/docker-compose.bdd.yml` for Linux + FreeRTOS, `.github/workflows/ci.yml` for Windows):

| Runner | Filter |
| --- | --- |
| Linux (syslog-ng) | `not @wip` |
| FreeRTOS (syslog-ng-freertos via QEMU) | `not @wip and not @freertoswip and not @rtc and not @windows_wip and (@udp or @tcp)` |
| Windows (OTel Collector) | `not @wip and not @windows_wip and not @buffered` |
| Linux (syslog-ng) | `not @wip and not @no_rtc` |
| FreeRTOS (syslog-ng-freertos via QEMU) | `not @wip and not @freertoswip and not @rtc and not @windows_wip and (@udp or @tcp or @tls or @mtls)` |
| Windows (OTel Collector) | `not @wip and not @windows_wip and not @no_rtc` |
Comment on lines +149 to +155

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Windows filter docs now conflict with the local Windows command example.

Line 155 updates Windows to not @wipand not@windows_wipand not@no_rtc, but the local command later in this file still uses `not `@buffered (Line 200). Please align both to one canonical filter to avoid running the wrong scenario set locally.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~149-~149: The official name of this software platform is spelled with a capital “H”.
Context: ...-compose.bdd.ymlfor Linux + FreeRTOS,.github/workflows/ci.yml` for Windows): | Runn...

(GITHUB)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/bdd.md` around lines 149 - 155, The Windows runner filter in the docs is
inconsistent: the canonical filter string "not `@wip` and not `@windows_wip` and not
`@no_rtc`" (used in the Runner table) conflicts with the local command example
that uses "not `@buffered`"; update the local command example to use the canonical
Windows filter string so both references match, or alternatively change the
table entry to "not `@buffered`" if you intend that as canonical—ensure you update
the occurrences of the unique filter strings ("not `@wip` and not `@windows_wip` and
not `@no_rtc`" and "not `@buffered`") and any local command examples that reference
the Windows filter so they are identical.


## Two oracles, one step file

Expand All @@ -160,10 +164,10 @@ Step definitions read the active oracle from `ORACLE_FORMAT`:
| OTel Collector Contrib (JSON Lines) | `otel-jsonl` | `Bdd/output/received.jsonl` | Windows native |

`parse_oracle_line` dispatches to the right parser; both produce the same flat field dict
(`PRIORITY`, `TIMESTAMP`, `HOSTNAME`, `APP_NAME`, `PROCID`, ...) so the `Then` steps don't
need to know which oracle is in use. Walking-skeleton scope only — `STRUCTURED_DATA`
re-rendering for the OTel parser is added when the structured-data scenarios are
promoted out of `@windows_wip`.
(`PRIORITY`, `TIMESTAMP`, `HOSTNAME`, `APP_NAME`, `PROCID`, `STRUCTURED_DATA`, ...) so the
`Then` steps don't need to know which oracle is in use. The OTel parser re-renders the SD-ELEMENT
back into RFC 5424 wire form via `_render_otel_structured_data` so the structured-data scenarios
run cross-platform against the same assertions.

## Local Windows BDD setup

Expand Down
Loading
Loading