Move to kernel style SPDX license identifiers
[lttng-tools.git] / src / bin / lttng-sessiond / tracker.c
CommitLineData
a8c3ad3e 1/*
ab5be9fa 2 * Copyright (C) 2018 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
a8c3ad3e 3 *
ab5be9fa 4 * SPDX-License-Identifier: GPL-2.0-only
a8c3ad3e 5 *
a8c3ad3e
MD
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>
2d97a006 20#include <lttng/tracker-internal.h>
a8c3ad3e
MD
21
22#define FALLBACK_USER_BUFLEN 16384
23#define FALLBACK_GROUP_BUFLEN 16384
24
25struct 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
42error:
43 free(t);
44 return NULL;
45}
46
47static 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);
2d97a006
JR
54
55 return lttng_tracker_id_is_equal(tracker_node->id, tracker_key);
a8c3ad3e
MD
56}
57
58static unsigned long hash_tracker_key(
59 const struct lttng_tracker_id *tracker_key)
60{
61 unsigned long key_hash = 0;
2d97a006
JR
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);
a8c3ad3e 70
2d97a006 71 switch (type) {
a8c3ad3e
MD
72 case LTTNG_ID_ALL:
73 break;
74 case LTTNG_ID_VALUE:
75 key_hash ^= hash_key_ulong(
2d97a006 76 (void *) (unsigned long) value, lttng_ht_seed);
a8c3ad3e
MD
77 break;
78 case LTTNG_ID_STRING:
2d97a006 79 key_hash ^= hash_key_str(string, lttng_ht_seed);
a8c3ad3e
MD
80 break;
81 case LTTNG_ID_UNKNOWN:
82 break;
83 }
2d97a006
JR
84 key_hash ^= hash_key_ulong(
85 (void *) (unsigned long) type, lttng_ht_seed);
a8c3ad3e
MD
86 return key_hash;
87}
88
2d97a006 89static struct lttng_tracker_id **lttng_tracker_list_lookup(
a8c3ad3e
MD
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
108static 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
2d97a006 113 lttng_tracker_id_destroy(n->id);
a8c3ad3e
MD
114 free(n);
115}
116
117static 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
129static 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. */
141int lttng_tracker_list_add(struct lttng_tracker_list *tracker_list,
142 const struct lttng_tracker_id *_id)
143{
2d97a006
JR
144 struct lttng_tracker_id **id;
145 struct lttng_tracker_list_node *n = NULL;
a8c3ad3e
MD
146 int ret;
147
2d97a006 148 if (lttng_tracker_id_get_type(_id) == LTTNG_ID_ALL) {
a8c3ad3e
MD
149 /* Track all, so remove each individual item. */
150 lttng_tracker_list_reset(tracker_list);
2d97a006
JR
151 ret = LTTNG_OK;
152 goto error;
a8c3ad3e
MD
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) {
2d97a006
JR
162 ret = LTTNG_ERR_ID_TRACKED;
163 goto error;
a8c3ad3e
MD
164 }
165 n = zmalloc(sizeof(*n));
166 if (!n) {
2d97a006
JR
167 ret = LTTNG_ERR_NOMEM;
168 goto error;
a8c3ad3e 169 }
2d97a006 170
a7a533cd 171 n->id = lttng_tracker_id_duplicate(_id);
2d97a006
JR
172 if (!n->id) {
173 ret = LTTNG_ERR_NOMEM;
174 goto error;
a8c3ad3e
MD
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();
2d97a006 181 cds_lfht_add(tracker_list->ht, hash_tracker_key(n->id), &n->ht_node);
a8c3ad3e
MD
182 rcu_read_unlock();
183
184 return LTTNG_OK;
185
186error:
187 free(n);
188 return ret;
189}
190
191/*
192 * Lookup and remove.
193 * Protected by session mutex held by caller.
194 */
195int 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;
2d97a006 199 struct lttng_tracker_id **id;
a8c3ad3e
MD
200 struct lttng_tracker_list_node *n;
201
2d97a006 202 if (lttng_tracker_id_get_type(_id) == LTTNG_ID_ALL) {
a8c3ad3e
MD
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
220rcu_unlock:
221 rcu_read_unlock();
222end:
223 return ret;
224}
225
226void 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
236static 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 }
270end_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 }
292end:
293 free(buf);
294 return retval;
295}
296
297static 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 }
331end_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 }
353end:
354 free(buf);
355 return retval;
356}
357
358int lttng_tracker_id_lookup_string(enum lttng_tracker_type tracker_type,
359 const struct lttng_tracker_id *id,
360 int *result)
361{
2d97a006
JR
362 enum lttng_tracker_id_status status;
363 int value;
364 const char *string;
365
366 switch (lttng_tracker_id_get_type(id)) {
a8c3ad3e
MD
367 case LTTNG_ID_ALL:
368 *result = -1;
369 return LTTNG_OK;
370 case LTTNG_ID_VALUE:
2d97a006
JR
371 status = lttng_tracker_id_get_value(id, &value);
372 if (status != LTTNG_TRACKER_ID_STATUS_OK) {
373 return LTTNG_ERR_INVALID;
374 }
a8c3ad3e
MD
375 *result = id->value;
376 return LTTNG_OK;
377 case LTTNG_ID_STRING:
2d97a006
JR
378 status = lttng_tracker_id_get_string(id, &string);
379 if (status != LTTNG_TRACKER_ID_STATUS_OK) {
380 return LTTNG_ERR_INVALID;
381 }
a8c3ad3e
MD
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.");
2d97a006 390 return lttng_lookup_user(string, result);
a8c3ad3e
MD
391 case LTTNG_TRACKER_GID:
392 case LTTNG_TRACKER_VGID:
393 DBG("Lookup of tracker GID/VGID by name.");
2d97a006 394 return lttng_lookup_group(string, result);
a8c3ad3e
MD
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.
2d97a006 406 * On success, _ids and the ids it contains must be freed by the caller.
a8c3ad3e 407 */
a7a533cd
JR
408int lttng_tracker_id_get_list(const struct lttng_tracker_list *tracker_list,
409 struct lttng_tracker_ids **_ids)
a8c3ad3e 410{
a7a533cd 411 int retval = LTTNG_OK, ret;
a8c3ad3e 412 struct lttng_tracker_list_node *n;
a7a533cd
JR
413 ssize_t count = 0, i = 0;
414 struct lttng_tracker_ids *ids = NULL;
415 struct lttng_tracker_id *id;
2d97a006 416 enum lttng_tracker_id_status status;
a8c3ad3e
MD
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 }
a7a533cd 424 ids = lttng_tracker_ids_create(count);
a8c3ad3e
MD
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) {
a7a533cd
JR
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) {
2d97a006
JR
440 retval = -LTTNG_ERR_NOMEM;
441 goto error;
a8c3ad3e
MD
442 }
443 i++;
444 }
a8c3ad3e
MD
445 break;
446 case LTTNG_TRACK_ALL:
a7a533cd
JR
447
448 ids = lttng_tracker_ids_create(1);
a8c3ad3e
MD
449 if (ids == NULL) {
450 PERROR("Failed to allocate tracked ID list");
451 retval = -LTTNG_ERR_NOMEM;
452 goto end;
453 }
a7a533cd
JR
454
455 id = lttng_tracker_ids_get_pointer_of_index(ids, 0);
456 status = lttng_tracker_id_set_all(id);
2d97a006
JR
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 }
a8c3ad3e
MD
462 break;
463 case LTTNG_TRACK_NONE:
a7a533cd
JR
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 }
a8c3ad3e
MD
471 break;
472 }
a7a533cd
JR
473 *_ids = ids;
474
a8c3ad3e
MD
475end:
476 return retval;
477
478error:
a7a533cd 479 lttng_tracker_ids_destroy(ids);
a8c3ad3e
MD
480 return retval;
481}
482
483int lttng_tracker_id_set_list(struct lttng_tracker_list *tracker_list,
a7a533cd 484 const struct lttng_tracker_ids *ids)
a8c3ad3e 485{
e283e4a0 486 unsigned int i, count;
a7a533cd 487 const struct lttng_tracker_id *id;
e283e4a0 488 enum lttng_tracker_id_status status;
a7a533cd
JR
489
490 assert(tracker_list);
491 assert(ids);
a8c3ad3e
MD
492
493 lttng_tracker_list_reset(tracker_list);
e283e4a0
JR
494
495 status = lttng_tracker_ids_get_count(ids, &count);
496 if (status != LTTNG_TRACKER_ID_STATUS_OK) {
497 return LTTNG_ERR_INVALID;
498 }
a7a533cd 499
a8c3ad3e
MD
500 if (count == 0) {
501 /* Set state to "track none". */
502 tracker_list->state = LTTNG_TRACK_NONE;
503 return LTTNG_OK;
504 }
a7a533cd
JR
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
a8c3ad3e 514 for (i = 0; i < count; i++) {
a8c3ad3e 515 int ret;
a7a533cd 516 id = lttng_tracker_ids_get_at_index(ids, i);
a8c3ad3e
MD
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.044423 seconds and 4 git commands to generate.