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