| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "libnetdata/libnetdata.h" |
| 4 | |
| 5 | static bool file_changed(const struct stat *statbuf __maybe_unused, struct timespec *last_modification_time __maybe_unused) { |
| 6 | #if defined(OS_MACOS) || defined(OS_WINDOWS) |
| 7 | return false; |
| 8 | #else |
| 9 | if(likely(statbuf->st_mtim.tv_sec == last_modification_time->tv_sec && |
| 10 | statbuf->st_mtim.tv_nsec == last_modification_time->tv_nsec)) return false; |
| 11 | |
| 12 | last_modification_time->tv_sec = statbuf->st_mtim.tv_sec; |
| 13 | last_modification_time->tv_nsec = statbuf->st_mtim.tv_nsec; |
| 14 | |
| 15 | return true; |
| 16 | #endif |
| 17 | } |
| 18 | |
| 19 | 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) { |
| 20 | struct stat statbuf; |
| 21 | if(unlikely(stat(filename, &statbuf) || !file_changed(&statbuf, last_modification_time))) |
| 22 | return 0; |
| 23 | |
| 24 | procfile *ff = procfile_open(filename, " :\t", PROCFILE_FLAG_DEFAULT); |
| 25 | if(unlikely(!ff)) return 0; |
| 26 | |
| 27 | ff = procfile_readall(ff); |
| 28 | if(unlikely(!ff)) return 0; |
| 29 | |
| 30 | size_t line, lines = procfile_lines(ff); |
| 31 | |
| 32 | size_t added = 0; |
| 33 | for(line = 0; line < lines ;line++) { |
| 34 | size_t words = procfile_linewords(ff, line); |
| 35 | if(unlikely(words < 3)) continue; |
| 36 | |
| 37 | char *name = procfile_lineword(ff, line, 0); |
| 38 | if(unlikely(!name || !*name)) continue; |
| 39 | |
| 40 | char *id_string = procfile_lineword(ff, line, 2); |
| 41 | if(unlikely(!id_string || !*id_string)) continue; |
| 42 | |
| 43 | uint32_t id = str2ull(id_string, NULL); |
| 44 | |
| 45 | cb(id, name, version); |
| 46 | added++; |
| 47 | } |
| 48 | |
| 49 | procfile_close(ff); |
| 50 | return added; |
| 51 | } |
| 52 | |
| 53 | void update_cached_host_users(void) { |
| 54 | if(!netdata_configured_host_prefix || !*netdata_configured_host_prefix) return; |
| 55 | |
| 56 | static SPINLOCK spinlock = SPINLOCK_INITIALIZER; |
| 57 | if(!spinlock_trylock(&spinlock)) return; |
| 58 | |
| 59 | char filename[FILENAME_MAX]; |
| 60 | static bool initialized = false; |
| 61 | |
| 62 | size_t added = 0; |
| 63 | |
| 64 | if(!initialized) { |
| 65 | initialized = true; |
| 66 | cached_usernames_init(); |
| 67 | } |
| 68 | |
| 69 | static uint32_t passwd_version = 0; |
| 70 | static struct timespec passwd_ts = { 0 }; |
| 71 | snprintfz(filename, FILENAME_MAX, "%s/etc/passwd", netdata_configured_host_prefix); |
| 72 | added = read_passwd_or_group(filename, &passwd_ts, cached_username_populate_by_uid, ++passwd_version); |
| 73 | if(added) cached_usernames_delete_old_versions(passwd_version); |
| 74 | |
| 75 | spinlock_unlock(&spinlock); |
| 76 | } |
| 77 | |
| 78 | void update_cached_host_groups(void) { |
| 79 | if(!netdata_configured_host_prefix || !*netdata_configured_host_prefix) return; |
| 80 | |
| 81 | static SPINLOCK spinlock = SPINLOCK_INITIALIZER; |
| 82 | if(!spinlock_trylock(&spinlock)) return; |
| 83 | |
| 84 | char filename[FILENAME_MAX]; |
| 85 | static bool initialized = false; |
| 86 | |
| 87 | size_t added = 0; |
| 88 | |
| 89 | if(!initialized) { |
| 90 | initialized = true; |
| 91 | cached_groupnames_init(); |
| 92 | } |
| 93 | |
| 94 | static uint32_t group_version = 0; |
| 95 | static struct timespec group_ts = { 0 }; |
| 96 | snprintfz(filename, FILENAME_MAX, "%s/etc/group", netdata_configured_host_prefix); |
| 97 | added = read_passwd_or_group(filename, &group_ts, cached_groupname_populate_by_gid, ++group_version); |
| 98 | if(added) cached_groupnames_delete_old_versions(group_version); |
| 99 | |
| 100 | spinlock_unlock(&spinlock); |
| 101 | } |