stdlib: Optimization qsort{_r} swap implementation
[glibc.git] / nss / nss_module.c
blob0104f88974600c8abfb47b355cd72e5ee6b70ac1
1 /* Global list of NSS service modules.
2 Copyright (c) 2020-2023 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
19 #include <nsswitch.h>
20 #include <nscd/nscd.h>
21 #include <nscd/nscd_proto.h>
23 #include <array_length.h>
24 #include <assert.h>
25 #include <atomic.h>
26 #include <dlfcn.h>
27 #include <gnu/lib-names.h>
28 #include <libc-lock.h>
29 #include <nss_dns.h>
30 #include <nss_files.h>
31 #include <stddef.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <pointer_guard.h>
37 /* Suffix after .so of NSS service modules. This is a bit of magic,
38 but we assume LIBNSS_FILES_SO looks like "libnss_files.so.2" and we
39 want a pointer to the ".2" part. We have no API to extract this
40 except through the auto-generated lib-names.h and some static
41 pointer manipulation. The "-1" accounts for the trailing NUL
42 included in the sizeof. */
43 static const char *const __nss_shlib_revision
44 = LIBNSS_FILES_SO + sizeof("libnss_files.so") - 1;
46 /* A single-linked list used to implement a mapping from service names
47 to NSS modules. (Most systems only use five or so modules, so a
48 list is sufficient here.) Elements of this list are never freed
49 during normal operation. */
50 static struct nss_module *nss_module_list;
52 /* Covers the list and also loading of individual NSS service
53 modules. */
54 __libc_lock_define (static, nss_module_list_lock);
56 #if defined SHARED && defined USE_NSCD
57 /* Nonzero if this is the nscd process. */
58 static bool is_nscd;
59 /* The callback passed to the init functions when nscd is used. */
60 static void (*nscd_init_cb) (size_t, struct traced_file *);
61 #endif
63 /* Allocate the service NAME with length NAME_LENGTH. If the service
64 is already allocated in the nss_module_list cache then we return a
65 pointer to the struct nss_module, otherwise we try to allocate a
66 new struct nss_module entry and add it to the global
67 nss_modules_list cache. If we fail to allocate the entry we return
68 NULL. Failure to allocate the entry is always transient. */
69 struct nss_module *
70 __nss_module_allocate (const char *name, size_t name_length)
72 __libc_lock_lock (nss_module_list_lock);
74 struct nss_module *result = NULL;
75 for (struct nss_module *p = nss_module_list; p != NULL; p = p->next)
76 if (strncmp (p->name, name, name_length) == 0
77 && p->name[name_length] == '\0')
79 /* Return the previously existing object. */
80 result = p;
81 break;
84 if (result == NULL)
86 /* Allocate a new list entry if the name was not found in the
87 list. */
88 result = malloc (sizeof (*result) + name_length + 1);
89 if (result != NULL)
91 result->state = nss_module_uninitialized;
92 memcpy (result->name, name, name_length);
93 result->name[name_length] = '\0';
94 result->handle = NULL;
95 result->next = nss_module_list;
96 nss_module_list = result;
100 __libc_lock_unlock (nss_module_list_lock);
101 return result;
104 /* Long enough to store the name of any function in the
105 nss_function_name_array list below, as getprotobynumber_r is the
106 longest entry in that list. */
107 typedef char function_name[sizeof("getprotobynumber_r")];
109 static const function_name nss_function_name_array[] =
111 #undef DEFINE_NSS_FUNCTION
112 #define DEFINE_NSS_FUNCTION(x) #x,
113 #include "function.def"
116 /* Loads a built-in module, binding the symbols using the supplied
117 callback function. Always returns true. */
118 static bool
119 module_load_builtin (struct nss_module *module,
120 void (*bind) (nss_module_functions_untyped))
122 /* Initialize the function pointers, following the double-checked
123 locking idiom. */
124 __libc_lock_lock (nss_module_list_lock);
125 switch ((enum nss_module_state) atomic_load_acquire (&module->state))
127 case nss_module_uninitialized:
128 case nss_module_failed:
129 bind (module->functions.untyped);
131 for (int i = 0; i < nss_module_functions_count; ++i)
132 PTR_MANGLE (module->functions.untyped[i]);
134 module->handle = NULL;
135 /* Synchronizes with unlocked __nss_module_load atomic_load_acquire. */
136 atomic_store_release (&module->state, nss_module_loaded);
137 break;
138 case nss_module_loaded:
139 /* Nothing to clean up. */
140 break;
142 __libc_lock_unlock (nss_module_list_lock);
143 return true;
146 /* Loads the built-in nss_files module. */
147 static bool
148 module_load_nss_files (struct nss_module *module)
150 #if defined SHARED && defined USE_NSCD
151 if (is_nscd)
153 void (*cb) (size_t, struct traced_file *) = nscd_init_cb;
154 PTR_DEMANGLE (cb);
155 _nss_files_init (cb);
157 #endif
158 return module_load_builtin (module, __nss_files_functions);
161 /* Loads the built-in nss_dns module. */
162 static bool
163 module_load_nss_dns (struct nss_module *module)
165 return module_load_builtin (module, __nss_dns_functions);
168 /* Internal implementation of __nss_module_load. */
169 static bool
170 module_load (struct nss_module *module)
172 if (strcmp (module->name, "files") == 0)
173 return module_load_nss_files (module);
174 if (strcmp (module->name, "dns") == 0)
175 return module_load_nss_dns (module);
177 void *handle;
179 char *shlib_name;
180 if (__asprintf (&shlib_name, "libnss_%s.so%s",
181 module->name, __nss_shlib_revision) < 0)
182 /* This is definitely a temporary failure. Do not update
183 module->state. This will trigger another attempt at the next
184 call. */
185 return false;
187 handle = __libc_dlopen (shlib_name);
188 free (shlib_name);
191 /* Failing to load the module can be caused by several different
192 scenarios. One such scenario is that the module has been removed
193 from the disk. In which case the in-memory version is all that
194 we have, and if the module->state indidates it is loaded then we
195 can use it. */
196 if (handle == NULL)
198 /* dlopen failure. We do not know if this a temporary or
199 permanent error. See bug 22041. Update the state using the
200 double-checked locking idiom. */
202 __libc_lock_lock (nss_module_list_lock);
203 bool result = result;
204 switch ((enum nss_module_state) atomic_load_acquire (&module->state))
206 case nss_module_uninitialized:
207 atomic_store_release (&module->state, nss_module_failed);
208 result = false;
209 break;
210 case nss_module_loaded:
211 result = true;
212 break;
213 case nss_module_failed:
214 result = false;
215 break;
217 __libc_lock_unlock (nss_module_list_lock);
218 return result;
221 nss_module_functions_untyped pointers;
223 /* Look up and store locally all the function pointers we may need
224 later. Doing this now means the data will not change in the
225 future. */
226 for (size_t idx = 0; idx < array_length (nss_function_name_array); ++idx)
228 char *function_name;
229 if (__asprintf (&function_name, "_nss_%s_%s",
230 module->name, nss_function_name_array[idx]) < 0)
232 /* Definitely a temporary error. */
233 __libc_dlclose (handle);
234 return false;
236 pointers[idx] = __libc_dlsym (handle, function_name);
237 free (function_name);
238 PTR_MANGLE (pointers[idx]);
241 # if defined SHARED && defined USE_NSCD
242 if (is_nscd)
244 /* Call the init function when nscd is used. */
245 size_t initlen = (5 + strlen (module->name)
246 + strlen ("_init") + 1);
247 char init_name[initlen];
249 /* Construct the init function name. */
250 __stpcpy (__stpcpy (__stpcpy (init_name,
251 "_nss_"),
252 module->name),
253 "_init");
255 /* Find the optional init function. */
256 void (*ifct) (void (*) (size_t, struct traced_file *))
257 = __libc_dlsym (handle, init_name);
258 if (ifct != NULL)
260 void (*cb) (size_t, struct traced_file *) = nscd_init_cb;
261 PTR_DEMANGLE (cb);
262 ifct (cb);
265 # endif
267 /* Install the function pointers, following the double-checked
268 locking idiom. Delay this after all processing, in case loading
269 the module triggers unwinding. */
270 __libc_lock_lock (nss_module_list_lock);
271 switch ((enum nss_module_state) atomic_load_acquire (&module->state))
273 case nss_module_uninitialized:
274 case nss_module_failed:
275 memcpy (module->functions.untyped, pointers,
276 sizeof (module->functions.untyped));
277 module->handle = handle;
278 /* Synchronizes with unlocked __nss_module_load atomic_load_acquire. */
279 atomic_store_release (&module->state, nss_module_loaded);
280 break;
281 case nss_module_loaded:
282 /* If the module was already loaded, close our own handle. This
283 does not actually unload the modules, only the reference
284 counter is decremented for the loaded module. */
285 __libc_dlclose (handle);
286 break;
288 __libc_lock_unlock (nss_module_list_lock);
289 return true;
292 /* Force the module identified by MODULE to be loaded. We return
293 false if the module could not be loaded, true otherwise. Loading
294 the module requires looking up all the possible interface APIs and
295 caching the results. */
296 bool
297 __nss_module_load (struct nss_module *module)
299 switch ((enum nss_module_state) atomic_load_acquire (&module->state))
301 case nss_module_uninitialized:
302 return module_load (module);
303 case nss_module_loaded:
304 /* Loading has already succeeded. */
305 return true;
306 case nss_module_failed:
307 /* Loading previously failed. */
308 return false;
310 __builtin_unreachable ();
313 static int
314 name_search (const void *left, const void *right)
316 return strcmp (left, right);
319 /* Load module MODULE (if it isn't already) and return a pointer to
320 the module's implementation of NAME, otherwise return NULL on
321 failure or error. */
322 void *
323 __nss_module_get_function (struct nss_module *module, const char *name)
325 /* A successful dlopen might clobber errno. */
326 int saved_errno = errno;
328 if (!__nss_module_load (module))
330 /* Reporting module load failure is currently inaccurate. See
331 bug 22041. Not changing errno is the conservative choice. */
332 __set_errno (saved_errno);
333 return NULL;
336 __set_errno (saved_errno);
338 function_name *name_entry = bsearch (name, nss_function_name_array,
339 array_length (nss_function_name_array),
340 sizeof (function_name), name_search);
341 assert (name_entry != NULL);
342 size_t idx = name_entry - nss_function_name_array;
343 void *fptr = module->functions.untyped[idx];
344 PTR_DEMANGLE (fptr);
345 return fptr;
348 #if defined SHARED && defined USE_NSCD
349 /* Load all libraries for the service. */
350 static void
351 nss_load_all_libraries (enum nss_database service)
353 nss_action_list ni = NULL;
355 if (__nss_database_get (service, &ni))
356 while (ni->module != NULL)
358 __nss_module_load (ni->module);
359 ++ni;
363 define_traced_file (pwd, _PATH_NSSWITCH_CONF);
364 define_traced_file (grp, _PATH_NSSWITCH_CONF);
365 define_traced_file (hst, _PATH_NSSWITCH_CONF);
366 define_traced_file (serv, _PATH_NSSWITCH_CONF);
367 define_traced_file (netgr, _PATH_NSSWITCH_CONF);
369 /* Called by nscd and nscd alone. */
370 void
371 __nss_disable_nscd (void (*cb) (size_t, struct traced_file *))
373 void (*cb1) (size_t, struct traced_file *);
374 cb1 = cb;
375 PTR_MANGLE (cb);
376 nscd_init_cb = cb;
377 is_nscd = true;
379 /* Find all the relevant modules so that the init functions are called. */
380 nss_load_all_libraries (nss_database_passwd);
381 nss_load_all_libraries (nss_database_group);
382 nss_load_all_libraries (nss_database_hosts);
383 nss_load_all_libraries (nss_database_services);
385 /* Make sure NSCD purges its cache if nsswitch.conf changes. */
386 init_traced_file (&pwd_traced_file.file, _PATH_NSSWITCH_CONF, 0);
387 cb1 (pwddb, &pwd_traced_file.file);
388 init_traced_file (&grp_traced_file.file, _PATH_NSSWITCH_CONF, 0);
389 cb1 (grpdb, &grp_traced_file.file);
390 init_traced_file (&hst_traced_file.file, _PATH_NSSWITCH_CONF, 0);
391 cb1 (hstdb, &hst_traced_file.file);
392 init_traced_file (&serv_traced_file.file, _PATH_NSSWITCH_CONF, 0);
393 cb1 (servdb, &serv_traced_file.file);
394 init_traced_file (&netgr_traced_file.file, _PATH_NSSWITCH_CONF, 0);
395 cb1 (netgrdb, &netgr_traced_file.file);
397 /* Disable all uses of NSCD. */
398 __nss_not_use_nscd_passwd = -1;
399 __nss_not_use_nscd_group = -1;
400 __nss_not_use_nscd_hosts = -1;
401 __nss_not_use_nscd_services = -1;
402 __nss_not_use_nscd_netgroup = -1;
404 #endif
406 /* Block attempts to dlopen any module we haven't already opened. */
407 void
408 __nss_module_disable_loading (void)
410 __libc_lock_lock (nss_module_list_lock);
412 for (struct nss_module *p = nss_module_list; p != NULL; p = p->next)
413 if (p->state == nss_module_uninitialized)
414 p->state = nss_module_failed;
416 __libc_lock_unlock (nss_module_list_lock);
419 void
420 __nss_module_freeres (void)
422 struct nss_module *current = nss_module_list;
423 while (current != NULL)
425 /* Ignore built-in modules (which have a NULL handle). */
426 if (current->state == nss_module_loaded
427 && current->handle != NULL)
428 __libc_dlclose (current->handle);
430 struct nss_module *next = current->next;
431 free (current);
432 current = next;
434 nss_module_list = NULL;