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