Branch data Line data Source code
1 : : /* SPDX-FileCopyrightText: Copyright 2026 Cozens Software Solutions Limited
2 : : * SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 OR LicenseRef-PolyForm-Internal-Use-1.0.0 OR LicenseRef-COSOSO-Commercial
3 : : */
4 : :
5 : : #include "SolidSyslogMessageFormatter.h"
6 : :
7 : : #include <stdbool.h>
8 : : #include <stddef.h>
9 : : #include <stdint.h>
10 : :
11 : : #include "SolidSyslog.h"
12 : : #include "SolidSyslogFormatter.h"
13 : : #include "SolidSyslogHeaderFieldFunction.h"
14 : : #include "SolidSyslogHeaderFieldPrivate.h"
15 : : #include "SolidSyslogPrival.h"
16 : : #include "SolidSyslogSdElementPrivate.h"
17 : : #include "SolidSyslogStructuredData.h"
18 : : #include "SolidSyslogTimestamp.h"
19 : : #include "SolidSyslogTimestampFormatter.h"
20 : : #include "SolidSyslogTunables.h"
21 : :
22 : : struct SolidSyslogFormatter;
23 : :
24 : : enum
25 : : {
26 : : SOLIDSYSLOG_MAX_APP_NAME_SIZE = 49,
27 : : SOLIDSYSLOG_MAX_HOSTNAME_SIZE = 256,
28 : : SOLIDSYSLOG_MAX_MSGID_SIZE = 33,
29 : : SOLIDSYSLOG_MAX_PROCESS_ID_SIZE = 129
30 : : };
31 : :
32 : : static inline void MessageFormatter_FormatPrival(struct SolidSyslogFormatter* formatter, uint8_t prival);
33 : : static inline uint8_t MessageFormatter_MakePrival(const struct SolidSyslogMessage* message);
34 : : static inline uint8_t MessageFormatter_CombineFacilityAndSeverity(uint8_t facility, uint8_t severity);
35 : : static inline bool MessageFormatter_PrivalComponentsAreValid(uint8_t facility, uint8_t severity);
36 : : static inline bool MessageFormatter_FacilityIsValid(uint8_t facility);
37 : : static inline bool MessageFormatter_SeverityIsValid(uint8_t severity);
38 : : static inline void MessageFormatter_FormatTimestamp(
39 : : struct SolidSyslogFormatter* formatter,
40 : : SolidSyslogClockFunction clock
41 : : );
42 : : static inline void MessageFormatter_FormatStringField(
43 : : struct SolidSyslogFormatter* formatter,
44 : : SolidSyslogHeaderFieldFunction fn,
45 : : void* context,
46 : : size_t maxSize
47 : : );
48 : : static inline void MessageFormatter_FormatMsgId(struct SolidSyslogFormatter* formatter, const char* messageId);
49 : : static inline bool MessageFormatter_StringIsValid(const char* value);
50 : : static inline void MessageFormatter_FormatStructuredData(
51 : : struct SolidSyslogFormatter* formatter,
52 : : struct SolidSyslogStructuredData** baseSd,
53 : : size_t baseSdCount,
54 : : struct SolidSyslogStructuredData** messageSd,
55 : : size_t messageSdCount
56 : : );
57 : : static inline void MessageFormatter_FormatSdElements(
58 : : struct SolidSyslogSdElement* element,
59 : : struct SolidSyslogStructuredData** sd,
60 : : size_t sdCount
61 : : );
62 : : static inline void MessageFormatter_FormatMsg(struct SolidSyslogFormatter* formatter, const char* msg);
63 : : static inline const char* MessageFormatter_SkipLeadingBom(const char* msg);
64 : :
65 : 6630 : void SolidSyslogMessageFormatter_Format(
66 : : struct SolidSyslogFormatter* formatter,
67 : : const struct SolidSyslogMessage* message,
68 : : const struct SolidSyslogMessageFormatterContext* context,
69 : : struct SolidSyslogStructuredData** messageSd,
70 : : size_t messageSdCount
71 : : )
72 : : {
73 : 6630 : MessageFormatter_FormatPrival(formatter, MessageFormatter_MakePrival(message));
74 : 6630 : SolidSyslogFormatter_AsciiCharacter(formatter, '1');
75 : 6630 : SolidSyslogFormatter_AsciiCharacter(formatter, ' ');
76 : 6630 : MessageFormatter_FormatTimestamp(formatter, context->Clock);
77 : 6630 : SolidSyslogFormatter_AsciiCharacter(formatter, ' ');
78 : 6630 : MessageFormatter_FormatStringField(
79 : : formatter,
80 : 6630 : context->GetHostname,
81 : 6630 : context->GetHostnameContext,
82 : : SOLIDSYSLOG_MAX_HOSTNAME_SIZE
83 : : );
84 : 6630 : SolidSyslogFormatter_AsciiCharacter(formatter, ' ');
85 : 6630 : MessageFormatter_FormatStringField(
86 : : formatter,
87 : 6630 : context->GetAppName,
88 : 6630 : context->GetAppNameContext,
89 : : SOLIDSYSLOG_MAX_APP_NAME_SIZE
90 : : );
91 : 6630 : SolidSyslogFormatter_AsciiCharacter(formatter, ' ');
92 : 6630 : MessageFormatter_FormatStringField(
93 : : formatter,
94 : 6630 : context->GetProcessId,
95 : 6630 : context->GetProcessIdContext,
96 : : SOLIDSYSLOG_MAX_PROCESS_ID_SIZE
97 : : );
98 : 6630 : SolidSyslogFormatter_AsciiCharacter(formatter, ' ');
99 : 6630 : MessageFormatter_FormatMsgId(formatter, message->MessageId);
100 : 6630 : SolidSyslogFormatter_AsciiCharacter(formatter, ' ');
101 : 6630 : MessageFormatter_FormatStructuredData(formatter, context->Sd, context->SdCount, messageSd, messageSdCount);
102 : 6630 : MessageFormatter_FormatMsg(formatter, message->Msg);
103 : 6630 : }
104 : :
105 : 6630 : static inline void MessageFormatter_FormatPrival(struct SolidSyslogFormatter* formatter, uint8_t prival)
106 : : {
107 : 6630 : SolidSyslogFormatter_AsciiCharacter(formatter, '<');
108 : 6630 : SolidSyslogFormatter_Uint32(formatter, prival);
109 : 6630 : SolidSyslogFormatter_AsciiCharacter(formatter, '>');
110 : 6630 : }
111 : :
112 : 6630 : static inline uint8_t MessageFormatter_MakePrival(const struct SolidSyslogMessage* message)
113 : : {
114 : 6630 : uint8_t facility = (uint8_t) message->Facility;
115 : 6630 : uint8_t severity = (uint8_t) message->Severity;
116 : : uint8_t prival =
117 : 6630 : MessageFormatter_CombineFacilityAndSeverity(SOLIDSYSLOG_FACILITY_SYSLOG, SOLIDSYSLOG_SEVERITY_ERROR);
118 : :
119 [ + + ]: 6630 : if (MessageFormatter_PrivalComponentsAreValid(facility, severity))
120 : : {
121 : 6628 : prival = MessageFormatter_CombineFacilityAndSeverity(facility, severity);
122 : : }
123 : :
124 : 6630 : return prival;
125 : : }
126 : :
127 : 13258 : static inline uint8_t MessageFormatter_CombineFacilityAndSeverity(uint8_t facility, uint8_t severity)
128 : : {
129 : 13258 : return (uint8_t) ((facility * UINT8_C(8)) + severity);
130 : : }
131 : :
132 : 6630 : static inline bool MessageFormatter_PrivalComponentsAreValid(uint8_t facility, uint8_t severity)
133 : : {
134 [ + + + + ]: 6630 : return MessageFormatter_FacilityIsValid(facility) && MessageFormatter_SeverityIsValid(severity);
135 : : }
136 : :
137 : 6630 : static inline bool MessageFormatter_FacilityIsValid(uint8_t facility)
138 : : {
139 : 6630 : return facility <= (uint8_t) SOLIDSYSLOG_FACILITY_LOCAL7;
140 : : }
141 : :
142 : 6629 : static inline bool MessageFormatter_SeverityIsValid(uint8_t severity)
143 : : {
144 : 6629 : return severity <= (uint8_t) SOLIDSYSLOG_SEVERITY_DEBUG;
145 : : }
146 : :
147 : 6630 : static inline void MessageFormatter_FormatTimestamp(
148 : : struct SolidSyslogFormatter* formatter,
149 : : SolidSyslogClockFunction clock
150 : : )
151 : : {
152 : 6630 : struct SolidSyslogTimestamp ts = {0};
153 : :
154 : 6630 : clock(&ts);
155 : 6630 : SolidSyslogTimestampFormatter_Format(formatter, &ts);
156 : 6630 : }
157 : :
158 : 19890 : static inline void MessageFormatter_FormatStringField(
159 : : struct SolidSyslogFormatter* formatter,
160 : : SolidSyslogHeaderFieldFunction fn,
161 : : void* context,
162 : : size_t maxSize
163 : : )
164 : : {
165 : 19890 : size_t lengthBefore = SolidSyslogFormatter_Length(formatter);
166 : : struct SolidSyslogHeaderField field;
167 : :
168 : : /* maxSize is the field's storage size (carries a NUL slot); the usable
169 : : * field width is one less - matching the RFC HOSTNAME / APP-NAME / PROCID
170 : : * caps the scratch-field formatter enforced before this writer existed. */
171 : 19890 : SolidSyslogHeaderField_FromFormatter(&field, formatter, maxSize - 1U);
172 : 19890 : fn(&field, context);
173 : :
174 [ + + ]: 19890 : if (SolidSyslogFormatter_Length(formatter) == lengthBefore)
175 : : {
176 : 19780 : SolidSyslogFormatter_NilValue(formatter);
177 : : }
178 : 19890 : }
179 : :
180 : 6630 : static inline void MessageFormatter_FormatMsgId(struct SolidSyslogFormatter* formatter, const char* messageId)
181 : : {
182 : 6630 : size_t lengthBefore = SolidSyslogFormatter_Length(formatter);
183 : :
184 [ + + ]: 6630 : if (MessageFormatter_StringIsValid(messageId))
185 : : {
186 : 6536 : SolidSyslogFormatter_PrintUsAsciiString(formatter, messageId, SOLIDSYSLOG_MAX_MSGID_SIZE - 1);
187 : : }
188 : :
189 [ + + ]: 6630 : if (SolidSyslogFormatter_Length(formatter) == lengthBefore)
190 : : {
191 : 94 : SolidSyslogFormatter_NilValue(formatter);
192 : : }
193 : 6630 : }
194 : :
195 : 19818 : static inline bool MessageFormatter_StringIsValid(const char* value)
196 : : {
197 [ + + + + ]: 19818 : return (value != NULL) && (value[0] != '\0');
198 : : }
199 : :
200 : 6630 : static inline void MessageFormatter_FormatStructuredData(
201 : : struct SolidSyslogFormatter* formatter,
202 : : struct SolidSyslogStructuredData** baseSd,
203 : : size_t baseSdCount,
204 : : struct SolidSyslogStructuredData** messageSd,
205 : : size_t messageSdCount
206 : : )
207 : : {
208 : 6630 : size_t lengthBefore = SolidSyslogFormatter_Length(formatter);
209 : : struct SolidSyslogSdElement element;
210 : :
211 : 6630 : SolidSyslogSdElement_FromFormatter(&element, formatter);
212 : 6630 : MessageFormatter_FormatSdElements(&element, baseSd, baseSdCount);
213 : 6630 : MessageFormatter_FormatSdElements(&element, messageSd, messageSdCount);
214 : :
215 [ + + ]: 6630 : if (SolidSyslogFormatter_Length(formatter) == lengthBefore)
216 : : {
217 : 147 : SolidSyslogFormatter_NilValue(formatter);
218 : : }
219 : 6630 : }
220 : :
221 : 13260 : static inline void MessageFormatter_FormatSdElements(
222 : : struct SolidSyslogSdElement* element,
223 : : struct SolidSyslogStructuredData** sd,
224 : : size_t sdCount
225 : : )
226 : : {
227 [ + + ]: 20238 : for (size_t i = 0U; i < sdCount; i++)
228 : : {
229 : : /* Skip NULL entries rather than dereference them. Per-instance slots are
230 : : expected to use SolidSyslogNullSd, but a per-message array is supplied
231 : : at the call site where a conditionally-absent SD is naturally NULL -
232 : : the library must not crash on caller input. */
233 [ + + ]: 6978 : if (sd[i] != NULL)
234 : : {
235 : 6976 : SolidSyslogStructuredData_Format(sd[i], element);
236 : : }
237 : : }
238 : 13260 : }
239 : :
240 : 6630 : static inline void MessageFormatter_FormatMsg(struct SolidSyslogFormatter* formatter, const char* msg)
241 : : {
242 : : /* Guard msg before SkipLeadingBom dereferences it, then guard the
243 : : * post-strip body so a caller-supplied BOM-only string emits no
244 : : * dangling SP-BOM (RFC 5424 section 6.4 - the BOM belongs to a non-empty MSG). */
245 [ + + ]: 6630 : if (MessageFormatter_StringIsValid(msg))
246 : : {
247 : 6558 : const char* body = MessageFormatter_SkipLeadingBom(msg);
248 : :
249 [ + + ]: 6558 : if (MessageFormatter_StringIsValid(body))
250 : : {
251 : 6557 : SolidSyslogFormatter_AsciiCharacter(formatter, ' ');
252 : 6557 : SolidSyslogFormatter_Bom(formatter);
253 : 6557 : SolidSyslogFormatter_BoundedString(formatter, body, SOLIDSYSLOG_MAX_MESSAGE_SIZE);
254 : : }
255 : : }
256 : 6630 : }
257 : :
258 : : /* MSG body MUST start with the UTF-8 BOM per RFC 5424 section 6.4. We always
259 : : * emit the BOM ourselves; if the caller already prefixed one, strip it
260 : : * so the wire frame contains exactly one. */
261 : 6558 : static inline const char* MessageFormatter_SkipLeadingBom(const char* msg)
262 : : {
263 : 6558 : const unsigned char* bytes = (const unsigned char*) msg;
264 : 6558 : const char* result = msg;
265 [ + + + + : 6558 : if ((bytes[0] == 0xEFU) && (bytes[1] == 0xBBU) && (bytes[2] == 0xBFU))
+ + ]
266 : : {
267 : 2 : result = &msg[3];
268 : : }
269 : 6558 : return result;
270 : : }
|