302971767742a41f792f907484e2e91bc10e9c7f
[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 int ret;
229
230 if (!tracker_list) {
231 return;
232 }
233 lttng_tracker_list_reset(tracker_list);
234 ret = cds_lfht_destroy(tracker_list->ht, NULL);
235 assert(!ret);
236 free(tracker_list);
237 }
238
239 static int lttng_lookup_user(const char *username, int *result)
240 {
241 struct passwd p, *pres;
242 int ret, retval = LTTNG_OK;
243 char *buf = NULL;
244 ssize_t buflen;
245
246 buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
247 if (buflen < 0) {
248 buflen = FALLBACK_USER_BUFLEN;
249 }
250 buf = zmalloc(buflen);
251 if (!buf) {
252 retval = LTTNG_ERR_NOMEM;
253 goto end;
254 }
255 for (;;) {
256 ret = getpwnam_r(username, &p, buf, buflen, &pres);
257 switch (ret) {
258 case EINTR:
259 continue;
260 case ERANGE:
261 buflen *= 2;
262 free(buf);
263 buf = zmalloc(buflen);
264 if (!buf) {
265 retval = LTTNG_ERR_NOMEM;
266 goto end;
267 }
268 continue;
269 default:
270 goto end_loop;
271 }
272 }
273 end_loop:
274
275 switch (ret) {
276 case 0:
277 if (pres == NULL) {
278 retval = LTTNG_ERR_USER_NOT_FOUND;
279 } else {
280 *result = (int) p.pw_uid;
281 DBG("Lookup of tracker UID/VUID: name '%s' maps to id %d.",
282 username, *result);
283 retval = LTTNG_OK;
284 }
285 break;
286 case ENOENT:
287 case ESRCH:
288 case EBADF:
289 case EPERM:
290 retval = LTTNG_ERR_USER_NOT_FOUND;
291 break;
292 default:
293 retval = LTTNG_ERR_NOMEM;
294 }
295 end:
296 free(buf);
297 return retval;
298 }
299
300 static int lttng_lookup_group(const char *groupname, int *result)
301 {
302 struct group g, *gres;
303 int ret, retval = LTTNG_OK;
304 char *buf = NULL;
305 ssize_t buflen;
306
307 buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
308 if (buflen < 0) {
309 buflen = FALLBACK_GROUP_BUFLEN;
310 }
311 buf = zmalloc(buflen);
312 if (!buf) {
313 retval = LTTNG_ERR_NOMEM;
314 goto end;
315 }
316 for (;;) {
317 ret = getgrnam_r(groupname, &g, buf, buflen, &gres);
318 switch (ret) {
319 case EINTR:
320 continue;
321 case ERANGE:
322 buflen *= 2;
323 free(buf);
324 buf = zmalloc(buflen);
325 if (!buf) {
326 retval = LTTNG_ERR_NOMEM;
327 goto end;
328 }
329 continue;
330 default:
331 goto end_loop;
332 }
333 }
334 end_loop:
335
336 switch (ret) {
337 case 0:
338 if (gres == NULL) {
339 retval = LTTNG_ERR_GROUP_NOT_FOUND;
340 } else {
341 *result = (int) g.gr_gid;
342 DBG("Lookup of tracker GID/GUID: name '%s' maps to id %d.",
343 groupname, *result);
344 retval = LTTNG_OK;
345 }
346 break;
347 case ENOENT:
348 case ESRCH:
349 case EBADF:
350 case EPERM:
351 retval = LTTNG_ERR_GROUP_NOT_FOUND;
352 break;
353 default:
354 retval = LTTNG_ERR_NOMEM;
355 }
356 end:
357 free(buf);
358 return retval;
359 }
360
361 int lttng_tracker_id_lookup_string(enum lttng_tracker_type tracker_type,
362 const struct lttng_tracker_id *id,
363 int *result)
364 {
365 enum lttng_tracker_id_status status;
366 int value;
367 const char *string;
368
369 switch (lttng_tracker_id_get_type(id)) {
370 case LTTNG_ID_ALL:
371 *result = -1;
372 return LTTNG_OK;
373 case LTTNG_ID_VALUE:
374 status = lttng_tracker_id_get_value(id, &value);
375 if (status != LTTNG_TRACKER_ID_STATUS_OK) {
376 return LTTNG_ERR_INVALID;
377 }
378 *result = id->value;
379 return LTTNG_OK;
380 case LTTNG_ID_STRING:
381 status = lttng_tracker_id_get_string(id, &string);
382 if (status != LTTNG_TRACKER_ID_STATUS_OK) {
383 return LTTNG_ERR_INVALID;
384 }
385 switch (tracker_type) {
386 case LTTNG_TRACKER_PID:
387 case LTTNG_TRACKER_VPID:
388 ERR("Lookup of tracker PID/VPID by name unsupported.");
389 return LTTNG_ERR_INVALID;
390 case LTTNG_TRACKER_UID:
391 case LTTNG_TRACKER_VUID:
392 DBG("Lookup of tracker UID/VUID by name.");
393 return lttng_lookup_user(string, result);
394 case LTTNG_TRACKER_GID:
395 case LTTNG_TRACKER_VGID:
396 DBG("Lookup of tracker GID/VGID by name.");
397 return lttng_lookup_group(string, result);
398 default:
399 return LTTNG_ERR_INVALID;
400 }
401 break;
402 default:
403 return LTTNG_ERR_INVALID;
404 }
405 }
406
407 /*
408 * Protected by session mutex held by caller.
409 * On success, _ids and the ids it contains must be freed by the caller.
410 */
411 int lttng_tracker_id_get_list(const struct lttng_tracker_list *tracker_list,
412 struct lttng_tracker_ids **_ids)
413 {
414 int retval = LTTNG_OK, ret;
415 struct lttng_tracker_list_node *n;
416 ssize_t count = 0, i = 0;
417 struct lttng_tracker_ids *ids = NULL;
418 struct lttng_tracker_id *id;
419 enum lttng_tracker_id_status status;
420
421 switch (tracker_list->state) {
422 case LTTNG_TRACK_LIST:
423 cds_list_for_each_entry (
424 n, &tracker_list->list_head, list_node) {
425 count++;
426 }
427 ids = lttng_tracker_ids_create(count);
428 if (ids == NULL) {
429 PERROR("Failed to allocate tracked ID list");
430 retval = -LTTNG_ERR_NOMEM;
431 goto end;
432 }
433 cds_list_for_each_entry (
434 n, &tracker_list->list_head, list_node) {
435 id = lttng_tracker_ids_get_pointer_of_index(ids, i);
436 if (!id) {
437 retval = -LTTNG_ERR_INVALID;
438 goto error;
439 }
440
441 ret = lttng_tracker_id_copy(id, n->id);
442 if (ret) {
443 retval = -LTTNG_ERR_NOMEM;
444 goto error;
445 }
446 i++;
447 }
448 break;
449 case LTTNG_TRACK_ALL:
450
451 ids = lttng_tracker_ids_create(1);
452 if (ids == NULL) {
453 PERROR("Failed to allocate tracked ID list");
454 retval = -LTTNG_ERR_NOMEM;
455 goto end;
456 }
457
458 id = lttng_tracker_ids_get_pointer_of_index(ids, 0);
459 status = lttng_tracker_id_set_all(id);
460 if (status != LTTNG_TRACKER_ID_STATUS_OK) {
461 ERR("Invalid tracker id for track all");
462 retval = -LTTNG_ERR_INVALID;
463 goto error;
464 }
465 break;
466 case LTTNG_TRACK_NONE:
467 /* No ids track, so we return 0 element collection. */
468 ids = lttng_tracker_ids_create(0);
469 if (ids == NULL) {
470 PERROR("alloc list ids");
471 retval = -LTTNG_ERR_NOMEM;
472 goto end;
473 }
474 break;
475 }
476 *_ids = ids;
477
478 end:
479 return retval;
480
481 error:
482 lttng_tracker_ids_destroy(ids);
483 return retval;
484 }
485
486 int lttng_tracker_id_set_list(struct lttng_tracker_list *tracker_list,
487 const struct lttng_tracker_ids *ids)
488 {
489 unsigned int i, count;
490 const struct lttng_tracker_id *id;
491 enum lttng_tracker_id_status status;
492
493 assert(tracker_list);
494 assert(ids);
495
496 lttng_tracker_list_reset(tracker_list);
497
498 status = lttng_tracker_ids_get_count(ids, &count);
499 if (status != LTTNG_TRACKER_ID_STATUS_OK) {
500 return LTTNG_ERR_INVALID;
501 }
502
503 if (count == 0) {
504 /* Set state to "track none". */
505 tracker_list->state = LTTNG_TRACK_NONE;
506 return LTTNG_OK;
507 }
508
509 if (count == 1) {
510 id = lttng_tracker_ids_get_at_index(ids, 0);
511 if (lttng_tracker_id_get_type(id) == LTTNG_ID_ALL) {
512 /* Track all. */
513 return LTTNG_OK;
514 }
515 }
516
517 for (i = 0; i < count; i++) {
518 int ret;
519 id = lttng_tracker_ids_get_at_index(ids, i);
520 ret = lttng_tracker_list_add(tracker_list, id);
521 if (ret != LTTNG_OK) {
522 return ret;
523 }
524 }
525 return LTTNG_OK;
526 }
This page took 0.039499 seconds and 4 git commands to generate.