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