diff options
Diffstat (limited to 'src/libnetdata/os/system-maps')
-rw-r--r-- | src/libnetdata/os/system-maps/cache-host-users-and-groups.c | 101 | ||||
-rw-r--r-- | src/libnetdata/os/system-maps/cache-host-users-and-groups.h | 9 | ||||
-rw-r--r-- | src/libnetdata/os/system-maps/cached-gid-groupname.c | 149 | ||||
-rw-r--r-- | src/libnetdata/os/system-maps/cached-gid-groupname.h | 24 | ||||
-rw-r--r-- | src/libnetdata/os/system-maps/cached-sid-username.c | 200 | ||||
-rw-r--r-- | src/libnetdata/os/system-maps/cached-sid-username.h | 17 | ||||
-rw-r--r-- | src/libnetdata/os/system-maps/cached-uid-username.c | 149 | ||||
-rw-r--r-- | src/libnetdata/os/system-maps/cached-uid-username.h | 24 | ||||
-rw-r--r-- | src/libnetdata/os/system-maps/system-services.h | 96 |
9 files changed, 769 insertions, 0 deletions
diff --git a/src/libnetdata/os/system-maps/cache-host-users-and-groups.c b/src/libnetdata/os/system-maps/cache-host-users-and-groups.c new file mode 100644 index 000000000..53825fd35 --- /dev/null +++ b/src/libnetdata/os/system-maps/cache-host-users-and-groups.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "libnetdata/libnetdata.h" + +static bool file_changed(const struct stat *statbuf __maybe_unused, struct timespec *last_modification_time __maybe_unused) { +#if defined(OS_MACOS) || defined(OS_WINDOWS) + return false; +#else + if(likely(statbuf->st_mtim.tv_sec == last_modification_time->tv_sec && + statbuf->st_mtim.tv_nsec == last_modification_time->tv_nsec)) return false; + + last_modification_time->tv_sec = statbuf->st_mtim.tv_sec; + last_modification_time->tv_nsec = statbuf->st_mtim.tv_nsec; + + return true; +#endif +} + +static size_t read_passwd_or_group(const char *filename, struct timespec *last_modification_time, void (*cb)(uint32_t gid, const char *name, uint32_t version), uint32_t version) { + struct stat statbuf; + if(unlikely(stat(filename, &statbuf) || !file_changed(&statbuf, last_modification_time))) + return 0; + + procfile *ff = procfile_open(filename, " :\t", PROCFILE_FLAG_DEFAULT); + if(unlikely(!ff)) return 0; + + ff = procfile_readall(ff); + if(unlikely(!ff)) return 0; + + size_t line, lines = procfile_lines(ff); + + size_t added = 0; + for(line = 0; line < lines ;line++) { + size_t words = procfile_linewords(ff, line); + if(unlikely(words < 3)) continue; + + char *name = procfile_lineword(ff, line, 0); + if(unlikely(!name || !*name)) continue; + + char *id_string = procfile_lineword(ff, line, 2); + if(unlikely(!id_string || !*id_string)) continue; + + uint32_t id = str2ull(id_string, NULL); + + cb(id, name, version); + added++; + } + + procfile_close(ff); + return added; +} + +void update_cached_host_users(void) { + if(!netdata_configured_host_prefix || !*netdata_configured_host_prefix) return; + + static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER; + if(!spinlock_trylock(&spinlock)) return; + + char filename[FILENAME_MAX]; + static bool initialized = false; + + size_t added = 0; + + if(!initialized) { + initialized = true; + cached_usernames_init(); + } + + static uint32_t passwd_version = 0; + static struct timespec passwd_ts = { 0 }; + snprintfz(filename, FILENAME_MAX, "%s/etc/passwd", netdata_configured_host_prefix); + added = read_passwd_or_group(filename, &passwd_ts, cached_username_populate_by_uid, ++passwd_version); + if(added) cached_usernames_delete_old_versions(passwd_version); + + spinlock_unlock(&spinlock); +} + +void update_cached_host_groups(void) { + if(!netdata_configured_host_prefix || !*netdata_configured_host_prefix) return; + + static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER; + if(!spinlock_trylock(&spinlock)) return; + + char filename[FILENAME_MAX]; + static bool initialized = false; + + size_t added = 0; + + if(!initialized) { + initialized = true; + cached_groupnames_init(); + } + + static uint32_t group_version = 0; + static struct timespec group_ts = { 0 }; + snprintfz(filename, FILENAME_MAX, "%s/etc/group", netdata_configured_host_prefix); + added = read_passwd_or_group(filename, &group_ts, cached_groupname_populate_by_gid, ++group_version); + if(added) cached_groupnames_delete_old_versions(group_version); + + spinlock_unlock(&spinlock); +} diff --git a/src/libnetdata/os/system-maps/cache-host-users-and-groups.h b/src/libnetdata/os/system-maps/cache-host-users-and-groups.h new file mode 100644 index 000000000..7a84bcadf --- /dev/null +++ b/src/libnetdata/os/system-maps/cache-host-users-and-groups.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef NETDATA_CACHE_HOST_USERS_AND_GROUPS_H +#define NETDATA_CACHE_HOST_USERS_AND_GROUPS_H + +void update_cached_host_users(void); +void update_cached_host_groups(void); + +#endif //NETDATA_CACHE_HOST_USERS_AND_GROUPS_H diff --git a/src/libnetdata/os/system-maps/cached-gid-groupname.c b/src/libnetdata/os/system-maps/cached-gid-groupname.c new file mode 100644 index 000000000..3fabe94a2 --- /dev/null +++ b/src/libnetdata/os/system-maps/cached-gid-groupname.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "cached-gid-groupname.h" + +// -------------------------------------------------------------------------------------------------------------------- +// hashtable for caching gid to groupname mappings +// key is the gid, value is groupname (STRING) + +#define SIMPLE_HASHTABLE_KEY_TYPE gid_t +#define SIMPLE_HASHTABLE_VALUE_TYPE CACHED_GROUPNAME +#define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION cached_groupname_to_gid_ptr +#define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION compar_gid_ptr +#define SIMPLE_HASHTABLE_NAME _GROUPNAMES_CACHE +#include "libnetdata/simple_hashtable/simple_hashtable.h" + +static struct { + bool initialized; + SPINLOCK spinlock; + SIMPLE_HASHTABLE_GROUPNAMES_CACHE ht; +} group_cache = { + .initialized = false, + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .ht = { 0 }, +}; + +static gid_t *cached_groupname_to_gid_ptr(CACHED_GROUPNAME *cu) { + return &cu->gid; +} + +static bool compar_gid_ptr(gid_t *a, gid_t *b) { + return *a == *b; +} + +void cached_groupname_populate_by_gid(gid_t gid, const char *groupname, uint32_t version) { + internal_fatal(!group_cache.initialized, "system-users cache needs to be initialized"); + if(!groupname || !*groupname) return; + + spinlock_lock(&group_cache.spinlock); + + XXH64_hash_t hash = XXH3_64bits(&gid, sizeof(gid)); + SIMPLE_HASHTABLE_SLOT_GROUPNAMES_CACHE *sl = simple_hashtable_get_slot_GROUPNAMES_CACHE(&group_cache.ht, hash, &gid, true); + CACHED_GROUPNAME *cg = SIMPLE_HASHTABLE_SLOT_DATA(sl); + if(!cg || (cg->version && version > cg->version)) { + internal_fatal(cg && cg->gid != gid, "invalid gid matched from cache"); + + if(cg) + string_freez(cg->groupname); + else + cg = callocz(1, sizeof(*cg)); + + cg->version = version; + cg->gid = gid; + cg->groupname = string_strdupz(groupname); + simple_hashtable_set_slot_GROUPNAMES_CACHE(&group_cache.ht, sl, hash, cg); + } + + spinlock_unlock(&group_cache.spinlock); +} + +CACHED_GROUPNAME cached_groupname_get_by_gid(gid_t gid) { + internal_fatal(!group_cache.initialized, "system-users cache needs to be initialized"); + + spinlock_lock(&group_cache.spinlock); + + XXH64_hash_t hash = XXH3_64bits(&gid, sizeof(gid)); + SIMPLE_HASHTABLE_SLOT_GROUPNAMES_CACHE *sl = simple_hashtable_get_slot_GROUPNAMES_CACHE(&group_cache.ht, hash, &gid, true); + CACHED_GROUPNAME *cg = SIMPLE_HASHTABLE_SLOT_DATA(sl); + if(!cg) { + cg = callocz(1, sizeof(*cg)); + + static char tmp[1024]; // we are inside a global spinlock - it is ok to be static + struct group gr, *result = NULL; + + if (getgrgid_r(gid, &gr, tmp, sizeof(tmp), &result) != 0 || !result || !gr.gr_name || !(*gr.gr_name)) { + char name[UINT64_MAX_LENGTH]; + print_uint64(name, gid); + cg->groupname = string_strdupz(name); + } + else + cg->groupname = string_strdupz(gr.gr_name); + + cg->gid = gid; + simple_hashtable_set_slot_GROUPNAMES_CACHE(&group_cache.ht, sl, hash, cg); + } + + internal_fatal(cg->gid != gid, "invalid gid matched from cache"); + + CACHED_GROUPNAME rc = { + .version = cg->version, + .gid = cg->gid, + .groupname = string_dup(cg->groupname), + }; + + spinlock_unlock(&group_cache.spinlock); + return rc; +} + +void cached_groupname_release(CACHED_GROUPNAME cg) { + string_freez(cg.groupname); +} + +void cached_groupnames_init(void) { + if(group_cache.initialized) return; + group_cache.initialized = true; + + spinlock_init(&group_cache.spinlock); + simple_hashtable_init_GROUPNAMES_CACHE(&group_cache.ht, 100); +} + +void cached_groupnames_destroy(void) { + if(!group_cache.initialized) return; + + spinlock_lock(&group_cache.spinlock); + + for(SIMPLE_HASHTABLE_SLOT_GROUPNAMES_CACHE *sl = simple_hashtable_first_read_only_GROUPNAMES_CACHE(&group_cache.ht); + sl; + sl = simple_hashtable_next_read_only_GROUPNAMES_CACHE(&group_cache.ht, sl)) { + CACHED_GROUPNAME *u = SIMPLE_HASHTABLE_SLOT_DATA(sl); + if(u) { + string_freez(u->groupname); + freez(u); + // simple_hashtable_del_slot_GROUPNAMES_CACHE(&uc.ht, sl); + } + } + + simple_hashtable_destroy_GROUPNAMES_CACHE(&group_cache.ht); + group_cache.initialized = false; + + spinlock_unlock(&group_cache.spinlock); +} + +void cached_groupnames_delete_old_versions(uint32_t version) { + if(!group_cache.initialized) return; + + spinlock_lock(&group_cache.spinlock); + + for(SIMPLE_HASHTABLE_SLOT_GROUPNAMES_CACHE *sl = simple_hashtable_first_read_only_GROUPNAMES_CACHE(&group_cache.ht); + sl; + sl = simple_hashtable_next_read_only_GROUPNAMES_CACHE(&group_cache.ht, sl)) { + CACHED_GROUPNAME *cg = SIMPLE_HASHTABLE_SLOT_DATA(sl); + if(cg && cg->version && cg->version < version) { + string_freez(cg->groupname); + freez(cg); + simple_hashtable_del_slot_GROUPNAMES_CACHE(&group_cache.ht, sl); + } + } + + spinlock_unlock(&group_cache.spinlock); +} diff --git a/src/libnetdata/os/system-maps/cached-gid-groupname.h b/src/libnetdata/os/system-maps/cached-gid-groupname.h new file mode 100644 index 000000000..81a62523e --- /dev/null +++ b/src/libnetdata/os/system-maps/cached-gid-groupname.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef NETDATA_CACHED_UID_GROUPNAME_H +#define NETDATA_CACHED_UID_GROUPNAME_H + +#include "libnetdata/libnetdata.h" + +struct netdata_string; + +typedef struct { + uint32_t version; + gid_t gid; + struct netdata_string *groupname; +} CACHED_GROUPNAME; + +void cached_groupname_populate_by_gid(gid_t gid, const char *groupname, uint32_t version); +CACHED_GROUPNAME cached_groupname_get_by_gid(gid_t gid); +void cached_groupname_release(CACHED_GROUPNAME cg); +void cached_groupnames_delete_old_versions(uint32_t version); + +void cached_groupnames_init(void); +void cached_groupnames_destroy(void); + +#endif //NETDATA_CACHED_UID_GROUPNAME_H diff --git a/src/libnetdata/os/system-maps/cached-sid-username.c b/src/libnetdata/os/system-maps/cached-sid-username.c new file mode 100644 index 000000000..a0f90c546 --- /dev/null +++ b/src/libnetdata/os/system-maps/cached-sid-username.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "../../libnetdata.h" + +#if defined(OS_WINDOWS) +#include "cached-sid-username.h" + +typedef struct { + size_t len; + uint8_t sid[]; +} SID_KEY; + +typedef struct { + // IMPORTANT: + // This is malloc'd ! You have to manually set fields to zero. + + STRING *account; + STRING *domain; + STRING *full; + STRING *sid_str; + + // this needs to be last, because of its variable size + SID_KEY key; +} SID_VALUE; + +#define SIMPLE_HASHTABLE_NAME _SID +#define SIMPLE_HASHTABLE_VALUE_TYPE SID_VALUE +#define SIMPLE_HASHTABLE_KEY_TYPE SID_KEY +#define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION sid_value_to_key +#define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION sid_cache_compar +#define SIMPLE_HASHTABLE_SAMPLE_IMPLEMENTATION 1 +#include "libnetdata/simple_hashtable/simple_hashtable.h" + +static struct { + SPINLOCK spinlock; + struct simple_hashtable_SID hashtable; +} sid_globals = { + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .hashtable = { 0 }, +}; + +static inline SID_KEY *sid_value_to_key(SID_VALUE *s) { + return &s->key; +} + +static inline bool sid_cache_compar(SID_KEY *a, SID_KEY *b) { + return a->len == b->len && memcmp(&a->sid, &b->sid, a->len) == 0; +} + +void cached_sid_username_init(void) { + simple_hashtable_init_SID(&sid_globals.hashtable, 100); +} + +static char *account2utf8(const wchar_t *user) { + static __thread char buffer[256]; + if(utf16_to_utf8(buffer, sizeof(buffer), user, -1, NULL) == 0) + buffer[0] = '\0'; + return buffer; +} + +static char *domain2utf8(const wchar_t *domain) { + static __thread char buffer[256]; + if(utf16_to_utf8(buffer, sizeof(buffer), domain, -1, NULL) == 0) + buffer[0] = '\0'; + return buffer; +} + +static void lookup_user_in_system(SID_VALUE *sv) { + static __thread wchar_t account_unicode[256]; + static __thread wchar_t domain_unicode[256]; + static __thread char tmp[512 + 2]; + + DWORD account_name_size = sizeof(account_unicode) / sizeof(account_unicode[0]); + DWORD domain_name_size = sizeof(domain_unicode) / sizeof(domain_unicode[0]); + SID_NAME_USE sid_type; + + if (LookupAccountSidW(NULL, sv->key.sid, account_unicode, &account_name_size, domain_unicode, &domain_name_size, &sid_type)) { + const char *account = account2utf8(account_unicode); + const char *domain = domain2utf8(domain_unicode); + snprintfz(tmp, sizeof(tmp), "%s\\%s", domain, account); + sv->domain = string_strdupz(domain); + sv->account = string_strdupz(account); + sv->full = string_strdupz(tmp); + } + else { + sv->domain = NULL; + sv->account = NULL; + sv->full = NULL; + } + + wchar_t *sid_string = NULL; + if (ConvertSidToStringSidW(sv->key.sid, &sid_string)) + sv->sid_str = string_strdupz(account2utf8(sid_string)); + else + sv->sid_str = NULL; +} + +static SID_VALUE *lookup_or_convert_user_id_to_name_lookup(PSID sid) { + if(!sid || !IsValidSid(sid)) + return NULL; + + size_t size = GetLengthSid(sid); + + size_t tmp_size = sizeof(SID_VALUE) + size; + size_t tmp_key_size = sizeof(SID_KEY) + size; + uint8_t buf[tmp_size]; + SID_VALUE *tmp = (SID_VALUE *)&buf; + memcpy(&tmp->key.sid, sid, size); + tmp->key.len = size; + + spinlock_lock(&sid_globals.spinlock); + SID_VALUE *found = simple_hashtable_get_SID(&sid_globals.hashtable, &tmp->key, tmp_key_size); + spinlock_unlock(&sid_globals.spinlock); + if(found) return found; + + // allocate the SID_VALUE + found = mallocz(tmp_size); + memcpy(found, buf, tmp_size); + + lookup_user_in_system(found); + + // add it to the cache + spinlock_lock(&sid_globals.spinlock); + simple_hashtable_set_SID(&sid_globals.hashtable, &found->key, tmp_key_size, found); + spinlock_unlock(&sid_globals.spinlock); + + return found; +} + +bool cached_sid_to_account_domain_sidstr(PSID sid, TXT_UTF8 *dst_account, TXT_UTF8 *dst_domain, TXT_UTF8 *dst_sid_str) { + SID_VALUE *found = lookup_or_convert_user_id_to_name_lookup(sid); + + if(found) { + if (found->account) { + txt_utf8_resize(dst_account, string_strlen(found->account) + 1, false); + memcpy(dst_account->data, string2str(found->account), string_strlen(found->account) + 1); + dst_account->used = string_strlen(found->account) + 1; + } + else + txt_utf8_empty(dst_account); + + if (found->domain) { + txt_utf8_resize(dst_domain, string_strlen(found->domain) + 1, false); + memcpy(dst_domain->data, string2str(found->domain), string_strlen(found->domain) + 1); + dst_domain->used = string_strlen(found->domain) + 1; + } + else + txt_utf8_empty(dst_domain); + + if (found->sid_str) { + txt_utf8_resize(dst_sid_str, string_strlen(found->sid_str) + 1, false); + memcpy(dst_sid_str->data, string2str(found->sid_str), string_strlen(found->sid_str) + 1); + dst_sid_str->used = string_strlen(found->sid_str) + 1; + } + else + txt_utf8_empty(dst_sid_str); + + return true; + } + + txt_utf8_empty(dst_account); + txt_utf8_empty(dst_domain); + txt_utf8_empty(dst_sid_str); + return false; +} + +bool cached_sid_to_buffer_append(PSID sid, BUFFER *dst, const char *prefix) { + SID_VALUE *found = lookup_or_convert_user_id_to_name_lookup(sid); + size_t added = 0; + + if(found) { + if (found->full) { + if (prefix && *prefix) + buffer_strcat(dst, prefix); + + buffer_fast_strcat(dst, string2str(found->full), string_strlen(found->full)); + added++; + } + if (found->sid_str) { + if (prefix && *prefix) + buffer_strcat(dst, prefix); + + buffer_fast_strcat(dst, string2str(found->sid_str), string_strlen(found->sid_str)); + added++; + } + } + + return added > 0; +} + +STRING *cached_sid_fullname_or_sid_str(PSID sid) { + SID_VALUE *found = lookup_or_convert_user_id_to_name_lookup(sid); + if(found) { + if(found->full) return string_dup(found->full); + return string_dup(found->sid_str); + } + return NULL; +} + +#endif
\ No newline at end of file diff --git a/src/libnetdata/os/system-maps/cached-sid-username.h b/src/libnetdata/os/system-maps/cached-sid-username.h new file mode 100644 index 000000000..4077cad11 --- /dev/null +++ b/src/libnetdata/os/system-maps/cached-sid-username.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef NETDATA_CACHED_SID_USERNAME_H +#define NETDATA_CACHED_SID_USERNAME_H + +#include "../../libnetdata.h" + +#if defined(OS_WINDOWS) +#include "../../string/utf8.h" + +bool cached_sid_to_account_domain_sidstr(void *sid, TXT_UTF8 *dst_account, TXT_UTF8 *dst_domain, TXT_UTF8 *dst_sid_str); +bool cached_sid_to_buffer_append(void *sid, BUFFER *dst, const char *prefix); +void cached_sid_username_init(void); +STRING *cached_sid_fullname_or_sid_str(void *sid); +#endif + +#endif //NETDATA_CACHED_SID_USERNAME_H diff --git a/src/libnetdata/os/system-maps/cached-uid-username.c b/src/libnetdata/os/system-maps/cached-uid-username.c new file mode 100644 index 000000000..35d93f2f0 --- /dev/null +++ b/src/libnetdata/os/system-maps/cached-uid-username.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "cached-uid-username.h" + +// -------------------------------------------------------------------------------------------------------------------- +// hashtable for caching uid to username mappings +// key is the uid, value is username (STRING) + +#define SIMPLE_HASHTABLE_KEY_TYPE uid_t +#define SIMPLE_HASHTABLE_VALUE_TYPE CACHED_USERNAME +#define SIMPLE_HASHTABLE_VALUE2KEY_FUNCTION cached_username_to_uid_ptr +#define SIMPLE_HASHTABLE_COMPARE_KEYS_FUNCTION compar_uid_ptr +#define SIMPLE_HASHTABLE_NAME _USERNAMES_CACHE +#include "libnetdata/simple_hashtable/simple_hashtable.h" + +static struct { + bool initialized; + SPINLOCK spinlock; + SIMPLE_HASHTABLE_USERNAMES_CACHE ht; +} user_cache = { + .initialized = false, + .spinlock = NETDATA_SPINLOCK_INITIALIZER, + .ht = { 0 }, +}; + +static uid_t *cached_username_to_uid_ptr(CACHED_USERNAME *cu) { + return &cu->uid; +} + +static bool compar_uid_ptr(uid_t *a, uid_t *b) { + return *a == *b; +} + +void cached_username_populate_by_uid(uid_t uid, const char *username, uint32_t version) { + internal_fatal(!user_cache.initialized, "system-users cache needs to be initialized"); + if(!username || !*username) return; + + spinlock_lock(&user_cache.spinlock); + + XXH64_hash_t hash = XXH3_64bits(&uid, sizeof(uid)); + SIMPLE_HASHTABLE_SLOT_USERNAMES_CACHE *sl = simple_hashtable_get_slot_USERNAMES_CACHE(&user_cache.ht, hash, &uid, true); + CACHED_USERNAME *cu = SIMPLE_HASHTABLE_SLOT_DATA(sl); + if(!cu || (cu->version && version > cu->version)) { + internal_fatal(cu && cu->uid != uid, "invalid uid matched from cache"); + + if(cu) + string_freez(cu->username); + else + cu = callocz(1, sizeof(*cu)); + + cu->version = version; + cu->uid = uid; + cu->username = string_strdupz(username); + simple_hashtable_set_slot_USERNAMES_CACHE(&user_cache.ht, sl, hash, cu); + } + + spinlock_unlock(&user_cache.spinlock); +} + +CACHED_USERNAME cached_username_get_by_uid(uid_t uid) { + internal_fatal(!user_cache.initialized, "system-users cache needs to be initialized"); + + spinlock_lock(&user_cache.spinlock); + + XXH64_hash_t hash = XXH3_64bits(&uid, sizeof(uid)); + SIMPLE_HASHTABLE_SLOT_USERNAMES_CACHE *sl = simple_hashtable_get_slot_USERNAMES_CACHE(&user_cache.ht, hash, &uid, true); + CACHED_USERNAME *cu = SIMPLE_HASHTABLE_SLOT_DATA(sl); + if(!cu) { + cu = callocz(1, sizeof(*cu)); + + static char tmp[1024]; // we are inside a global spinlock - it is ok to be static + struct passwd pw, *result = NULL; + + if (getpwuid_r(uid, &pw, tmp, sizeof(tmp), &result) != 0 || !result || !pw.pw_name || !(*pw.pw_name)) { + char name[UINT64_MAX_LENGTH]; + print_uint64(name, uid); + cu->username = string_strdupz(name); + } + else + cu->username = string_strdupz(pw.pw_name); + + cu->uid = uid; + simple_hashtable_set_slot_USERNAMES_CACHE(&user_cache.ht, sl, hash, cu); + } + + internal_fatal(cu->uid != uid, "invalid uid matched from cache"); + + CACHED_USERNAME rc = { + .version = cu->version, + .uid = cu->uid, + .username = string_dup(cu->username), + }; + + spinlock_unlock(&user_cache.spinlock); + return rc; +} + +void cached_username_release(CACHED_USERNAME cu) { + string_freez(cu.username); +} + +void cached_usernames_init(void) { + if(user_cache.initialized) return; + user_cache.initialized = true; + + spinlock_init(&user_cache.spinlock); + simple_hashtable_init_USERNAMES_CACHE(&user_cache.ht, 100); +} + +void cached_usernames_destroy(void) { + if(!user_cache.initialized) return; + + spinlock_lock(&user_cache.spinlock); + + for(SIMPLE_HASHTABLE_SLOT_USERNAMES_CACHE *sl = simple_hashtable_first_read_only_USERNAMES_CACHE(&user_cache.ht); + sl; + sl = simple_hashtable_next_read_only_USERNAMES_CACHE(&user_cache.ht, sl)) { + CACHED_USERNAME *u = SIMPLE_HASHTABLE_SLOT_DATA(sl); + if(u) { + string_freez(u->username); + freez(u); + // simple_hashtable_del_slot_USERNAMES_CACHE(&uc.ht, sl); + } + } + + simple_hashtable_destroy_USERNAMES_CACHE(&user_cache.ht); + user_cache.initialized = false; + + spinlock_unlock(&user_cache.spinlock); +} + +void cached_usernames_delete_old_versions(uint32_t version) { + if(!user_cache.initialized) return; + + spinlock_lock(&user_cache.spinlock); + + for(SIMPLE_HASHTABLE_SLOT_USERNAMES_CACHE *sl = simple_hashtable_first_read_only_USERNAMES_CACHE(&user_cache.ht); + sl; + sl = simple_hashtable_next_read_only_USERNAMES_CACHE(&user_cache.ht, sl)) { + CACHED_USERNAME *cu = SIMPLE_HASHTABLE_SLOT_DATA(sl); + if(cu && cu->version && cu->version < version) { + string_freez(cu->username); + freez(cu); + simple_hashtable_del_slot_USERNAMES_CACHE(&user_cache.ht, sl); + } + } + + spinlock_unlock(&user_cache.spinlock); +} diff --git a/src/libnetdata/os/system-maps/cached-uid-username.h b/src/libnetdata/os/system-maps/cached-uid-username.h new file mode 100644 index 000000000..b7c52c7c4 --- /dev/null +++ b/src/libnetdata/os/system-maps/cached-uid-username.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef NETDATA_CACHED_UID_USERNAME_H +#define NETDATA_CACHED_UID_USERNAME_H + +#include "libnetdata/libnetdata.h" + +struct netdata_string; + +typedef struct { + uint32_t version; + uid_t uid; + struct netdata_string *username; +} CACHED_USERNAME; + +void cached_username_populate_by_uid(uid_t uid, const char *username, uint32_t version); +CACHED_USERNAME cached_username_get_by_uid(uid_t uid); +void cached_username_release(CACHED_USERNAME cu); + +void cached_usernames_init(void); +void cached_usernames_destroy(void); +void cached_usernames_delete_old_versions(uint32_t version); + +#endif //NETDATA_CACHED_UID_USERNAME_H diff --git a/src/libnetdata/os/system-maps/system-services.h b/src/libnetdata/os/system-maps/system-services.h new file mode 100644 index 000000000..5d3592bbf --- /dev/null +++ b/src/libnetdata/os/system-maps/system-services.h @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef NETDATA_SYSTEM_SERVICES_H +#define NETDATA_SYSTEM_SERVICES_H + +#include "libnetdata/libnetdata.h" +#include <netdb.h> + +// -------------------------------------------------------------------------------------------------------------------- +// hashtable for caching port and protocol to service name mappings +// key is the combination of protocol and port packed into an uint64_t, value is service name (STRING) + +#define SIMPLE_HASHTABLE_VALUE_TYPE STRING +#define SIMPLE_HASHTABLE_NAME _SERVICENAMES_CACHE +#include "libnetdata/simple_hashtable/simple_hashtable.h" + +typedef struct servicenames_cache { + SPINLOCK spinlock; + SIMPLE_HASHTABLE_SERVICENAMES_CACHE ht; +} SERVICENAMES_CACHE; + +static inline const char *system_servicenames_ipproto2str(uint16_t ipproto) { + return (ipproto == IPPROTO_TCP) ? "tcp" : "udp"; +} + +static inline const char *static_portnames(uint16_t port, uint16_t ipproto) { + if(port == 19999 && ipproto == IPPROTO_TCP) + return "netdata"; + + if(port == 8125) + return "statsd"; + + return NULL; +} + +static inline STRING *system_servicenames_cache_lookup(SERVICENAMES_CACHE *sc, uint16_t port, uint16_t ipproto) { + struct { + uint16_t ipproto; + uint16_t port; + } key = { + .ipproto = ipproto, + .port = port, + }; + XXH64_hash_t hash = XXH3_64bits(&key, sizeof(key)); + + spinlock_lock(&sc->spinlock); + + SIMPLE_HASHTABLE_SLOT_SERVICENAMES_CACHE *sl = simple_hashtable_get_slot_SERVICENAMES_CACHE(&sc->ht, hash, &key, true); + STRING *s = SIMPLE_HASHTABLE_SLOT_DATA(sl); + if (!s) { + const char *st = static_portnames(port, ipproto); + if(st) { + s = string_strdupz(st); + } + else { + struct servent *se = getservbyport(htons(port), system_servicenames_ipproto2str(ipproto)); + + if (!se || !se->s_name) { + char name[50]; + snprintfz(name, sizeof(name), "%u/%s", port, system_servicenames_ipproto2str(ipproto)); + s = string_strdupz(name); + } + else + s = string_strdupz(se->s_name); + } + + simple_hashtable_set_slot_SERVICENAMES_CACHE(&sc->ht, sl, hash, s); + } + + s = string_dup(s); + spinlock_unlock(&sc->spinlock); + return s; +} + +static inline SERVICENAMES_CACHE *system_servicenames_cache_init(void) { + SERVICENAMES_CACHE *sc = callocz(1, sizeof(*sc)); + spinlock_init(&sc->spinlock); + simple_hashtable_init_SERVICENAMES_CACHE(&sc->ht, 100); + return sc; +} + +static inline void system_servicenames_cache_destroy(SERVICENAMES_CACHE *sc) { + spinlock_lock(&sc->spinlock); + + for (SIMPLE_HASHTABLE_SLOT_SERVICENAMES_CACHE *sl = simple_hashtable_first_read_only_SERVICENAMES_CACHE(&sc->ht); + sl; + sl = simple_hashtable_next_read_only_SERVICENAMES_CACHE(&sc->ht, sl)) { + STRING *s = SIMPLE_HASHTABLE_SLOT_DATA(sl); + string_freez(s); + } + + simple_hashtable_destroy_SERVICENAMES_CACHE(&sc->ht); + freez(sc); +} + +#endif //NETDATA_SYSTEM_SERVICES_H |