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