ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
syslog.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file syslog.h
6 * @brief The syslog originator (RFC 5424), carried one message per UDP datagram (RFC 5426).
7 *
8 * RFC 5424 sec 3 names the ends: an originator "generates syslog content to be carried in a
9 * message", a collector "gathers syslog content for further analysis". This module is the
10 * originator, and the collector is a UDP endpoint on the well-known port 514 (RFC 5426 sec 3.3).
11 *
12 * RFC 5424 sec 6 gives the message:
13 *
14 * SYSLOG-MSG = HEADER SP STRUCTURED-DATA [SP MSG]
15 * HEADER = PRI VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID
16 *
17 * The line built here is `<PRIVAL>1 - HOSTNAME APP-NAME - - - MSG`. VERSION is "1" (sec 6.2.2),
18 * PRIVAL is Facility * 8 + Severity in 0..191 (sec 6.2.1), and TIMESTAMP (sec 6.2.3), PROCID
19 * (sec 6.2.6), MSGID (sec 6.2.7) and STRUCTURED-DATA (sec 6.3) are each the NILVALUE "-"
20 * (sec 6, `NILVALUE = "-"`). MSG is MSG-ANY, the caller's octets with no leading BOM (sec 6.4).
21 *
22 * RFC 5426 sec 3.1: one syslog message per datagram, no additional data in the payload. Nothing is
23 * acknowledged (RFC 5426 sec 4.1), so a send reports only that the stack took the octets.
24 *
25 * @ref PROTOCORE_SYSLOG_MSG_MAX bounds the line a log builds; RFC 5426 sec 3.2 puts the receiver floor
26 * at 480 octets for IPv4 and 1180 for IPv6. A line that does not fit reports 0 bytes and no
27 * datagram leaves.
28 *
29 * HOSTNAME and APP-NAME are copied into fixed storage at init, so nothing a caller passes has to
30 * outlive the call.
31 *
32 * The module exports one symbol, @ref Syslog. Everything in syslog.c has internal linkage.
33 *
34 * @author Douglas Quigg (dstroy0)
35 * @date 2026
36 */
37
38#ifndef PROTOCORE_SYSLOG_H
39#define PROTOCORE_SYSLOG_H
40
41#include "protocore_config.h" // the entry point: protocore_types.h for the widths
42
43#if PROTOCORE_ENABLE_SYSLOG
44
46
47/**
48 * @brief RFC 5424 sec 6.2.1 Severity, the low three bits of PRIVAL (lower is more severe).
49 *
50 * The section states these values "are not normative but often used".
51 */
52typedef enum PROTO_ENUM_PACKED
53{
54 SYSLOG_EMERG = 0, ///< Emergency: system is unusable
55 SYSLOG_ALERT = 1, ///< Alert: action must be taken immediately
56 SYSLOG_CRIT = 2, ///< Critical: critical conditions
57 SYSLOG_ERR = 3, ///< Error: error conditions
58 SYSLOG_WARNING = 4, ///< Warning: warning conditions
59 SYSLOG_NOTICE = 5, ///< Notice: normal but significant condition
60 SYSLOG_INFO = 6, ///< Informational: informational messages
61 SYSLOG_DEBUG = 7, ///< Debug: debug-level messages
62} SyslogSeverity;
63
64/**
65 * @brief RFC 5424 sec 6.2.1 Facility, the value PRIVAL multiplies by 8.
66 *
67 * The section states these values "are not normative but often used".
68 */
69typedef enum PROTO_ENUM_PACKED
70{
71 SYSLOG_FAC_USER = 1, ///< user-level messages
72 SYSLOG_FAC_DAEMON = 3, ///< system daemons
73 SYSLOG_FAC_LOCAL0 = 16, ///< local use 0 (local0)
74 SYSLOG_FAC_LOCAL1 = 17, ///< local use 1 (local1)
75 SYSLOG_FAC_LOCAL7 = 23, ///< local use 7 (local7)
76} SyslogFacility;
77
78/** @brief RFC 5426 sec 3.3: the collector endpoint every datagram is sent to. */
79typedef struct
80{
81 const char *addr; ///< the collector's address as text, v4 or v6, parsed once by an init
82 uint16_t port; ///< its UDP port; 514 is the well-known one
83} SyslogCollectorArgs;
84
85/** @brief RFC 5424 sec 6.2: the HEADER fields a line carries, less the per-record Severity. */
86typedef struct
87{
88 const char *hostname; ///< HOSTNAME (sec 6.2.4); NULL or "" emits the NILVALUE "-"
89 const char *app_name; ///< APP-NAME (sec 6.2.5); NULL or "" emits the NILVALUE "-"
90 SyslogFacility facility; ///< the Facility half of PRIVAL (sec 6.2.1)
91} SyslogHeaderArgs;
92
93/** @brief One record: the Severity half of PRIVAL (RFC 5424 sec 6.2.1) and its MSG (sec 6.4). */
94typedef struct
95{
96 SyslogSeverity severity; ///< the Severity half of PRIVAL
97 const char *msg; ///< MSG-ANY, free-form octets; NULL emits an empty MSG
98} SyslogRecordArgs;
99
100/** @brief Where a formatted SYSLOG-MSG lands. */
101typedef struct
102{
103 char *out; ///< the buffer a format writes the line into
104 size_t cap; ///< how much room it has, the NUL included
105} SyslogLineArgs;
106
107/**
108 * @brief The syslog originator.
109 *
110 * A caller sets the members a call takes, invokes it through ::Syslog, and reads the outcome off
111 * the same handle.
112 *
113 * No slot member: one originator sends to one collector, so no call names a row.
114 *
115 * @var SyslogNs::collector the collector an init parses and a log sends to (RFC 5426 sec 3.3)
116 * @var SyslogNs::header the HEADER fields a format stamps (RFC 5424 sec 6.2)
117 * @var SyslogNs::record the Severity and MSG one record carries (RFC 5424 sec 6.2.1, sec 6.4)
118 * @var SyslogNs::line the buffer a format writes into
119 * @var SyslogNs::ok a call's true/false outcome
120 * @var SyslogNs::n the SYSLOG-MSG length a format wrote, excluding the NUL, 0 if it did not fit
121 * @var SyslogNs::init parse the collector address and copy HOSTNAME, APP-NAME and Facility into storage
122 * @var SyslogNs::format build one SYSLOG-MSG from @c header and @c record into @c line
123 * @var SyslogNs::log stamp the stored HEADER fields onto @c header, format into the client's
124 * scratch, and send that one message as one datagram (RFC 5426 sec 3.1)
125 */
126typedef struct
127{
128 SyslogCollectorArgs collector; ///< where the datagrams go
129 SyslogHeaderArgs header; ///< what every line's HEADER says
130 SyslogRecordArgs record; ///< what one record says
131 SyslogLineArgs line; ///< where the formatted line lands
132 proto_bool ok;
133 size_t n;
134} SyslogVars;
135
136/** @brief The operands and the outcome. */
137extern SyslogVars SyslogV;
138
139/** @brief The entries. */
140typedef struct
141{
142 void (*const init)(uint8_t *work);
143 void (*const format)(uint8_t *work);
144 void (*const log)(uint8_t *work);
145} SyslogNs;
146
147// What the table binds, defined once in the .c and taking one parameter each: everything
148// else an entry needs is an operand in SyslogV or a region of the borrow at a fixed offset.
149void protocore_syslog_init(uint8_t *work);
150void protocore_syslog_format(uint8_t *work);
151void protocore_syslog_log(uint8_t *work);
152
153// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
154// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
155// `Syslog.init(work)` resolves to a named function and becomes a DIRECT call. An extern table
156// leaves the call indirect and the symbol live at every level, -O2 -flto included.
157static const SyslogNs Syslog __attribute__((unused)) = {
158 .init = protocore_syslog_init,
159 .format = protocore_syslog_format,
160 .log = protocore_syslog_log,
161};
162
163/**
164 * @brief The PROTOCORE_SYSLOG_BORROW bytes this module's state lives in.
165 *
166 * Stated beside the namespace rather than on it: an entry takes a borrow, and this is where
167 * that borrow comes from. Taken once from the end of the pool, which no mark and no release
168 * walks, so the state lasts the life of the program.
169 *
170 * @return the span.
171 */
172uint8_t *protocore_syslog_span(void);
173
175
176#endif // PROTOCORE_ENABLE_SYSLOG
177
178#endif // PROTOCORE_SYSLOG_H
PROTO_ENUM_PACKED
Application protocol spoken on a listener port or connection slot.
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97