Move to kernel style SPDX license identifiers
[lttng-tools.git] / src / bin / lttng-sessiond / tracker.c
1 /*
2 * Copyright (C) 2018 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8 #define _LGPL_SOURCE
9 #include <grp.h>
10 #include <pwd.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13
14 #include "tracker.h"
15 #include <common/defaults.h>
16 #include <common/error.h>
17 #include <common/hashtable/hashtable.h>
18 #include <common/hashtable/utils.h>
19 #include <lttng/lttng-error.h>
20 #include <lttng/tracker-internal.h>
21
22 #define FALLBACK_USER_BUFLEN 16384
23 #define FALLBACK_GROUP_BUFLEN 16384
24
25 struct lttng_tracker_list *lttng_tracker_list_create(void)
26 {
27 struct lttng_tracker_list *t;
28
29 t = zmalloc(sizeof(*t));
30 if (!t) {
31 return NULL;
32 }
33 t->ht = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
34 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
35 if (!t->ht) {
36 goto error;
37 }
38 CDS_INIT_LIST_HEAD(&t->list_head);
39 t->state = LTTNG_TRACK_ALL;
40 return t;
41
42 error:
43 free(t);
44 return NULL;
45 }
46
47 static int match_tracker_key(struct cds_lfht_node *node, const void *key)
48 {
49 const struct lttng_tracker_id *tracker_key = key;
50 struct lttng_tracker_list_node *tracker_node;
51
52 tracker_node = caa_container_of(
53 node, struct lttng_tracker_list_node, ht_node);
54
55 return lttng_tracker_id_is_equal(tracker_node->id, tracker_key);
56 }
57
58 static unsigned long hash_tracker_key(
59 const struct lttng_tracker_id *tracker_key)
60 {
61 unsigned long key_hash = 0;
62 int value;
63 const char *string;
64 enum lttng_tracker_id_type type;
65
66 /* We do not care for invalid state during hash computation */
67 type = lttng_tracker_id_get_type(tracker_key);
68 (void) lttng_tracker_id_get_value(tracker_key, &value);
69 (void) lttng_tracker_id_get_string(tracker_key, &string);
70
71 switch (type) {
72 case LTTNG_ID_ALL:
73 break;
74 case LTTNG_ID_VALUE:
75 key_hash ^= hash_key_ulong(
76 (void *) (unsigned long) value, lttng_ht_seed);
77 break;
78 case LTTNG_ID_STRING:
79 key_hash ^= hash_key_str(string, lttng_ht_seed);
80 break;
81 case LTTNG_ID_UNKNOWN:
82 break;
83 }
84 key_hash ^= hash_key_ulong(
85 (void *) (unsigned long) type, lttng_ht_seed);
86 return key_hash;
87 }
88
89 static struct lttng_tracker_id **lttng_tracker_list_lookup(
90 const struct lttng_tracker_list *tracker_list,
91 const struct lttng_tracker_id *key)
92 {
93 struct lttng_tracker_list_node *list_node;
94 struct cds_lfht_iter iter;
95 struct cds_lfht_node *node;
96
97 cds_lfht_lookup(tracker_list->ht, hash_tracker_key(key),
98 match_tracker_key, key, &iter);
99 node = cds_lfht_iter_get_node(&iter);
100 if (!node) {
101 return NULL;
102 }
103 list_node = caa_container_of(
104 node, struct lttng_tracker_list_node, ht_node);
105 return &list_node->id;
106 }
107
108 static void destroy_list_node_rcu(struct rcu_head *head)
109 {
110 struct lttng_tracker_list_node *n = caa_container_of(
111 head, struct lttng_tracker_list_node, rcu_head);
112
113 lttng_tracker_id_destroy(n->id);
114 free(n);
115 }
116
117 static void _lttng_tracker_list_remove(struct lttng_tracker_list *tracker_list,
118 struct lttng_tracker_list_node *n)
119 {
120 cds_list_del(&n->list_node);
121
122 rcu_read_lock();
123 cds_lfht_del(tracker_list->ht, &n->ht_node);
124 rcu_read_unlock();
125
126 call_rcu(&n->rcu_head, destroy_list_node_rcu);
127 }
128
129 static void lttng_tracker_list_reset(struct lttng_tracker_list *tracker_list)
130 {
131 struct lttng_tracker_list_node *n, *t;
132
133 cds_list_for_each_entry_safe (
134 n, t, &tracker_list->list_head, list_node) {
135 _lttng_tracker_list_remove(tracker_list, n);
136 }
137 tracker_list->state = LTTNG_TRACK_ALL;
138 }
139
140 /* Protected by session mutex held by caller. */
141 int lttng_tracker_list_add(struct lttng_tracker_list *tracker_list,
142 const struct lttng_tracker_id *_id)
143 {
144 struct lttng_tracker_id **id;
145 struct lttng_tracker_list_node *n = NULL;
146 int ret;
147
148 if (lttng_tracker_id_get_type(_id) == LTTNG_ID_ALL) {
149 /* Track all, so remove each individual item. */
150 lttng_tracker_list_reset(tracker_list);
151 ret = LTTNG_OK;
152 goto error;
153 }
154 rcu_read_lock();
155 id = lttng_tracker_list_lookup(tracker_list, _id);
156 /*
157 * It is okay to release the RCU read lock here since id is only checked
158 * for != NULL and not dereferenced.
159 */
160 rcu_read_unlock();
161 if (id) {
162 ret = LTTNG_ERR_ID_TRACKED;
163 goto error;
164 }
165 n = zmalloc(sizeof(*n));
166 if (!n) {
167 ret = LTTNG_ERR_NOMEM;
168 goto error;
169 }
170
171 n->id = lttng_tracker_id_duplicate(_id);
172 if (!n->id) {
173 ret = LTTNG_ERR_NOMEM;
174 goto error;
175 }
176
177 cds_list_add_tail(&n->list_node, &tracker_list->list_head);
178 tracker_list->state = LTTNG_TRACK_LIST;
179
180 rcu_read_lock();
181 cds_lfht_add(tracker_list->ht, hash_tracker_key(n->id), &n->ht_node);
182 rcu_read_unlock();
183
184 return LTTNG_OK;
185
186 error:
187 free(n);
188 return ret;
189 }
190
191 /*
192 * Lookup and remove.
193 * Protected by session mutex held by caller.
194 */
195 int lttng_tracker_list_remove(struct lttng_tracker_list *tracker_list,
196 const struct lttng_tracker_id *_id)
197 {
198 enum lttng_error_code ret = LTTNG_OK;
199 struct lttng_tracker_id **id;
200 struct lttng_tracker_list_node *n;
201
202 if (lttng_tracker_id_get_type(_id) == LTTNG_ID_ALL) {
203 /* Untrack all. */
204 lttng_tracker_list_reset(tracker_list);
205 /* Set state to "track none". */
206 tracker_list->state = LTTNG_TRACK_NONE;
207 goto end;
208 }
209
210 rcu_read_lock();
211 id = lttng_tracker_list_lookup(tracker_list, _id);
212 if (!id) {
213 ret = LTTNG_ERR_ID_NOT_TRACKED;
214 goto rcu_unlock;
215 }
216
217 n = caa_container_of(id, struct lttng_tracker_list_node, id);
218 _lttng_tracker_list_remove(tracker_list, n);
219
220 rcu_unlock:
221 rcu_read_unlock();
222 end:
223 return ret;
224 }
225
226 void lttng_tracker_list_destroy(struct lttng_tracker_list *tracker_list)
227 {
228 if (!tracker_list) {
229 return;
230 }
231 lttng_tracker_list_reset(tracker_list);
232 cds_lfht_destroy(tracker_list->ht, NULL);
233 free(tracker_list);
234 }
235
236 static int lttng_lookup_user(const char *username, int *result)
237 {
238 struct passwd p, *pres;
239 int ret, retval = LTTNG_OK;
240 char *buf = NULL;
241 ssize_t buflen;
242
243 buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
244 if (buflen < 0) {
245 buflen = FALLBACK_USER_BUFLEN;
246 }
247 buf = zmalloc(buflen);
248 if (!buf) {
249 retval = LTTNG_ERR_NOMEM;
250 goto end;
251 }
252 for (;;) {
253 ret = getpwnam_r(username, &p, buf, buflen, &pres);
254 switch (ret) {
255 case EINTR:
256 continue;
257 case ERANGE:
258 buflen *= 2;
259 free(buf);
260 buf = zmalloc(buflen);
261 if (!buf) {
262 retval = LTTNG_ERR_NOMEM;
263 goto end;
264 }
265 continue;
266 default:
267 goto end_loop;
268 }
269 }
270 end_loop:
271
272 switch (ret) {
273 case 0:
274 if (pres == NULL) {
275 retval = LTTNG_ERR_USER_NOT_FOUND;
276 } else {
277 *result = (int) p.pw_uid;
278 DBG("Lookup of tracker UID/VUID: name '%s' maps to id %d.",
279 username, *result);
280 retval = LTTNG_OK;
281 }
282 break;
283 case ENOENT:
284 case ESRCH:
285 case EBADF:
286 case EPERM:
287 retval = LTTNG_ERR_USER_NOT_FOUND;
288 break;
289 default:
290 retval = LTTNG_ERR_NOMEM;
291 }
292 end:
293 free(buf);
294 return retval;
295 }
296
297 static int lttng_lookup_group(const char *groupname, int *result)
298 {
299 struct group g, *gres;
300 int ret, retval = LTTNG_OK;
301 char *buf = NULL;
302 ssize_t buflen;
303
304 buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
305 if (buflen < 0) {
306 buflen = FALLBACK_GROUP_BUFLEN;
307 }
308 buf = zmalloc(buflen);
309 if (!buf) {
310 retval = LTTNG_ERR_NOMEM;
311 goto end;
312 }
313 for (;;) {
314 ret = getgrnam_r(groupname, &g, buf, buflen, &gres);
315 switch (ret) {
316 case EINTR:
317 continue;
318 case ERANGE:
319 buflen *= 2;
320 free(buf);
321 buf = zmalloc(buflen);
322 if (!buf) {
323 retval = LTTNG_ERR_NOMEM;
324 goto end;
325 }
326 continue;
327 default:
328 goto end_loop;
329 }
330 }
331 end_loop:
332
333 switch (ret) {
334 case 0:
335 if (gres == NULL) {
336 retval = LTTNG_ERR_GROUP_NOT_FOUND;
337 } else {
338 *result = (int) g.gr_gid;
339 DBG("Lookup of tracker GID/GUID: name '%s' maps to id %d.",
340 groupname, *result);
341 retval = LTTNG_OK;
342 }
343 break;
344 case ENOENT:
345 case ESRCH:
346 case EBADF:
347 case EPERM:
348 retval = LTTNG_ERR_GROUP_NOT_FOUND;
349 break;
350 default:
351 retval = LTTNG_ERR_NOMEM;
352 }
353 end:
354 free(buf);
355 return retval;
356 }
357
358 int lttng_tracker_id_lookup_string(enum lttng_tracker_type tracker_type,
359 const struct lttng_tracker_id *id,
360 int *result)
361 {
362 enum lttng_tracker_id_status status;
363 int value;
364 const char *string;
365
366 switch (lttng_tracker_id_get_type(id)) {
367 case LTTNG_ID_ALL:
368 *result = -1;
369 return LTTNG_OK;
370 case LTTNG_ID_VALUE:
371 status = lttng_tracker_id_get_value(id, &value);
372 if (status != LTTNG_TRACKER_ID_STATUS_OK) {
373 return LTTNG_ERR_INVALID;
374 }
375 *result = id->value;
376 return LTTNG_OK;
377 case LTTNG_ID_STRING:
378 status = lttng_tracker_id_get_string(id, &string);
379 if (status != LTTNG_TRACKER_ID_STATUS_OK) {
380 return LTTNG_ERR_INVALID;
381 }
382 switch (tracker_type) {
383 case LTTNG_TRACKER_PID:
384 case LTTNG_TRACKER_VPID:
385 ERR("Lookup of tracker PID/VPID by name unsupported.");
386 return LTTNG_ERR_INVALID;
387 case LTTNG_TRACKER_UID:
388 case LTTNG_TRACKER_VUID:
389 DBG("Lookup of tracker UID/VUID by name.");
390 return lttng_lookup_user(string, result);
391 case LTTNG_TRACKER_GID:
392 case LTTNG_TRACKER_VGID:
393 DBG("Lookup of tracker GID/VGID by name.");
394 return lttng_lookup_group(string, result);
395 default:
396 return LTTNG_ERR_INVALID;
397 }
398 break;
399 default:
400 return LTTNG_ERR_INVALID;
401 }
402 }
403
404 /*
405 * Protected by session mutex held by caller.
406 * On success, _ids and the ids it contains must be freed by the caller.
407 */
408 int lttng_tracker_id_get_list(const struct lttng_tracker_list *tracker_list,
409 struct lttng_tracker_ids **_ids)
410 {
411 int retval = LTTNG_OK, ret;
412 struct lttng_tracker_list_node *n;
413 ssize_t count = 0, i = 0;
414 struct lttng_tracker_ids *ids = NULL;
415 struct lttng_tracker_id *id;
416 enum lttng_tracker_id_status status;
417
418 switch (tracker_list->state) {
419 case LTTNG_TRACK_LIST:
420 cds_list_for_each_entry (
421 n, &tracker_list->list_head, list_node) {
422 count++;
423 }
424 ids = lttng_tracker_ids_create(count);
425 if (ids == NULL) {
426 PERROR("Failed to allocate tracked ID list");
427 retval = -LTTNG_ERR_NOMEM;
428 goto end;
429 }
430 cds_list_for_each_entry (
431 n, &tracker_list->list_head, list_node) {
432 id = lttng_tracker_ids_get_pointer_of_index(ids, i);
433 if (!id) {
434 retval = -LTTNG_ERR_INVALID;
435 goto error;
436 }
437
438 ret = lttng_tracker_id_copy(id, n->id);
439 if (ret) {
440 retval = -LTTNG_ERR_NOMEM;
441 goto error;
442 }
443 i++;
444 }
445 break;
446 case LTTNG_TRACK_ALL:
447
448 ids = lttng_tracker_ids_create(1);
449 if (ids == NULL) {
450 PERROR("Failed to allocate tracked ID list");
451 retval = -LTTNG_ERR_NOMEM;
452 goto end;
453 }
454
455 id = lttng_tracker_ids_get_pointer_of_index(ids, 0);
456 status = lttng_tracker_id_set_all(id);
457 if (status != LTTNG_TRACKER_ID_STATUS_OK) {
458 ERR("Invalid tracker id for track all");
459 retval = -LTTNG_ERR_INVALID;
460 goto error;
461 }
462 break;
463 case LTTNG_TRACK_NONE:
464 /* No ids track, so we return 0 element collection. */
465 ids = lttng_tracker_ids_create(0);
466 if (ids == NULL) {
467 PERROR("alloc list ids");
468 retval = -LTTNG_ERR_NOMEM;
469 goto end;
470 }
471 break;
472 }
473 *_ids = ids;
474
475 end:
476 return retval;
477
478 error:
479 lttng_tracker_ids_destroy(ids);
480 return retval;
481 }
482
483 int lttng_tracker_id_set_list(struct lttng_tracker_list *tracker_list,
484 const struct lttng_tracker_ids *ids)
485 {
486 unsigned int i, count;
487 const struct lttng_tracker_id *id;
488 enum lttng_tracker_id_status status;
489
490 assert(tracker_list);
491 assert(ids);
492
493 lttng_tracker_list_reset(tracker_list);
494
495 status = lttng_tracker_ids_get_count(ids, &count);
496 if (status != LTTNG_TRACKER_ID_STATUS_OK) {
497 return LTTNG_ERR_INVALID;
498 }
499
500 if (count == 0) {
501 /* Set state to "track none". */
502 tracker_list->state = LTTNG_TRACK_NONE;
503 return LTTNG_OK;
504 }
505
506 if (count == 1) {
507 id = lttng_tracker_ids_get_at_index(ids, 0);
508 if (lttng_tracker_id_get_type(id) == LTTNG_ID_ALL) {
509 /* Track all. */
510 return LTTNG_OK;
511 }
512 }
513
514 for (i = 0; i < count; i++) {
515 int ret;
516 id = lttng_tracker_ids_get_at_index(ids, i);
517 ret = lttng_tracker_list_add(tracker_list, id);
518 if (ret != LTTNG_OK) {
519 return ret;
520 }
521 }
522 return LTTNG_OK;
523 }
This page took 0.039677 seconds and 5 git commands to generate.