Commit | Line | Data |
---|---|---|
a58c490f | 1 | /* |
ab5be9fa | 2 | * Copyright (C) 2017 Jérémie Galarneau <jeremie.galarneau@efficios.com> |
a58c490f | 3 | * |
ab5be9fa | 4 | * SPDX-License-Identifier: LGPL-2.1-only |
a58c490f | 5 | * |
a58c490f JG |
6 | */ |
7 | ||
8 | #include <lttng/trigger/trigger-internal.h> | |
9 | #include <lttng/condition/condition-internal.h> | |
10 | #include <lttng/action/action-internal.h> | |
3da864a9 | 11 | #include <common/credentials.h> |
9e620ea7 JG |
12 | #include <common/payload.h> |
13 | #include <common/payload-view.h> | |
a58c490f | 14 | #include <common/error.h> |
3da864a9 | 15 | #include <common/optional.h> |
a58c490f JG |
16 | #include <assert.h> |
17 | ||
18 | LTTNG_HIDDEN | |
19 | bool lttng_trigger_validate(struct lttng_trigger *trigger) | |
20 | { | |
21 | bool valid; | |
22 | ||
23 | if (!trigger) { | |
24 | valid = false; | |
25 | goto end; | |
26 | } | |
27 | ||
64eafdf6 JR |
28 | if (!trigger->creds.uid.is_set) { |
29 | valid = false; | |
30 | goto end; | |
31 | } | |
32 | ||
a58c490f JG |
33 | valid = lttng_condition_validate(trigger->condition) && |
34 | lttng_action_validate(trigger->action); | |
35 | end: | |
36 | return valid; | |
37 | } | |
38 | ||
39 | struct lttng_trigger *lttng_trigger_create( | |
40 | struct lttng_condition *condition, | |
41 | struct lttng_action *action) | |
42 | { | |
43 | struct lttng_trigger *trigger = NULL; | |
44 | ||
45 | if (!condition || !action) { | |
46 | goto end; | |
47 | } | |
48 | ||
49 | trigger = zmalloc(sizeof(struct lttng_trigger)); | |
50 | if (!trigger) { | |
51 | goto end; | |
52 | } | |
53 | ||
f01d28b4 JR |
54 | urcu_ref_init(&trigger->ref); |
55 | ||
7ca172c1 | 56 | lttng_condition_get(condition); |
a58c490f | 57 | trigger->condition = condition; |
7ca172c1 JR |
58 | |
59 | lttng_action_get(action); | |
a58c490f | 60 | trigger->action = action; |
3da864a9 | 61 | |
a58c490f JG |
62 | end: |
63 | return trigger; | |
64 | } | |
65 | ||
7ca172c1 JR |
66 | /* |
67 | * Note: the lack of reference counting 'get' on the condition object is normal. | |
68 | * This API was exposed as such in 2.11. The client is not expected to call | |
69 | * lttng_condition_destroy on the returned object. | |
70 | */ | |
a58c490f JG |
71 | struct lttng_condition *lttng_trigger_get_condition( |
72 | struct lttng_trigger *trigger) | |
73 | { | |
74 | return trigger ? trigger->condition : NULL; | |
75 | } | |
76 | ||
9b63a4aa JG |
77 | LTTNG_HIDDEN |
78 | const struct lttng_condition *lttng_trigger_get_const_condition( | |
79 | const struct lttng_trigger *trigger) | |
80 | { | |
81 | return trigger->condition; | |
82 | } | |
83 | ||
7ca172c1 JR |
84 | |
85 | /* | |
86 | * Note: the lack of reference counting 'get' on the action object is normal. | |
87 | * This API was exposed as such in 2.11. The client is not expected to call | |
88 | * lttng_action_destroy on the returned object. | |
89 | */ | |
e2ba1c78 | 90 | struct lttng_action *lttng_trigger_get_action( |
a58c490f JG |
91 | struct lttng_trigger *trigger) |
92 | { | |
93 | return trigger ? trigger->action : NULL; | |
94 | } | |
95 | ||
9b63a4aa JG |
96 | LTTNG_HIDDEN |
97 | const struct lttng_action *lttng_trigger_get_const_action( | |
98 | const struct lttng_trigger *trigger) | |
99 | { | |
100 | return trigger->action; | |
101 | } | |
102 | ||
f01d28b4 | 103 | static void trigger_destroy_ref(struct urcu_ref *ref) |
a58c490f | 104 | { |
f01d28b4 JR |
105 | struct lttng_trigger *trigger = |
106 | container_of(ref, struct lttng_trigger, ref); | |
7ca172c1 JR |
107 | struct lttng_action *action = lttng_trigger_get_action(trigger); |
108 | struct lttng_condition *condition = | |
109 | lttng_trigger_get_condition(trigger); | |
110 | ||
7ca172c1 JR |
111 | assert(action); |
112 | assert(condition); | |
113 | ||
114 | /* Release ownership. */ | |
115 | lttng_action_put(action); | |
116 | lttng_condition_put(condition); | |
117 | ||
a58c490f JG |
118 | free(trigger); |
119 | } | |
120 | ||
f01d28b4 JR |
121 | void lttng_trigger_destroy(struct lttng_trigger *trigger) |
122 | { | |
123 | lttng_trigger_put(trigger); | |
124 | } | |
125 | ||
a58c490f | 126 | LTTNG_HIDDEN |
c0a66c84 JG |
127 | ssize_t lttng_trigger_create_from_payload( |
128 | struct lttng_payload_view *src_view, | |
a58c490f JG |
129 | struct lttng_trigger **trigger) |
130 | { | |
131 | ssize_t ret, offset = 0, condition_size, action_size; | |
132 | struct lttng_condition *condition = NULL; | |
133 | struct lttng_action *action = NULL; | |
134 | const struct lttng_trigger_comm *trigger_comm; | |
64eafdf6 JR |
135 | struct lttng_credentials creds = { |
136 | .uid = LTTNG_OPTIONAL_INIT_UNSET, | |
137 | .gid = LTTNG_OPTIONAL_INIT_UNSET, | |
138 | }; | |
a58c490f JG |
139 | |
140 | if (!src_view || !trigger) { | |
141 | ret = -1; | |
142 | goto end; | |
143 | } | |
144 | ||
145 | /* lttng_trigger_comm header */ | |
c0a66c84 | 146 | trigger_comm = (typeof(trigger_comm)) src_view->buffer.data; |
64eafdf6 JR |
147 | |
148 | /* Set the trigger's creds. */ | |
149 | if (trigger_comm->uid > (uint64_t) ((uid_t) -1)) { | |
150 | /* UID out of range for this platform. */ | |
151 | ret = -1; | |
152 | goto end; | |
153 | } | |
154 | ||
155 | LTTNG_OPTIONAL_SET(&creds.uid, trigger_comm->uid); | |
156 | ||
a58c490f | 157 | offset += sizeof(*trigger_comm); |
c0a66c84 JG |
158 | { |
159 | /* struct lttng_condition */ | |
160 | struct lttng_payload_view condition_view = | |
161 | lttng_payload_view_from_view( | |
162 | src_view, offset, -1); | |
163 | ||
164 | condition_size = lttng_condition_create_from_payload(&condition_view, | |
165 | &condition); | |
166 | } | |
a58c490f | 167 | |
a58c490f JG |
168 | if (condition_size < 0) { |
169 | ret = condition_size; | |
170 | goto end; | |
171 | } | |
c0a66c84 | 172 | |
a58c490f | 173 | offset += condition_size; |
c0a66c84 JG |
174 | { |
175 | /* struct lttng_action */ | |
176 | struct lttng_payload_view action_view = | |
177 | lttng_payload_view_from_view( | |
178 | src_view, offset, -1); | |
179 | ||
180 | action_size = lttng_action_create_from_payload(&action_view, &action); | |
181 | } | |
a58c490f | 182 | |
a58c490f JG |
183 | if (action_size < 0) { |
184 | ret = action_size; | |
185 | goto end; | |
186 | } | |
187 | offset += action_size; | |
188 | ||
189 | /* Unexpected size of inner-elements; the buffer is corrupted. */ | |
190 | if ((ssize_t) trigger_comm->length != condition_size + action_size) { | |
191 | ret = -1; | |
192 | goto error; | |
193 | } | |
194 | ||
195 | *trigger = lttng_trigger_create(condition, action); | |
196 | if (!*trigger) { | |
197 | ret = -1; | |
198 | goto error; | |
199 | } | |
c0a66c84 | 200 | |
64eafdf6 JR |
201 | lttng_trigger_set_credentials(*trigger, &creds); |
202 | ||
7ca172c1 JR |
203 | /* |
204 | * The trigger object owns references to the action and condition | |
205 | * objects. | |
206 | */ | |
207 | lttng_condition_put(condition); | |
208 | condition = NULL; | |
209 | ||
210 | lttng_action_put(action); | |
211 | action = NULL; | |
212 | ||
a58c490f | 213 | ret = offset; |
7ca172c1 | 214 | |
a58c490f JG |
215 | error: |
216 | lttng_condition_destroy(condition); | |
217 | lttng_action_destroy(action); | |
7ca172c1 | 218 | end: |
a58c490f JG |
219 | return ret; |
220 | } | |
221 | ||
222 | /* | |
a58c490f JG |
223 | * Both elements are stored contiguously, see their "*_comm" structure |
224 | * for the detailed format. | |
225 | */ | |
226 | LTTNG_HIDDEN | |
3647288f | 227 | int lttng_trigger_serialize(struct lttng_trigger *trigger, |
c0a66c84 | 228 | struct lttng_payload *payload) |
a58c490f | 229 | { |
3647288f JG |
230 | int ret; |
231 | size_t header_offset, size_before_payload; | |
c0a66c84 | 232 | struct lttng_trigger_comm trigger_comm = {}; |
3647288f | 233 | struct lttng_trigger_comm *header; |
64eafdf6 JR |
234 | const struct lttng_credentials *creds = NULL; |
235 | ||
236 | creds = lttng_trigger_get_credentials(trigger); | |
237 | assert(creds); | |
238 | ||
239 | trigger_comm.uid = LTTNG_OPTIONAL_GET(creds->uid); | |
a58c490f | 240 | |
c0a66c84 JG |
241 | header_offset = payload->buffer.size; |
242 | ret = lttng_dynamic_buffer_append(&payload->buffer, &trigger_comm, | |
3647288f JG |
243 | sizeof(trigger_comm)); |
244 | if (ret) { | |
a58c490f JG |
245 | goto end; |
246 | } | |
247 | ||
c0a66c84 JG |
248 | size_before_payload = payload->buffer.size; |
249 | ret = lttng_condition_serialize(trigger->condition, payload); | |
3647288f | 250 | if (ret) { |
a58c490f JG |
251 | goto end; |
252 | } | |
a58c490f | 253 | |
c0a66c84 | 254 | ret = lttng_action_serialize(trigger->action, payload); |
3647288f | 255 | if (ret) { |
a58c490f JG |
256 | goto end; |
257 | } | |
a58c490f | 258 | |
3647288f | 259 | /* Update payload size. */ |
c0a66c84 JG |
260 | header = (typeof(header)) (payload->buffer.data + header_offset); |
261 | header->length = payload->buffer.size - size_before_payload; | |
a58c490f JG |
262 | end: |
263 | return ret; | |
264 | } | |
3da864a9 | 265 | |
85c06c44 JR |
266 | LTTNG_HIDDEN |
267 | bool lttng_trigger_is_equal( | |
268 | const struct lttng_trigger *a, const struct lttng_trigger *b) | |
269 | { | |
270 | /* | |
271 | * Name is not taken into account since it is cosmetic only. | |
272 | */ | |
273 | if (!lttng_condition_is_equal(a->condition, b->condition)) { | |
274 | return false; | |
275 | } | |
276 | ||
277 | if (!lttng_action_is_equal(a->action, b->action)) { | |
278 | return false; | |
279 | } | |
280 | ||
281 | if (!lttng_credentials_is_equal(lttng_trigger_get_credentials(a), | |
282 | lttng_trigger_get_credentials(b))) { | |
283 | return false; | |
284 | } | |
285 | ||
286 | return true; | |
287 | } | |
288 | ||
f01d28b4 JR |
289 | LTTNG_HIDDEN |
290 | void lttng_trigger_get(struct lttng_trigger *trigger) | |
291 | { | |
292 | urcu_ref_get(&trigger->ref); | |
293 | } | |
294 | ||
295 | LTTNG_HIDDEN | |
296 | void lttng_trigger_put(struct lttng_trigger *trigger) | |
297 | { | |
298 | if (!trigger) { | |
299 | return; | |
300 | } | |
301 | ||
302 | urcu_ref_put(&trigger->ref , trigger_destroy_ref); | |
303 | } | |
304 | ||
3da864a9 JR |
305 | LTTNG_HIDDEN |
306 | const struct lttng_credentials *lttng_trigger_get_credentials( | |
307 | const struct lttng_trigger *trigger) | |
308 | { | |
64eafdf6 | 309 | return &trigger->creds; |
3da864a9 JR |
310 | } |
311 | ||
312 | LTTNG_HIDDEN | |
64eafdf6 | 313 | void lttng_trigger_set_credentials(struct lttng_trigger *trigger, |
3da864a9 JR |
314 | const struct lttng_credentials *creds) |
315 | { | |
316 | assert(creds); | |
64eafdf6 JR |
317 | trigger->creds = *creds; |
318 | } | |
319 | ||
320 | enum lttng_trigger_status lttng_trigger_set_owner_uid( | |
321 | struct lttng_trigger *trigger, uid_t uid) | |
322 | { | |
323 | enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK; | |
324 | const struct lttng_credentials creds = { | |
325 | .uid = LTTNG_OPTIONAL_INIT_VALUE(uid), | |
326 | .gid = LTTNG_OPTIONAL_INIT_UNSET, | |
327 | }; | |
328 | ||
329 | if (!trigger) { | |
330 | ret = LTTNG_TRIGGER_STATUS_INVALID; | |
331 | goto end; | |
332 | } | |
333 | ||
334 | /* Client-side validation only to report a clearer error. */ | |
335 | if (geteuid() != 0) { | |
336 | ret = LTTNG_TRIGGER_STATUS_PERMISSION_DENIED; | |
337 | goto end; | |
338 | } | |
339 | ||
340 | lttng_trigger_set_credentials(trigger, &creds); | |
341 | ||
342 | end: | |
343 | return ret; | |
344 | } | |
345 | ||
346 | enum lttng_trigger_status lttng_trigger_get_owner_uid( | |
347 | const struct lttng_trigger *trigger, uid_t *uid) | |
348 | { | |
349 | enum lttng_trigger_status ret = LTTNG_TRIGGER_STATUS_OK; | |
350 | const struct lttng_credentials *creds = NULL; | |
351 | ||
352 | if (!trigger || !uid ) { | |
353 | ret = LTTNG_TRIGGER_STATUS_INVALID; | |
354 | goto end; | |
355 | } | |
356 | ||
357 | if (!trigger->creds.uid.is_set ) { | |
358 | ret = LTTNG_TRIGGER_STATUS_UNSET; | |
359 | goto end; | |
360 | } | |
361 | ||
362 | creds = lttng_trigger_get_credentials(trigger); | |
363 | *uid = lttng_credentials_get_uid(creds); | |
364 | ||
365 | end: | |
366 | return ret; | |
3da864a9 | 367 | } |