master
c 149 lines 4.87 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "cached-uid-username.h"
4
5 // --------------------------------------------------------------------------------------------------------------------
6 // hashtable for caching uid to username mappings
7 // key is the uid, value is username (STRING)
8
9 #define SIMPLE_HASHTABLE_KEY_TYPE uid_t
10 #define SIMPLE_HASHTABLE_VALUE_TYPE CACHED_USERNAME *
11 #define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION cached_username_to_uid_ptr
12 #define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION compar_uid_ptr
13 #define SIMPLE_HASHTABLE_NAME _USERNAMES_CACHE
14 #include "libnetdata/simple_hashtable/simple_hashtable.h"
15
16 static struct {
17 bool initialized;
18 SPINLOCK spinlock;
19 SIMPLE_HASHTABLE_USERNAMES_CACHE ht;
20 } user_cache = {
21 .initialized = false,
22 .spinlock = SPINLOCK_INITIALIZER,
23 .ht = { 0 },
24 };
25
26 static uid_t *cached_username_to_uid_ptr(CACHED_USERNAME *cu) {
27 return &cu->uid;
28 }
29
30 static bool compar_uid_ptr(uid_t *a, uid_t *b) {
31 return *a == *b;
32 }
33
34 void cached_username_populate_by_uid(uid_t uid, const char *username, uint32_t version) {
35 internal_fatal(!user_cache.initialized, "system-users cache needs to be initialized");
36 if(!username || !*username) return;
37
38 spinlock_lock(&user_cache.spinlock);
39
40 XXH64_hash_t hash = XXH3_64bits(&uid, sizeof(uid));
41 SIMPLE_HASHTABLE_SLOT_USERNAMES_CACHE *sl = simple_hashtable_get_slot_USERNAMES_CACHE(&user_cache.ht, hash, &uid, true);
42 CACHED_USERNAME *cu = SIMPLE_HASHTABLE_SLOT_DATA(sl);
43 if(!cu || (cu->version && version > cu->version)) {
44 internal_fatal(cu && cu->uid != uid, "invalid uid matched from cache");
45
46 if(cu)
47 string_freez(cu->username);
48 else
49 cu = callocz(1, sizeof(*cu));
50
51 cu->version = version;
52 cu->uid = uid;
53 cu->username = string_strdupz(username);
54 simple_hashtable_set_slot_USERNAMES_CACHE(&user_cache.ht, sl, hash, cu);
55 }
56
57 spinlock_unlock(&user_cache.spinlock);
58 }
59
60 CACHED_USERNAME cached_username_get_by_uid(uid_t uid) {
61 internal_fatal(!user_cache.initialized, "system-users cache needs to be initialized");
62
63 spinlock_lock(&user_cache.spinlock);
64
65 XXH64_hash_t hash = XXH3_64bits(&uid, sizeof(uid));
66 SIMPLE_HASHTABLE_SLOT_USERNAMES_CACHE *sl = simple_hashtable_get_slot_USERNAMES_CACHE(&user_cache.ht, hash, &uid, true);
67 CACHED_USERNAME *cu = SIMPLE_HASHTABLE_SLOT_DATA(sl);
68 if(!cu) {
69 cu = callocz(1, sizeof(*cu));
70
71 static char tmp[1024]; // we are inside a global spinlock - it is ok to be static
72 struct passwd pw, *result = NULL;
73
74 if (getpwuid_r(uid, &pw, tmp, sizeof(tmp), &result) != 0 || !result || !pw.pw_name || !(*pw.pw_name)) {
75 char name[UINT64_MAX_LENGTH];
76 print_uint64(name, uid);
77 cu->username = string_strdupz(name);
78 }
79 else
80 cu->username = string_strdupz(pw.pw_name);
81
82 cu->uid = uid;
83 simple_hashtable_set_slot_USERNAMES_CACHE(&user_cache.ht, sl, hash, cu);
84 }
85
86 internal_fatal(cu->uid != uid, "invalid uid matched from cache");
87
88 CACHED_USERNAME rc = {
89 .version = cu->version,
90 .uid = cu->uid,
91 .username = string_dup(cu->username),
92 };
93
94 spinlock_unlock(&user_cache.spinlock);
95 return rc;
96 }
97
98 void cached_username_release(CACHED_USERNAME cu) {
99 string_freez(cu.username);
100 }
101
102 void cached_usernames_init(void) {
103 if(user_cache.initialized) return;
104 user_cache.initialized = true;
105
106 spinlock_init(&user_cache.spinlock);
107 simple_hashtable_init_USERNAMES_CACHE(&user_cache.ht, 100);
108 }
109
110 void cached_usernames_destroy(void) {
111 if(!user_cache.initialized) return;
112
113 spinlock_lock(&user_cache.spinlock);
114
115 for(SIMPLE_HASHTABLE_SLOT_USERNAMES_CACHE *sl = simple_hashtable_first_read_only_USERNAMES_CACHE(&user_cache.ht);
116 sl;
117 sl = simple_hashtable_next_read_only_USERNAMES_CACHE(&user_cache.ht, sl)) {
118 CACHED_USERNAME *u = SIMPLE_HASHTABLE_SLOT_DATA(sl);
119 if(u) {
120 string_freez(u->username);
121 freez(u);
122 // simple_hashtable_del_slot_USERNAMES_CACHE(&uc.ht, sl);
123 }
124 }
125
126 simple_hashtable_destroy_USERNAMES_CACHE(&user_cache.ht);
127 user_cache.initialized = false;
128
129 spinlock_unlock(&user_cache.spinlock);
130 }
131
132 void cached_usernames_delete_old_versions(uint32_t version) {
133 if(!user_cache.initialized) return;
134
135 spinlock_lock(&user_cache.spinlock);
136
137 for(SIMPLE_HASHTABLE_SLOT_USERNAMES_CACHE *sl = simple_hashtable_first_read_only_USERNAMES_CACHE(&user_cache.ht);
138 sl;
139 sl = simple_hashtable_next_read_only_USERNAMES_CACHE(&user_cache.ht, sl)) {
140 CACHED_USERNAME *cu = SIMPLE_HASHTABLE_SLOT_DATA(sl);
141 if(cu && cu->version && cu->version < version) {
142 string_freez(cu->username);
143 freez(cu);
144 simple_hashtable_del_slot_USERNAMES_CACHE(&user_cache.ht, sl);
145 }
146 }
147
148 spinlock_unlock(&user_cache.spinlock);
149 }