Commit | Line | Data |
---|---|---|
5024c2ac JR |
1 | /* |
2 | * Copyright (C) 2019 Simon Marchi <simon.marchi@efficios.com> | |
3 | * | |
4 | * SPDX-License-Identifier: LGPL-2.1-only | |
5 | * | |
6 | */ | |
7 | ||
8 | #include <assert.h> | |
9 | #include <common/dynamic-array.h> | |
10 | #include <common/error.h> | |
11 | #include <common/macros.h> | |
12 | #include <lttng/action/action-internal.h> | |
13 | #include <lttng/action/group-internal.h> | |
14 | #include <lttng/action/group.h> | |
15 | ||
16 | struct lttng_action_group { | |
17 | struct lttng_action parent; | |
18 | ||
19 | /* The array own the action elements */ | |
20 | struct lttng_dynamic_pointer_array actions; | |
21 | }; | |
22 | ||
23 | struct lttng_action_group_comm { | |
24 | uint32_t action_count; | |
25 | ||
26 | /* | |
27 | * Variable data: each element serialized sequentially. | |
28 | */ | |
29 | char data[]; | |
30 | } LTTNG_PACKED; | |
31 | ||
32 | static void destroy_lttng_action_group_element(void *ptr) | |
33 | { | |
34 | struct lttng_action *element = (struct lttng_action *) ptr; | |
35 | lttng_action_destroy(element); | |
36 | } | |
37 | ||
38 | static struct lttng_action_group *action_group_from_action( | |
39 | const struct lttng_action *action) | |
40 | { | |
41 | assert(action); | |
42 | ||
43 | return container_of(action, struct lttng_action_group, parent); | |
44 | } | |
45 | ||
46 | static const struct lttng_action_group *action_group_from_action_const( | |
47 | const struct lttng_action *action) | |
48 | { | |
49 | assert(action); | |
50 | ||
51 | return container_of(action, struct lttng_action_group, parent); | |
52 | } | |
53 | ||
54 | static bool lttng_action_group_validate(struct lttng_action *action) | |
55 | { | |
56 | unsigned int i, count; | |
57 | struct lttng_action_group *action_group; | |
58 | bool valid; | |
59 | ||
60 | assert(lttng_action_get_type(action) == LTTNG_ACTION_TYPE_GROUP); | |
61 | ||
62 | action_group = action_group_from_action(action); | |
63 | ||
64 | count = lttng_dynamic_pointer_array_get_count(&action_group->actions); | |
65 | ||
66 | for (i = 0; i < count; i++) { | |
67 | struct lttng_action *child = | |
68 | lttng_dynamic_pointer_array_get_pointer( | |
69 | &action_group->actions, i); | |
70 | ||
71 | assert(child); | |
72 | ||
73 | if (!lttng_action_validate(child)) { | |
74 | valid = false; | |
75 | goto end; | |
76 | } | |
77 | } | |
78 | ||
79 | valid = true; | |
80 | ||
81 | end: | |
82 | return valid; | |
83 | } | |
84 | ||
85 | static bool lttng_action_group_is_equal(const struct lttng_action *_a, const struct lttng_action *_b) | |
86 | { | |
87 | bool is_equal = false; | |
88 | unsigned int i; | |
89 | unsigned int a_count, b_count; | |
90 | ||
91 | if (lttng_action_group_get_count(_a, &a_count) != LTTNG_ACTION_STATUS_OK) { | |
92 | goto end; | |
93 | } | |
94 | if (lttng_action_group_get_count(_b, &b_count) != LTTNG_ACTION_STATUS_OK) { | |
95 | goto end; | |
96 | } | |
97 | ||
98 | ||
99 | if (a_count != b_count) { | |
100 | goto end; | |
101 | } | |
102 | ||
103 | for (i = 0; i < a_count; i++) { | |
104 | const struct lttng_action *child_a = | |
105 | lttng_action_group_get_at_index_const(_a, i); | |
106 | const struct lttng_action *child_b = | |
107 | lttng_action_group_get_at_index_const(_b, i); | |
108 | ||
109 | assert(child_a); | |
110 | assert(child_b); | |
111 | ||
112 | if (!lttng_action_is_equal(child_a, child_b)) { | |
113 | goto end; | |
114 | } | |
115 | } | |
116 | ||
117 | is_equal = true; | |
118 | end: | |
119 | return is_equal; | |
120 | } | |
121 | ||
122 | static int lttng_action_group_serialize( | |
123 | struct lttng_action *action, struct lttng_dynamic_buffer *buf) | |
124 | { | |
125 | struct lttng_action_group *action_group; | |
126 | struct lttng_action_group_comm comm; | |
127 | int ret; | |
128 | unsigned int i, count; | |
129 | ||
130 | assert(action); | |
131 | assert(buf); | |
132 | assert(lttng_action_get_type(action) == LTTNG_ACTION_TYPE_GROUP); | |
133 | ||
134 | action_group = action_group_from_action(action); | |
135 | ||
136 | DBG("Serializing action group"); | |
137 | ||
138 | count = lttng_dynamic_pointer_array_get_count(&action_group->actions); | |
139 | ||
140 | comm.action_count = count; | |
141 | ||
142 | ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm)); | |
143 | if (ret) { | |
144 | ret = -1; | |
145 | goto end; | |
146 | } | |
147 | ||
148 | for (i = 0; i < count; i++) { | |
149 | struct lttng_action *child = | |
150 | lttng_dynamic_pointer_array_get_pointer( | |
151 | &action_group->actions, i); | |
152 | assert(child); | |
153 | ||
154 | ret = lttng_action_serialize(child, buf); | |
155 | if (ret) { | |
156 | goto end; | |
157 | } | |
158 | } | |
159 | ||
160 | ret = 0; | |
161 | ||
162 | end: | |
163 | return ret; | |
164 | } | |
165 | ||
166 | static void lttng_action_group_destroy(struct lttng_action *action) | |
167 | { | |
168 | struct lttng_action_group *action_group; | |
169 | ||
170 | if (!action) { | |
171 | goto end; | |
172 | } | |
173 | ||
174 | action_group = action_group_from_action(action); | |
175 | ||
176 | lttng_dynamic_pointer_array_reset(&action_group->actions); | |
177 | ||
178 | free(action_group); | |
179 | ||
180 | end: | |
181 | return; | |
182 | } | |
183 | ||
184 | ssize_t lttng_action_group_create_from_buffer( | |
185 | const struct lttng_buffer_view *view, | |
186 | struct lttng_action **p_action) | |
187 | { | |
188 | ssize_t consumed_len; | |
189 | struct lttng_action_group_comm *comm; | |
190 | struct lttng_action *group; | |
191 | struct lttng_action *child_action = NULL; | |
192 | enum lttng_action_status status; | |
193 | size_t i; | |
194 | ||
195 | group = lttng_action_group_create(); | |
196 | if (!group) { | |
197 | consumed_len = -1; | |
198 | goto end; | |
199 | } | |
200 | ||
201 | comm = (struct lttng_action_group_comm *) view->data; | |
202 | ||
203 | consumed_len = sizeof(struct lttng_action_group_comm); | |
204 | ||
205 | for (i = 0; i < comm->action_count; i++) { | |
206 | ssize_t consumed_len_child; | |
207 | struct lttng_buffer_view child_view; | |
208 | ||
209 | child_view = lttng_buffer_view_from_view( | |
210 | view, consumed_len, view->size - consumed_len); | |
211 | consumed_len_child = lttng_action_create_from_buffer( | |
212 | &child_view, &child_action); | |
213 | ||
214 | status = lttng_action_group_add_action(group, child_action); | |
215 | if (status != LTTNG_ACTION_STATUS_OK) { | |
216 | consumed_len = -1; | |
217 | goto end; | |
218 | } | |
219 | child_action = NULL; | |
220 | ||
221 | consumed_len += consumed_len_child; | |
222 | } | |
223 | ||
224 | *p_action = group; | |
225 | group = NULL; | |
226 | ||
227 | end: | |
228 | lttng_action_group_destroy(group); | |
229 | lttng_action_destroy(child_action); | |
230 | ||
231 | return consumed_len; | |
232 | } | |
233 | ||
234 | struct lttng_action *lttng_action_group_create(void) | |
235 | { | |
236 | struct lttng_action_group *action_group; | |
237 | struct lttng_action *action; | |
238 | ||
239 | action_group = zmalloc(sizeof(struct lttng_action_group)); | |
240 | if (!action_group) { | |
241 | action = NULL; | |
242 | goto end; | |
243 | } | |
244 | ||
245 | action = &action_group->parent; | |
246 | ||
247 | lttng_action_init(action, LTTNG_ACTION_TYPE_GROUP, | |
248 | lttng_action_group_validate, | |
249 | lttng_action_group_serialize, | |
250 | lttng_action_group_is_equal, | |
251 | lttng_action_group_destroy); | |
252 | ||
253 | lttng_dynamic_pointer_array_init(&action_group->actions, | |
254 | destroy_lttng_action_group_element); | |
255 | ||
256 | end: | |
257 | return action; | |
258 | } | |
259 | ||
260 | enum lttng_action_status lttng_action_group_add_action( | |
261 | struct lttng_action *group, struct lttng_action *action) | |
262 | { | |
263 | struct lttng_action_group *action_group; | |
264 | enum lttng_action_status status; | |
265 | int ret; | |
266 | ||
267 | if (!group || | |
268 | (lttng_action_get_type(group) != | |
269 | LTTNG_ACTION_TYPE_GROUP) || | |
270 | !action) { | |
271 | status = LTTNG_ACTION_STATUS_INVALID; | |
272 | goto end; | |
273 | } | |
274 | ||
275 | /* | |
276 | * Don't allow adding groups in groups for now, since we're afraid of | |
277 | * cycles. | |
278 | */ | |
279 | if (lttng_action_get_type(action) == LTTNG_ACTION_TYPE_GROUP) { | |
280 | status = LTTNG_ACTION_STATUS_INVALID; | |
281 | goto end; | |
282 | } | |
283 | ||
284 | action_group = action_group_from_action(group); | |
285 | ||
286 | ret = lttng_dynamic_pointer_array_add_pointer(&action_group->actions, | |
287 | action); | |
288 | if (ret < 0) { | |
289 | status = LTTNG_ACTION_STATUS_ERROR; | |
290 | goto end; | |
291 | } | |
292 | ||
293 | status = LTTNG_ACTION_STATUS_OK; | |
294 | end: | |
295 | return status; | |
296 | } | |
297 | ||
298 | enum lttng_action_status lttng_action_group_get_count( | |
299 | const struct lttng_action *group, unsigned int *count) | |
300 | { | |
301 | const struct lttng_action_group *action_group; | |
302 | enum lttng_action_status status = LTTNG_ACTION_STATUS_OK; | |
303 | ||
304 | if (!group || (lttng_action_get_type_const(group) != | |
305 | LTTNG_ACTION_TYPE_GROUP)) { | |
306 | status = LTTNG_ACTION_STATUS_INVALID; | |
307 | *count = 0; | |
308 | goto end; | |
309 | } | |
310 | ||
311 | action_group = action_group_from_action_const(group); | |
312 | ||
313 | *count = lttng_dynamic_pointer_array_get_count(&action_group->actions); | |
314 | end: | |
315 | return status; | |
316 | } | |
317 | ||
318 | const struct lttng_action *lttng_action_group_get_at_index_const( | |
319 | const struct lttng_action *group, unsigned int index) | |
320 | { | |
321 | unsigned int count; | |
322 | const struct lttng_action_group *action_group; | |
323 | const struct lttng_action * action = NULL; | |
324 | ||
325 | if (lttng_action_group_get_count(group, &count) != | |
326 | LTTNG_ACTION_STATUS_OK) { | |
327 | goto end; | |
328 | } | |
329 | ||
330 | if (index >= count) { | |
331 | goto end; | |
332 | } | |
333 | ||
334 | action_group = action_group_from_action_const(group); | |
335 | action = lttng_dynamic_pointer_array_get_pointer(&action_group->actions, | |
336 | index); | |
337 | end: | |
338 | return action; | |
339 | } |