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