Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * Copyright (c) 2022 Meta Platforms, Inc. and affiliates.
4 : : */
5 : :
6 : : #include "cgen/printer.h"
7 : :
8 : : #include <errno.h>
9 : : #include <stdlib.h>
10 : : #include <string.h>
11 : :
12 : : #include <bpfilter/dump.h>
13 : : #include <bpfilter/helper.h>
14 : : #include <bpfilter/list.h>
15 : : #include <bpfilter/logger.h>
16 : : #include <bpfilter/pack.h>
17 : :
18 : : /**
19 : : * @struct bf_printer_msg
20 : : *
21 : : * Represents a message to be printed by a generated BPF program.
22 : : */
23 : : struct bf_printer_msg
24 : : {
25 : : /// Offset of the message, in the concatenated messages string.
26 : : size_t offset;
27 : : /// Length of the message, including the nul termination character.
28 : : size_t len;
29 : : /// Message.
30 : : const char *str;
31 : : };
32 : :
33 : : /**
34 : : * @struct bf_printer
35 : : *
36 : : * The printer context. It stores all the messages to be printed by any of the
37 : : * generated BPF program, and the runtime data required to maintain the
38 : : * log messages BPF map.
39 : : */
40 : : struct bf_printer
41 : : {
42 : : /// List of messages.
43 : : bf_list msgs;
44 : : };
45 : :
46 : : #define _free_bf_printer_msg_ __attribute__((__cleanup__(_bf_printer_msg_free)))
47 : :
48 : : static void _bf_printer_msg_free(struct bf_printer_msg **msg);
49 : :
50 : : /**
51 : : * Allocate and initialise a new printer message.
52 : : *
53 : : * @param msg On success, points to the newly allocated and initialised
54 : : * printer message. Can't be NULL.
55 : : * @return 0 on success, or negative errno value on error.
56 : : */
57 : 86 : static int _bf_printer_msg_new(struct bf_printer_msg **msg)
58 : : {
59 : 86 : _free_bf_printer_msg_ struct bf_printer_msg *_msg = NULL;
60 : :
61 : : bf_assert(msg);
62 : :
63 : 86 : _msg = calloc(1, sizeof(*_msg));
64 [ + - ]: 86 : if (!_msg)
65 : : return -ENOMEM;
66 : :
67 : 86 : *msg = TAKE_PTR(_msg);
68 : :
69 : 86 : return 0;
70 : : }
71 : :
72 : : /**
73 : : * @brief Allocate and initialize a new printer message from serialized data.
74 : : *
75 : : * @param msg Printer message object to allocate and initialize from the
76 : : * serialized data. The caller will own the object. On failure, `*msg`
77 : : * is unchanged. Can't be NULL.
78 : : * @param node Node containing the serialized message.
79 : : * @return 0 on success, or a negative errno value on failure.
80 : : */
81 : 2 : static int _bf_printer_msg_new_from_pack(struct bf_printer_msg **msg,
82 : : bf_rpack_node_t node)
83 : : {
84 : 2 : _free_bf_printer_msg_ struct bf_printer_msg *_msg = NULL;
85 : : int r;
86 : :
87 : : bf_assert(msg);
88 : :
89 : 2 : r = _bf_printer_msg_new(&_msg);
90 [ - + ]: 2 : if (r)
91 [ # # ]: 0 : return bf_err_r(r, "failed to create bf_printer_msg from pack");
92 : :
93 : 2 : r = bf_rpack_kv_u64(node, "offset", &_msg->offset);
94 [ - + ]: 2 : if (r)
95 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_printer_msg.offset");
96 : :
97 : 2 : r = bf_rpack_kv_u64(node, "len", &_msg->len);
98 [ - + ]: 2 : if (r)
99 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_printer_msg.len");
100 : :
101 : 2 : r = bf_rpack_kv_str(node, "str", (char **)&_msg->str);
102 [ - + ]: 2 : if (r)
103 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_printer_msg.str");
104 : :
105 : 2 : *msg = TAKE_PTR(_msg);
106 : :
107 : 2 : return 0;
108 : : }
109 : :
110 : : /**
111 : : * Deinitialise and deallocate a printer message.
112 : : *
113 : : * @param msg Printer message. Can't be NULL.
114 : : */
115 : 260 : static void _bf_printer_msg_free(struct bf_printer_msg **msg)
116 : : {
117 : : bf_assert(msg);
118 : :
119 [ + + ]: 260 : if (!*msg)
120 : : return;
121 : :
122 : : // Compiler will complain if str is const
123 : 86 : free((char *)(*msg)->str);
124 : :
125 : 86 : free(*msg);
126 : 86 : *msg = NULL;
127 : : }
128 : :
129 : : /**
130 : : * @brief Serialize a printer message.
131 : : *
132 : : * @param msg Printer message to serialize. Can't be NULL.
133 : : * @param pack `bf_wpack_t` object to serialize the printer message into.
134 : : * Can't be NULL.
135 : : * @return 0 on success, or a negative error value on failure.
136 : : */
137 : 240 : static int _bf_printer_msg_pack(const struct bf_printer_msg *msg,
138 : : bf_wpack_t *pack)
139 : : {
140 : : bf_assert(msg);
141 : : bf_assert(pack);
142 : :
143 : 240 : bf_wpack_kv_u64(pack, "offset", msg->offset);
144 : 240 : bf_wpack_kv_u64(pack, "len", msg->len);
145 : 240 : bf_wpack_kv_str(pack, "str", msg->str);
146 : :
147 [ - + ]: 240 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
148 : : }
149 : :
150 : 2 : static void _bf_printer_msg_dump(const struct bf_printer_msg *msg,
151 : : prefix_t *prefix)
152 : : {
153 : : bf_assert(msg);
154 : : bf_assert(prefix);
155 : :
156 [ + - ]: 2 : DUMP(prefix, "struct bf_printer_msg at %p", msg);
157 : :
158 : 2 : bf_dump_prefix_push(prefix);
159 [ + - ]: 2 : DUMP(prefix, "offset: %lu", msg->offset);
160 [ + - ]: 2 : DUMP(prefix, "len: %lu", msg->len);
161 [ + - ]: 2 : DUMP(bf_dump_prefix_last(prefix), "str: '%s'", msg->str);
162 : 2 : bf_dump_prefix_pop(prefix);
163 : 2 : }
164 : :
165 : 84 : size_t bf_printer_msg_offset(const struct bf_printer_msg *msg)
166 : : {
167 : : bf_assert(msg);
168 : 84 : return msg->offset;
169 : : }
170 : :
171 : 0 : size_t bf_printer_msg_len(const struct bf_printer_msg *msg)
172 : : {
173 : : bf_assert(msg);
174 : 0 : return msg->len;
175 : : }
176 : :
177 : 64 : int bf_printer_new(struct bf_printer **printer)
178 : : {
179 : 64 : _free_bf_printer_ struct bf_printer *_printer = NULL;
180 : :
181 : : bf_assert(printer);
182 : :
183 : 64 : _printer = malloc(sizeof(*_printer));
184 [ + - ]: 64 : if (!_printer)
185 : : return -ENOMEM;
186 : :
187 : 64 : _printer->msgs =
188 : : bf_list_default(_bf_printer_msg_free, _bf_printer_msg_pack);
189 : :
190 : 64 : *printer = TAKE_PTR(_printer);
191 : :
192 : 64 : return 0;
193 : : }
194 : :
195 : 2 : int bf_printer_new_from_pack(struct bf_printer **printer, bf_rpack_node_t node)
196 : : {
197 : 2 : _free_bf_printer_ struct bf_printer *_printer = NULL;
198 : : bf_rpack_node_t child, m_node;
199 : : int r;
200 : :
201 : : bf_assert(printer);
202 : :
203 : 2 : r = bf_printer_new(&_printer);
204 [ - + ]: 2 : if (r)
205 [ # # ]: 0 : return bf_err_r(r, "failed to create a bf_printer from pack");
206 : :
207 : 2 : r = bf_rpack_kv_array(node, "msgs", &child);
208 [ - + ]: 2 : if (r)
209 [ # # ]: 0 : return bf_rpack_key_err(r, "bf_printer.msgs");
210 [ + - - + : 8 : bf_rpack_array_foreach (child, m_node) {
+ + ]
211 : 4 : _free_bf_printer_msg_ struct bf_printer_msg *msg = NULL;
212 : :
213 [ + - + - : 2 : r = bf_list_emplace(&_printer->msgs, _bf_printer_msg_new_from_pack, msg,
- - - - ]
214 : : m_node);
215 : : if (r) {
216 [ # # ]: 0 : return bf_err_r(
217 : : r, "failed to unpack bf_printer_msg into bf_printer.msgs");
218 : : }
219 : : }
220 : :
221 : 2 : *printer = TAKE_PTR(_printer);
222 : :
223 : 2 : return 0;
224 : : }
225 : :
226 : 130 : void bf_printer_free(struct bf_printer **printer)
227 : : {
228 : : bf_assert(printer);
229 : :
230 [ + + ]: 130 : if (!*printer)
231 : : return;
232 : :
233 : 64 : bf_list_clean(&(*printer)->msgs);
234 : :
235 : 64 : free(*printer);
236 : 64 : *printer = NULL;
237 : : }
238 : :
239 : 164 : int bf_printer_pack(const struct bf_printer *printer, bf_wpack_t *pack)
240 : : {
241 : : bf_assert(printer);
242 : : bf_assert(pack);
243 : :
244 : 164 : bf_wpack_kv_list(pack, "msgs", &printer->msgs);
245 : :
246 [ - + ]: 164 : return bf_wpack_is_valid(pack) ? 0 : -EINVAL;
247 : : }
248 : :
249 : 2 : void bf_printer_dump(const struct bf_printer *printer, prefix_t *prefix)
250 : : {
251 : : bf_assert(printer);
252 : : bf_assert(prefix);
253 : :
254 [ + - ]: 2 : DUMP(prefix, "struct bf_printer at %p", printer);
255 : :
256 : 2 : bf_dump_prefix_push(prefix);
257 [ + - ]: 2 : DUMP(prefix, "msgs: bf_list<bf_printer_msg>[%lu]",
258 : : bf_list_size(&printer->msgs));
259 : 2 : bf_dump_prefix_push(prefix);
260 [ + - - + : 8 : bf_list_foreach (&printer->msgs, msg_node) {
+ + ]
261 : : struct bf_printer_msg *msg = bf_list_node_get_data(msg_node);
262 : :
263 [ + - ]: 2 : if (bf_list_is_tail(&printer->msgs, msg_node))
264 : 2 : bf_dump_prefix_last(prefix);
265 : :
266 : 2 : _bf_printer_msg_dump(msg, prefix);
267 : : }
268 : 2 : bf_dump_prefix_pop(prefix);
269 : :
270 : 2 : bf_dump_prefix_pop(prefix);
271 : 2 : }
272 : :
273 : : /**
274 : : * Get the total size of the concatenated messages.
275 : : *
276 : : * @param printer Printer context. Can't be NULL.
277 : : * @return Total size of the concatenated messages.
278 : : */
279 : : static size_t _bf_printer_total_size(const struct bf_printer *printer)
280 : : {
281 : : bf_list_node *last_msg_node;
282 : : struct bf_printer_msg *last_msg;
283 : :
284 : : bf_assert(printer);
285 : :
286 [ + - + + ]: 144 : if (!(last_msg_node = bf_list_get_tail(&printer->msgs)))
287 : : return 0;
288 : :
289 : : last_msg = bf_list_node_get_data(last_msg_node);
290 : :
291 : 24 : return last_msg->offset + last_msg->len;
292 : : }
293 : :
294 : 84 : const struct bf_printer_msg *bf_printer_add_msg(struct bf_printer *printer,
295 : : const char *str)
296 : : {
297 : 84 : _free_bf_printer_msg_ struct bf_printer_msg *msg = NULL;
298 : : int r;
299 : :
300 : : bf_assert(printer);
301 : : bf_assert(str);
302 : :
303 : : // Check if an identical message is already stored in the context.
304 [ + + - + : 192 : bf_list_foreach (&printer->msgs, msg_node) {
+ + ]
305 : : struct bf_printer_msg *msg = bf_list_node_get_data(msg_node);
306 : :
307 [ + - ]: 24 : if (bf_streq(msg->str, str))
308 : : return msg;
309 : : }
310 : :
311 : : // Otherwise, create a new message.
312 : 84 : r = _bf_printer_msg_new(&msg);
313 [ - + ]: 84 : if (r) {
314 [ # # ]: 0 : bf_err("failed to create a new bf_printer_msg object");
315 : 0 : return NULL;
316 : : }
317 : :
318 [ + + ]: 84 : msg->len = strlen(str) + 1;
319 : : // Next expected offset is equal to the current total size of the
320 : : // concatenated string
321 : 84 : msg->offset = _bf_printer_total_size(printer);
322 : 84 : msg->str = strdup(str);
323 [ - + ]: 84 : if (!msg->str)
324 : : return NULL;
325 : :
326 : 84 : r = bf_list_add_tail(&printer->msgs, msg);
327 [ - + ]: 84 : if (r) {
328 [ # # ]: 0 : bf_err("failed to add a new printer message to the printer context");
329 : 0 : return NULL;
330 : : }
331 : :
332 : 84 : return TAKE_PTR(msg);
333 : : }
334 : :
335 [ + - ]: 60 : int bf_printer_assemble(const struct bf_printer *printer, void **str,
336 : : size_t *str_len)
337 : : {
338 : : _cleanup_free_ char *_str = NULL;
339 : : size_t _str_len;
340 : :
341 : : bf_assert(printer);
342 : : bf_assert(str);
343 : : bf_assert(str_len);
344 : :
345 : : _str_len = _bf_printer_total_size(printer);
346 : :
347 : : // If the printer doesn't contain any message, the string should only
348 : : // contain \0.
349 [ - + ]: 60 : if (_str_len == 0) {
350 : 0 : _str = malloc(1);
351 [ # # ]: 0 : if (!_str)
352 : : return -ENOMEM;
353 : :
354 : 0 : *_str = '\0';
355 : : _str_len = 1;
356 : : } else {
357 : : _str_len = _bf_printer_total_size(printer);
358 : 60 : _str = malloc(_str_len);
359 [ - + ]: 60 : if (!_str)
360 : : return -ENOMEM;
361 : :
362 [ + - + + ]: 228 : bf_list_foreach (&printer->msgs, msg_node) {
363 : : struct bf_printer_msg *msg = bf_list_node_get_data(msg_node);
364 [ + + ]: 84 : memcpy(_str + msg->offset, msg->str, msg->len);
365 : : }
366 : : }
367 : :
368 : 60 : *str = TAKE_PTR(_str);
369 : 60 : *str_len = _str_len;
370 : :
371 : 60 : return 0;
372 : : }
|