Backport: trackers: add sessiond tracker list implementation
[lttng-tools.git] / src / bin / lttng-sessiond / tracker.c
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 <sys/types.h>
20 #include <pwd.h>
21 #include <grp.h>
22 #include <unistd.h>
23
24 #include <common/defaults.h>
25 #include <common/hashtable/utils.h>
26 #include <common/hashtable/hashtable.h>
27 #include <common/error.h>
28 #include "tracker.h"
29 #include <lttng/lttng-error.h>
30
31 #define FALLBACK_USER_BUFLEN 16384
32 #define FALLBACK_GROUP_BUFLEN 16384
33
34 struct 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,
43 1, 0, 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
51 error:
52 free(t);
53 return NULL;
54 }
55
56 static
57 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(node, struct lttng_tracker_list_node,
63 ht_node);
64 if (tracker_node->id.type != tracker_key->type)
65 return 0;
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 break;
73 case LTTNG_ID_STRING:
74 if (strcmp(tracker_node->id.string, tracker_key->string) != 0)
75 return 0;
76 break;
77 default:
78 return 0;
79 }
80 return 1;
81 }
82
83 static
84 unsigned long hash_tracker_key(const struct lttng_tracker_id *tracker_key)
85 {
86 unsigned long key_hash = 0;
87
88 switch (tracker_key->type) {
89 case LTTNG_ID_ALL:
90 break;
91 case LTTNG_ID_VALUE:
92 key_hash ^= hash_key_ulong((void *)(unsigned long)tracker_key->value,
93 lttng_ht_seed);
94 break;
95 case LTTNG_ID_STRING:
96 key_hash ^= hash_key_str(tracker_key->string, lttng_ht_seed);
97 break;
98 case LTTNG_ID_UNKNOWN:
99 break;
100 }
101 key_hash ^= hash_key_ulong((void *)(unsigned long)tracker_key->type,
102 lttng_ht_seed);
103 return key_hash;
104 }
105
106 static
107 struct lttng_tracker_id *lttng_tracker_list_lookup(
108 struct lttng_tracker_list *tracker_list,
109 const struct lttng_tracker_id *key)
110 {
111 struct lttng_tracker_list_node *list_node;
112 struct cds_lfht_iter iter;
113 struct cds_lfht_node *node;
114
115 cds_lfht_lookup(tracker_list->ht,
116 hash_tracker_key(key),
117 match_tracker_key,
118 key,
119 &iter);
120 node = cds_lfht_iter_get_node(&iter);
121 if (!node) {
122 return NULL;
123 }
124 list_node = caa_container_of(node, struct lttng_tracker_list_node,
125 ht_node);
126 return &list_node->id;
127 }
128
129 static
130 void destroy_list_node_rcu(struct rcu_head *head)
131 {
132 struct lttng_tracker_list_node *n =
133 caa_container_of(head, struct lttng_tracker_list_node, rcu_head);
134
135 free(n->id.string);
136 free(n);
137 }
138
139 static
140 void _lttng_tracker_list_remove(struct lttng_tracker_list *tracker_list,
141 struct lttng_tracker_list_node *n)
142 {
143 cds_list_del(&n->list_node);
144
145 rcu_read_lock();
146 cds_lfht_del(tracker_list->ht, &n->ht_node);
147 rcu_read_unlock();
148
149 call_rcu(&n->rcu_head, destroy_list_node_rcu);
150 }
151
152 static
153 void lttng_tracker_list_reset(struct lttng_tracker_list *tracker_list)
154 {
155 struct lttng_tracker_list_node *n, *t;
156
157 cds_list_for_each_entry_safe(n, t, &tracker_list->list_head,
158 list_node) {
159 _lttng_tracker_list_remove(tracker_list, n);
160 }
161 tracker_list->state = LTTNG_TRACK_ALL;
162 }
163
164 /* Protected by session mutex held by caller. */
165 int lttng_tracker_list_add(struct lttng_tracker_list *tracker_list,
166 const struct lttng_tracker_id *_id)
167 {
168 struct lttng_tracker_id *id;
169 struct lttng_tracker_list_node *n;
170 int ret;
171
172 if (_id->type == LTTNG_ID_ALL) {
173 /* Track all, so remove each individual item. */
174 lttng_tracker_list_reset(tracker_list);
175 return LTTNG_OK;
176 }
177 rcu_read_lock();
178 id = lttng_tracker_list_lookup(tracker_list, _id);
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
207 error:
208 free(n);
209 return ret;
210 }
211
212 /* Lookup and remove */
213 /* Protected by session mutex held by caller. */
214 int lttng_tracker_list_remove(struct lttng_tracker_list *tracker_list,
215 const struct lttng_tracker_id *_id)
216 {
217 struct lttng_tracker_id *id;
218 struct lttng_tracker_list_node *n;
219
220 if (_id->type == LTTNG_ID_ALL) {
221 /* Untrack all. */
222 lttng_tracker_list_reset(tracker_list);
223 /* Set state to "track none". */
224 tracker_list->state = LTTNG_TRACK_NONE;
225 return LTTNG_OK;
226 }
227
228 rcu_read_lock();
229 id = lttng_tracker_list_lookup(tracker_list, _id);
230 if (!id) {
231 rcu_read_unlock();
232 return LTTNG_ERR_ID_NOT_TRACKED;
233 }
234 n = caa_container_of(id, struct lttng_tracker_list_node, id);
235 _lttng_tracker_list_remove(tracker_list, n);
236 rcu_read_unlock();
237 return LTTNG_OK;
238 }
239
240 void lttng_tracker_list_destroy(struct lttng_tracker_list *tracker_list)
241 {
242 if (!tracker_list)
243 return;
244 lttng_tracker_list_reset(tracker_list);
245 cds_lfht_destroy(tracker_list->ht, NULL);
246 free(tracker_list);
247 }
248
249 static
250 int lttng_lookup_user(const char *username, int *result)
251 {
252 struct passwd p, *pres;
253 int ret, retval = LTTNG_OK;
254 char *buf = NULL;
255 ssize_t buflen;
256
257 buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
258 if (buflen < 0) {
259 buflen = FALLBACK_USER_BUFLEN;
260 }
261 buf = zmalloc(buflen);
262 if (!buf) {
263 retval = LTTNG_ERR_NOMEM;
264 goto end;
265 }
266 for (;;) {
267 ret = getpwnam_r(username, &p, buf, buflen, &pres);
268 switch (ret) {
269 case EINTR:
270 continue;
271 case ERANGE:
272 buflen *= 2;
273 free(buf);
274 buf = zmalloc(buflen);
275 if (!buf) {
276 retval = LTTNG_ERR_NOMEM;
277 goto end;
278 }
279 continue;
280 default:
281 goto end_loop;
282 }
283 }
284 end_loop:
285
286 switch (ret) {
287 case 0:
288 if (pres == NULL) {
289 retval = LTTNG_ERR_USER_NOT_FOUND;
290 } else {
291 *result = (int) p.pw_uid;
292 DBG("Lookup of tracker UID/VUID: name '%s' maps to id %d.",
293 username, *result);
294 retval = LTTNG_OK;
295 }
296 break;
297 case ENOENT:
298 case ESRCH:
299 case EBADF:
300 case EPERM:
301 retval = LTTNG_ERR_USER_NOT_FOUND;
302 break;
303 default:
304 retval = LTTNG_ERR_NOMEM;
305 }
306 end:
307 free(buf);
308 return retval;
309 }
310
311 static
312 int lttng_lookup_group(const char *groupname, int *result)
313 {
314 struct group g, *gres;
315 int ret, retval = LTTNG_OK;
316 char *buf = NULL;
317 ssize_t buflen;
318
319 buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
320 if (buflen < 0) {
321 buflen = FALLBACK_GROUP_BUFLEN;
322 }
323 buf = zmalloc(buflen);
324 if (!buf) {
325 retval = LTTNG_ERR_NOMEM;
326 goto end;
327 }
328 for (;;) {
329 ret = getgrnam_r(groupname, &g, buf, buflen, &gres);
330 switch (ret) {
331 case EINTR:
332 continue;
333 case ERANGE:
334 buflen *= 2;
335 free(buf);
336 buf = zmalloc(buflen);
337 if (!buf) {
338 retval = LTTNG_ERR_NOMEM;
339 goto end;
340 }
341 continue;
342 default:
343 goto end_loop;
344 }
345 }
346 end_loop:
347
348 switch (ret) {
349 case 0:
350 if (gres == NULL) {
351 retval = LTTNG_ERR_GROUP_NOT_FOUND;
352 } else {
353 *result = (int) g.gr_gid;
354 DBG("Lookup of tracker GID/GUID: name '%s' maps to id %d.",
355 groupname, *result);
356 retval = LTTNG_OK;
357 }
358 break;
359 case ENOENT:
360 case ESRCH:
361 case EBADF:
362 case EPERM:
363 retval = LTTNG_ERR_GROUP_NOT_FOUND;
364 break;
365 default:
366 retval = LTTNG_ERR_NOMEM;
367 }
368 end:
369 free(buf);
370 return retval;
371 }
372
373 int lttng_tracker_id_lookup_string(enum lttng_tracker_type tracker_type,
374 const struct lttng_tracker_id *id,
375 int *result)
376 {
377 switch (id->type) {
378 case LTTNG_ID_ALL:
379 *result = -1;
380 return LTTNG_OK;
381 case LTTNG_ID_VALUE:
382 *result = id->value;
383 return LTTNG_OK;
384 case LTTNG_ID_STRING:
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(id->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(id->string, result);
398 default:
399 return LTTNG_ERR_INVALID;
400 }
401 break;
402 default:
403 return LTTNG_ERR_INVALID;
404 }
405 }
406
407 /* Protected by session mutex held by caller. */
408 /* On success, _ids and the strings it contains must be freed by caller. */
409 ssize_t lttng_tracker_id_get_list(struct lttng_tracker_list *tracker_list,
410 struct lttng_tracker_id **_ids)
411 {
412 struct lttng_tracker_list_node *n;
413 ssize_t count = 0, i = 0, retval = 0;
414 struct lttng_tracker_id *ids;
415
416 switch (tracker_list->state) {
417 case LTTNG_TRACK_LIST:
418 cds_list_for_each_entry(n, &tracker_list->list_head, list_node) {
419 count++;
420 }
421 ids = zmalloc(sizeof(*ids) * count);
422 if (ids == NULL) {
423 PERROR("alloc list ids");
424 retval = -LTTNG_ERR_NOMEM;
425 goto end;
426 }
427 cds_list_for_each_entry(n, &tracker_list->list_head, list_node) {
428 ids[i].type = n->id.type;
429 ids[i].value = n->id.value;
430 if (ids[i].type == LTTNG_ID_STRING) {
431 ids[i].string = strdup(n->id.string);
432 if (!ids[i].string) {
433 retval = -LTTNG_ERR_NOMEM;
434 goto error;
435 }
436 }
437 i++;
438 }
439 *_ids = ids;
440 retval = count;
441 break;
442 case LTTNG_TRACK_ALL:
443 ids = zmalloc(sizeof(*ids));
444 if (ids == NULL) {
445 PERROR("alloc list ids");
446 retval = -LTTNG_ERR_NOMEM;
447 goto end;
448 }
449 ids->type = LTTNG_TRACK_ALL;
450 *_ids = ids;
451 retval = 1;
452 break;
453 case LTTNG_TRACK_NONE:
454 /* No ids track, so we return 0 element. */
455 *_ids = NULL;
456 break;
457 }
458 end:
459 return retval;
460
461 error:
462 for (i = 0; i < count; i++) {
463 free(ids[i].string);
464 }
465 free(ids);
466 return retval;
467 }
468
469 int lttng_tracker_id_set_list(struct lttng_tracker_list *tracker_list,
470 struct lttng_tracker_id *_ids,
471 size_t count)
472 {
473 size_t i;
474
475 lttng_tracker_list_reset(tracker_list);
476 if (count == 1 && _ids[0].type == LTTNG_ID_ALL) {
477 /* Track all. */
478 return LTTNG_OK;
479 }
480 if (count == 0) {
481 /* Set state to "track none". */
482 tracker_list->state = LTTNG_TRACK_NONE;
483 return LTTNG_OK;
484 }
485 for (i = 0; i < count; i++) {
486 struct lttng_tracker_id *id = &_ids[i];
487 int ret;
488
489 ret = lttng_tracker_list_add(tracker_list, id);
490 if (ret != LTTNG_OK) {
491 return ret;
492 }
493 }
494 return LTTNG_OK;
495 }
This page took 0.056006 seconds and 5 git commands to generate.