// SPDX-License-Identifier: GPL-3.0-or-later #include "apps_plugin.h" // ---------------------------------------------------------------------------- // read users and groups from files enum user_or_group_id_type { USER_ID, GROUP_ID }; struct user_or_group_ids { enum user_or_group_id_type type; avl_tree_type index; struct user_or_group_id *root; char filename[FILENAME_MAX + 1]; }; int user_id_compare(void* a, void* b) { if(((struct user_or_group_id *)a)->id.uid < ((struct user_or_group_id *)b)->id.uid) return -1; else if(((struct user_or_group_id *)a)->id.uid > ((struct user_or_group_id *)b)->id.uid) return 1; else return 0; } struct user_or_group_ids all_user_ids = { .type = USER_ID, .index = { NULL, user_id_compare }, .root = NULL, .filename = "", }; int group_id_compare(void* a, void* b) { if(((struct user_or_group_id *)a)->id.gid < ((struct user_or_group_id *)b)->id.gid) return -1; else if(((struct user_or_group_id *)a)->id.gid > ((struct user_or_group_id *)b)->id.gid) return 1; else return 0; } struct user_or_group_ids all_group_ids = { .type = GROUP_ID, .index = { NULL, group_id_compare }, .root = NULL, .filename = "", }; int file_changed(const struct stat *statbuf __maybe_unused, struct timespec *last_modification_time __maybe_unused) { #if defined(__APPLE__) return 0; #else if(likely(statbuf->st_mtim.tv_sec == last_modification_time->tv_sec && statbuf->st_mtim.tv_nsec == last_modification_time->tv_nsec)) return 0; last_modification_time->tv_sec = statbuf->st_mtim.tv_sec; last_modification_time->tv_nsec = statbuf->st_mtim.tv_nsec; return 1; #endif } int read_user_or_group_ids(struct user_or_group_ids *ids, struct timespec *last_modification_time) { struct stat statbuf; if(unlikely(stat(ids->filename, &statbuf))) return 1; else if(likely(!file_changed(&statbuf, last_modification_time))) return 0; procfile *ff = procfile_open(ids->filename, " :\t", PROCFILE_FLAG_DEFAULT); if(unlikely(!ff)) return 1; ff = procfile_readall(ff); if(unlikely(!ff)) return 1; size_t line, lines = procfile_lines(ff); 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; struct user_or_group_id *user_or_group_id = callocz(1, sizeof(struct user_or_group_id)); if(ids->type == USER_ID) user_or_group_id->id.uid = (uid_t) str2ull(id_string, NULL); else user_or_group_id->id.gid = (uid_t) str2ull(id_string, NULL); user_or_group_id->name = strdupz(name); user_or_group_id->updated = 1; struct user_or_group_id *existing_user_id = NULL; if(likely(ids->root)) existing_user_id = (struct user_or_group_id *)avl_search(&ids->index, (avl_t *) user_or_group_id); if(unlikely(existing_user_id)) { freez(existing_user_id->name); existing_user_id->name = user_or_group_id->name; existing_user_id->updated = 1; freez(user_or_group_id); } else { if(unlikely(avl_insert(&ids->index, (avl_t *) user_or_group_id) != (void *) user_or_group_id)) { netdata_log_error("INTERNAL ERROR: duplicate indexing of id during realloc"); } user_or_group_id->next = ids->root; ids->root = user_or_group_id; } } procfile_close(ff); // remove unused ids struct user_or_group_id *user_or_group_id = ids->root, *prev_user_id = NULL; while(user_or_group_id) { if(unlikely(!user_or_group_id->updated)) { if(unlikely((struct user_or_group_id *)avl_remove(&ids->index, (avl_t *) user_or_group_id) != user_or_group_id)) netdata_log_error("INTERNAL ERROR: removal of unused id from index, removed a different id"); if(prev_user_id) prev_user_id->next = user_or_group_id->next; else ids->root = user_or_group_id->next; freez(user_or_group_id->name); freez(user_or_group_id); if(prev_user_id) user_or_group_id = prev_user_id->next; else user_or_group_id = ids->root; } else { user_or_group_id->updated = 0; prev_user_id = user_or_group_id; user_or_group_id = user_or_group_id->next; } } return 0; } struct user_or_group_id *user_id_find(struct user_or_group_id *user_id_to_find) { if(*netdata_configured_host_prefix) { static struct timespec last_passwd_modification_time; int ret = read_user_or_group_ids(&all_user_ids, &last_passwd_modification_time); if(likely(!ret && all_user_ids.index.root)) return (struct user_or_group_id *)avl_search(&all_user_ids.index, (avl_t *)user_id_to_find); } return NULL; } struct user_or_group_id *group_id_find(struct user_or_group_id *group_id_to_find) { if(*netdata_configured_host_prefix) { static struct timespec last_group_modification_time; int ret = read_user_or_group_ids(&all_group_ids, &last_group_modification_time); if(likely(!ret && all_group_ids.index.root)) return (struct user_or_group_id *)avl_search(&all_group_ids.index, (avl_t *) &group_id_to_find); } return NULL; } void users_and_groups_init(void) { snprintfz(all_user_ids.filename, FILENAME_MAX, "%s/etc/passwd", netdata_configured_host_prefix); debug_log("passwd file: '%s'", all_user_ids.filename); snprintfz(all_group_ids.filename, FILENAME_MAX, "%s/etc/group", netdata_configured_host_prefix); debug_log("group file: '%s'", all_group_ids.filename); }