2 * Copyright (C) 2018 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
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.
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.
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.
21 #include <sys/types.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>
31 #define FALLBACK_USER_BUFLEN 16384
32 #define FALLBACK_GROUP_BUFLEN 16384
34 struct lttng_tracker_list
*lttng_tracker_list_create(void)
36 struct lttng_tracker_list
*t
;
38 t
= zmalloc(sizeof(*t
));
42 t
->ht
= cds_lfht_new(DEFAULT_HT_SIZE
, 1, 0,
43 CDS_LFHT_AUTO_RESIZE
| CDS_LFHT_ACCOUNTING
, NULL
);
47 CDS_INIT_LIST_HEAD(&t
->list_head
);
48 t
->state
= LTTNG_TRACK_ALL
;
56 static int match_tracker_key(struct cds_lfht_node
*node
, const void *key
)
58 const struct lttng_tracker_id
*tracker_key
= key
;
59 struct lttng_tracker_list_node
*tracker_node
;
61 tracker_node
= caa_container_of(
62 node
, struct lttng_tracker_list_node
, ht_node
);
63 if (tracker_node
->id
.type
!= tracker_key
->type
) {
66 switch (tracker_node
->id
.type
) {
70 if (tracker_node
->id
.value
!= tracker_key
->value
) {
75 if (strcmp(tracker_node
->id
.string
, tracker_key
->string
) != 0) {
85 static unsigned long hash_tracker_key(
86 const struct lttng_tracker_id
*tracker_key
)
88 unsigned long key_hash
= 0;
90 switch (tracker_key
->type
) {
94 key_hash
^= hash_key_ulong(
95 (void *) (unsigned long) tracker_key
->value
,
99 key_hash
^= hash_key_str(tracker_key
->string
, lttng_ht_seed
);
101 case LTTNG_ID_UNKNOWN
:
104 key_hash
^= hash_key_ulong((void *) (unsigned long) tracker_key
->type
,
109 static struct lttng_tracker_id
*lttng_tracker_list_lookup(
110 const struct lttng_tracker_list
*tracker_list
,
111 const struct lttng_tracker_id
*key
)
113 struct lttng_tracker_list_node
*list_node
;
114 struct cds_lfht_iter iter
;
115 struct cds_lfht_node
*node
;
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
);
123 list_node
= caa_container_of(
124 node
, struct lttng_tracker_list_node
, ht_node
);
125 return &list_node
->id
;
128 static void destroy_list_node_rcu(struct rcu_head
*head
)
130 struct lttng_tracker_list_node
*n
= caa_container_of(
131 head
, struct lttng_tracker_list_node
, rcu_head
);
137 static void _lttng_tracker_list_remove(struct lttng_tracker_list
*tracker_list
,
138 struct lttng_tracker_list_node
*n
)
140 cds_list_del(&n
->list_node
);
143 cds_lfht_del(tracker_list
->ht
, &n
->ht_node
);
146 call_rcu(&n
->rcu_head
, destroy_list_node_rcu
);
149 static void lttng_tracker_list_reset(struct lttng_tracker_list
*tracker_list
)
151 struct lttng_tracker_list_node
*n
, *t
;
153 cds_list_for_each_entry_safe (
154 n
, t
, &tracker_list
->list_head
, list_node
) {
155 _lttng_tracker_list_remove(tracker_list
, n
);
157 tracker_list
->state
= LTTNG_TRACK_ALL
;
160 /* Protected by session mutex held by caller. */
161 int lttng_tracker_list_add(struct lttng_tracker_list
*tracker_list
,
162 const struct lttng_tracker_id
*_id
)
164 struct lttng_tracker_id
*id
;
165 struct lttng_tracker_list_node
*n
;
168 if (_id
->type
== LTTNG_ID_ALL
) {
169 /* Track all, so remove each individual item. */
170 lttng_tracker_list_reset(tracker_list
);
174 id
= lttng_tracker_list_lookup(tracker_list
, _id
);
176 * It is okay to release the RCU read lock here since id is only checked
177 * for != NULL and not dereferenced.
181 return LTTNG_ERR_ID_TRACKED
;
183 n
= zmalloc(sizeof(*n
));
185 return LTTNG_ERR_NOMEM
;
188 if (_id
->type
== LTTNG_ID_STRING
) {
189 n
->id
.string
= strdup(_id
->string
);
191 ret
= LTTNG_ERR_NOMEM
;
198 cds_list_add_tail(&n
->list_node
, &tracker_list
->list_head
);
199 tracker_list
->state
= LTTNG_TRACK_LIST
;
202 cds_lfht_add(tracker_list
->ht
, hash_tracker_key(&n
->id
), &n
->ht_node
);
214 * Protected by session mutex held by caller.
216 int lttng_tracker_list_remove(struct lttng_tracker_list
*tracker_list
,
217 const struct lttng_tracker_id
*_id
)
219 enum lttng_error_code ret
= LTTNG_OK
;
220 struct lttng_tracker_id
*id
;
221 struct lttng_tracker_list_node
*n
;
223 if (_id
->type
== LTTNG_ID_ALL
) {
225 lttng_tracker_list_reset(tracker_list
);
226 /* Set state to "track none". */
227 tracker_list
->state
= LTTNG_TRACK_NONE
;
232 id
= lttng_tracker_list_lookup(tracker_list
, _id
);
234 ret
= LTTNG_ERR_ID_NOT_TRACKED
;
238 n
= caa_container_of(id
, struct lttng_tracker_list_node
, id
);
239 _lttng_tracker_list_remove(tracker_list
, n
);
247 void lttng_tracker_list_destroy(struct lttng_tracker_list
*tracker_list
)
252 lttng_tracker_list_reset(tracker_list
);
253 cds_lfht_destroy(tracker_list
->ht
, NULL
);
257 static int lttng_lookup_user(const char *username
, int *result
)
259 struct passwd p
, *pres
;
260 int ret
, retval
= LTTNG_OK
;
264 buflen
= sysconf(_SC_GETPW_R_SIZE_MAX
);
266 buflen
= FALLBACK_USER_BUFLEN
;
268 buf
= zmalloc(buflen
);
270 retval
= LTTNG_ERR_NOMEM
;
274 ret
= getpwnam_r(username
, &p
, buf
, buflen
, &pres
);
281 buf
= zmalloc(buflen
);
283 retval
= LTTNG_ERR_NOMEM
;
296 retval
= LTTNG_ERR_USER_NOT_FOUND
;
298 *result
= (int) p
.pw_uid
;
299 DBG("Lookup of tracker UID/VUID: name '%s' maps to id %d.",
308 retval
= LTTNG_ERR_USER_NOT_FOUND
;
311 retval
= LTTNG_ERR_NOMEM
;
318 static int lttng_lookup_group(const char *groupname
, int *result
)
320 struct group g
, *gres
;
321 int ret
, retval
= LTTNG_OK
;
325 buflen
= sysconf(_SC_GETGR_R_SIZE_MAX
);
327 buflen
= FALLBACK_GROUP_BUFLEN
;
329 buf
= zmalloc(buflen
);
331 retval
= LTTNG_ERR_NOMEM
;
335 ret
= getgrnam_r(groupname
, &g
, buf
, buflen
, &gres
);
342 buf
= zmalloc(buflen
);
344 retval
= LTTNG_ERR_NOMEM
;
357 retval
= LTTNG_ERR_GROUP_NOT_FOUND
;
359 *result
= (int) g
.gr_gid
;
360 DBG("Lookup of tracker GID/GUID: name '%s' maps to id %d.",
369 retval
= LTTNG_ERR_GROUP_NOT_FOUND
;
372 retval
= LTTNG_ERR_NOMEM
;
379 int lttng_tracker_id_lookup_string(enum lttng_tracker_type tracker_type
,
380 const struct lttng_tracker_id
*id
,
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
);
405 return LTTNG_ERR_INVALID
;
409 return LTTNG_ERR_INVALID
;
414 * Protected by session mutex held by caller.
415 * On success, _ids and the strings it contains must be freed by caller.
417 ssize_t
lttng_tracker_id_get_list(const struct lttng_tracker_list
*tracker_list
,
418 struct lttng_tracker_id
**_ids
)
420 struct lttng_tracker_list_node
*n
;
421 ssize_t count
= 0, i
= 0, retval
= 0;
422 struct lttng_tracker_id
*ids
;
424 switch (tracker_list
->state
) {
425 case LTTNG_TRACK_LIST
:
426 cds_list_for_each_entry (
427 n
, &tracker_list
->list_head
, list_node
) {
430 ids
= zmalloc(sizeof(*ids
) * count
);
432 PERROR("Failed to allocate tracked ID list");
433 retval
= -LTTNG_ERR_NOMEM
;
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
;
452 case LTTNG_TRACK_ALL
:
453 ids
= zmalloc(sizeof(*ids
));
455 PERROR("Failed to allocate tracked ID list");
456 retval
= -LTTNG_ERR_NOMEM
;
459 ids
->type
= LTTNG_TRACK_ALL
;
463 case LTTNG_TRACK_NONE
:
464 /* No ids track, so we return 0 element. */
472 for (i
= 0; i
< count
; i
++) {
479 int lttng_tracker_id_set_list(struct lttng_tracker_list
*tracker_list
,
480 struct lttng_tracker_id
*_ids
,
485 lttng_tracker_list_reset(tracker_list
);
486 if (count
== 1 && _ids
[0].type
== LTTNG_ID_ALL
) {
491 /* Set state to "track none". */
492 tracker_list
->state
= LTTNG_TRACK_NONE
;
495 for (i
= 0; i
< count
; i
++) {
496 struct lttng_tracker_id
*id
= &_ids
[i
];
499 ret
= lttng_tracker_list_add(tracker_list
, id
);
500 if (ret
!= LTTNG_OK
) {