users.c (5491B)
1 // users.c -- operating system user information 2 3 #include <stdbool.h> 4 #include <stdio.h> 5 #include <string.h> 6 #include <uv.h> 7 8 #include "auto/config.h" 9 #include "nvim/ascii_defs.h" 10 #include "nvim/cmdexpand_defs.h" 11 #include "nvim/garray.h" 12 #include "nvim/garray_defs.h" 13 #include "nvim/memory.h" 14 #include "nvim/os/os.h" 15 #include "nvim/os/os_defs.h" 16 #include "nvim/vim_defs.h" 17 #ifdef HAVE_PWD_FUNCS 18 # include <pwd.h> 19 #endif 20 #ifdef MSWIN 21 # include <lm.h> 22 23 # include "nvim/mbyte.h" 24 # include "nvim/message.h" 25 #endif 26 27 #include "os/users.c.generated.h" 28 29 // All user names (for ~user completion as done by shell). 30 static garray_T ga_users = GA_EMPTY_INIT_VALUE; 31 32 // Add a user name to the list of users in garray_T *users. 33 // Do nothing if user name is NULL or empty. 34 static void add_user(garray_T *users, char *user, bool need_copy) 35 { 36 char *user_copy = (user != NULL && need_copy) 37 ? xstrdup(user) : user; 38 39 if (user_copy == NULL || *user_copy == NUL) { 40 if (need_copy) { 41 xfree(user_copy); 42 } 43 return; 44 } 45 GA_APPEND(char *, users, user_copy); 46 } 47 48 // Initialize users garray and fill it with os usernames. 49 // Return Ok for success, FAIL for failure. 50 int os_get_usernames(garray_T *users) 51 { 52 if (users == NULL) { 53 return FAIL; 54 } 55 ga_init(users, sizeof(char *), 20); 56 57 #ifdef HAVE_PWD_FUNCS 58 { 59 struct passwd *pw; 60 61 setpwent(); 62 while ((pw = getpwent()) != NULL) { 63 add_user(users, pw->pw_name, true); 64 } 65 endpwent(); 66 } 67 #elif defined(MSWIN) 68 { 69 DWORD nusers = 0, ntotal = 0, i; 70 PUSER_INFO_0 uinfo; 71 72 if (NetUserEnum(NULL, 0, 0, (LPBYTE *)&uinfo, MAX_PREFERRED_LENGTH, 73 &nusers, &ntotal, NULL) == NERR_Success) { 74 for (i = 0; i < nusers; i++) { 75 char *user; 76 int conversion_result = utf16_to_utf8(uinfo[i].usri0_name, -1, &user); 77 if (conversion_result != 0) { 78 semsg("utf16_to_utf8 failed: %d", conversion_result); 79 break; 80 } 81 add_user(users, user, false); 82 } 83 84 NetApiBufferFree(uinfo); 85 } 86 } 87 #endif 88 #ifdef HAVE_PWD_FUNCS 89 { 90 char *user_env = os_getenv_noalloc("USER"); 91 92 // The $USER environment variable may be a valid remote user name (NIS, 93 // LDAP) not already listed by getpwent(), as getpwent() only lists 94 // local user names. If $USER is not already listed, check whether it 95 // is a valid remote user name using getpwnam() and if it is, add it to 96 // the list of user names. 97 98 if (user_env != NULL && *user_env != NUL) { 99 int i; 100 101 for (i = 0; i < users->ga_len; i++) { 102 char *local_user = ((char **)users->ga_data)[i]; 103 104 if (strcmp(local_user, user_env) == 0) { 105 break; 106 } 107 } 108 109 if (i == users->ga_len) { 110 struct passwd *pw = getpwnam(user_env); // NOLINT 111 112 if (pw != NULL) { 113 add_user(users, pw->pw_name, true); 114 } 115 } 116 } 117 } 118 #endif 119 120 return OK; 121 } 122 123 /// Gets the username that owns the current Nvim process. 124 /// 125 /// @param s[out] Username. 126 /// @param len Length of `s`. 127 /// 128 /// @return OK if a name found. 129 int os_get_username(char *s, size_t len) 130 { 131 #ifdef UNIX 132 return os_get_uname((uv_uid_t)getuid(), s, len); 133 #else 134 // TODO(equalsraf): Windows GetUserName() 135 return os_get_uname((uv_uid_t)0, s, len); 136 #endif 137 } 138 139 /// Gets the username associated with `uid`. 140 /// 141 /// @param uid User id. 142 /// @param s[out] Username, or `uid` on failure. 143 /// @param len Length of `s`. 144 /// 145 /// @return OK if a username was found, else FAIL. 146 int os_get_uname(uv_uid_t uid, char *s, size_t len) 147 { 148 #ifdef HAVE_PWD_FUNCS 149 struct passwd *pw; 150 151 if ((pw = getpwuid(uid)) != NULL // NOLINT(runtime/threadsafe_fn) 152 && pw->pw_name != NULL && *(pw->pw_name) != NUL) { 153 xstrlcpy(s, pw->pw_name, len); 154 return OK; 155 } 156 #endif 157 snprintf(s, len, "%d", (int)uid); 158 return FAIL; // a number is not a name 159 } 160 161 /// Gets the user directory for the given username, or NULL on failure. 162 /// 163 /// Caller must free() the returned string. 164 char *os_get_userdir(const char *name) 165 { 166 #ifdef HAVE_PWD_FUNCS 167 if (name == NULL || *name == NUL) { 168 return NULL; 169 } 170 struct passwd *pw = getpwnam(name); // NOLINT(runtime/threadsafe_fn) 171 if (pw != NULL) { 172 // save the string from the static passwd entry into malloced memory 173 return xstrdup(pw->pw_dir); 174 } 175 #endif 176 return NULL; 177 } 178 179 #if defined(EXITFREE) 180 181 void free_users(void) 182 { 183 ga_clear_strings(&ga_users); 184 } 185 186 #endif 187 188 /// Find all user names for user completion. 189 /// 190 /// Done only once and then cached. 191 static void init_users(void) 192 { 193 static bool lazy_init_done = false; 194 195 if (lazy_init_done) { 196 return; 197 } 198 199 lazy_init_done = true; 200 201 os_get_usernames(&ga_users); 202 } 203 204 /// Given to ExpandGeneric() to obtain user names. 205 char *get_users(expand_T *xp, int idx) 206 { 207 init_users(); 208 if (idx < ga_users.ga_len) { 209 return ((char **)ga_users.ga_data)[idx]; 210 } 211 return NULL; 212 } 213 214 /// Check whether name matches a user name. 215 /// 216 /// @return 0 if name does not match any user name. 217 /// 1 if name partially matches the beginning of a user name. 218 /// 2 is name fully matches a user name. 219 int match_user(char *name) 220 { 221 int n = (int)strlen(name); 222 int result = 0; 223 224 init_users(); 225 for (int i = 0; i < ga_users.ga_len; i++) { 226 if (strcmp(((char **)ga_users.ga_data)[i], name) == 0) { 227 return 2; // full match 228 } 229 if (strncmp(((char **)ga_users.ga_data)[i], name, (size_t)n) == 0) { 230 result = 1; // partial match 231 } 232 } 233 return result; 234 }