| /* Copyright (C) 2000-2014 Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <errno.h> |
| #include <netdb.h> |
| #include "nsswitch.h" |
| |
| /* Set up NIP to run through the services. If ALL is zero, use NIP's |
| current location if it's not nil. Return nonzero if there are no |
| services (left). */ |
| static int |
| setup (const char *func_name, db_lookup_function lookup_fct, |
| void **fctp, service_user **nip, service_user **startp, int all) |
| { |
| int no_more; |
| if (*startp == NULL) |
| { |
| no_more = lookup_fct (nip, func_name, NULL, fctp); |
| *startp = no_more ? (service_user *) -1l : *nip; |
| } |
| else if (*startp == (service_user *) -1l) |
| /* No services at all. */ |
| return 1; |
| else |
| { |
| if (all || !*nip) |
| /* Reset to the beginning of the service list. */ |
| *nip = *startp; |
| /* Look up the first function. */ |
| no_more = __nss_lookup (nip, func_name, NULL, fctp); |
| } |
| return no_more; |
| } |
| |
| void |
| __nss_setent (const char *func_name, db_lookup_function lookup_fct, |
| service_user **nip, service_user **startp, |
| service_user **last_nip, int stayopen, int *stayopen_tmp, |
| int res) |
| { |
| union |
| { |
| setent_function f; |
| void *ptr; |
| } fct; |
| int no_more; |
| |
| if (res && __res_maybe_init (&_res, 0) == -1) |
| { |
| __set_h_errno (NETDB_INTERNAL); |
| return; |
| } |
| |
| /* Cycle through the services and run their `setXXent' functions until |
| we find an available service. */ |
| no_more = setup (func_name, lookup_fct, &fct.ptr, nip, |
| startp, 1); |
| while (! no_more) |
| { |
| int is_last_nip = *nip == *last_nip; |
| enum nss_status status; |
| |
| if (stayopen_tmp) |
| status = DL_CALL_FCT (fct.f, (*stayopen_tmp)); |
| else |
| status = DL_CALL_FCT (fct.f, (0)); |
| |
| no_more = __nss_next2 (nip, func_name, NULL, &fct.ptr, status, 0); |
| if (is_last_nip) |
| *last_nip = *nip; |
| } |
| |
| if (stayopen_tmp) |
| *stayopen_tmp = stayopen; |
| } |
| |
| |
| void |
| __nss_endent (const char *func_name, db_lookup_function lookup_fct, |
| service_user **nip, service_user **startp, |
| service_user **last_nip, int res) |
| { |
| union |
| { |
| endent_function f; |
| void *ptr; |
| } fct; |
| int no_more; |
| |
| if (res && __res_maybe_init (&_res, 0) == -1) |
| { |
| __set_h_errno (NETDB_INTERNAL); |
| return; |
| } |
| |
| /* Cycle through all the services and run their endXXent functions. */ |
| no_more = setup (func_name, lookup_fct, &fct.ptr, nip, startp, 1); |
| while (! no_more) |
| { |
| /* Ignore status, we force check in __NSS_NEXT. */ |
| DL_CALL_FCT (fct.f, ()); |
| |
| if (*nip == *last_nip) |
| /* We have processed all services which were used. */ |
| break; |
| |
| no_more = __nss_next2 (nip, func_name, NULL, &fct.ptr, 0, 1); |
| } |
| *last_nip = *nip = NULL; |
| } |
| |
| |
| int |
| __nss_getent_r (const char *getent_func_name, |
| const char *setent_func_name, |
| db_lookup_function lookup_fct, |
| service_user **nip, service_user **startp, |
| service_user **last_nip, int *stayopen_tmp, int res, |
| void *resbuf, char *buffer, size_t buflen, |
| void **result, int *h_errnop) |
| { |
| union |
| { |
| getent_function f; |
| void *ptr; |
| } fct; |
| int no_more; |
| enum nss_status status; |
| |
| if (res && __res_maybe_init (&_res, 0) == -1) |
| { |
| *h_errnop = NETDB_INTERNAL; |
| *result = NULL; |
| return errno; |
| } |
| |
| /* Initialize status to return if no more functions are found. */ |
| status = NSS_STATUS_NOTFOUND; |
| |
| /* Run through available functions, starting with the same function last |
| run. We will repeat each function as long as it succeeds, and then go |
| on to the next service action. */ |
| no_more = setup (getent_func_name, lookup_fct, &fct.ptr, nip, |
| startp, 0); |
| while (! no_more) |
| { |
| int is_last_nip = *nip == *last_nip; |
| |
| status = DL_CALL_FCT (fct.f, |
| (resbuf, buffer, buflen, &errno, &h_errno)); |
| |
| /* The status is NSS_STATUS_TRYAGAIN and errno is ERANGE the |
| provided buffer is too small. In this case we should give |
| the user the possibility to enlarge the buffer and we should |
| not simply go on with the next service (even if the TRYAGAIN |
| action tells us so). */ |
| if (status == NSS_STATUS_TRYAGAIN |
| && (h_errnop == NULL || *h_errnop == NETDB_INTERNAL) |
| && errno == ERANGE) |
| break; |
| |
| do |
| { |
| no_more = __nss_next2 (nip, getent_func_name, NULL, &fct.ptr, |
| status, 0); |
| |
| if (is_last_nip) |
| *last_nip = *nip; |
| |
| if (! no_more) |
| { |
| /* Call the `setXXent' function. This wasn't done before. */ |
| union |
| { |
| setent_function f; |
| void *ptr; |
| } sfct; |
| |
| no_more = __nss_lookup (nip, setent_func_name, NULL, &sfct.ptr); |
| |
| if (! no_more) |
| { |
| if (stayopen_tmp) |
| status = DL_CALL_FCT (sfct.f, (*stayopen_tmp)); |
| else |
| status = DL_CALL_FCT (sfct.f, (0)); |
| } |
| else |
| status = NSS_STATUS_NOTFOUND; |
| } |
| } |
| while (! no_more && status != NSS_STATUS_SUCCESS); |
| } |
| |
| *result = status == NSS_STATUS_SUCCESS ? resbuf : NULL; |
| return (status == NSS_STATUS_SUCCESS ? 0 |
| : status != NSS_STATUS_TRYAGAIN ? ENOENT |
| /* h_errno functions only set errno if h_errno is NETDB_INTERNAL. */ |
| : (h_errnop == NULL || *h_errnop == NETDB_INTERNAL) ? errno |
| : EAGAIN); |
| } |