nwrap: Cast max_hostents to avoid warnings
[obnox/samba/samba-obnox.git] / lib / nss_wrapper / nss_wrapper.c
index 8767fbfd8921feadb358bf81695225f69fdf5a86..a8cbc222ac79e4d4f330f933289cf031263644b0 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) Stefan Metzmacher 2007 <metze@samba.org>
  * Copyright (C) Guenther Deschner 2009 <gd@samba.org>
+ * Copyright (C) Andreas Schneider 2013 <asn@samba.org>
  *
  * All rights reserved.
  *
  * SUCH DAMAGE.
  */
 
-#ifdef _SAMBA_BUILD_
+#include "config.h"
 
-/* defining this gives us the posix getpwnam_r() calls on solaris
-   Thanks to heimdal for this */
+#include <pthread.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include <netinet/in.h>
+
+#include <search.h>
+#include <assert.h>
+
+/*
+ * Defining _POSIX_PTHREAD_SEMANTICS before including pwd.h and grp.h  gives us
+ * the posix getpwnam_r(), getpwuid_r(), getgrnam_r and getgrgid_r calls on
+ * Solaris
+ */
 #ifndef _POSIX_PTHREAD_SEMANTICS
 #define _POSIX_PTHREAD_SEMANTICS
 #endif
 
-#define NSS_WRAPPER_NOT_REPLACE
-#include "../replace/replace.h"
-#include "system/passwd.h"
-#include "system/filesys.h"
-#include "../nsswitch/nsstest.h"
+#include <pwd.h>
+#include <grp.h>
+#ifdef HAVE_SHADOW_H
+#include <shadow.h>
+#endif /* HAVE_SHADOW_H */
+
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <dlfcn.h>
 
-#else /* _SAMBA_BUILD_ */
+#if defined(HAVE_NSS_H)
+/* Linux and BSD */
+#include <nss.h>
 
-#error nss_wrapper_only_supported_in_samba_yet
+typedef enum nss_status NSS_STATUS;
+#elif defined(HAVE_NSS_COMMON_H)
+/* Solaris */
+#include <nss_common.h>
+#include <nss_dbdefs.h>
+#include <nsswitch.h>
 
+typedef nss_status_t NSS_STATUS;
+
+# define NSS_STATUS_SUCCESS     NSS_SUCCESS
+# define NSS_STATUS_NOTFOUND    NSS_NOTFOUND
+# define NSS_STATUS_UNAVAIL     NSS_UNAVAIL
+# define NSS_STATUS_TRYAGAIN    NSS_TRYAGAIN
+#else
+# error "No nsswitch support detected"
+#endif
+
+#ifndef PTR_DIFF
+#define PTR_DIFF(p1, p2) ((ptrdiff_t)(((const char *)(p1)) - (const char *)(p2)))
 #endif
 
 #ifndef _PUBLIC_
 #define _PUBLIC_
 #endif
 
-/* not all systems have _r functions... */
-#ifndef HAVE_GETPWNAM_R
-#define getpwnam_r(name, pwdst, buf, buflen, pwdstp)   ENOSYS
+#ifndef EAI_NODATA
+#define EAI_NODATA EAI_NONAME
 #endif
-#ifndef HAVE_GETPWUID_R
-#define getpwuid_r(uid, pwdst, buf, buflen, pwdstp)    ENOSYS
-#endif
-#ifndef HAVE_GETPWENT_R
-#define getpwent_r(pwdst, buf, buflen, pwdstp)         ENOSYS
-#endif
-#ifndef HAVE_GETGRNAM_R
-#define getgrnam_r(name, grdst, buf, buflen, grdstp)   ENOSYS
+
+#ifndef EAI_ADDRFAMILY
+#define EAI_ADDRFAMILY EAI_FAMILY
 #endif
-#ifndef HAVE_GETGRGID_R
-#define getgrgid_r(gid, grdst, buf, buflen, grdstp)    ENOSYS
+
+#ifndef __STRING
+#define __STRING(x)    #x
 #endif
-#ifndef HAVE_GETGRENT_R
-#define getgrent_r(grdst, buf, buflen, grdstp)         ENOSYS
+
+#ifndef __STRINGSTRING
+#define __STRINGSTRING(x) __STRING(x)
 #endif
 
-/* not all systems have getgrouplist */
-#ifndef HAVE_GETGROUPLIST
-#define getgrouplist(user, group, groups, ngroups)     0
+#ifndef __LINESTR__
+#define __LINESTR__ __STRINGSTRING(__LINE__)
 #endif
 
-/* LD_PRELOAD doesn't work yet, so REWRITE_CALLS is all we support
- * for now */
-#define REWRITE_CALLS
+#ifndef __location__
+#define __location__ __FILE__ ":" __LINESTR__
+#endif
 
-#ifdef REWRITE_CALLS
+#ifndef DNS_NAME_MAX
+#define DNS_NAME_MAX 255
+#endif
 
-#define real_getpwnam          getpwnam
-#define real_getpwnam_r                getpwnam_r
-#define real_getpwuid          getpwuid
-#define real_getpwuid_r                getpwuid_r
+/* GCC have printf type attribute check. */
+#ifdef HAVE_ATTRIBUTE_PRINTF_FORMAT
+#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
+#else
+#define PRINTF_ATTRIBUTE(a,b)
+#endif /* HAVE_ATTRIBUTE_PRINTF_FORMAT */
 
-#define real_setpwent          setpwent
-#define real_getpwent          getpwent
-#define real_getpwent_r                getpwent_r
-#define real_endpwent          endpwent
+#ifdef HAVE_DESTRUCTOR_ATTRIBUTE
+#define DESTRUCTOR_ATTRIBUTE __attribute__ ((destructor))
+#else
+#define DESTRUCTOR_ATTRIBUTE
+#endif /* HAVE_DESTRUCTOR_ATTRIBUTE */
 
-/*
-#define real_getgrlst          getgrlst
-#define real_getgrlst_r                getgrlst_r
-#define real_initgroups_dyn    initgroups_dyn
-*/
-#define real_initgroups                initgroups
-#define real_getgrouplist      getgrouplist
-
-#define real_getgrnam          getgrnam
-#define real_getgrnam_r                getgrnam_r
-#define real_getgrgid          getgrgid
-#define real_getgrgid_r                getgrgid_r
-
-#define real_setgrent          setgrent
-#define real_getgrent          getgrent
-#define real_getgrent_r                getgrent_r
-#define real_endgrent          endgrent
+#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0)
 
+#ifndef SAFE_FREE
+#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0)
 #endif
 
-#if 0
-# ifdef DEBUG
-# define NWRAP_ERROR(args)     DEBUG(0, args)
-# else
-# define NWRAP_ERROR(args)     printf args
-# endif
+#ifdef HAVE_IPV6
+#define NWRAP_INET_ADDRSTRLEN INET6_ADDRSTRLEN
 #else
-#define NWRAP_ERROR(args)
+#define NWRAP_INET_ADDRSTRLEN INET_ADDRSTRLEN
 #endif
 
-#if 0
-# ifdef DEBUG
-# define NWRAP_DEBUG(args)     DEBUG(0, args)
-# else
-# define NWRAP_DEBUG(args)     printf args
-# endif
+#define NWRAP_LOCK(m) do { \
+       pthread_mutex_lock(&( m ## _mutex)); \
+} while(0)
+
+#define NWRAP_UNLOCK(m) do { \
+       pthread_mutex_unlock(&( m ## _mutex)); \
+} while(0)
+
+
+static bool nwrap_initialized = false;
+static pthread_mutex_t nwrap_initialized_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* The mutex or accessing the id */
+static pthread_mutex_t nwrap_global_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t nwrap_gr_global_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t nwrap_he_global_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t nwrap_pw_global_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t nwrap_sp_global_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Add new global locks here please */
+/* Also don't forget to add locks to
+ * nwrap_init() function.
+ */
+# define NWRAP_LOCK_ALL do { \
+       NWRAP_LOCK(nwrap_initialized); \
+       NWRAP_LOCK(nwrap_global); \
+       NWRAP_LOCK(nwrap_gr_global); \
+       NWRAP_LOCK(nwrap_he_global); \
+       NWRAP_LOCK(nwrap_pw_global); \
+       NWRAP_LOCK(nwrap_sp_global); \
+} while (0);
+
+# define NWRAP_UNLOCK_ALL do {\
+       NWRAP_UNLOCK(nwrap_sp_global); \
+       NWRAP_UNLOCK(nwrap_pw_global); \
+       NWRAP_UNLOCK(nwrap_he_global); \
+       NWRAP_UNLOCK(nwrap_gr_global); \
+       NWRAP_UNLOCK(nwrap_global); \
+       NWRAP_UNLOCK(nwrap_initialized); \
+} while (0);
+
+static void nwrap_thread_prepare(void)
+{
+       NWRAP_LOCK_ALL;
+}
+
+static void nwrap_thread_parent(void)
+{
+       NWRAP_UNLOCK_ALL;
+}
+
+static void nwrap_thread_child(void)
+{
+       NWRAP_UNLOCK_ALL;
+}
+
+enum nwrap_dbglvl_e {
+       NWRAP_LOG_ERROR = 0,
+       NWRAP_LOG_WARN,
+       NWRAP_LOG_DEBUG,
+       NWRAP_LOG_TRACE
+};
+
+#ifdef NDEBUG
+# define NWRAP_LOG(...)
 #else
-#define NWRAP_DEBUG(args)
-#endif
 
-#if 0
-# ifdef DEBUG
-# define NWRAP_VERBOSE(args)   DEBUG(0, args)
-# else
-# define NWRAP_VERBOSE(args)   printf args
-# endif
+static void nwrap_log(enum nwrap_dbglvl_e dbglvl, const char *func, const char *format, ...) PRINTF_ATTRIBUTE(3, 4);
+# define NWRAP_LOG(dbglvl, ...) nwrap_log((dbglvl), __func__, __VA_ARGS__)
+
+static void nwrap_log(enum nwrap_dbglvl_e dbglvl,
+                     const char *func,
+                     const char *format, ...)
+{
+       char buffer[1024];
+       va_list va;
+       const char *d;
+       unsigned int lvl = 0;
+       int pid = getpid();
+
+       d = getenv("NSS_WRAPPER_DEBUGLEVEL");
+       if (d != NULL) {
+               lvl = atoi(d);
+       }
+
+       va_start(va, format);
+       vsnprintf(buffer, sizeof(buffer), format, va);
+       va_end(va);
+
+       if (lvl >= dbglvl) {
+               switch (dbglvl) {
+                       case NWRAP_LOG_ERROR:
+                               fprintf(stderr,
+                                       "NWRAP_ERROR(%d) - %s: %s\n",
+                                       pid, func, buffer);
+                               break;
+                       case NWRAP_LOG_WARN:
+                               fprintf(stderr,
+                                       "NWRAP_WARN(%d) - %s: %s\n",
+                                       pid, func, buffer);
+                               break;
+                       case NWRAP_LOG_DEBUG:
+                               fprintf(stderr,
+                                       "NWRAP_DEBUG(%d) - %s: %s\n",
+                                       pid, func, buffer);
+                               break;
+                       case NWRAP_LOG_TRACE:
+                               fprintf(stderr,
+                                       "NWRAP_TRACE(%d) - %s: %s\n",
+                                       pid, func, buffer);
+                               break;
+               }
+       }
+}
+#endif /* NDEBUG NWRAP_LOG */
+
+struct nwrap_libc_fns {
+       struct passwd *(*_libc_getpwnam)(const char *name);
+       int (*_libc_getpwnam_r)(const char *name, struct passwd *pwd,
+                      char *buf, size_t buflen, struct passwd **result);
+       struct passwd *(*_libc_getpwuid)(uid_t uid);
+       int (*_libc_getpwuid_r)(uid_t uid, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result);
+       void (*_libc_setpwent)(void);
+       struct passwd *(*_libc_getpwent)(void);
+#ifdef HAVE_SOLARIS_GETPWENT_R
+       struct passwd *(*_libc_getpwent_r)(struct passwd *pwbuf, char *buf, size_t buflen);
+#else
+       int (*_libc_getpwent_r)(struct passwd *pwbuf, char *buf, size_t buflen, struct passwd **pwbufp);
+#endif
+       void (*_libc_endpwent)(void);
+       int (*_libc_initgroups)(const char *user, gid_t gid);
+       struct group *(*_libc_getgrnam)(const char *name);
+       int (*_libc_getgrnam_r)(const char *name, struct group *grp, char *buf, size_t buflen, struct group **result);
+       struct group *(*_libc_getgrgid)(gid_t gid);
+       int (*_libc_getgrgid_r)(gid_t gid, struct group *grp, char *buf, size_t buflen, struct group **result);
+       void (*_libc_setgrent)(void);
+       struct group *(*_libc_getgrent)(void);
+#ifdef HAVE_SOLARIS_GETGRENT_R
+       struct group *(*_libc_getgrent_r)(struct group *group, char *buf, size_t buflen);
 #else
-#define NWRAP_VERBOSE(args)
+       int (*_libc_getgrent_r)(struct group *group, char *buf, size_t buflen, struct group **result);
+#endif
+       void (*_libc_endgrent)(void);
+       int (*_libc_getgrouplist)(const char *user, gid_t group, gid_t *groups, int *ngroups);
+
+       void (*_libc_sethostent)(int stayopen);
+       struct hostent *(*_libc_gethostent)(void);
+       void (*_libc_endhostent)(void);
+
+       struct hostent *(*_libc_gethostbyname)(const char *name);
+#ifdef HAVE_GETHOSTBYNAME2 /* GNU extension */
+       struct hostent *(*_libc_gethostbyname2)(const char *name, int af);
 #endif
+       struct hostent *(*_libc_gethostbyaddr)(const void *addr, socklen_t len, int type);
+
+       int (*_libc_getaddrinfo)(const char *node, const char *service,
+                                const struct addrinfo *hints,
+                                struct addrinfo **res);
+       int (*_libc_getnameinfo)(const struct sockaddr *sa, socklen_t salen,
+                                char *host, size_t hostlen,
+                                char *serv, size_t servlen,
+                                int flags);
+       int (*_libc_gethostname)(char *name, size_t len);
+#ifdef HAVE_GETHOSTBYNAME_R
+       int (*_libc_gethostbyname_r)(const char *name,
+                                    struct hostent *ret,
+                                    char *buf, size_t buflen,
+                                    struct hostent **result, int *h_errnop);
+#endif
+#ifdef HAVE_GETHOSTBYADDR_R
+       int (*_libc_gethostbyaddr_r)(const void *addr, socklen_t len, int type,
+                                    struct hostent *ret,
+                                    char *buf, size_t buflen,
+                                    struct hostent **result, int *h_errnop);
+#endif
+};
 
 struct nwrap_module_nss_fns {
        NSS_STATUS (*_nss_getpwnam_r)(const char *name, struct passwd *result, char *buffer,
@@ -213,6 +403,12 @@ struct nwrap_ops {
        void            (*nw_endgrent)(struct nwrap_backend *b);
 };
 
+/* Public prototypes */
+
+bool nss_wrapper_enabled(void);
+bool nss_wrapper_shadow_enabled(void);
+bool nss_wrapper_hosts_enabled(void);
+
 /* prototypes for files backend */
 
 
@@ -328,25 +524,182 @@ struct nwrap_ops nwrap_module_ops = {
        .nw_endgrent    = nwrap_module_endgrent,
 };
 
+struct nwrap_libc {
+       void *handle;
+       void *nsl_handle;
+       void *sock_handle;
+       struct nwrap_libc_fns *fns;
+};
+
 struct nwrap_main {
-       const char *nwrap_switch;
        int num_backends;
        struct nwrap_backend *backends;
+       struct nwrap_libc *libc;
+};
+
+static struct nwrap_main *nwrap_main_global;
+static struct nwrap_main __nwrap_main_global;
+
+/*
+ * PROTOTYPES
+ */
+static int nwrap_convert_he_ai(const struct hostent *he,
+                              unsigned short port,
+                              const struct addrinfo *hints,
+                              struct addrinfo **pai,
+                              bool skip_canonname);
+
+/*
+ * VECTORS
+ */
+
+#define DEFAULT_VECTOR_CAPACITY 16
+
+struct nwrap_vector {
+       void **items;
+       size_t count;
+       size_t capacity;
 };
 
-struct nwrap_main *nwrap_main_global;
-struct nwrap_main __nwrap_main_global;
+/* Macro returns pointer to first element of vector->items array.
+ *
+ * nwrap_vector is used as a memory backend which take care of
+ * memory allocations and other stuff like memory growing.
+ * nwrap_vectors should not be considered as some abstract structures.
+ * On this level, vectors are more handy than direct realloc/malloc
+ * calls.
+ *
+ * nwrap_vector->items is array inside nwrap_vector which can be
+ * directly pointed by libc structure assembled by cwrap itself.
+ *
+ * EXAMPLE:
+ *
+ * 1) struct hostent contains char **h_addr_list element.
+ * 2) nwrap_vector holds array of pointers to addresses.
+ *    It's easier to use vector to store results of
+ *    file parsing etc.
+ *
+ * Now, pretend that cwrap assembled struct hostent and
+ * we need to set h_addr_list to point to nwrap_vector.
+ * Idea behind is to shield users from internal nwrap_vector
+ * implementation.
+ * (Yes, not fully - array terminated by NULL is needed because
+ * it's result expected by libc function caller.)
+ *
+ *
+ * CODE EXAMPLE:
+ *
+ * struct hostent he;
+ * struct nwrap_vector *vector = malloc(sizeof(struct nwrap_vector));
+ * ... don't care about failed allocation now ...
+ *
+ * ... fill nwrap vector ...
+ *
+ * struct hostent he;
+ * he.h_addr_list = nwrap_vector_head(vector);
+ *
+ */
+#define nwrap_vector_head(vect) ((void *)((vect)->items))
+
+#define nwrap_vector_foreach(item, vect, iter) \
+       for (iter = 0, (item) = (vect).items == NULL ? NULL : (vect).items[0]; \
+            item != NULL; \
+            (item) = (vect).items[++iter])
+
+#define nwrap_vector_is_initialized(vector) ((vector)->items != NULL)
+
+static inline bool nwrap_vector_init(struct nwrap_vector *const vector)
+{
+       if (vector == NULL) {
+               return false;
+       }
+
+       /* count is initialized by ZERO_STRUCTP */
+       ZERO_STRUCTP(vector);
+       vector->items = malloc(sizeof(void *) * (DEFAULT_VECTOR_CAPACITY + 1));
+       if (vector->items == NULL) {
+               return false;
+       }
+       vector->capacity = DEFAULT_VECTOR_CAPACITY;
+       memset(vector->items, '\0', sizeof(void *) * (DEFAULT_VECTOR_CAPACITY + 1));
+
+       return true;
+}
+
+static bool nwrap_vector_add_item(struct nwrap_vector *vector, void *const item)
+{
+       assert (vector != NULL);
+
+       if (vector->items == NULL) {
+               nwrap_vector_init(vector);
+       }
+
+       if (vector->count == vector->capacity) {
+               /* Items array _MUST_ be NULL terminated because it's passed
+                * as result to caller which expect NULL terminated array from libc.
+                */
+               void **items = realloc(vector->items, sizeof(void *) * ((vector->capacity * 2) + 1));
+               if (items == NULL) {
+                       return false;
+               }
+               vector->items = items;
+
+               /* Don't count ending NULL to capacity */
+               vector->capacity *= 2;
+       }
+
+       vector->items[vector->count] = item;
+
+       vector->count += 1;
+       vector->items[vector->count] = NULL;
+
+       return true;
+}
+
+static bool nwrap_vector_merge(struct nwrap_vector *dst,
+                              struct nwrap_vector *src)
+{
+       void **dst_items = NULL;
+       size_t count;
+
+       if (src->count == 0) {
+               return true;
+       }
+
+       count = dst->count + src->count;
+
+       /* We don't need reallocation if we have enough capacity. */
+       if (src->count > (dst->capacity - dst->count)) {
+               dst_items = (void **)realloc(dst->items, (count + 1) * sizeof(void *));
+               if (dst_items == NULL) {
+                       return false;
+               }
+               dst->items = dst_items;
+               dst->capacity = count;
+       }
+
+       memcpy((void *)(((long *)dst->items) + dst->count),
+              src->items,
+              src->count * sizeof(void *));
+       dst->count = count;
+
+       return true;
+}
 
 struct nwrap_cache {
        const char *path;
        int fd;
+       FILE *fp;
        struct stat st;
-       uint8_t *buf;
        void *private_data;
+
+       struct nwrap_vector lines;
+
        bool (*parse_line)(struct nwrap_cache *, char *line);
        void (*unload)(struct nwrap_cache *);
 };
 
+/* passwd */
 struct nwrap_pw {
        struct nwrap_cache *cache;
 
@@ -361,6 +714,24 @@ struct nwrap_pw nwrap_pw_global;
 static bool nwrap_pw_parse_line(struct nwrap_cache *nwrap, char *line);
 static void nwrap_pw_unload(struct nwrap_cache *nwrap);
 
+/* shadow */
+#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM)
+struct nwrap_sp {
+       struct nwrap_cache *cache;
+
+       struct spwd *list;
+       int num;
+       int idx;
+};
+
+struct nwrap_cache __nwrap_cache_sp;
+struct nwrap_sp nwrap_sp_global;
+
+static bool nwrap_sp_parse_line(struct nwrap_cache *nwrap, char *line);
+static void nwrap_sp_unload(struct nwrap_cache *nwrap);
+#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */
+
+/* group */
 struct nwrap_gr {
        struct nwrap_cache *cache;
 
@@ -372,1885 +743,4795 @@ struct nwrap_gr {
 struct nwrap_cache __nwrap_cache_gr;
 struct nwrap_gr nwrap_gr_global;
 
-static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line);
-static void nwrap_gr_unload(struct nwrap_cache *nwrap);
+/* hosts */
+static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line);
+static void nwrap_he_unload(struct nwrap_cache *nwrap);
 
-static void *nwrap_load_module_fn(struct nwrap_backend *b,
-                                 const char *fn_name)
-{
-       void *res;
-       char *s;
+struct nwrap_addrdata {
+       unsigned char host_addr[16]; /* IPv4 or IPv6 address */
+};
 
-       if (!b->so_handle) {
-               NWRAP_ERROR(("%s: no handle\n",
-                            __location__));
-               return NULL;
-       }
+static size_t max_hostents = 100;
 
-       if (asprintf(&s, "_nss_%s_%s", b->name, fn_name) == -1) {
-               NWRAP_ERROR(("%s: out of memory\n",
-                            __location__));
-               return NULL;
-       }
+struct nwrap_entdata {
+       struct nwrap_addrdata addr;
+       struct hostent ht;
 
-       res = dlsym(b->so_handle, s);
-       if (!res) {
-               NWRAP_ERROR(("%s: cannot find function %s in %s\n",
-                            __location__, s, b->so_path));
-       }
-       free(s);
-       s = NULL;
-       return res;
-}
+       struct nwrap_vector nwrap_addrdata;
 
-static struct nwrap_module_nss_fns *nwrap_load_module_fns(struct nwrap_backend *b)
-{
-       struct nwrap_module_nss_fns *fns;
+       ssize_t aliases_count;
+};
 
-       if (!b->so_handle) {
-               return NULL;
-       }
+struct nwrap_entlist {
+       struct nwrap_entlist *next;
+       struct nwrap_entdata *ed;
+};
 
-       fns = (struct nwrap_module_nss_fns *)malloc(sizeof(struct nwrap_module_nss_fns));
-       if (!fns) {
-               return NULL;
-       }
+struct nwrap_he {
+       struct nwrap_cache *cache;
 
-       fns->_nss_getpwnam_r    = (NSS_STATUS (*)(const char *, struct passwd *, char *, size_t, int *))
-                                 nwrap_load_module_fn(b, "getpwnam_r");
-       fns->_nss_getpwuid_r    = (NSS_STATUS (*)(uid_t, struct passwd *, char *, size_t, int *))
-                                 nwrap_load_module_fn(b, "getpwuid_r");
-       fns->_nss_setpwent      = (NSS_STATUS(*)(void))
-                                 nwrap_load_module_fn(b, "setpwent");
-       fns->_nss_getpwent_r    = (NSS_STATUS (*)(struct passwd *, char *, size_t, int *))
-                                 nwrap_load_module_fn(b, "getpwent_r");
-       fns->_nss_endpwent      = (NSS_STATUS(*)(void))
-                                 nwrap_load_module_fn(b, "endpwent");
-       fns->_nss_initgroups    = (NSS_STATUS (*)(const char *, gid_t, long int *, long int *, gid_t **, long int, int *))
-                                 nwrap_load_module_fn(b, "initgroups_dyn");
-       fns->_nss_getgrnam_r    = (NSS_STATUS (*)(const char *, struct group *, char *, size_t, int *))
-                                 nwrap_load_module_fn(b, "getgrnam_r");
-       fns->_nss_getgrgid_r    = (NSS_STATUS (*)(gid_t, struct group *, char *, size_t, int *))
-                                 nwrap_load_module_fn(b, "getgrgid_r");
-       fns->_nss_setgrent      = (NSS_STATUS(*)(void))
-                                 nwrap_load_module_fn(b, "setgrent");
-       fns->_nss_getgrent_r    = (NSS_STATUS (*)(struct group *, char *, size_t, int *))
-                                 nwrap_load_module_fn(b, "getgrent_r");
-       fns->_nss_endgrent      = (NSS_STATUS(*)(void))
-                                 nwrap_load_module_fn(b, "endgrent");
+       struct nwrap_entdata *list;
+       struct nwrap_vector entdata;
 
-       return fns;
-}
+       int num;
+       int idx;
+};
 
-static void *nwrap_load_module(const char *so_path)
-{
-       void *h;
+static struct nwrap_cache __nwrap_cache_he;
+static struct nwrap_he nwrap_he_global;
 
-       if (!so_path || !strlen(so_path)) {
-               return NULL;
-       }
 
-       h = dlopen(so_path, RTLD_LAZY);
-       if (!h) {
-               NWRAP_ERROR(("%s: cannot open shared library %s\n",
-                            __location__, so_path));
-               return NULL;
+/*********************************************************
+ * NWRAP PROTOTYPES
+ *********************************************************/
+
+static void nwrap_init(void);
+static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line);
+static void nwrap_gr_unload(struct nwrap_cache *nwrap);
+void nwrap_destructor(void) DESTRUCTOR_ATTRIBUTE;
+
+/*********************************************************
+ * NWRAP LIBC LOADER FUNCTIONS
+ *********************************************************/
+
+enum nwrap_lib {
+    NWRAP_LIBC,
+    NWRAP_LIBNSL,
+    NWRAP_LIBSOCKET,
+};
+
+#ifndef NDEBUG
+static const char *nwrap_str_lib(enum nwrap_lib lib)
+{
+       switch (lib) {
+       case NWRAP_LIBC:
+               return "libc";
+       case NWRAP_LIBNSL:
+               return "libnsl";
+       case NWRAP_LIBSOCKET:
+               return "libsocket";
        }
 
-       return h;
+       /* Compiler would warn us about unhandled enum value if we get here */
+       return "unknown";
 }
+#endif
 
-static bool nwrap_module_init(const char *name,
-                             struct nwrap_ops *ops,
-                             const char *so_path,
-                             int *num_backends,
-                             struct nwrap_backend **backends)
+static void *nwrap_load_lib_handle(enum nwrap_lib lib)
 {
-       struct nwrap_backend *b;
+       int flags = RTLD_LAZY;
+       void *handle = NULL;
+       int i;
 
-       *backends = (struct nwrap_backend *)realloc(*backends,
-               sizeof(struct nwrap_backend) * ((*num_backends) + 1));
-       if (!*backends) {
-               NWRAP_ERROR(("%s: out of memory\n",
-                            __location__));
-               return false;
-       }
+#ifdef RTLD_DEEPBIND
+       flags |= RTLD_DEEPBIND;
+#endif
 
-       b = &((*backends)[*num_backends]);
+       switch (lib) {
+       case NWRAP_LIBNSL:
+#ifdef HAVE_LIBNSL
+               handle = nwrap_main_global->libc->nsl_handle;
+               if (handle == NULL) {
+                       for (i = 10; i >= 0; i--) {
+                               char soname[256] = {0};
+
+                               snprintf(soname, sizeof(soname), "libnsl.so.%d", i);
+                               handle = dlopen(soname, flags);
+                               if (handle != NULL) {
+                                       break;
+                               }
+                       }
 
-       b->name = name;
-       b->ops = ops;
-       b->so_path = so_path;
+                       nwrap_main_global->libc->nsl_handle = handle;
+               }
+               break;
+#endif
+               /* FALL TROUGH */
+       case NWRAP_LIBSOCKET:
+#ifdef HAVE_LIBSOCKET
+               handle = nwrap_main_global->libc->sock_handle;
+               if (handle == NULL) {
+                       for (i = 10; i >= 0; i--) {
+                               char soname[256] = {0};
+
+                               snprintf(soname, sizeof(soname), "libsocket.so.%d", i);
+                               handle = dlopen(soname, flags);
+                               if (handle != NULL) {
+                                       break;
+                               }
+                       }
 
-       if (so_path != NULL) {
-               b->so_handle = nwrap_load_module(so_path);
-               b->fns = nwrap_load_module_fns(b);
-               if (b->fns == NULL) {
-                       return false;
+                       nwrap_main_global->libc->sock_handle = handle;
                }
-       } else {
-               b->so_handle = NULL;
-               b->fns = NULL;
+               break;
+#endif
+               /* FALL TROUGH */
+       case NWRAP_LIBC:
+               handle = nwrap_main_global->libc->handle;
+               if (handle == NULL) {
+                       for (i = 10; i >= 0; i--) {
+                               char soname[256] = {0};
+
+                               snprintf(soname, sizeof(soname), "libc.so.%d", i);
+                               handle = dlopen(soname, flags);
+                               if (handle != NULL) {
+                                       break;
+                               }
+                       }
+
+                       nwrap_main_global->libc->handle = handle;
+               }
+               break;
        }
 
-       (*num_backends)++;
+       if (handle == NULL) {
+#ifdef RTLD_NEXT
+               handle = nwrap_main_global->libc->handle
+                      = nwrap_main_global->libc->sock_handle
+                      = nwrap_main_global->libc->nsl_handle
+                      = RTLD_NEXT;
+#else
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Failed to dlopen library: %s\n",
+                         dlerror());
+               exit(-1);
+#endif
+       }
 
-       return true;
+       return handle;
 }
 
-static void nwrap_backend_init(struct nwrap_main *r)
+static void *_nwrap_load_lib_function(enum nwrap_lib lib, const char *fn_name)
 {
-       const char *winbind_so_path = getenv("NSS_WRAPPER_WINBIND_SO_PATH");
+       void *handle;
+       void *func;
 
-       r->num_backends = 0;
-       r->backends = NULL;
+       nwrap_init();
 
-       if (!nwrap_module_init("files", &nwrap_files_ops, NULL,
-                              &r->num_backends,
-                              &r->backends)) {
-               NWRAP_ERROR(("%s: failed to initialize 'files' backend\n",
-                            __location__));
-               return;
-       }
+       handle = nwrap_load_lib_handle(lib);
 
-       if (winbind_so_path && strlen(winbind_so_path)) {
-               if (!nwrap_module_init("winbind", &nwrap_module_ops, winbind_so_path,
-                                      &r->num_backends,
-                                      &r->backends)) {
-                       NWRAP_ERROR(("%s: failed to initialize 'winbind' backend\n",
-                                    __location__));
-                       return;
-               }
+       func = dlsym(handle, fn_name);
+       if (func == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                               "Failed to find %s: %s\n",
+                               fn_name, dlerror());
+               exit(-1);
        }
+
+       NWRAP_LOG(NWRAP_LOG_TRACE,
+                       "Loaded %s from %s",
+                       fn_name, nwrap_str_lib(lib));
+       return func;
 }
 
-static void nwrap_init(void)
+#define nwrap_load_lib_function(lib, fn_name) \
+       if (nwrap_main_global->libc->fns->_libc_##fn_name == NULL) { \
+               *(void **) (&nwrap_main_global->libc->fns->_libc_##fn_name) = \
+                       _nwrap_load_lib_function(lib, #fn_name); \
+       }
+
+/* INTERNAL HELPER FUNCTIONS */
+static void nwrap_lines_unload(struct nwrap_cache *const nwrap)
 {
-       static bool initialized;
+       size_t p;
+       void *item;
+       nwrap_vector_foreach(item, nwrap->lines, p) {
+               /* Maybe some vectors were merged ... */
+               SAFE_FREE(item);
+       }
+       SAFE_FREE(nwrap->lines.items);
+       ZERO_STRUCTP(&nwrap->lines);
+}
 
-       if (initialized) return;
-       initialized = true;
+/*
+ * IMPORTANT
+ *
+ * Functions expeciall from libc need to be loaded individually, you can't load
+ * all at once or gdb will segfault at startup. The same applies to valgrind and
+ * has probably something todo with with the linker.
+ * So we need load each function at the point it is called the first time.
+ */
+static struct passwd *libc_getpwnam(const char *name)
+{
+       nwrap_load_lib_function(NWRAP_LIBC, getpwnam);
 
-       nwrap_main_global = &__nwrap_main_global;
+       return nwrap_main_global->libc->fns->_libc_getpwnam(name);
+}
 
-       nwrap_backend_init(nwrap_main_global);
+#ifdef HAVE_GETPWNAM_R
+static int libc_getpwnam_r(const char *name,
+                          struct passwd *pwd,
+                          char *buf,
+                          size_t buflen,
+                          struct passwd **result)
+{
+#ifdef HAVE___POSIX_GETPWNAM_R
+       if (nwrap_main_global->libc->fns->_libc_getpwnam_r == NULL) {
+               *(void **) (&nwrap_main_global->libc->fns->_libc_getpwnam_r) =
+                       _nwrap_load_lib_function(NWRAP_LIBC, "__posix_getpwnam_r");
+       }
+#else
+       nwrap_load_lib_function(NWRAP_LIBC, getpwnam_r);
+#endif
 
-       nwrap_pw_global.cache = &__nwrap_cache_pw;
-
-       nwrap_pw_global.cache->path = getenv("NSS_WRAPPER_PASSWD");
-       nwrap_pw_global.cache->fd = -1;
-       nwrap_pw_global.cache->private_data = &nwrap_pw_global;
-       nwrap_pw_global.cache->parse_line = nwrap_pw_parse_line;
-       nwrap_pw_global.cache->unload = nwrap_pw_unload;
+       return nwrap_main_global->libc->fns->_libc_getpwnam_r(name,
+                                                             pwd,
+                                                             buf,
+                                                             buflen,
+                                                             result);
+}
+#endif
 
-       nwrap_gr_global.cache = &__nwrap_cache_gr;
+static struct passwd *libc_getpwuid(uid_t uid)
+{
+       nwrap_load_lib_function(NWRAP_LIBC, getpwuid);
 
-       nwrap_gr_global.cache->path = getenv("NSS_WRAPPER_GROUP");
-       nwrap_gr_global.cache->fd = -1;
-       nwrap_gr_global.cache->private_data = &nwrap_gr_global;
-       nwrap_gr_global.cache->parse_line = nwrap_gr_parse_line;
-       nwrap_gr_global.cache->unload = nwrap_gr_unload;
+       return nwrap_main_global->libc->fns->_libc_getpwuid(uid);
 }
 
-static bool nwrap_enabled(void)
+#ifdef HAVE_GETPWUID_R
+static int libc_getpwuid_r(uid_t uid,
+                          struct passwd *pwd,
+                          char *buf,
+                          size_t buflen,
+                          struct passwd **result)
 {
-       nwrap_init();
-
-       if (!nwrap_pw_global.cache->path) {
-               return false;
-       }
-       if (nwrap_pw_global.cache->path[0] == '\0') {
-               return false;
-       }
-       if (!nwrap_gr_global.cache->path) {
-               return false;
-       }
-       if (nwrap_gr_global.cache->path[0] == '\0') {
-               return false;
+#ifdef HAVE___POSIX_GETPWUID_R
+       if (nwrap_main_global->libc->fns->_libc_getpwuid_r == NULL) {
+               *(void **) (&nwrap_main_global->libc->fns->_libc_getpwuid_r) =
+                       _nwrap_load_lib_function(NWRAP_LIBC, "__posix_getpwuid_r");
        }
+#else
+       nwrap_load_lib_function(NWRAP_LIBC, getpwuid_r);
+#endif
 
-       return true;
+       return nwrap_main_global->libc->fns->_libc_getpwuid_r(uid,
+                                                             pwd,
+                                                             buf,
+                                                             buflen,
+                                                             result);
 }
+#endif
 
-static bool nwrap_parse_file(struct nwrap_cache *nwrap)
+static inline void str_tolower(char *dst, char *src)
 {
-       int ret;
-       uint8_t *buf = NULL;
-       char *nline;
+       register char *src_tmp = src;
+       register char *dst_tmp = dst;
 
-       if (nwrap->st.st_size == 0) {
-               NWRAP_DEBUG(("%s: size == 0\n",
-                            __location__));
-               goto done;
+       while (*src_tmp != '\0') {
+               *dst_tmp = tolower(*src_tmp);
+               ++src_tmp;
+               ++dst_tmp;
        }
+}
 
-       if (nwrap->st.st_size > INT32_MAX) {
-               NWRAP_ERROR(("%s: size[%u] larger than INT32_MAX\n",
-                            __location__, (unsigned)nwrap->st.st_size));
-               goto failed;
-       }
+static bool str_tolower_copy(char **dst_name, const char *const src_name)
+{
+       char *h_name_lower;
 
-       ret = lseek(nwrap->fd, 0, SEEK_SET);
-       if (ret != 0) {
-               NWRAP_ERROR(("%s: lseek - %d\n",__location__,ret));
-               goto failed;
+       if ((dst_name == NULL) || (src_name == NULL)) {
+               return false;
        }
 
-       buf = (uint8_t *)malloc(nwrap->st.st_size + 1);
-       if (!buf) {
-               NWRAP_ERROR(("%s: malloc failed\n",__location__));
-               goto failed;
+       h_name_lower = strdup(src_name);
+       if (h_name_lower == NULL) {
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "Out of memory while strdup");
+               return false;
        }
 
-       ret = read(nwrap->fd, buf, nwrap->st.st_size);
-       if (ret != nwrap->st.st_size) {
-               NWRAP_ERROR(("%s: read(%u) gave %d\n",
-                            __location__, (unsigned)nwrap->st.st_size, ret));
-               goto failed;
-       }
+       str_tolower(h_name_lower, h_name_lower);
+       *dst_name = h_name_lower;
+       return true;
+}
 
-       buf[nwrap->st.st_size] = '\0';
+static void libc_setpwent(void)
+{
+       nwrap_load_lib_function(NWRAP_LIBC, setpwent);
 
-       nline = (char *)buf;
-       while (nline && nline[0]) {
-               char *line;
-               char *e;
-               bool ok;
+       nwrap_main_global->libc->fns->_libc_setpwent();
+}
 
-               line = nline;
-               nline = NULL;
+static struct passwd *libc_getpwent(void)
+{
+       nwrap_load_lib_function(NWRAP_LIBC, getpwent);
 
-               e = strchr(line, '\n');
-               if (e) {
-                       e[0] = '\0';
-                       e++;
-                       if (e[0] == '\r') {
-                               e[0] = '\0';
-                               e++;
-                       }
-                       nline = e;
-               }
+       return nwrap_main_global->libc->fns->_libc_getpwent();
+}
 
-               NWRAP_VERBOSE(("%s:'%s'\n",__location__, line));
+#ifdef HAVE_SOLARIS_GETPWENT_R
+static struct passwd *libc_getpwent_r(struct passwd *pwdst,
+                                     char *buf,
+                                     int buflen)
+{
+       nwrap_load_lib_function(NWRAP_LIBC, getpwent_r);
 
-               if (strlen(line) == 0) {
-                       continue;
-               }
+       return nwrap_main_global->libc->fns->_libc_getpwent_r(pwdst,
+                                                             buf,
+                                                             buflen);
+}
+#else /* HAVE_SOLARIS_GETPWENT_R */
+static int libc_getpwent_r(struct passwd *pwdst,
+                          char *buf,
+                          size_t buflen,
+                          struct passwd **pwdstp)
+{
+       nwrap_load_lib_function(NWRAP_LIBC, getpwent_r);
 
-               ok = nwrap->parse_line(nwrap, line);
-               if (!ok) {
-                       goto failed;
-               }
-       }
+       return nwrap_main_global->libc->fns->_libc_getpwent_r(pwdst,
+                                                             buf,
+                                                             buflen,
+                                                             pwdstp);
+}
+#endif /* HAVE_SOLARIS_GETPWENT_R */
 
-done:
-       nwrap->buf = buf;
-       return true;
+static void libc_endpwent(void)
+{
+       nwrap_load_lib_function(NWRAP_LIBC, endpwent);
 
-failed:
-       if (buf) free(buf);
-       return false;
+       nwrap_main_global->libc->fns->_libc_endpwent();
 }
 
-static void nwrap_files_cache_unload(struct nwrap_cache *nwrap)
+static int libc_initgroups(const char *user, gid_t gid)
 {
-       nwrap->unload(nwrap);
+       nwrap_load_lib_function(NWRAP_LIBC, initgroups);
 
-       if (nwrap->buf) free(nwrap->buf);
-
-       nwrap->buf = NULL;
+       return nwrap_main_global->libc->fns->_libc_initgroups(user, gid);
 }
 
-static void nwrap_files_cache_reload(struct nwrap_cache *nwrap)
+static struct group *libc_getgrnam(const char *name)
 {
-       struct stat st;
-       int ret;
-       bool ok;
-       bool retried = false;
+       nwrap_load_lib_function(NWRAP_LIBC, getgrnam);
 
-reopen:
-       if (nwrap->fd < 0) {
-               nwrap->fd = open(nwrap->path, O_RDONLY);
-               if (nwrap->fd < 0) {
-                       NWRAP_ERROR(("%s: unable to open '%s' readonly %d:%s\n",
-                                    __location__,
-                                    nwrap->path, nwrap->fd,
-                                    strerror(errno)));
-                       return;
-               }
-               NWRAP_VERBOSE(("%s: open '%s'\n", __location__, nwrap->path));
-       }
+       return nwrap_main_global->libc->fns->_libc_getgrnam(name);
+}
 
-       ret = fstat(nwrap->fd, &st);
-       if (ret != 0) {
-               NWRAP_ERROR(("%s: fstat(%s) - %d:%s\n",
-                            __location__,
-                            nwrap->path,
-                            ret, strerror(errno)));
-               return;
+#ifdef HAVE_GETGRNAM_R
+static int libc_getgrnam_r(const char *name,
+                          struct group *grp,
+                          char *buf,
+                          size_t buflen,
+                          struct group **result)
+{
+#ifdef HAVE___POSIX_GETGRNAM_R
+       if (nwrap_main_global->libc->fns->_libc_getgrnam_r == NULL) {
+               *(void **) (&nwrap_main_global->libc->fns->_libc_getgrnam_r) =
+                       _nwrap_load_lib_function(NWRAP_LIBC, "__posix_getgrnam_r");
        }
+#else
+       nwrap_load_lib_function(NWRAP_LIBC, getgrnam_r);
+#endif
 
-       if (retried == false && st.st_nlink == 0) {
-               /* maybe someone has replaced the file... */
-               NWRAP_DEBUG(("%s: st_nlink == 0, reopen %s\n",
-                            __location__, nwrap->path));
-               retried = true;
-               memset(&nwrap->st, 0, sizeof(nwrap->st));
-               close(nwrap->fd);
-               nwrap->fd = -1;
-               goto reopen;
-       }
+       return nwrap_main_global->libc->fns->_libc_getgrnam_r(name,
+                                                             grp,
+                                                             buf,
+                                                             buflen,
+                                                             result);
+}
+#endif
 
-       if (st.st_mtime == nwrap->st.st_mtime) {
-               NWRAP_VERBOSE(("%s: st_mtime[%u] hasn't changed, skip reload\n",
-                              __location__, (unsigned)st.st_mtime));
-               return;
+static struct group *libc_getgrgid(gid_t gid)
+{
+       nwrap_load_lib_function(NWRAP_LIBC, getgrgid);
+
+       return nwrap_main_global->libc->fns->_libc_getgrgid(gid);
+}
+
+#ifdef HAVE_GETGRGID_R
+static int libc_getgrgid_r(gid_t gid,
+                          struct group *grp,
+                          char *buf,
+                          size_t buflen,
+                          struct group **result)
+{
+#ifdef HAVE___POSIX_GETGRGID_R
+       if (nwrap_main_global->libc->fns->_libc_getgrgid_r == NULL) {
+               *(void **) (&nwrap_main_global->libc->fns->_libc_getgrgid_r) =
+                       _nwrap_load_lib_function(NWRAP_LIBC, "__posix_getgrgid_r");
        }
-       NWRAP_DEBUG(("%s: st_mtime has changed [%u] => [%u], start reload\n",
-                    __location__, (unsigned)st.st_mtime,
-                    (unsigned)nwrap->st.st_mtime));
+#else
+       nwrap_load_lib_function(NWRAP_LIBC, getgrgid_r);
+#endif
 
-       nwrap->st = st;
+       return nwrap_main_global->libc->fns->_libc_getgrgid_r(gid,
+                                                             grp,
+                                                             buf,
+                                                             buflen,
+                                                             result);
+}
+#endif
 
-       nwrap_files_cache_unload(nwrap);
+static void libc_setgrent(void)
+{
+       nwrap_load_lib_function(NWRAP_LIBC, setgrent);
 
-       ok = nwrap_parse_file(nwrap);
-       if (!ok) {
-               NWRAP_ERROR(("%s: failed to reload %s\n",
-                            __location__, nwrap->path));
-               nwrap_files_cache_unload(nwrap);
-       }
-       NWRAP_DEBUG(("%s: reloaded %s\n",
-                    __location__, nwrap->path));
+       nwrap_main_global->libc->fns->_libc_setgrent();
 }
 
-/*
- * the caller has to call nwrap_unload() on failure
- */
-static bool nwrap_pw_parse_line(struct nwrap_cache *nwrap, char *line)
+static struct group *libc_getgrent(void)
 {
-       struct nwrap_pw *nwrap_pw;
-       char *c;
-       char *p;
-       char *e;
-       struct passwd *pw;
-       size_t list_size;
+       nwrap_load_lib_function(NWRAP_LIBC, getgrent);
 
-       nwrap_pw = (struct nwrap_pw *)nwrap->private_data;
+       return nwrap_main_global->libc->fns->_libc_getgrent();
+}
 
-       list_size = sizeof(*nwrap_pw->list) * (nwrap_pw->num+1);
-       pw = (struct passwd *)realloc(nwrap_pw->list, list_size);
-       if (!pw) {
-               NWRAP_ERROR(("%s:realloc(%u) failed\n",
-                            __location__, list_size));
-               return false;
-       }
-       nwrap_pw->list = pw;
+#ifdef HAVE_GETGRENT_R
+#ifdef HAVE_SOLARIS_GETGRENT_R
+static struct group *libc_getgrent_r(struct group *group,
+                                    char *buf,
+                                    size_t buflen)
+{
+       nwrap_load_lib_function(NWRAP_LIBC, getgrent_r);
 
-       pw = &nwrap_pw->list[nwrap_pw->num];
+       return nwrap_main_global->libc->fns->_libc_getgrent_r(group,
+                                                             buf,
+                                                             buflen);
+}
+#else /* !HAVE_SOLARIS_GETGRENT_R */
+static int libc_getgrent_r(struct group *group,
+                          char *buf,
+                          size_t buflen,
+                          struct group **result)
+{
+       nwrap_load_lib_function(NWRAP_LIBC, getgrent_r);
 
-       c = line;
+       return nwrap_main_global->libc->fns->_libc_getgrent_r(group,
+                                                             buf,
+                                                             buflen,
+                                                             result);
+}
+#endif /* HAVE_SOLARIS_GETGRENT_R */
+#endif /* HAVE_GETGRENT_R */
 
-       /* name */
-       p = strchr(c, ':');
-       if (!p) {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
-                            __location__, line, c));
-               return false;
-       }
-       *p = '\0';
-       p++;
-       pw->pw_name = c;
-       c = p;
+static void libc_endgrent(void)
+{
+       nwrap_load_lib_function(NWRAP_LIBC, endgrent);
 
-       NWRAP_VERBOSE(("name[%s]\n", pw->pw_name));
+       nwrap_main_global->libc->fns->_libc_endgrent();
+}
 
-       /* password */
-       p = strchr(c, ':');
-       if (!p) {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
-                            __location__, line, c));
-               return false;
-       }
-       *p = '\0';
-       p++;
-       pw->pw_passwd = c;
-       c = p;
+#ifdef HAVE_GETGROUPLIST
+static int libc_getgrouplist(const char *user,
+                            gid_t group,
+                            gid_t *groups,
+                            int *ngroups)
+{
+       nwrap_load_lib_function(NWRAP_LIBC, getgrouplist);
 
-       NWRAP_VERBOSE(("password[%s]\n", pw->pw_passwd));
+       return nwrap_main_global->libc->fns->_libc_getgrouplist(user,
+                                                               group,
+                                                               groups,
+                                                               ngroups);
+}
+#endif
 
-       /* uid */
-       p = strchr(c, ':');
-       if (!p) {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
-                            __location__, line, c));
-               return false;
-       }
-       *p = '\0';
-       p++;
-       e = NULL;
-       pw->pw_uid = (uid_t)strtoul(c, &e, 10);
-       if (c == e) {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
-                            __location__, line, c, strerror(errno)));
-               return false;
-       }
-       if (e == NULL) {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
-                            __location__, line, c, strerror(errno)));
-               return false;
-       }
-       if (e[0] != '\0') {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
-                            __location__, line, c, strerror(errno)));
-               return false;
-       }
-       c = p;
+static void libc_sethostent(int stayopen)
+{
+       nwrap_load_lib_function(NWRAP_LIBNSL, sethostent);
 
-       NWRAP_VERBOSE(("uid[%u]\n", pw->pw_uid));
+       nwrap_main_global->libc->fns->_libc_sethostent(stayopen);
+}
 
-       /* gid */
-       p = strchr(c, ':');
-       if (!p) {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
-                            __location__, line, c));
-               return false;
-       }
-       *p = '\0';
-       p++;
-       e = NULL;
-       pw->pw_gid = (gid_t)strtoul(c, &e, 10);
-       if (c == e) {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
-                            __location__, line, c, strerror(errno)));
-               return false;
-       }
-       if (e == NULL) {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
-                            __location__, line, c, strerror(errno)));
-               return false;
-       }
-       if (e[0] != '\0') {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
-                            __location__, line, c, strerror(errno)));
-               return false;
-       }
-       c = p;
+static struct hostent *libc_gethostent(void)
+{
+       nwrap_load_lib_function(NWRAP_LIBNSL, gethostent);
 
-       NWRAP_VERBOSE(("gid[%u]\n", pw->pw_gid));
+       return nwrap_main_global->libc->fns->_libc_gethostent();
+}
 
-       /* gecos */
-       p = strchr(c, ':');
-       if (!p) {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
-                            __location__, line, c));
-               return false;
-       }
-       *p = '\0';
-       p++;
-       pw->pw_gecos = c;
-       c = p;
+static void libc_endhostent(void)
+{
+       nwrap_load_lib_function(NWRAP_LIBNSL, endhostent);
 
-       NWRAP_VERBOSE(("gecos[%s]\n", pw->pw_gecos));
+       nwrap_main_global->libc->fns->_libc_endhostent();
+}
 
-       /* dir */
-       p = strchr(c, ':');
-       if (!p) {
-               NWRAP_ERROR(("%s:'%s'\n",__location__,c));
-               return false;
-       }
-       *p = '\0';
-       p++;
-       pw->pw_dir = c;
-       c = p;
+static struct hostent *libc_gethostbyname(const char *name)
+{
+       nwrap_load_lib_function(NWRAP_LIBNSL, gethostbyname);
 
-       NWRAP_VERBOSE(("dir[%s]\n", pw->pw_dir));
+       return nwrap_main_global->libc->fns->_libc_gethostbyname(name);
+}
 
-       /* shell */
-       pw->pw_shell = c;
-       NWRAP_VERBOSE(("shell[%s]\n", pw->pw_shell));
+#ifdef HAVE_GETHOSTBYNAME2 /* GNU extension */
+static struct hostent *libc_gethostbyname2(const char *name, int af)
+{
+       nwrap_load_lib_function(NWRAP_LIBNSL, gethostbyname2);
 
-       NWRAP_DEBUG(("add user[%s:%s:%u:%u:%s:%s:%s]\n",
-                    pw->pw_name, pw->pw_passwd,
-                    pw->pw_uid, pw->pw_gid,
-                    pw->pw_gecos, pw->pw_dir, pw->pw_shell));
+       return nwrap_main_global->libc->fns->_libc_gethostbyname2(name, af);
+}
+#endif
 
-       nwrap_pw->num++;
-       return true;
+static struct hostent *libc_gethostbyaddr(const void *addr,
+                                         socklen_t len,
+                                         int type)
+{
+       nwrap_load_lib_function(NWRAP_LIBNSL, gethostbyaddr);
+
+       return nwrap_main_global->libc->fns->_libc_gethostbyaddr(addr,
+                                                                len,
+                                                                type);
 }
 
-static void nwrap_pw_unload(struct nwrap_cache *nwrap)
+static int libc_gethostname(char *name, size_t len)
 {
-       struct nwrap_pw *nwrap_pw;
-       nwrap_pw = (struct nwrap_pw *)nwrap->private_data;
+       nwrap_load_lib_function(NWRAP_LIBNSL, gethostname);
 
-       if (nwrap_pw->list) free(nwrap_pw->list);
+       return nwrap_main_global->libc->fns->_libc_gethostname(name, len);
+}
 
-       nwrap_pw->list = NULL;
-       nwrap_pw->num = 0;
-       nwrap_pw->idx = 0;
+#ifdef HAVE_GETHOSTBYNAME_R
+static int libc_gethostbyname_r(const char *name,
+                               struct hostent *ret,
+                               char *buf,
+                               size_t buflen,
+                               struct hostent **result,
+                               int *h_errnop)
+{
+       nwrap_load_lib_function(NWRAP_LIBNSL, gethostbyname_r);
+
+       return nwrap_main_global->libc->fns->_libc_gethostbyname_r(name,
+                                                                  ret,
+                                                                  buf,
+                                                                  buflen,
+                                                                  result,
+                                                                  h_errnop);
 }
+#endif
 
-static int nwrap_pw_copy_r(const struct passwd *src, struct passwd *dst,
-                          char *buf, size_t buflen, struct passwd **dstp)
+#ifdef HAVE_GETHOSTBYADDR_R
+static int libc_gethostbyaddr_r(const void *addr,
+                               socklen_t len,
+                               int type,
+                               struct hostent *ret,
+                               char *buf,
+                               size_t buflen,
+                               struct hostent **result,
+                               int *h_errnop)
 {
-       char *first;
-       char *last;
-       off_t ofs;
+       nwrap_load_lib_function(NWRAP_LIBNSL, gethostbyaddr_r);
+
+       return nwrap_main_global->libc->fns->_libc_gethostbyaddr_r(addr,
+                                                                  len,
+                                                                  type,
+                                                                  ret,
+                                                                  buf,
+                                                                  buflen,
+                                                                  result,
+                                                                  h_errnop);
+}
+#endif
 
-       first = src->pw_name;
+static int libc_getaddrinfo(const char *node,
+                           const char *service,
+                           const struct addrinfo *hints,
+                           struct addrinfo **res)
+{
+       nwrap_load_lib_function(NWRAP_LIBSOCKET, getaddrinfo);
 
-       last = src->pw_shell;
-       while (*last) last++;
+       return nwrap_main_global->libc->fns->_libc_getaddrinfo(node,
+                                                              service,
+                                                              hints,
+                                                              res);
+}
 
-       ofs = PTR_DIFF(last + 1, first);
+static int libc_getnameinfo(const struct sockaddr *sa,
+                           socklen_t salen,
+                           char *host,
+                           size_t hostlen,
+                           char *serv,
+                           size_t servlen,
+                           int flags)
+{
+       nwrap_load_lib_function(NWRAP_LIBSOCKET, getnameinfo);
+
+       return nwrap_main_global->libc->fns->_libc_getnameinfo(sa,
+                                                              salen,
+                                                              host,
+                                                              hostlen,
+                                                              serv,
+                                                              servlen,
+                                                              flags);
+}
 
-       if (ofs > buflen) {
-               return ERANGE;
-       }
+/*********************************************************
+ * NWRAP NSS MODULE LOADER FUNCTIONS
+ *********************************************************/
 
-       memcpy(buf, first, ofs);
+static void *nwrap_load_module_fn(struct nwrap_backend *b,
+                                 const char *fn_name)
+{
+       void *res;
+       char *s;
 
-       ofs = PTR_DIFF(src->pw_name, first);
-       dst->pw_name = buf + ofs;
-       ofs = PTR_DIFF(src->pw_passwd, first);
-       dst->pw_passwd = buf + ofs;
-       dst->pw_uid = src->pw_uid;
-       dst->pw_gid = src->pw_gid;
-       ofs = PTR_DIFF(src->pw_gecos, first);
-       dst->pw_gecos = buf + ofs;
-       ofs = PTR_DIFF(src->pw_dir, first);
-       dst->pw_dir = buf + ofs;
-       ofs = PTR_DIFF(src->pw_shell, first);
-       dst->pw_shell = buf + ofs;
+       if (!b->so_handle) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "No handle");
+               return NULL;
+       }
 
-       if (dstp) {
-               *dstp = dst;
+       if (asprintf(&s, "_nss_%s_%s", b->name, fn_name) == -1) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory");
+               return NULL;
        }
 
-       return 0;
+       res = dlsym(b->so_handle, s);
+       if (!res) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Cannot find function %s in %s",
+                         s, b->so_path);
+       }
+       SAFE_FREE(s);
+       return res;
 }
 
-/*
- * the caller has to call nwrap_unload() on failure
- */
-static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line)
+static struct nwrap_module_nss_fns *nwrap_load_module_fns(struct nwrap_backend *b)
 {
-       struct nwrap_gr *nwrap_gr;
-       char *c;
-       char *p;
-       char *e;
-       struct group *gr;
-       size_t list_size;
-       unsigned nummem;
+       struct nwrap_module_nss_fns *fns;
 
-       nwrap_gr = (struct nwrap_gr *)nwrap->private_data;
+       if (!b->so_handle) {
+               return NULL;
+       }
 
-       list_size = sizeof(*nwrap_gr->list) * (nwrap_gr->num+1);
-       gr = (struct group *)realloc(nwrap_gr->list, list_size);
-       if (!gr) {
-               NWRAP_ERROR(("%s:realloc failed\n",__location__));
-               return false;
+       fns = (struct nwrap_module_nss_fns *)malloc(sizeof(struct nwrap_module_nss_fns));
+       if (!fns) {
+               return NULL;
        }
-       nwrap_gr->list = gr;
 
-       gr = &nwrap_gr->list[nwrap_gr->num];
+       *(void **)(&fns->_nss_getpwnam_r) =
+               nwrap_load_module_fn(b, "getpwnam_r");
+       *(void **)(&fns->_nss_getpwuid_r) =
+               nwrap_load_module_fn(b, "getpwuid_r");
+       *(void **)(&fns->_nss_setpwent) =
+               nwrap_load_module_fn(b, "setpwent");
+       *(void **)(&fns->_nss_getpwent_r) =
+               nwrap_load_module_fn(b, "getpwent_r");
+       *(void **)(&fns->_nss_endpwent) =
+               nwrap_load_module_fn(b, "endpwent");
+       *(void **)(&fns->_nss_initgroups) =
+               nwrap_load_module_fn(b, "initgroups_dyn");
+       *(void **)(&fns->_nss_getgrnam_r) =
+               nwrap_load_module_fn(b, "getgrnam_r");
+       *(void **)(&fns->_nss_getgrgid_r)=
+               nwrap_load_module_fn(b, "getgrgid_r");
+       *(void **)(&fns->_nss_setgrent) =
+               nwrap_load_module_fn(b, "setgrent");
+       *(void **)(&fns->_nss_getgrent_r) =
+               nwrap_load_module_fn(b, "getgrent_r");
+       *(void **)(&fns->_nss_endgrent) =
+               nwrap_load_module_fn(b, "endgrent");
 
-       c = line;
+       return fns;
+}
 
-       /* name */
-       p = strchr(c, ':');
-       if (!p) {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
-                            __location__, line, c));
-               return false;
+static void *nwrap_load_module(const char *so_path)
+{
+       void *h;
+
+       if (!so_path || !strlen(so_path)) {
+               return NULL;
        }
-       *p = '\0';
-       p++;
-       gr->gr_name = c;
-       c = p;
 
-       NWRAP_VERBOSE(("name[%s]\n", gr->gr_name));
+       h = dlopen(so_path, RTLD_LAZY);
+       if (!h) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Cannot open shared library %s",
+                         so_path);
+               return NULL;
+       }
 
-       /* password */
-       p = strchr(c, ':');
-       if (!p) {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
-                            __location__, line, c));
+       return h;
+}
+
+static bool nwrap_module_init(const char *name,
+                             struct nwrap_ops *ops,
+                             const char *so_path,
+                             int *num_backends,
+                             struct nwrap_backend **backends)
+{
+       struct nwrap_backend *b;
+
+       *backends = (struct nwrap_backend *)realloc(*backends,
+               sizeof(struct nwrap_backend) * ((*num_backends) + 1));
+       if (!*backends) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory");
                return false;
        }
-       *p = '\0';
-       p++;
-       gr->gr_passwd = c;
-       c = p;
 
-       NWRAP_VERBOSE(("password[%s]\n", gr->gr_passwd));
+       b = &((*backends)[*num_backends]);
 
-       /* gid */
-       p = strchr(c, ':');
-       if (!p) {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
-                            __location__, line, c));
+       b->name = name;
+       b->ops = ops;
+       b->so_path = so_path;
+
+       if (so_path != NULL) {
+               b->so_handle = nwrap_load_module(so_path);
+               b->fns = nwrap_load_module_fns(b);
+               if (b->fns == NULL) {
+                       return false;
+               }
+       } else {
+               b->so_handle = NULL;
+               b->fns = NULL;
+       }
+
+       (*num_backends)++;
+
+       return true;
+}
+
+static void nwrap_libc_init(struct nwrap_main *r)
+{
+       r->libc = malloc(sizeof(struct nwrap_libc));
+       if (r->libc == NULL) {
+               printf("Failed to allocate memory for libc");
+               exit(-1);
+       }
+       ZERO_STRUCTP(r->libc);
+
+       r->libc->fns = malloc(sizeof(struct nwrap_libc_fns));
+       if (r->libc->fns == NULL) {
+               printf("Failed to allocate memory for libc functions");
+               exit(-1);
+       }
+       ZERO_STRUCTP(r->libc->fns);
+}
+
+static void nwrap_backend_init(struct nwrap_main *r)
+{
+       const char *module_so_path = getenv("NSS_WRAPPER_MODULE_SO_PATH");
+       const char *module_fn_name = getenv("NSS_WRAPPER_MODULE_FN_PREFIX");
+
+       r->num_backends = 0;
+       r->backends = NULL;
+
+       if (!nwrap_module_init("files", &nwrap_files_ops, NULL,
+                              &r->num_backends,
+                              &r->backends)) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Failed to initialize 'files' backend");
+               return;
+       }
+
+       if (module_so_path != NULL &&
+           module_so_path[0] != '\0' &&
+           module_fn_name != NULL &&
+           module_fn_name[0] != '\0') {
+               if (!nwrap_module_init(module_fn_name,
+                                      &nwrap_module_ops,
+                                      module_so_path,
+                                      &r->num_backends,
+                                      &r->backends)) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "Failed to initialize '%s' backend",
+                                 module_fn_name);
+                       return;
+               }
+       }
+}
+
+static void nwrap_init(void)
+{
+       const char *env;
+       char *endptr;
+       size_t max_hostents_tmp;
+
+       NWRAP_LOCK(nwrap_initialized);
+       if (nwrap_initialized) {
+               NWRAP_UNLOCK(nwrap_initialized);
+               return;
+       }
+
+       /*
+        * Still holding nwrap_initialized lock here.
+        * We don't use NWRAP_(UN)LOCK_ALL macros here because we
+        * want to avoid overhead when other threads do their job.
+        */
+       NWRAP_LOCK(nwrap_global);
+       NWRAP_LOCK(nwrap_gr_global);
+       NWRAP_LOCK(nwrap_he_global);
+       NWRAP_LOCK(nwrap_pw_global);
+       NWRAP_LOCK(nwrap_sp_global);
+
+       nwrap_initialized = true;
+
+       /* Initialize pthread_atfork handlers */
+       pthread_atfork(&nwrap_thread_prepare, &nwrap_thread_parent,
+                      &nwrap_thread_child);
+
+       env = getenv("NSS_WRAPPER_MAX_HOSTENTS");
+       if (env != NULL) {
+               max_hostents_tmp = (size_t)strtol(env, &endptr, 10);
+               if (((env != '\0') && (endptr == '\0')) ||
+                   (max_hostents_tmp == 0)) {
+                       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                                 "Error parsing NSS_WRAPPER_MAX_HOSTENTS "
+                                 "value or value is too small. "
+                                 "Using default value: %lu.",
+                                 (unsigned long)max_hostents);
+               } else {
+                       max_hostents = max_hostents_tmp;
+               }
+       }
+       /* Initialize hash table */
+       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                 "Initializing hash table of size %lu items.",
+                 (unsigned long)max_hostents);
+       if (hcreate(max_hostents) == 0) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Failed to initialize hash table");
+               goto done;
+       }
+
+       nwrap_main_global = &__nwrap_main_global;
+
+       nwrap_libc_init(nwrap_main_global);
+
+       nwrap_backend_init(nwrap_main_global);
+
+       /* passwd */
+       nwrap_pw_global.cache = &__nwrap_cache_pw;
+
+       nwrap_pw_global.cache->path = getenv("NSS_WRAPPER_PASSWD");
+       nwrap_pw_global.cache->fp = NULL;
+       nwrap_pw_global.cache->fd = -1;
+       nwrap_pw_global.cache->private_data = &nwrap_pw_global;
+       nwrap_pw_global.cache->parse_line = nwrap_pw_parse_line;
+       nwrap_pw_global.cache->unload = nwrap_pw_unload;
+
+       /* shadow */
+#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM)
+       nwrap_sp_global.cache = &__nwrap_cache_sp;
+
+       nwrap_sp_global.cache->path = getenv("NSS_WRAPPER_SHADOW");
+       nwrap_sp_global.cache->fp = NULL;
+       nwrap_sp_global.cache->fd = -1;
+       nwrap_sp_global.cache->private_data = &nwrap_sp_global;
+       nwrap_sp_global.cache->parse_line = nwrap_sp_parse_line;
+       nwrap_sp_global.cache->unload = nwrap_sp_unload;
+#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */
+
+       /* group */
+       nwrap_gr_global.cache = &__nwrap_cache_gr;
+
+       nwrap_gr_global.cache->path = getenv("NSS_WRAPPER_GROUP");
+       nwrap_gr_global.cache->fp = NULL;
+       nwrap_gr_global.cache->fd = -1;
+       nwrap_gr_global.cache->private_data = &nwrap_gr_global;
+       nwrap_gr_global.cache->parse_line = nwrap_gr_parse_line;
+       nwrap_gr_global.cache->unload = nwrap_gr_unload;
+
+       /* hosts */
+       nwrap_he_global.cache = &__nwrap_cache_he;
+
+       nwrap_he_global.cache->path = getenv("NSS_WRAPPER_HOSTS");
+       nwrap_he_global.cache->fp = NULL;
+       nwrap_he_global.cache->fd = -1;
+       nwrap_he_global.cache->private_data = &nwrap_he_global;
+       nwrap_he_global.cache->parse_line = nwrap_he_parse_line;
+       nwrap_he_global.cache->unload = nwrap_he_unload;
+
+done:
+       /* We hold all locks here so we can use NWRAP_UNLOCK_ALL. */
+       NWRAP_UNLOCK_ALL;
+}
+
+bool nss_wrapper_enabled(void)
+{
+       nwrap_init();
+
+       if (nwrap_pw_global.cache->path == NULL ||
+           nwrap_pw_global.cache->path[0] == '\0') {
                return false;
        }
-       *p = '\0';
-       p++;
-       e = NULL;
-       gr->gr_gid = (gid_t)strtoul(c, &e, 10);
-       if (c == e) {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
-                            __location__, line, c, strerror(errno)));
+       if (nwrap_gr_global.cache->path == NULL ||
+           nwrap_gr_global.cache->path[0] == '\0') {
                return false;
        }
-       if (e == NULL) {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
-                            __location__, line, c, strerror(errno)));
+
+       return true;
+}
+
+#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM)
+bool nss_wrapper_shadow_enabled(void)
+{
+       nwrap_init();
+
+       if (nwrap_sp_global.cache->path == NULL ||
+           nwrap_sp_global.cache->path[0] == '\0') {
                return false;
        }
-       if (e[0] != '\0') {
-               NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
-                            __location__, line, c, strerror(errno)));
+
+       return true;
+}
+#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */
+
+bool nss_wrapper_hosts_enabled(void)
+{
+       nwrap_init();
+
+       if (nwrap_he_global.cache->path == NULL ||
+           nwrap_he_global.cache->path[0] == '\0') {
                return false;
        }
-       c = p;
 
-       NWRAP_VERBOSE(("gid[%u]\n", gr->gr_gid));
+       return true;
+}
 
-       /* members */
-       gr->gr_mem = (char **)malloc(sizeof(char *));
-       if (!gr->gr_mem) {
-               NWRAP_ERROR(("%s:calloc failed\n",__location__));
+static bool nwrap_hostname_enabled(void)
+{
+       nwrap_init();
+
+       if (getenv("NSS_WRAPPER_HOSTNAME") == NULL) {
                return false;
        }
-       gr->gr_mem[0] = NULL;
 
-       for(nummem=0; p; nummem++) {
-               char **m;
-               size_t m_size;
-               c = p;
-               p = strchr(c, ',');
-               if (p) {
-                       *p = '\0';
-                       p++;
+       return true;
+}
+
+static bool nwrap_parse_file(struct nwrap_cache *nwrap)
+{
+       char *line = NULL;
+       ssize_t n;
+       /* Unused but getline needs it */
+       size_t len;
+       bool ok;
+
+       if (nwrap->st.st_size == 0) {
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "size == 0");
+               return true;
+       }
+
+       /* Support for 32-bit system I guess */
+       if (nwrap->st.st_size > INT32_MAX) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Size[%u] larger than INT32_MAX",
+                         (unsigned)nwrap->st.st_size);
+               return false;
+       }
+
+       rewind(nwrap->fp);
+
+       do {
+               n = getline(&line, &len, nwrap->fp);
+               if (n < 0) {
+                       SAFE_FREE(line);
+                       if (feof(nwrap->fp)) {
+                               break;
+                       }
+
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "Unable to read line from file: %s",
+                                 nwrap->path);
+                       return false;
                }
 
-               if (strlen(c) == 0) {
-                       break;
+               if (line[n - 1] == '\n') {
+                       line[n - 1] = '\0';
+               }
+
+               if (line[0] == '\0') {
+                       SAFE_FREE(line);
+                       continue;
+               }
+
+               ok = nwrap->parse_line(nwrap, line);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "Unable to parse line file: %s",
+                                 line);
+                       SAFE_FREE(line);
+                       return false;
+               }
+
+               /* Line is parsed without issues so add it to list */
+               ok = nwrap_vector_add_item(&(nwrap->lines), (void *const) line);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "Unable to add line to vector");
+                       return false;
+               }
+
+               /* This forces getline to allocate new memory for line. */
+               line = NULL;
+       } while (!feof(nwrap->fp));
+
+       return true;
+}
+
+static void nwrap_files_cache_unload(struct nwrap_cache *nwrap)
+{
+       nwrap->unload(nwrap);
+
+       nwrap_lines_unload(nwrap);
+}
+
+static bool nwrap_files_cache_reload(struct nwrap_cache *nwrap)
+{
+       struct stat st;
+       int ret;
+       bool ok;
+       bool retried = false;
+
+       assert(nwrap != NULL);
+
+reopen:
+       if (nwrap->fd < 0) {
+               nwrap->fp = fopen(nwrap->path, "re");
+               if (nwrap->fp == NULL) {
+                       nwrap->fd = -1;
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "Unable to open '%s' readonly %d:%s",
+                                 nwrap->path, nwrap->fd,
+                                 strerror(errno));
+                       return false;
+
+               }
+               nwrap->fd = fileno(nwrap->fp);
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "Open '%s'", nwrap->path);
+       }
+
+       ret = fstat(nwrap->fd, &st);
+       if (ret != 0) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "fstat(%s) - %d:%s",
+                         nwrap->path,
+                         ret,
+                         strerror(errno));
+               fclose(nwrap->fp);
+               nwrap->fp = NULL;
+               nwrap->fd = -1;
+               return false;
+       }
+
+       if (retried == false && st.st_nlink == 0) {
+               /* maybe someone has replaced the file... */
+               NWRAP_LOG(NWRAP_LOG_TRACE,
+                         "st_nlink == 0, reopen %s",
+                         nwrap->path);
+               retried = true;
+               memset(&nwrap->st, 0, sizeof(nwrap->st));
+               fclose(nwrap->fp);
+               nwrap->fp = NULL;
+               nwrap->fd = -1;
+               goto reopen;
+       }
+
+       if (st.st_mtime == nwrap->st.st_mtime) {
+               NWRAP_LOG(NWRAP_LOG_TRACE,
+                         "st_mtime[%u] hasn't changed, skip reload",
+                         (unsigned)st.st_mtime);
+               return true;
+       }
+
+       NWRAP_LOG(NWRAP_LOG_TRACE,
+                 "st_mtime has changed [%u] => [%u], start reload",
+                 (unsigned)st.st_mtime,
+                 (unsigned)nwrap->st.st_mtime);
+
+       nwrap->st = st;
+
+       nwrap_files_cache_unload(nwrap);
+
+       ok = nwrap_parse_file(nwrap);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Failed to reload %s", nwrap->path);
+               nwrap_files_cache_unload(nwrap);
+               return false;
+       }
+
+       NWRAP_LOG(NWRAP_LOG_TRACE, "Reloaded %s", nwrap->path);
+       return true;
+}
+
+/*
+ * the caller has to call nwrap_unload() on failure
+ */
+static bool nwrap_pw_parse_line(struct nwrap_cache *nwrap, char *line)
+{
+       struct nwrap_pw *nwrap_pw;
+       char *c;
+       char *p;
+       char *e;
+       struct passwd *pw;
+       size_t list_size;
+
+       nwrap_pw = (struct nwrap_pw *)nwrap->private_data;
+
+       list_size = sizeof(*nwrap_pw->list) * (nwrap_pw->num+1);
+       pw = (struct passwd *)realloc(nwrap_pw->list, list_size);
+       if (!pw) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "realloc(%u) failed",
+                         (unsigned)list_size);
+               return false;
+       }
+       nwrap_pw->list = pw;
+
+       pw = &nwrap_pw->list[nwrap_pw->num];
+
+       c = line;
+
+       /* name */
+       p = strchr(c, ':');
+       if (!p) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Invalid line[%s]: '%s'",
+                         line,
+                         c);
+               return false;
+       }
+       *p = '\0';
+       p++;
+       pw->pw_name = c;
+       c = p;
+
+       NWRAP_LOG(NWRAP_LOG_TRACE, "name[%s]\n", pw->pw_name);
+
+       /* password */
+       p = strchr(c, ':');
+       if (!p) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c);
+               return false;
+       }
+       *p = '\0';
+       p++;
+       pw->pw_passwd = c;
+       c = p;
+
+       NWRAP_LOG(NWRAP_LOG_TRACE, "password[%s]\n", pw->pw_passwd);
+
+       /* uid */
+       p = strchr(c, ':');
+       if (!p) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c);
+               return false;
+       }
+       *p = '\0';
+       p++;
+       e = NULL;
+       pw->pw_uid = (uid_t)strtoul(c, &e, 10);
+       if (c == e) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Invalid line[%s]: '%s' - %s",
+                         line, c, strerror(errno));
+               return false;
+       }
+       if (e == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Invalid line[%s]: '%s' - %s",
+                         line, c, strerror(errno));
+               return false;
+       }
+       if (e[0] != '\0') {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Invalid line[%s]: '%s' - %s",
+                         line, c, strerror(errno));
+               return false;
+       }
+       c = p;
+
+       NWRAP_LOG(NWRAP_LOG_TRACE, "uid[%u]", pw->pw_uid);
+
+       /* gid */
+       p = strchr(c, ':');
+       if (!p) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c);
+               return false;
+       }
+       *p = '\0';
+       p++;
+       e = NULL;
+       pw->pw_gid = (gid_t)strtoul(c, &e, 10);
+       if (c == e) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Invalid line[%s]: '%s' - %s",
+                         line, c, strerror(errno));
+               return false;
+       }
+       if (e == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Invalid line[%s]: '%s' - %s",
+                         line, c, strerror(errno));
+               return false;
+       }
+       if (e[0] != '\0') {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Invalid line[%s]: '%s' - %s",
+                         line, c, strerror(errno));
+               return false;
+       }
+       c = p;
+
+       NWRAP_LOG(NWRAP_LOG_TRACE, "gid[%u]\n", pw->pw_gid);
+
+       /* gecos */
+       p = strchr(c, ':');
+       if (!p) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "invalid line[%s]: '%s'", line, c);
+               return false;
+       }
+       *p = '\0';
+       p++;
+       pw->pw_gecos = c;
+       c = p;
+
+       NWRAP_LOG(NWRAP_LOG_TRACE, "gecos[%s]", pw->pw_gecos);
+
+       /* dir */
+       p = strchr(c, ':');
+       if (!p) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "'%s'", c);
+               return false;
+       }
+       *p = '\0';
+       p++;
+       pw->pw_dir = c;
+       c = p;
+
+       NWRAP_LOG(NWRAP_LOG_TRACE, "dir[%s]", pw->pw_dir);
+
+       /* shell */
+       pw->pw_shell = c;
+       NWRAP_LOG(NWRAP_LOG_TRACE, "shell[%s]", pw->pw_shell);
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                 "Added user[%s:%s:%u:%u:%s:%s:%s]",
+                 pw->pw_name, pw->pw_passwd,
+                 pw->pw_uid, pw->pw_gid,
+                 pw->pw_gecos, pw->pw_dir, pw->pw_shell);
+
+       nwrap_pw->num++;
+       return true;
+}
+
+static void nwrap_pw_unload(struct nwrap_cache *nwrap)
+{
+       struct nwrap_pw *nwrap_pw;
+       nwrap_pw = (struct nwrap_pw *)nwrap->private_data;
+
+       SAFE_FREE(nwrap_pw->list);
+       nwrap_pw->num = 0;
+       nwrap_pw->idx = 0;
+}
+
+static int nwrap_pw_copy_r(const struct passwd *src, struct passwd *dst,
+                          char *buf, size_t buflen, struct passwd **dstp)
+{
+       char *first;
+       char *last;
+       off_t ofs;
+
+       first = src->pw_name;
+
+       last = src->pw_shell;
+       while (*last) last++;
+
+       ofs = PTR_DIFF(last + 1, first);
+
+       if (ofs > (off_t) buflen) {
+               return ERANGE;
+       }
+
+       memcpy(buf, first, ofs);
+
+       ofs = PTR_DIFF(src->pw_name, first);
+       dst->pw_name = buf + ofs;
+       ofs = PTR_DIFF(src->pw_passwd, first);
+       dst->pw_passwd = buf + ofs;
+       dst->pw_uid = src->pw_uid;
+       dst->pw_gid = src->pw_gid;
+       ofs = PTR_DIFF(src->pw_gecos, first);
+       dst->pw_gecos = buf + ofs;
+       ofs = PTR_DIFF(src->pw_dir, first);
+       dst->pw_dir = buf + ofs;
+       ofs = PTR_DIFF(src->pw_shell, first);
+       dst->pw_shell = buf + ofs;
+
+       if (dstp) {
+               *dstp = dst;
+       }
+
+       return 0;
+}
+
+#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM)
+static bool nwrap_sp_parse_line(struct nwrap_cache *nwrap, char *line)
+{
+       struct nwrap_sp *nwrap_sp;
+       struct spwd *sp;
+       size_t list_size;
+       char *c;
+       char *e;
+       char *p;
+
+       nwrap_sp = (struct nwrap_sp *)nwrap->private_data;
+
+       list_size = sizeof(*nwrap_sp->list) * (nwrap_sp->num+1);
+       sp = (struct spwd *)realloc(nwrap_sp->list, list_size);
+       if (sp == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "realloc(%u) failed",
+                         (unsigned)list_size);
+               return false;
+       }
+       nwrap_sp->list = sp;
+
+       sp = &nwrap_sp->list[nwrap_sp->num];
+
+       c = line;
+
+       /* name */
+       p = strchr(c, ':');
+       if (p == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "name -- Invalid line[%s]: '%s'",
+                         line,
+                         c);
+               return false;
+       }
+       *p = '\0';
+       p++;
+       sp->sp_namp = c;
+       c = p;
+
+       NWRAP_LOG(NWRAP_LOG_TRACE, "name[%s]\n", sp->sp_namp);
+
+       /* pwd */
+       p = strchr(c, ':');
+       if (p == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "pwd -- Invalid line[%s]: '%s'",
+                         line,
+                         c);
+               return false;
+       }
+       *p = '\0';
+       p++;
+       sp->sp_pwdp = c;
+       c = p;
+
+       /* lstchg (long) */
+       if (c[0] == ':') {
+               sp->sp_lstchg = -1;
+               p++;
+       } else {
+               p = strchr(c, ':');
+               if (p == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "lstchg -- Invalid line[%s]: '%s'",
+                                 line,
+                                 c);
+                       return false;
+               }
+               *p = '\0';
+               p++;
+               sp->sp_lstchg = strtol(c, &e, 10);
+               if (c == e) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "lstchg -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "lstchg -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e[0] != '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "lstchg -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+       }
+       c = p;
+
+       /* min (long) */
+       if (c[0] == ':') {
+               sp->sp_min = -1;
+               p++;
+       } else {
+               p = strchr(c, ':');
+               if (p == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "min -- Invalid line[%s]: '%s'",
+                                 line,
+                                 c);
+                       return false;
+               }
+               *p = '\0';
+               p++;
+               sp->sp_min = strtol(c, &e, 10);
+               if (c == e) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "min -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "min -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e[0] != '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "min -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+       }
+       c = p;
+
+       /* max (long) */
+       if (c[0] == ':') {
+               sp->sp_max = -1;
+               p++;
+       } else {
+               p = strchr(c, ':');
+               if (p == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "max -- Invalid line[%s]: '%s'",
+                                 line,
+                                 c);
+                       return false;
+               }
+               *p = '\0';
+               p++;
+               sp->sp_max = strtol(c, &e, 10);
+               if (c == e) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "max -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "max -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e[0] != '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "max -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+       }
+       c = p;
+
+       /* warn (long) */
+       if (c[0] == ':') {
+               sp->sp_warn = -1;
+               p++;
+       } else {
+               p = strchr(c, ':');
+               if (p == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "warn -- Invalid line[%s]: '%s'",
+                                 line,
+                                 c);
+                       return false;
+               }
+               *p = '\0';
+               p++;
+               sp->sp_warn = strtol(c, &e, 10);
+               if (c == e) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "warn -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "warn -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e[0] != '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "warn -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+       }
+       c = p;
+
+       /* inact (long) */
+       if (c[0] == ':') {
+               sp->sp_inact = -1;
+               p++;
+       } else {
+               p = strchr(c, ':');
+               if (p == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "inact -- Invalid line[%s]: '%s'",
+                                 line,
+                                 c);
+                       return false;
+               }
+               *p = '\0';
+               p++;
+               sp->sp_inact = strtol(c, &e, 10);
+               if (c == e) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "inact -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "inact -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e[0] != '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "inact -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+       }
+       c = p;
+
+       /* expire (long) */
+       if (c[0] == ':') {
+               sp->sp_expire = -1;
+               p++;
+       } else {
+               p = strchr(c, ':');
+               if (p == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "expire -- Invalid line[%s]: '%s'",
+                                 line,
+                                 c);
+                       return false;
+               }
+               *p = '\0';
+               p++;
+               sp->sp_expire = strtol(c, &e, 10);
+               if (c == e) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "expire -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e == NULL) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "expire -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+               if (e[0] != '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "expire -- Invalid line[%s]: '%s' - %s",
+                                 line, c, strerror(errno));
+                       return false;
+               }
+       }
+       c = p;
+
+       nwrap_sp->num++;
+       return true;
+}
+
+static void nwrap_sp_unload(struct nwrap_cache *nwrap)
+{
+       struct nwrap_sp *nwrap_sp;
+       nwrap_sp = (struct nwrap_sp *)nwrap->private_data;
+
+       SAFE_FREE(nwrap_sp->list);
+       nwrap_sp->num = 0;
+       nwrap_sp->idx = 0;
+}
+#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */
+
+/*
+ * the caller has to call nwrap_unload() on failure
+ */
+static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line)
+{
+       struct nwrap_gr *nwrap_gr;
+       char *c;
+       char *p;
+       char *e;
+       struct group *gr;
+       size_t list_size;
+       unsigned nummem;
+
+       nwrap_gr = (struct nwrap_gr *)nwrap->private_data;
+
+       list_size = sizeof(*nwrap_gr->list) * (nwrap_gr->num+1);
+       gr = (struct group *)realloc(nwrap_gr->list, list_size);
+       if (!gr) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "realloc failed");
+               return false;
+       }
+       nwrap_gr->list = gr;
+
+       gr = &nwrap_gr->list[nwrap_gr->num];
+
+       c = line;
+
+       /* name */
+       p = strchr(c, ':');
+       if (!p) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c);
+               return false;
+       }
+       *p = '\0';
+       p++;
+       gr->gr_name = c;
+       c = p;
+
+       NWRAP_LOG(NWRAP_LOG_TRACE, "name[%s]", gr->gr_name);
+
+       /* password */
+       p = strchr(c, ':');
+       if (!p) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c);
+               return false;
+       }
+       *p = '\0';
+       p++;
+       gr->gr_passwd = c;
+       c = p;
+
+       NWRAP_LOG(NWRAP_LOG_TRACE, "password[%s]", gr->gr_passwd);
+
+       /* gid */
+       p = strchr(c, ':');
+       if (!p) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Invalid line[%s]: '%s'", line, c);
+               return false;
+       }
+       *p = '\0';
+       p++;
+       e = NULL;
+       gr->gr_gid = (gid_t)strtoul(c, &e, 10);
+       if (c == e) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Invalid line[%s]: '%s' - %s",
+                         line, c, strerror(errno));
+               return false;
+       }
+       if (e == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Invalid line[%s]: '%s' - %s",
+                         line, c, strerror(errno));
+               return false;
+       }
+       if (e[0] != '\0') {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Invalid line[%s]: '%s' - %s",
+                         line, c, strerror(errno));
+               return false;
+       }
+       c = p;
+
+       NWRAP_LOG(NWRAP_LOG_TRACE, "gid[%u]", gr->gr_gid);
+
+       /* members */
+       gr->gr_mem = (char **)malloc(sizeof(char *));
+       if (!gr->gr_mem) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory");
+               return false;
+       }
+       gr->gr_mem[0] = NULL;
+
+       for(nummem=0; p; nummem++) {
+               char **m;
+               size_t m_size;
+               c = p;
+               p = strchr(c, ',');
+               if (p) {
+                       *p = '\0';
+                       p++;
+               }
+
+               if (strlen(c) == 0) {
+                       break;
+               }
+
+               m_size = sizeof(char *) * (nummem+2);
+               m = (char **)realloc(gr->gr_mem, m_size);
+               if (!m) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "realloc(%zd) failed",
+                                 m_size);
+                       return false;
+               }
+               gr->gr_mem = m;
+               gr->gr_mem[nummem] = c;
+               gr->gr_mem[nummem+1] = NULL;
+
+               NWRAP_LOG(NWRAP_LOG_TRACE,
+                         "member[%u]: '%s'",
+                         nummem, gr->gr_mem[nummem]);
+       }
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                 "Added group[%s:%s:%u:] with %u members",
+                 gr->gr_name, gr->gr_passwd, gr->gr_gid, nummem);
+
+       nwrap_gr->num++;
+       return true;
+}
+
+static void nwrap_gr_unload(struct nwrap_cache *nwrap)
+{
+       int i;
+       struct nwrap_gr *nwrap_gr;
+       nwrap_gr = (struct nwrap_gr *)nwrap->private_data;
+
+       if (nwrap_gr->list) {
+               for (i=0; i < nwrap_gr->num; i++) {
+                       SAFE_FREE(nwrap_gr->list[i].gr_mem);
+               }
+               SAFE_FREE(nwrap_gr->list);
+       }
+
+       nwrap_gr->num = 0;
+       nwrap_gr->idx = 0;
+}
+
+static int nwrap_gr_copy_r(const struct group *src, struct group *dst,
+                          char *buf, size_t buflen, struct group **dstp)
+{
+       char *first;
+       char **lastm;
+       char *last = NULL;
+       off_t ofsb;
+       off_t ofsm;
+       off_t ofs;
+       unsigned i;
+
+       first = src->gr_name;
+
+       lastm = src->gr_mem;
+       while (*lastm) {
+               last = *lastm;
+               lastm++;
+       }
+
+       if (last == NULL) {
+               last = src->gr_passwd;
+       }
+       while (*last) last++;
+
+       ofsb = PTR_DIFF(last + 1, first);
+       ofsm = PTR_DIFF(lastm + 1, src->gr_mem);
+
+       if ((ofsb + ofsm) > (off_t) buflen) {
+               return ERANGE;
+       }
+
+       memcpy(buf, first, ofsb);
+       memcpy(buf + ofsb, src->gr_mem, ofsm);
+
+       ofs = PTR_DIFF(src->gr_name, first);
+       dst->gr_name = buf + ofs;
+       ofs = PTR_DIFF(src->gr_passwd, first);
+       dst->gr_passwd = buf + ofs;
+       dst->gr_gid = src->gr_gid;
+
+       dst->gr_mem = (char **)(buf + ofsb);
+       for (i=0; src->gr_mem[i]; i++) {
+               ofs = PTR_DIFF(src->gr_mem[i], first);
+               dst->gr_mem[i] = buf + ofs;
+       }
+
+       if (dstp) {
+               *dstp = dst;
+       }
+
+       return 0;
+}
+
+static struct nwrap_entlist *nwrap_entlist_init(struct nwrap_entdata *ed)
+{
+       struct nwrap_entlist *el;
+
+       if (ed == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "entry is NULL, can't create list item");
+               return NULL;
+       }
+
+       el = (struct nwrap_entlist *)malloc(sizeof(struct nwrap_entlist));
+       if (el == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "malloc failed");
+               return NULL;
+       }
+
+       el->next = NULL;
+       el->ed = ed;
+
+       return el;
+}
+
+static bool nwrap_ed_inventarize_add_new(char *const h_name,
+                                        struct nwrap_entdata *const ed)
+{
+       ENTRY e;
+       ENTRY *p;
+       struct nwrap_entlist *el;
+
+       if (h_name == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "h_name NULL - can't add");
+               return false;
+       }
+
+       el = nwrap_entlist_init(ed);
+       if (el == NULL) {
+               return false;
+       }
+
+       e.key = h_name;
+       e.data = (void *)el;
+
+       p = hsearch(e, ENTER);
+       if (p == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Hash table is full!");
+               return false;
+       }
+
+       return true;
+}
+
+static bool nwrap_ed_inventarize_add_to_existing(struct nwrap_entdata *const ed,
+                                                struct nwrap_entlist *const el)
+{
+       struct nwrap_entlist *cursor;
+       struct nwrap_entlist *el_new;
+
+       if (el == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "list is NULL, can not add");
+               return false;
+       }
+
+       el_new = nwrap_entlist_init(ed);
+       if (el_new == NULL) {
+               return false;
+       }
+
+       for (cursor = el; cursor->next != NULL; cursor = cursor->next)
+       {
+               if (cursor->ed == ed) {
+                       free(el_new);
+                       return false;
+               }
+       }
+
+       if (cursor->ed == ed) {
+               free(el_new);
+               return false;
+       }
+
+       cursor->next = el_new;
+       return true;
+}
+
+static bool nwrap_ed_inventarize(char *const name,
+                                struct nwrap_entdata *const ed)
+{
+       ENTRY e;
+       ENTRY *p;
+       bool ok;
+
+       e.key = name;
+       e.data = NULL;
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "Searching name: %s", e.key);
+
+       p = hsearch(e, FIND);
+       if (p == NULL) {
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found. Adding...", name);
+               ok = nwrap_ed_inventarize_add_new(name, ed);
+       } else {
+               struct nwrap_entlist *el = (struct nwrap_entlist *)p->data;
+
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s found. Add record to list.", name);
+               ok = nwrap_ed_inventarize_add_to_existing(ed, el);
+       }
+
+       return ok;
+}
+
+static bool nwrap_add_hname(struct nwrap_entdata *const ed)
+{
+       char *const h_name = (char *const)(ed->ht.h_name);
+       unsigned i;
+       bool ok;
+
+       ok = nwrap_ed_inventarize(h_name, ed);
+       if (!ok) {
+               return false;
+       }
+
+       if (ed->ht.h_aliases == NULL) {
+               return true;
+       }
+
+       /* Itemize aliases */
+       for (i = 0; ed->ht.h_aliases[i] != NULL; ++i) {
+               char *h_name_alias;
+
+               h_name_alias = ed->ht.h_aliases[i];
+
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "Add alias: %s", h_name_alias);
+
+               if (!nwrap_ed_inventarize(h_name_alias, ed)) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "Unable to add alias: %s", h_name_alias);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static bool nwrap_he_parse_line(struct nwrap_cache *nwrap, char *line)
+{
+       struct nwrap_he *nwrap_he = (struct nwrap_he *)nwrap->private_data;
+       bool do_aliases = true;
+       ssize_t aliases_count = 0;
+       char *p;
+       char *i;
+       char *n;
+
+       char *ip;
+       bool ok;
+
+       struct nwrap_entdata *ed = (struct nwrap_entdata *)
+                                  malloc(sizeof(struct nwrap_entdata));
+       if (ed == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Unable to allocate memory for nwrap_entdata");
+               return false;
+       }
+       ZERO_STRUCTP(ed);
+
+       i = line;
+
+       /*
+        * IP
+        */
+
+       /* Walk to first char */
+       for (p = i; *p != '.' && *p != ':' && !isxdigit((int) *p); p++) {
+               if (*p == '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "Invalid line[%s]: '%s'",
+                                 line, i);
+                       free(ed);
+                       return false;
+               }
+       }
+
+       for (i = p; !isspace((int)*p); p++) {
+               if (*p == '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "Invalid line[%s]: '%s'",
+                                 line, i);
+                       free(ed);
+                       return false;
+               }
+       }
+
+       *p = '\0';
+
+       if (inet_pton(AF_INET, i, ed->addr.host_addr)) {
+               ed->ht.h_addrtype = AF_INET;
+               ed->ht.h_length = 4;
+#ifdef HAVE_IPV6
+       } else if (inet_pton(AF_INET6, i, ed->addr.host_addr)) {
+               ed->ht.h_addrtype = AF_INET6;
+               ed->ht.h_length = 16;
+#endif
+       } else {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Invalid line[%s]: '%s'",
+                         line, i);
+
+               free(ed);
+               return false;
+       }
+       ip = i;
+
+       nwrap_vector_add_item(&(ed->nwrap_addrdata),
+                             (void *const)ed->addr.host_addr);
+       ed->ht.h_addr_list = nwrap_vector_head(&ed->nwrap_addrdata);
+
+       p++;
+
+       /*
+        * FQDN
+        */
+
+       /* Walk to first char */
+       for (n = p; *p != '_' && !isalnum((int) *p); p++) {
+               if (*p == '\0') {
+                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                 "Invalid line[%s]: '%s'",
+                                 line, n);
+
+                       free(ed);
+                       return false;
+               }
+       }
+
+       for (n = p; !isspace((int)*p); p++) {
+               if (*p == '\0') {
+                       do_aliases = false;
+                       break;
+               }
+       }
+
+       *p = '\0';
+
+       /* Convert to lowercase. This operate on same memory region */
+       str_tolower(n, n);
+       ed->ht.h_name = n;
+
+       /* glib's getent always dereferences he->h_aliases */
+       ed->ht.h_aliases = malloc(sizeof(char *));
+       if (ed->ht.h_aliases == NULL) {
+               free(ed);
+               return false;
+       }
+       ed->ht.h_aliases[0] = NULL;
+
+       /*
+        * Aliases
+        */
+       while (do_aliases) {
+               char **aliases;
+               char *a;
+
+               p++;
+
+               /* Walk to first char */
+               for (a = p; *p != '_' && !isalnum((int) *p); p++) {
+                       if (*p == '\0') {
+                               do_aliases = false;
+                               break;
+                       }
+               }
+               /* Only trailing spaces are left */
+               if (!do_aliases) {
+                       break;
+               }
+
+               for (a = p; !isspace((int)*p); p++) {
+                       if (*p == '\0') {
+                               do_aliases = false;
+                               break;
+                       }
+               }
+
+               *p = '\0';
+
+               aliases = realloc(ed->ht.h_aliases, sizeof(char *) * (aliases_count + 2));
+               if (aliases == NULL) {
+                       free(ed);
+                       return false;
+               }
+               ed->ht.h_aliases = aliases;
+
+               str_tolower(a, a);
+               aliases[aliases_count] = a;
+               aliases[aliases_count + 1] = NULL;
+
+               aliases_count += 1;
+       }
+
+       nwrap_vector_add_item(&(nwrap_he->entdata), (void *const)ed);
+
+       ed->aliases_count = aliases_count;
+       /* Inventarize item */
+       ok = nwrap_add_hname(ed);
+       if (!ok) {
+               return false;
+       }
+
+       ok = nwrap_ed_inventarize(ip, ed);
+       if (!ok) {
+               return false;
+       }
+
+       nwrap_he->num++;
+       return true;
+}
+
+static void nwrap_he_unload(struct nwrap_cache *nwrap)
+{
+       struct nwrap_he *nwrap_he =
+               (struct nwrap_he *)nwrap->private_data;
+       struct nwrap_entdata *ed;
+       size_t i;
+
+       nwrap_vector_foreach (ed, nwrap_he->entdata, i)
+       {
+               SAFE_FREE(ed->nwrap_addrdata.items);
+               SAFE_FREE(ed->ht.h_aliases);
+               SAFE_FREE(ed);
+       }
+       SAFE_FREE(nwrap_he->entdata.items);
+       nwrap_he->entdata.count = nwrap_he->entdata.capacity = 0;
+
+       nwrap_he->num = 0;
+       nwrap_he->idx = 0;
+}
+
+
+/* user functions */
+static struct passwd *nwrap_files_getpwnam(struct nwrap_backend *b,
+                                          const char *name)
+{
+       int i;
+       bool ok;
+
+       (void) b; /* unused */
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "Lookup user %s in files", name);
+
+       ok = nwrap_files_cache_reload(nwrap_pw_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading passwd file");
+               return NULL;
+       }
+
+       for (i=0; i<nwrap_pw_global.num; i++) {
+               if (strcmp(nwrap_pw_global.list[i].pw_name, name) == 0) {
+                       NWRAP_LOG(NWRAP_LOG_DEBUG, "user[%s] found", name);
+                       return &nwrap_pw_global.list[i];
+               }
+               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                         "user[%s] does not match [%s]",
+                         name,
+                         nwrap_pw_global.list[i].pw_name);
+       }
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "user[%s] not found\n", name);
+
+       errno = ENOENT;
+       return NULL;
+}
+
+static int nwrap_files_getpwnam_r(struct nwrap_backend *b,
+                                 const char *name, struct passwd *pwdst,
+                                 char *buf, size_t buflen, struct passwd **pwdstp)
+{
+       struct passwd *pw;
+
+       pw = nwrap_files_getpwnam(b, name);
+       if (!pw) {
+               if (errno == 0) {
+                       return ENOENT;
+               }
+               return errno;
+       }
+
+       return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
+}
+
+static struct passwd *nwrap_files_getpwuid(struct nwrap_backend *b,
+                                          uid_t uid)
+{
+       int i;
+       bool ok;
+
+       (void) b; /* unused */
+
+       ok = nwrap_files_cache_reload(nwrap_pw_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading passwd file");
+               return NULL;
+       }
+
+       for (i=0; i<nwrap_pw_global.num; i++) {
+               if (nwrap_pw_global.list[i].pw_uid == uid) {
+                       NWRAP_LOG(NWRAP_LOG_DEBUG, "uid[%u] found", uid);
+                       return &nwrap_pw_global.list[i];
+               }
+               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                         "uid[%u] does not match [%u]",
+                         uid,
+                         nwrap_pw_global.list[i].pw_uid);
+       }
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "uid[%u] not found\n", uid);
+
+       errno = ENOENT;
+       return NULL;
+}
+
+static int nwrap_files_getpwuid_r(struct nwrap_backend *b,
+                                 uid_t uid, struct passwd *pwdst,
+                                 char *buf, size_t buflen, struct passwd **pwdstp)
+{
+       struct passwd *pw;
+
+       pw = nwrap_files_getpwuid(b, uid);
+       if (!pw) {
+               if (errno == 0) {
+                       return ENOENT;
+               }
+               return errno;
+       }
+
+       return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
+}
+
+/* user enum functions */
+static void nwrap_files_setpwent(struct nwrap_backend *b)
+{
+       (void) b; /* unused */
+
+       nwrap_pw_global.idx = 0;
+}
+
+static struct passwd *nwrap_files_getpwent(struct nwrap_backend *b)
+{
+       struct passwd *pw;
+
+       (void) b; /* unused */
+
+       if (nwrap_pw_global.idx == 0) {
+               bool ok;
+               ok = nwrap_files_cache_reload(nwrap_pw_global.cache);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading passwd file");
+                       return NULL;
+               }
+       }
+
+       if (nwrap_pw_global.idx >= nwrap_pw_global.num) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       pw = &nwrap_pw_global.list[nwrap_pw_global.idx++];
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                 "return user[%s] uid[%u]",
+                 pw->pw_name, pw->pw_uid);
+
+       return pw;
+}
+
+static int nwrap_files_getpwent_r(struct nwrap_backend *b,
+                                 struct passwd *pwdst, char *buf,
+                                 size_t buflen, struct passwd **pwdstp)
+{
+       struct passwd *pw;
+
+       pw = nwrap_files_getpwent(b);
+       if (!pw) {
+               if (errno == 0) {
+                       return ENOENT;
+               }
+               return errno;
+       }
+
+       return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
+}
+
+static void nwrap_files_endpwent(struct nwrap_backend *b)
+{
+       (void) b; /* unused */
+
+       nwrap_pw_global.idx = 0;
+}
+
+/* shadow */
+
+#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM)
+
+#ifdef HAVE_SETSPENT
+static void nwrap_files_setspent(void)
+{
+       nwrap_sp_global.idx = 0;
+}
+
+static struct spwd *nwrap_files_getspent(void)
+{
+       struct spwd *sp;
+
+       if (nwrap_sp_global.idx == 0) {
+               bool ok;
+
+               ok = nwrap_files_cache_reload(nwrap_sp_global.cache);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading shadow file");
+                       return NULL;
+               }
+       }
+
+       if (nwrap_sp_global.idx >= nwrap_sp_global.num) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       sp = &nwrap_sp_global.list[nwrap_sp_global.idx++];
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                 "return user[%s]",
+                 sp->sp_namp);
+
+       return sp;
+}
+
+static void nwrap_files_endspent(void)
+{
+       nwrap_sp_global.idx = 0;
+}
+#endif /* HAVE_SETSPENT */
+
+static struct spwd *nwrap_files_getspnam(const char *name)
+{
+       int i;
+       bool ok;
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "Lookup user %s in files", name);
+
+       ok = nwrap_files_cache_reload(nwrap_sp_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading shadow file");
+               return NULL;
+       }
+
+       for (i=0; i<nwrap_sp_global.num; i++) {
+               if (strcmp(nwrap_sp_global.list[i].sp_namp, name) == 0) {
+                       NWRAP_LOG(NWRAP_LOG_DEBUG, "user[%s] found", name);
+                       return &nwrap_sp_global.list[i];
+               }
+               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                         "user[%s] does not match [%s]",
+                         name,
+                         nwrap_sp_global.list[i].sp_namp);
+       }
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "user[%s] not found\n", name);
+
+       errno = ENOENT;
+       return NULL;
+}
+#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */
+
+/* misc functions */
+static int nwrap_files_initgroups(struct nwrap_backend *b,
+                                 const char *user,
+                                 gid_t group)
+{
+       struct group *grp;
+       gid_t *groups;
+       int size = 1;
+       int rc;
+
+       groups = (gid_t *)malloc(size * sizeof(gid_t));
+       if (groups == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory");
+               errno = ENOMEM;
+               return -1;
+       }
+       groups[0] = group;
+
+       nwrap_files_setgrent(b);
+       while ((grp = nwrap_files_getgrent(b)) != NULL) {
+               int i = 0;
+
+               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                         "Inspecting %s for group membership",
+                         grp->gr_name);
+
+               for (i=0; grp->gr_mem && grp->gr_mem[i] != NULL; i++) {
+                       if (group != grp->gr_gid &&
+                           (strcmp(user, grp->gr_mem[i]) == 0)) {
+                               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                                         "%s is member of %s",
+                                         user,
+                                         grp->gr_name);
+
+                               groups = (gid_t *)realloc(groups,
+                                                         (size + 1) * sizeof(gid_t));
+                               if (groups == NULL) {
+                                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                                 "Out of memory");
+                                       errno = ENOMEM;
+                                       return -1;
+                               }
+
+                               groups[size] = grp->gr_gid;
+                               size++;
+                       }
+               }
+       }
+
+       nwrap_files_endgrent(b);
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                 "%s is member of %d groups",
+                 user, size);
+
+       /* This really only works if uid_wrapper is loaded */
+       rc = setgroups(size, groups);
+
+       free(groups);
+
+       return rc;
+}
+
+/* group functions */
+static struct group *nwrap_files_getgrnam(struct nwrap_backend *b,
+                                         const char *name)
+{
+       int i;
+       bool ok;
+
+       (void) b; /* unused */
+
+       ok = nwrap_files_cache_reload(nwrap_gr_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading group file");
+               return NULL;
+       }
+
+       for (i=0; i<nwrap_gr_global.num; i++) {
+               if (strcmp(nwrap_gr_global.list[i].gr_name, name) == 0) {
+                       NWRAP_LOG(NWRAP_LOG_DEBUG, "group[%s] found", name);
+                       return &nwrap_gr_global.list[i];
+               }
+               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                         "group[%s] does not match [%s]",
+                         name,
+                         nwrap_gr_global.list[i].gr_name);
+       }
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "group[%s] not found", name);
+
+       errno = ENOENT;
+       return NULL;
+}
+
+static int nwrap_files_getgrnam_r(struct nwrap_backend *b,
+                                 const char *name, struct group *grdst,
+                                 char *buf, size_t buflen, struct group **grdstp)
+{
+       struct group *gr;
+
+       gr = nwrap_files_getgrnam(b, name);
+       if (!gr) {
+               if (errno == 0) {
+                       return ENOENT;
+               }
+               return errno;
+       }
+
+       return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
+}
+
+static struct group *nwrap_files_getgrgid(struct nwrap_backend *b,
+                                         gid_t gid)
+{
+       int i;
+       bool ok;
+
+       (void) b; /* unused */
+
+       ok = nwrap_files_cache_reload(nwrap_gr_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading group file");
+               return NULL;
+       }
+
+       for (i=0; i<nwrap_gr_global.num; i++) {
+               if (nwrap_gr_global.list[i].gr_gid == gid) {
+                       NWRAP_LOG(NWRAP_LOG_DEBUG, "gid[%u] found", gid);
+                       return &nwrap_gr_global.list[i];
+               }
+               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                         "gid[%u] does not match [%u]",
+                         gid,
+                         nwrap_gr_global.list[i].gr_gid);
+       }
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "gid[%u] not found", gid);
+
+       errno = ENOENT;
+       return NULL;
+}
+
+static int nwrap_files_getgrgid_r(struct nwrap_backend *b,
+                                 gid_t gid, struct group *grdst,
+                                 char *buf, size_t buflen, struct group **grdstp)
+{
+       struct group *gr;
+
+       gr = nwrap_files_getgrgid(b, gid);
+       if (!gr) {
+               if (errno == 0) {
+                       return ENOENT;
+               }
+               return errno;
+       }
+
+       return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
+}
+
+/* group enum functions */
+static void nwrap_files_setgrent(struct nwrap_backend *b)
+{
+       (void) b; /* unused */
+
+       nwrap_gr_global.idx = 0;
+}
+
+static struct group *nwrap_files_getgrent(struct nwrap_backend *b)
+{
+       struct group *gr;
+
+       (void) b; /* unused */
+
+       if (nwrap_gr_global.idx == 0) {
+               bool ok;
+
+               ok = nwrap_files_cache_reload(nwrap_gr_global.cache);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading group file");
+                       return NULL;
+               }
+       }
+
+       if (nwrap_gr_global.idx >= nwrap_gr_global.num) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       gr = &nwrap_gr_global.list[nwrap_gr_global.idx++];
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                 "return group[%s] gid[%u]",
+                 gr->gr_name, gr->gr_gid);
+
+       return gr;
+}
+
+static int nwrap_files_getgrent_r(struct nwrap_backend *b,
+                                 struct group *grdst, char *buf,
+                                 size_t buflen, struct group **grdstp)
+{
+       struct group *gr;
+
+       gr = nwrap_files_getgrent(b);
+       if (!gr) {
+               if (errno == 0) {
+                       return ENOENT;
+               }
+               return errno;
+       }
+
+       return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
+}
+
+static void nwrap_files_endgrent(struct nwrap_backend *b)
+{
+       (void) b; /* unused */
+
+       nwrap_gr_global.idx = 0;
+}
+
+/* hosts functions */
+static int nwrap_files_gethostbyname(const char *name, int af,
+                                    struct hostent *result,
+                                    struct nwrap_vector *addr_list)
+{
+       struct nwrap_entlist *el;
+       struct hostent *he;
+       char *h_name_lower;
+       ENTRY e;
+       ENTRY *e_p;
+       char canon_name[DNS_NAME_MAX] = { 0 };
+       size_t name_len;
+       bool he_found = false;
+       bool ok;
+
+       ok = nwrap_files_cache_reload(nwrap_he_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "error loading hosts file");
+               goto no_ent;
+       }
+
+       name_len = strlen(name);
+       if (name_len < sizeof(canon_name) && name[name_len - 1] == '.') {
+               strncpy(canon_name, name, name_len - 1);
+               name = canon_name;
+       }
+
+       if (!str_tolower_copy(&h_name_lower, name)) {
+               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                         "Out of memory while converting to lower case");
+               goto no_ent;
+       }
+
+       /* Look at hash table for element */
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "Searching for name: %s", h_name_lower);
+       e.key = h_name_lower;
+       e.data = NULL;
+       e_p = hsearch(e, FIND);
+       if (e_p == NULL) {
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found.", h_name_lower);
+               SAFE_FREE(h_name_lower);
+               goto no_ent;
+       }
+       SAFE_FREE(h_name_lower);
+
+       /* Always cleanup vector and results */
+       if (!nwrap_vector_is_initialized(addr_list)) {
+               if (!nwrap_vector_init(addr_list)) {
+                       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                                 "Unable to initialize memory for addr_list vector");
+                       goto no_ent;
+               }
+       } else {
+               /* When vector is initialized data are valid no more.
+                * Quick way how to free vector is: */
+               addr_list->count = 0;
+       }
+
+       /* Iterate through results */
+       for (el = (struct nwrap_entlist *)e_p->data; el != NULL; el = el->next)
+       {
+               he = &(el->ed->ht);
+
+               /* Filter by address familiy if provided */
+               if (af != AF_UNSPEC && he->h_addrtype != af) {
+                       continue;
+               }
+
+               /*
+                * GLIBC HACK?
+                * glibc doesn't return ipv6 addresses when AF_UNSPEC is used
+                */
+               if (af == AF_UNSPEC && he->h_addrtype != AF_INET) {
+                       continue;
+               }
+
+               if (!he_found) {
+                       memcpy(result, he, sizeof(struct hostent));
+                       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                                 "Name found. Returning record for %s",
+                                 he->h_name);
+                       he_found = true;
+               }
+               nwrap_vector_merge(addr_list, &el->ed->nwrap_addrdata);
+               result->h_addr_list = nwrap_vector_head(addr_list);
+       }
+
+       if (he_found) {
+               return 0;
+       }
+       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                 "Name found in database. No records matches type.");
+
+no_ent:
+       errno = ENOENT;
+       return -1;
+}
+
+#ifdef HAVE_GETHOSTBYNAME_R
+static int nwrap_gethostbyname_r(const char *name,
+                                struct hostent *ret,
+                                char *buf, size_t buflen,
+                                struct hostent **result, int *h_errnop)
+{
+       struct nwrap_vector *addr_list = malloc(sizeof(struct nwrap_vector));
+       int rc;
+
+       if (addr_list == NULL) {
+               NWRAP_LOG(NWRAP_LOG_ERROR,
+                         "Unable to allocate memory for address list");
+               errno = ENOENT;
+               return -1;
+       }
+
+       ZERO_STRUCTP(addr_list);
+
+       rc = nwrap_files_gethostbyname(name, AF_UNSPEC, ret, addr_list);
+       if (rc == -1) {
+               *h_errnop = h_errno;
+               if (addr_list->items != NULL) {
+                       free(addr_list->items);
+               }
+               SAFE_FREE(addr_list);
+               errno = ENOENT;
+               return -1;
+       }
+
+       if (buflen < (addr_list->count * sizeof(void *))) {
+               SAFE_FREE(addr_list->items);
+               SAFE_FREE(addr_list);
+               return ERANGE;
+       }
+
+       /* Copy all to user provided buffer and change
+        * pointers in returned structure.
+        * +1 is for ending NULL pointer. */
+       memcpy(buf, addr_list->items, (addr_list->count + 1) * sizeof(void *));
+
+       free(addr_list->items);
+       free(addr_list);
+
+       ret->h_addr_list = (char **)buf;
+       *result = ret;
+       return 0;
+}
+
+int gethostbyname_r(const char *name,
+                   struct hostent *ret,
+                   char *buf, size_t buflen,
+                   struct hostent **result, int *h_errnop)
+{
+       if (!nss_wrapper_hosts_enabled()) {
+               return libc_gethostbyname_r(name,
+                                           ret,
+                                           buf,
+                                           buflen,
+                                           result,
+                                           h_errnop);
+       }
+
+       return nwrap_gethostbyname_r(name, ret, buf, buflen, result, h_errnop);
+}
+#endif
+
+static int nwrap_files_getaddrinfo(const char *name,
+                                  unsigned short port,
+                                  const struct addrinfo *hints,
+                                  struct addrinfo **ai)
+{
+       struct nwrap_entlist *el;
+       struct hostent *he;
+       struct addrinfo *ai_head = NULL;
+       struct addrinfo *ai_cur = NULL;
+       char *h_name_lower;
+       size_t name_len;
+       char canon_name[DNS_NAME_MAX] = { 0 };
+       bool skip_canonname = false;
+       ENTRY e = {
+               .key = NULL,
+       };
+       ENTRY *e_p = NULL;
+       int rc;
+       bool ok;
+
+       ok = nwrap_files_cache_reload(nwrap_he_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "error loading hosts file");
+               return EAI_SYSTEM;
+       }
+
+       name_len = strlen(name);
+       if (name_len < DNS_NAME_MAX && name[name_len - 1] == '.') {
+               strncpy(canon_name, name, name_len - 1);
+               name = canon_name;
+       }
+
+       if (!str_tolower_copy(&h_name_lower, name)) {
+               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                         "Out of memory while converting to lower case");
+               return EAI_MEMORY;
+       }
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "Searching for name: %s", h_name_lower);
+       e.key = h_name_lower;
+       e.data = NULL;
+       e_p = hsearch(e, FIND);
+       if (e_p == NULL) {
+               NWRAP_LOG(NWRAP_LOG_DEBUG, "Name %s not found.", h_name_lower);
+               SAFE_FREE(h_name_lower);
+               errno = ENOENT;
+               return EAI_NONAME;
+       }
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "Name: %s found.", h_name_lower);
+       SAFE_FREE(h_name_lower);
+
+       rc = EAI_NONAME;
+       for (el = (struct nwrap_entlist *)e_p->data; el != NULL; el = el->next)
+       {
+               int rc2;
+               struct addrinfo *ai_new = NULL;
+
+               he = &(el->ed->ht);
+
+               if (hints->ai_family != AF_UNSPEC &&
+                   he->h_addrtype != hints->ai_family)
+               {
+                       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                                 "Entry found but with wrong AF - "
+                                 "remembering EAI_ADDRINFO.");
+                       rc = EAI_ADDRFAMILY;
+                       continue;
+               }
+
+               /* Function allocates memory and returns it in ai. */
+               rc2 = nwrap_convert_he_ai(he,
+                                        port,
+                                        hints,
+                                        &ai_new,
+                                        skip_canonname);
+               if (rc2 != 0) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error converting he to ai");
+                       if (ai_head != NULL) {
+                               freeaddrinfo(ai_head);
+                       }
+                       return rc2;
+               }
+               skip_canonname = true;
+
+               if (ai_head == NULL) {
+                       ai_head = ai_new;
+               }
+               if (ai_cur != NULL) {
+                       ai_cur->ai_next = ai_new;
+               }
+               ai_cur = ai_new;
+       }
+
+       if (ai_head != NULL) {
+               rc = 0;
+       }
+
+       *ai = ai_head;
+
+       return rc;
+}
+
+static struct hostent *nwrap_files_gethostbyaddr(const void *addr,
+                                                socklen_t len, int type)
+{
+       struct hostent *he;
+       char ip[NWRAP_INET_ADDRSTRLEN] = {0};
+       struct nwrap_entdata *ed;
+       const char *a;
+       size_t i;
+       bool ok;
+
+       (void) len; /* unused */
+
+       ok = nwrap_files_cache_reload(nwrap_he_global.cache);
+       if (!ok) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "error loading hosts file");
+               return NULL;
+       }
+
+       a = inet_ntop(type, addr, ip, sizeof(ip));
+       if (a == NULL) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       nwrap_vector_foreach(ed, nwrap_he_global.entdata, i)
+       {
+               he = &(ed->ht);
+               if (he->h_addrtype != type) {
+                       continue;
+               }
+
+               if (memcmp(addr, he->h_addr_list[0], he->h_length) == 0) {
+                       return he;
+               }
+       }
+
+       errno = ENOENT;
+       return NULL;
+}
+
+#ifdef HAVE_GETHOSTBYADDR_R
+static int nwrap_gethostbyaddr_r(const void *addr, socklen_t len, int type,
+                                struct hostent *ret,
+                                char *buf, size_t buflen,
+                                struct hostent **result, int *h_errnop)
+{
+       *result = nwrap_files_gethostbyaddr(addr, len, type);
+       if (*result != NULL) {
+               memset(buf, '\0', buflen);
+               *ret = **result;
+               return 0;
+       } else {
+               *h_errnop = h_errno;
+               return -1;
+       }
+}
+
+int gethostbyaddr_r(const void *addr, socklen_t len, int type,
+                   struct hostent *ret,
+                   char *buf, size_t buflen,
+                   struct hostent **result, int *h_errnop)
+{
+       if (!nss_wrapper_hosts_enabled()) {
+               return libc_gethostbyaddr_r(addr,
+                                           len,
+                                           type,
+                                           ret,
+                                           buf,
+                                           buflen,
+                                           result,
+                                           h_errnop);
+       }
+
+       return nwrap_gethostbyaddr_r(addr, len, type, ret, buf, buflen, result, h_errnop);
+}
+#endif
+
+/* hosts enum functions */
+static void nwrap_files_sethostent(void)
+{
+       nwrap_he_global.idx = 0;
+}
+
+static struct hostent *nwrap_files_gethostent(void)
+{
+       struct hostent *he;
+
+       if (nwrap_he_global.idx == 0) {
+               bool ok;
+
+               ok = nwrap_files_cache_reload(nwrap_he_global.cache);
+               if (!ok) {
+                       NWRAP_LOG(NWRAP_LOG_ERROR, "Error loading hosts file");
+                       return NULL;
+               }
+       }
+
+       if (nwrap_he_global.idx >= nwrap_he_global.num) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       he = &((struct nwrap_entdata *)nwrap_he_global.entdata.items[nwrap_he_global.idx++])->ht;
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "return hosts[%s]", he->h_name);
+
+       return he;
+}
+
+static void nwrap_files_endhostent(void)
+{
+       nwrap_he_global.idx = 0;
+}
+
+/*
+ * module backend
+ */
+
+
+static struct passwd *nwrap_module_getpwnam(struct nwrap_backend *b,
+                                           const char *name)
+{
+       static struct passwd pwd;
+       static char buf[1000];
+       NSS_STATUS status;
+
+       if (!b->fns->_nss_getpwnam_r) {
+               return NULL;
+       }
+
+       status = b->fns->_nss_getpwnam_r(name, &pwd, buf, sizeof(buf), &errno);
+       if (status == NSS_STATUS_NOTFOUND) {
+               return NULL;
+       }
+       if (status != NSS_STATUS_SUCCESS) {
+               return NULL;
+       }
+
+       return &pwd;
+}
+
+static int nwrap_module_getpwnam_r(struct nwrap_backend *b,
+                                  const char *name, struct passwd *pwdst,
+                                  char *buf, size_t buflen, struct passwd **pwdstp)
+{
+       int ret;
+
+       (void) b; /* unused */
+       (void) pwdst; /* unused */
+       (void) pwdstp; /* unused */
+
+       if (!b->fns->_nss_getpwnam_r) {
+               return NSS_STATUS_NOTFOUND;
+       }
+
+       ret = b->fns->_nss_getpwnam_r(name, pwdst, buf, buflen, &errno);
+       switch (ret) {
+       case NSS_STATUS_SUCCESS:
+               return 0;
+       case NSS_STATUS_NOTFOUND:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ENOENT;
+       case NSS_STATUS_TRYAGAIN:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ERANGE;
+       default:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ret;
+       }
+}
+
+static struct passwd *nwrap_module_getpwuid(struct nwrap_backend *b,
+                                           uid_t uid)
+{
+       static struct passwd pwd;
+       static char buf[1000];
+       NSS_STATUS status;
+
+       if (!b->fns->_nss_getpwuid_r) {
+               return NULL;
+       }
+
+       status = b->fns->_nss_getpwuid_r(uid, &pwd, buf, sizeof(buf), &errno);
+       if (status == NSS_STATUS_NOTFOUND) {
+               return NULL;
+       }
+       if (status != NSS_STATUS_SUCCESS) {
+               return NULL;
+       }
+       return &pwd;
+}
+
+static int nwrap_module_getpwuid_r(struct nwrap_backend *b,
+                                  uid_t uid, struct passwd *pwdst,
+                                  char *buf, size_t buflen, struct passwd **pwdstp)
+{
+       int ret;
+
+       (void) pwdstp; /* unused */
+
+       if (!b->fns->_nss_getpwuid_r) {
+               return ENOENT;
+       }
+
+       ret = b->fns->_nss_getpwuid_r(uid, pwdst, buf, buflen, &errno);
+       switch (ret) {
+       case NSS_STATUS_SUCCESS:
+               return 0;
+       case NSS_STATUS_NOTFOUND:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ENOENT;
+       case NSS_STATUS_TRYAGAIN:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ERANGE;
+       default:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ret;
+       }
+}
+
+static void nwrap_module_setpwent(struct nwrap_backend *b)
+{
+       if (!b->fns->_nss_setpwent) {
+               return;
+       }
+
+       b->fns->_nss_setpwent();
+}
+
+static struct passwd *nwrap_module_getpwent(struct nwrap_backend *b)
+{
+       static struct passwd pwd;
+       static char buf[1000];
+       NSS_STATUS status;
+
+       if (!b->fns->_nss_getpwent_r) {
+               return NULL;
+       }
+
+       status = b->fns->_nss_getpwent_r(&pwd, buf, sizeof(buf), &errno);
+       if (status == NSS_STATUS_NOTFOUND) {
+               return NULL;
+       }
+       if (status != NSS_STATUS_SUCCESS) {
+               return NULL;
+       }
+       return &pwd;
+}
+
+static int nwrap_module_getpwent_r(struct nwrap_backend *b,
+                                  struct passwd *pwdst, char *buf,
+                                  size_t buflen, struct passwd **pwdstp)
+{
+       int ret;
+
+       (void) pwdstp; /* unused */
+
+       if (!b->fns->_nss_getpwent_r) {
+               return ENOENT;
+       }
+
+       ret = b->fns->_nss_getpwent_r(pwdst, buf, buflen, &errno);
+       switch (ret) {
+       case NSS_STATUS_SUCCESS:
+               return 0;
+       case NSS_STATUS_NOTFOUND:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ENOENT;
+       case NSS_STATUS_TRYAGAIN:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ERANGE;
+       default:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ret;
+       }
+}
+
+static void nwrap_module_endpwent(struct nwrap_backend *b)
+{
+       if (!b->fns->_nss_endpwent) {
+               return;
+       }
+
+       b->fns->_nss_endpwent();
+}
+
+static int nwrap_module_initgroups(struct nwrap_backend *b,
+                                  const char *user, gid_t group)
+{
+       gid_t *groups;
+       long int start;
+       long int size;
+
+       if (!b->fns->_nss_initgroups) {
+               return NSS_STATUS_UNAVAIL;
+       }
+
+       return b->fns->_nss_initgroups(user, group, &start, &size, &groups, 0, &errno);
+}
+
+static struct group *nwrap_module_getgrnam(struct nwrap_backend *b,
+                                          const char *name)
+{
+       static struct group grp;
+       static char *buf;
+       static int buflen = 1000;
+       NSS_STATUS status;
+
+       if (!b->fns->_nss_getgrnam_r) {
+               return NULL;
+       }
+
+       if (!buf) {
+               buf = (char *)malloc(buflen);
+       }
+again:
+       status = b->fns->_nss_getgrnam_r(name, &grp, buf, buflen, &errno);
+       if (status == NSS_STATUS_TRYAGAIN) {
+               buflen *= 2;
+               buf = (char *)realloc(buf, buflen);
+               if (!buf) {
+                       return NULL;
+               }
+               goto again;
+       }
+       if (status == NSS_STATUS_NOTFOUND) {
+               SAFE_FREE(buf);
+               return NULL;
+       }
+       if (status != NSS_STATUS_SUCCESS) {
+               SAFE_FREE(buf);
+               return NULL;
+       }
+       return &grp;
+}
+
+static int nwrap_module_getgrnam_r(struct nwrap_backend *b,
+                                  const char *name, struct group *grdst,
+                                  char *buf, size_t buflen, struct group **grdstp)
+{
+       int ret;
+
+       (void) grdstp; /* unused */
+
+       if (!b->fns->_nss_getgrnam_r) {
+               return ENOENT;
+       }
+
+       ret = b->fns->_nss_getgrnam_r(name, grdst, buf, buflen, &errno);
+       switch (ret) {
+       case NSS_STATUS_SUCCESS:
+               return 0;
+       case NSS_STATUS_NOTFOUND:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ENOENT;
+       case NSS_STATUS_TRYAGAIN:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ERANGE;
+       default:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ret;
+       }
+}
+
+static struct group *nwrap_module_getgrgid(struct nwrap_backend *b,
+                                          gid_t gid)
+{
+       static struct group grp;
+       static char *buf;
+       static int buflen = 1000;
+       NSS_STATUS status;
+
+       if (!b->fns->_nss_getgrgid_r) {
+               return NULL;
+       }
+
+       if (!buf) {
+               buf = (char *)malloc(buflen);
+       }
+
+again:
+       status = b->fns->_nss_getgrgid_r(gid, &grp, buf, buflen, &errno);
+       if (status == NSS_STATUS_TRYAGAIN) {
+               buflen *= 2;
+               buf = (char *)realloc(buf, buflen);
+               if (!buf) {
+                       return NULL;
+               }
+               goto again;
+       }
+       if (status == NSS_STATUS_NOTFOUND) {
+               SAFE_FREE(buf);
+               return NULL;
+       }
+       if (status != NSS_STATUS_SUCCESS) {
+               SAFE_FREE(buf);
+               return NULL;
+       }
+       return &grp;
+}
+
+static int nwrap_module_getgrgid_r(struct nwrap_backend *b,
+                                  gid_t gid, struct group *grdst,
+                                  char *buf, size_t buflen, struct group **grdstp)
+{
+       int ret;
+
+       (void) grdstp; /* unused */
+
+       if (!b->fns->_nss_getgrgid_r) {
+               return ENOENT;
+       }
+
+       ret = b->fns->_nss_getgrgid_r(gid, grdst, buf, buflen, &errno);
+       switch (ret) {
+       case NSS_STATUS_SUCCESS:
+               return 0;
+       case NSS_STATUS_NOTFOUND:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ENOENT;
+       case NSS_STATUS_TRYAGAIN:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ERANGE;
+       default:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ret;
+       }
+}
+
+static void nwrap_module_setgrent(struct nwrap_backend *b)
+{
+       if (!b->fns->_nss_setgrent) {
+               return;
+       }
+
+       b->fns->_nss_setgrent();
+}
+
+static struct group *nwrap_module_getgrent(struct nwrap_backend *b)
+{
+       static struct group grp;
+       static char *buf;
+       static int buflen = 1024;
+       NSS_STATUS status;
+
+       if (!b->fns->_nss_getgrent_r) {
+               return NULL;
+       }
+
+       if (!buf) {
+               buf = (char *)malloc(buflen);
+       }
+
+again:
+       status = b->fns->_nss_getgrent_r(&grp, buf, buflen, &errno);
+       if (status == NSS_STATUS_TRYAGAIN) {
+               buflen *= 2;
+               buf = (char *)realloc(buf, buflen);
+               if (!buf) {
+                       return NULL;
+               }
+               goto again;
+       }
+       if (status == NSS_STATUS_NOTFOUND) {
+               SAFE_FREE(buf);
+               return NULL;
+       }
+       if (status != NSS_STATUS_SUCCESS) {
+               SAFE_FREE(buf);
+               return NULL;
+       }
+       return &grp;
+}
+
+static int nwrap_module_getgrent_r(struct nwrap_backend *b,
+                                  struct group *grdst, char *buf,
+                                  size_t buflen, struct group **grdstp)
+{
+       int ret;
+
+       (void) grdstp; /* unused */
+
+       if (!b->fns->_nss_getgrent_r) {
+               return ENOENT;
+       }
+
+       ret = b->fns->_nss_getgrent_r(grdst, buf, buflen, &errno);
+       switch (ret) {
+       case NSS_STATUS_SUCCESS:
+               return 0;
+       case NSS_STATUS_NOTFOUND:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ENOENT;
+       case NSS_STATUS_TRYAGAIN:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ERANGE;
+       default:
+               if (errno != 0) {
+                       return errno;
+               }
+               return ret;
+       }
+}
+
+static void nwrap_module_endgrent(struct nwrap_backend *b)
+{
+       if (!b->fns->_nss_endgrent) {
+               return;
+       }
+
+       b->fns->_nss_endgrent();
+}
+
+/****************************************************************************
+ *   GETPWNAM
+ ***************************************************************************/
+
+static struct passwd *nwrap_getpwnam(const char *name)
+{
+       int i;
+       struct passwd *pwd;
+
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               pwd = b->ops->nw_getpwnam(b, name);
+               if (pwd) {
+                       return pwd;
+               }
+       }
+
+       return NULL;
+}
+
+struct passwd *getpwnam(const char *name)
+{
+       if (!nss_wrapper_enabled()) {
+               return libc_getpwnam(name);
+       }
+
+       return nwrap_getpwnam(name);
+}
+
+/****************************************************************************
+ *   GETPWNAM_R
+ ***************************************************************************/
+
+static int nwrap_getpwnam_r(const char *name, struct passwd *pwdst,
+                           char *buf, size_t buflen, struct passwd **pwdstp)
+{
+       int i,ret;
+
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               ret = b->ops->nw_getpwnam_r(b, name, pwdst, buf, buflen, pwdstp);
+               if (ret == ENOENT) {
+                       continue;
                }
+               return ret;
+       }
 
-               m_size = sizeof(char *) * (nummem+2);
-               m = (char **)realloc(gr->gr_mem, m_size);
-               if (!m) {
-                       NWRAP_ERROR(("%s:realloc(%u) failed\n",
-                                     __location__, m_size));
-                       return false;
-               }
-               gr->gr_mem = m;
-               gr->gr_mem[nummem] = c;
-               gr->gr_mem[nummem+1] = NULL;
+       return ENOENT;
+}
 
-               NWRAP_VERBOSE(("member[%u]: '%s'\n", nummem, gr->gr_mem[nummem]));
+#ifdef HAVE_GETPWNAM_R
+# ifdef HAVE_SOLARIS_GETPWNAM_R
+int getpwnam_r(const char *name, struct passwd *pwdst,
+              char *buf, int buflen, struct passwd **pwdstp)
+# else /* HAVE_SOLARIS_GETPWNAM_R */
+int getpwnam_r(const char *name, struct passwd *pwdst,
+              char *buf, size_t buflen, struct passwd **pwdstp)
+# endif /* HAVE_SOLARIS_GETPWNAM_R */
+{
+       if (!nss_wrapper_enabled()) {
+               return libc_getpwnam_r(name, pwdst, buf, buflen, pwdstp);
        }
 
-       NWRAP_DEBUG(("add group[%s:%s:%u:] with %u members\n",
-                    gr->gr_name, gr->gr_passwd, gr->gr_gid, nummem));
-
-       nwrap_gr->num++;
-       return true;
+       return nwrap_getpwnam_r(name, pwdst, buf, buflen, pwdstp);
 }
+#endif
 
-static void nwrap_gr_unload(struct nwrap_cache *nwrap)
+/****************************************************************************
+ *   GETPWUID
+ ***************************************************************************/
+
+static struct passwd *nwrap_getpwuid(uid_t uid)
 {
        int i;
-       struct nwrap_gr *nwrap_gr;
-       nwrap_gr = (struct nwrap_gr *)nwrap->private_data;
+       struct passwd *pwd;
 
-       if (nwrap_gr->list) {
-               for (i=0; i < nwrap_gr->num; i++) {
-                       if (nwrap_gr->list[i].gr_mem) {
-                               free(nwrap_gr->list[i].gr_mem);
-                       }
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               pwd = b->ops->nw_getpwuid(b, uid);
+               if (pwd) {
+                       return pwd;
                }
-               free(nwrap_gr->list);
        }
 
-       nwrap_gr->list = NULL;
-       nwrap_gr->num = 0;
-       nwrap_gr->idx = 0;
+       return NULL;
 }
 
-static int nwrap_gr_copy_r(const struct group *src, struct group *dst,
-                          char *buf, size_t buflen, struct group **dstp)
+struct passwd *getpwuid(uid_t uid)
 {
-       char *first;
-       char **lastm;
-       char *last = NULL;
-       off_t ofsb;
-       off_t ofsm;
-       off_t ofs;
-       unsigned i;
+       if (!nss_wrapper_enabled()) {
+               return libc_getpwuid(uid);
+       }
 
-       first = src->gr_name;
+       return nwrap_getpwuid(uid);
+}
 
-       lastm = src->gr_mem;
-       while (*lastm) {
-               last = *lastm;
-               lastm++;
-       }
+/****************************************************************************
+ *   GETPWUID_R
+ ***************************************************************************/
 
-       if (last == NULL) {
-               last = src->gr_passwd;
+static int nwrap_getpwuid_r(uid_t uid, struct passwd *pwdst,
+                           char *buf, size_t buflen, struct passwd **pwdstp)
+{
+       int i,ret;
+
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               ret = b->ops->nw_getpwuid_r(b, uid, pwdst, buf, buflen, pwdstp);
+               if (ret == ENOENT) {
+                       continue;
+               }
+               return ret;
        }
-       while (*last) last++;
 
-       ofsb = PTR_DIFF(last + 1, first);
-       ofsm = PTR_DIFF(lastm + 1, src->gr_mem);
+       return ENOENT;
+}
 
-       if ((ofsb + ofsm) > buflen) {
-               return ERANGE;
+#ifdef HAVE_SOLARIS_GETPWUID_R
+int getpwuid_r(uid_t uid, struct passwd *pwdst,
+              char *buf, int buflen, struct passwd **pwdstp)
+#else
+int getpwuid_r(uid_t uid, struct passwd *pwdst,
+              char *buf, size_t buflen, struct passwd **pwdstp)
+#endif
+{
+       if (!nss_wrapper_enabled()) {
+               return libc_getpwuid_r(uid, pwdst, buf, buflen, pwdstp);
        }
 
-       memcpy(buf, first, ofsb);
-       memcpy(buf + ofsb, src->gr_mem, ofsm);
+       return nwrap_getpwuid_r(uid, pwdst, buf, buflen, pwdstp);
+}
 
-       ofs = PTR_DIFF(src->gr_name, first);
-       dst->gr_name = buf + ofs;
-       ofs = PTR_DIFF(src->gr_passwd, first);
-       dst->gr_passwd = buf + ofs;
-       dst->gr_gid = src->gr_gid;
+/****************************************************************************
+ *   SETPWENT
+ ***************************************************************************/
 
-       dst->gr_mem = (char **)(buf + ofsb);
-       for (i=0; src->gr_mem[i]; i++) {
-               ofs = PTR_DIFF(src->gr_mem[i], first);
-               dst->gr_mem[i] = buf + ofs;
+static void nwrap_setpwent(void)
+{
+       int i;
+
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               b->ops->nw_setpwent(b);
        }
+}
 
-       if (dstp) {
-               *dstp = dst;
+void setpwent(void)
+{
+       if (!nss_wrapper_enabled()) {
+               libc_setpwent();
+               return;
        }
 
-       return 0;
+       nwrap_setpwent();
 }
 
-/* user functions */
-static struct passwd *nwrap_files_getpwnam(struct nwrap_backend *b,
-                                          const char *name)
+/****************************************************************************
+ *   GETPWENT
+ ***************************************************************************/
+
+static struct passwd *nwrap_getpwent(void)
 {
        int i;
+       struct passwd *pwd;
 
-       nwrap_files_cache_reload(nwrap_pw_global.cache);
-
-       for (i=0; i<nwrap_pw_global.num; i++) {
-               if (strcmp(nwrap_pw_global.list[i].pw_name, name) == 0) {
-                       NWRAP_DEBUG(("%s: user[%s] found\n",
-                                    __location__, name));
-                       return &nwrap_pw_global.list[i];
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               pwd = b->ops->nw_getpwent(b);
+               if (pwd) {
+                       return pwd;
                }
-               NWRAP_VERBOSE(("%s: user[%s] does not match [%s]\n",
-                              __location__, name,
-                              nwrap_pw_global.list[i].pw_name));
        }
 
-       NWRAP_DEBUG(("%s: user[%s] not found\n", __location__, name));
-
-       errno = ENOENT;
        return NULL;
 }
 
-static int nwrap_files_getpwnam_r(struct nwrap_backend *b,
-                                 const char *name, struct passwd *pwdst,
-                                 char *buf, size_t buflen, struct passwd **pwdstp)
+struct passwd *getpwent(void)
 {
-       struct passwd *pw;
-
-       pw = nwrap_files_getpwnam(b, name);
-       if (!pw) {
-               if (errno == 0) {
-                       return ENOENT;
-               }
-               return errno;
+       if (!nss_wrapper_enabled()) {
+               return libc_getpwent();
        }
 
-       return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
+       return nwrap_getpwent();
 }
 
-static struct passwd *nwrap_files_getpwuid(struct nwrap_backend *b,
-                                          uid_t uid)
-{
-       int i;
+/****************************************************************************
+ *   GETPWENT_R
+ ***************************************************************************/
 
-       nwrap_files_cache_reload(nwrap_pw_global.cache);
+static int nwrap_getpwent_r(struct passwd *pwdst, char *buf,
+                           size_t buflen, struct passwd **pwdstp)
+{
+       int i,ret;
 
-       for (i=0; i<nwrap_pw_global.num; i++) {
-               if (nwrap_pw_global.list[i].pw_uid == uid) {
-                       NWRAP_DEBUG(("%s: uid[%u] found\n",
-                                    __location__, uid));
-                       return &nwrap_pw_global.list[i];
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               ret = b->ops->nw_getpwent_r(b, pwdst, buf, buflen, pwdstp);
+               if (ret == ENOENT) {
+                       continue;
                }
-               NWRAP_VERBOSE(("%s: uid[%u] does not match [%u]\n",
-                              __location__, uid,
-                              nwrap_pw_global.list[i].pw_uid));
+               return ret;
        }
 
-       NWRAP_DEBUG(("%s: uid[%u] not found\n", __location__, uid));
-
-       errno = ENOENT;
-       return NULL;
+       return ENOENT;
 }
 
-static int nwrap_files_getpwuid_r(struct nwrap_backend *b,
-                                 uid_t uid, struct passwd *pwdst,
-                                 char *buf, size_t buflen, struct passwd **pwdstp)
+#ifdef HAVE_SOLARIS_GETPWENT_R
+struct passwd *getpwent_r(struct passwd *pwdst, char *buf, int buflen)
 {
-       struct passwd *pw;
+       struct passwd *pwdstp = NULL;
+       int rc;
 
-       pw = nwrap_files_getpwuid(b, uid);
-       if (!pw) {
-               if (errno == 0) {
-                       return ENOENT;
-               }
-               return errno;
+       if (!nss_wrapper_enabled()) {
+               return libc_getpwent_r(pwdst, buf, buflen);
+       }
+       rc = nwrap_getpwent_r(pwdst, buf, buflen, &pwdstp);
+       if (rc < 0) {
+               return NULL;
        }
 
-       return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
+       return pwdstp;
 }
-
-/* user enum functions */
-static void nwrap_files_setpwent(struct nwrap_backend *b)
+#else /* HAVE_SOLARIS_GETPWENT_R */
+int getpwent_r(struct passwd *pwdst, char *buf,
+              size_t buflen, struct passwd **pwdstp)
 {
-       nwrap_pw_global.idx = 0;
+       if (!nss_wrapper_enabled()) {
+               return libc_getpwent_r(pwdst, buf, buflen, pwdstp);
+       }
+
+       return nwrap_getpwent_r(pwdst, buf, buflen, pwdstp);
 }
+#endif /* HAVE_SOLARIS_GETPWENT_R */
 
-static struct passwd *nwrap_files_getpwent(struct nwrap_backend *b)
+/****************************************************************************
+ *   ENDPWENT
+ ***************************************************************************/
+
+static void nwrap_endpwent(void)
 {
-       struct passwd *pw;
+       int i;
 
-       if (nwrap_pw_global.idx == 0) {
-               nwrap_files_cache_reload(nwrap_pw_global.cache);
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               b->ops->nw_endpwent(b);
        }
+}
 
-       if (nwrap_pw_global.idx >= nwrap_pw_global.num) {
-               errno = ENOENT;
-               return NULL;
+void endpwent(void)
+{
+       if (!nss_wrapper_enabled()) {
+               libc_endpwent();
+               return;
        }
 
-       pw = &nwrap_pw_global.list[nwrap_pw_global.idx++];
-
-       NWRAP_VERBOSE(("%s: return user[%s] uid[%u]\n",
-                      __location__, pw->pw_name, pw->pw_uid));
-
-       return pw;
+       nwrap_endpwent();
 }
 
-static int nwrap_files_getpwent_r(struct nwrap_backend *b,
-                                 struct passwd *pwdst, char *buf,
-                                 size_t buflen, struct passwd **pwdstp)
+/****************************************************************************
+ *   INITGROUPS
+ ***************************************************************************/
+
+static int nwrap_initgroups(const char *user, gid_t group)
 {
-       struct passwd *pw;
+       int i;
 
-       pw = nwrap_files_getpwent(b);
-       if (!pw) {
-               if (errno == 0) {
-                       return ENOENT;
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               int rc;
+
+               rc = b->ops->nw_initgroups(b, user, group);
+               if (rc == 0) {
+                       return 0;
                }
-               return errno;
        }
 
-       return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
+       errno = ENOENT;
+       return -1;
 }
 
-static void nwrap_files_endpwent(struct nwrap_backend *b)
+int initgroups(const char *user, gid_t group)
 {
-       nwrap_pw_global.idx = 0;
-}
+       if (!nss_wrapper_enabled()) {
+               return libc_initgroups(user, group);
+       }
 
-/* misc functions */
-static int nwrap_files_initgroups(struct nwrap_backend *b,
-                                 const char *user, gid_t group)
-{
-       /* TODO: maybe we should also fake this... */
-       return EPERM;
+       return nwrap_initgroups(user, group);
 }
 
-/* group functions */
-static struct group *nwrap_files_getgrnam(struct nwrap_backend *b,
-                                         const char *name)
+/****************************************************************************
+ *   GETGRNAM
+ ***************************************************************************/
+
+static struct group *nwrap_getgrnam(const char *name)
 {
        int i;
+       struct group *grp;
 
-       nwrap_files_cache_reload(nwrap_gr_global.cache);
-
-       for (i=0; i<nwrap_gr_global.num; i++) {
-               if (strcmp(nwrap_gr_global.list[i].gr_name, name) == 0) {
-                       NWRAP_DEBUG(("%s: group[%s] found\n",
-                                    __location__, name));
-                       return &nwrap_gr_global.list[i];
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               grp = b->ops->nw_getgrnam(b, name);
+               if (grp) {
+                       return grp;
                }
-               NWRAP_VERBOSE(("%s: group[%s] does not match [%s]\n",
-                              __location__, name,
-                              nwrap_gr_global.list[i].gr_name));
        }
 
-       NWRAP_DEBUG(("%s: group[%s] not found\n", __location__, name));
-
-       errno = ENOENT;
        return NULL;
 }
 
-static int nwrap_files_getgrnam_r(struct nwrap_backend *b,
-                                 const char *name, struct group *grdst,
-                                 char *buf, size_t buflen, struct group **grdstp)
+struct group *getgrnam(const char *name)
 {
-       struct group *gr;
+       if (!nss_wrapper_enabled()) {
+               return libc_getgrnam(name);
+       }
 
-       gr = nwrap_files_getgrnam(b, name);
-       if (!gr) {
-               if (errno == 0) {
-                       return ENOENT;
+       return nwrap_getgrnam(name);
+}
+
+/****************************************************************************
+ *   GETGRNAM_R
+ ***************************************************************************/
+
+static int nwrap_getgrnam_r(const char *name, struct group *grdst,
+                           char *buf, size_t buflen, struct group **grdstp)
+{
+       int i, ret;
+
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               ret = b->ops->nw_getgrnam_r(b, name, grdst, buf, buflen, grdstp);
+               if (ret == ENOENT) {
+                       continue;
                }
-               return errno;
+               return ret;
        }
 
-       return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
+       return ENOENT;
 }
 
-static struct group *nwrap_files_getgrgid(struct nwrap_backend *b,
-                                         gid_t gid)
+#ifdef HAVE_GETGRNAM_R
+# ifdef HAVE_SOLARIS_GETGRNAM_R
+int getgrnam_r(const char *name, struct group *grp,
+               char *buf, int buflen, struct group **pgrp)
+# else /* HAVE_SOLARIS_GETGRNAM_R */
+int getgrnam_r(const char *name, struct group *grp,
+              char *buf, size_t buflen, struct group **pgrp)
+# endif /* HAVE_SOLARIS_GETGRNAM_R */
 {
-       int i;
+       if (!nss_wrapper_enabled()) {
+               return libc_getgrnam_r(name,
+                                      grp,
+                                      buf,
+                                      buflen,
+                                      pgrp);
+       }
 
-       nwrap_files_cache_reload(nwrap_gr_global.cache);
+       return nwrap_getgrnam_r(name, grp, buf, buflen, pgrp);
+}
+#endif /* HAVE_GETGRNAM_R */
 
-       for (i=0; i<nwrap_gr_global.num; i++) {
-               if (nwrap_gr_global.list[i].gr_gid == gid) {
-                       NWRAP_DEBUG(("%s: gid[%u] found\n",
-                                    __location__, gid));
-                       return &nwrap_gr_global.list[i];
+/****************************************************************************
+ *   GETGRGID
+ ***************************************************************************/
+
+static struct group *nwrap_getgrgid(gid_t gid)
+{
+       int i;
+       struct group *grp;
+
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               grp = b->ops->nw_getgrgid(b, gid);
+               if (grp) {
+                       return grp;
                }
-               NWRAP_VERBOSE(("%s: gid[%u] does not match [%u]\n",
-                              __location__, gid,
-                              nwrap_gr_global.list[i].gr_gid));
        }
 
-       NWRAP_DEBUG(("%s: gid[%u] not found\n", __location__, gid));
-
-       errno = ENOENT;
        return NULL;
 }
 
-static int nwrap_files_getgrgid_r(struct nwrap_backend *b,
-                                 gid_t gid, struct group *grdst,
-                                 char *buf, size_t buflen, struct group **grdstp)
+struct group *getgrgid(gid_t gid)
 {
-       struct group *gr;
-
-       gr = nwrap_files_getgrgid(b, gid);
-       if (!gr) {
-               if (errno == 0) {
-                       return ENOENT;
-               }
-               return errno;
+       if (!nss_wrapper_enabled()) {
+               return libc_getgrgid(gid);
        }
 
-       return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
+       return nwrap_getgrgid(gid);
 }
 
-/* group enum functions */
-static void nwrap_files_setgrent(struct nwrap_backend *b)
-{
-       nwrap_gr_global.idx = 0;
-}
+/****************************************************************************
+ *   GETGRGID_R
+ ***************************************************************************/
 
-static struct group *nwrap_files_getgrent(struct nwrap_backend *b)
+static int nwrap_getgrgid_r(gid_t gid, struct group *grdst,
+                           char *buf, size_t buflen, struct group **grdstp)
 {
-       struct group *gr;
-
-       if (nwrap_gr_global.idx == 0) {
-               nwrap_files_cache_reload(nwrap_gr_global.cache);
-       }
+       int i,ret;
 
-       if (nwrap_gr_global.idx >= nwrap_gr_global.num) {
-               errno = ENOENT;
-               return NULL;
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               ret = b->ops->nw_getgrgid_r(b, gid, grdst, buf, buflen, grdstp);
+               if (ret == ENOENT) {
+                       continue;
+               }
+               return ret;
        }
 
-       gr = &nwrap_gr_global.list[nwrap_gr_global.idx++];
+       return ENOENT;
+}
 
-       NWRAP_VERBOSE(("%s: return group[%s] gid[%u]\n",
-                      __location__, gr->gr_name, gr->gr_gid));
+#ifdef HAVE_GETGRGID_R
+# ifdef HAVE_SOLARIS_GETGRGID_R
+int getgrgid_r(gid_t gid, struct group *grdst,
+              char *buf, int buflen, struct group **grdstp)
+# else /* HAVE_SOLARIS_GETGRGID_R */
+int getgrgid_r(gid_t gid, struct group *grdst,
+              char *buf, size_t buflen, struct group **grdstp)
+# endif /* HAVE_SOLARIS_GETGRGID_R */
+{
+       if (!nss_wrapper_enabled()) {
+               return libc_getgrgid_r(gid, grdst, buf, buflen, grdstp);
+       }
 
-       return gr;
+       return nwrap_getgrgid_r(gid, grdst, buf, buflen, grdstp);
 }
+#endif
 
-static int nwrap_files_getgrent_r(struct nwrap_backend *b,
-                                 struct group *grdst, char *buf,
-                                 size_t buflen, struct group **grdstp)
+/****************************************************************************
+ *   SETGRENT
+ ***************************************************************************/
+
+static void nwrap_setgrent(void)
 {
-       struct group *gr;
+       int i;
 
-       gr = nwrap_files_getgrent(b);
-       if (!gr) {
-               if (errno == 0) {
-                       return ENOENT;
-               }
-               return errno;
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               b->ops->nw_setgrent(b);
        }
-
-       return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
 }
 
-static void nwrap_files_endgrent(struct nwrap_backend *b)
+#ifdef HAVE_BSD_SETGRENT
+int setgrent(void)
+#else
+void setgrent(void)
+#endif
 {
-       nwrap_gr_global.idx = 0;
-}
+       if (!nss_wrapper_enabled()) {
+               libc_setgrent();
+               goto out;
+       }
 
-/*
- * module backend
- */
+       nwrap_setgrent();
 
-#ifndef SAFE_FREE
-#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0)
+out:
+#ifdef HAVE_BSD_SETGRENT
+       return 0;
+#else
+       return;
 #endif
+}
 
-static struct passwd *nwrap_module_getpwnam(struct nwrap_backend *b,
-                                           const char *name)
+/****************************************************************************
+ *   GETGRENT
+ ***************************************************************************/
+
+static struct group *nwrap_getgrent(void)
 {
-       static struct passwd pwd;
-       static char buf[1000];
-       NSS_STATUS status;
+       int i;
+       struct group *grp;
 
-       if (!b->fns->_nss_getpwnam_r) {
-               return NULL;
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               grp = b->ops->nw_getgrent(b);
+               if (grp) {
+                       return grp;
+               }
        }
 
-       status = b->fns->_nss_getpwnam_r(name, &pwd, buf, sizeof(buf), &errno);
-       if (status == NSS_STATUS_NOTFOUND) {
-               return NULL;
-       }
-       if (status != NSS_STATUS_SUCCESS) {
-               return NULL;
-       }
-       return &pwd;
+       return NULL;
 }
 
-static int nwrap_module_getpwnam_r(struct nwrap_backend *b,
-                                  const char *name, struct passwd *pwdst,
-                                  char *buf, size_t buflen, struct passwd **pwdstp)
+struct group *getgrent(void)
 {
-       int ret;
-
-       if (!b->fns->_nss_getpwnam_r) {
-               return NSS_STATUS_NOTFOUND;
+       if (!nss_wrapper_enabled()) {
+               return libc_getgrent();
        }
 
-       ret = b->fns->_nss_getpwnam_r(name, pwdst, buf, buflen, &errno);
-       switch (ret) {
-       case NSS_STATUS_SUCCESS:
-               return 0;
-       case NSS_STATUS_NOTFOUND:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ENOENT;
-       case NSS_STATUS_TRYAGAIN:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ERANGE;
-       default:
-               if (errno != 0) {
-                       return errno;
+       return nwrap_getgrent();
+}
+
+/****************************************************************************
+ *   GETGRENT_R
+ ***************************************************************************/
+
+static int nwrap_getgrent_r(struct group *grdst, char *buf,
+                           size_t buflen, struct group **grdstp)
+{
+       int i,ret;
+
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               ret = b->ops->nw_getgrent_r(b, grdst, buf, buflen, grdstp);
+               if (ret == ENOENT) {
+                       continue;
                }
                return ret;
        }
+
+       return ENOENT;
 }
 
-static struct passwd *nwrap_module_getpwuid(struct nwrap_backend *b,
-                                           uid_t uid)
+#ifdef HAVE_SOLARIS_GETGRENT_R
+struct group *getgrent_r(struct group *src, char *buf, int buflen)
 {
-       static struct passwd pwd;
-       static char buf[1000];
-       NSS_STATUS status;
+       struct group *grdstp = NULL;
+       int rc;
 
-       if (!b->fns->_nss_getpwuid_r) {
-               return NULL;
+       if (!nss_wrapper_enabled()) {
+               return libc_getgrent_r(src, buf, buflen);
        }
 
-       status = b->fns->_nss_getpwuid_r(uid, &pwd, buf, sizeof(buf), &errno);
-       if (status == NSS_STATUS_NOTFOUND) {
+       rc = nwrap_getgrent_r(src, buf, buflen, &grdstp);
+       if (rc < 0) {
                return NULL;
        }
-       if (status != NSS_STATUS_SUCCESS) {
-               return NULL;
+
+       return grdstp;
+}
+#else /* HAVE_SOLARIS_GETGRENT_R */
+int getgrent_r(struct group *src, char *buf,
+              size_t buflen, struct group **grdstp)
+{
+       if (!nss_wrapper_enabled()) {
+               return libc_getgrent_r(src, buf, buflen, grdstp);
        }
-       return &pwd;
+
+       return nwrap_getgrent_r(src, buf, buflen, grdstp);
 }
+#endif /* HAVE_SOLARIS_GETGRENT_R */
 
-static int nwrap_module_getpwuid_r(struct nwrap_backend *b,
-                                  uid_t uid, struct passwd *pwdst,
-                                  char *buf, size_t buflen, struct passwd **pwdstp)
-{
-       int ret;
+/****************************************************************************
+ *   ENDGRENT
+ ***************************************************************************/
 
-       if (!b->fns->_nss_getpwuid_r) {
-               return ENOENT;
-       }
+static void nwrap_endgrent(void)
+{
+       int i;
 
-       ret = b->fns->_nss_getpwuid_r(uid, pwdst, buf, buflen, &errno);
-       switch (ret) {
-       case NSS_STATUS_SUCCESS:
-               return 0;
-       case NSS_STATUS_NOTFOUND:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ENOENT;
-       case NSS_STATUS_TRYAGAIN:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ERANGE;
-       default:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ret;
+       for (i=0; i < nwrap_main_global->num_backends; i++) {
+               struct nwrap_backend *b = &nwrap_main_global->backends[i];
+               b->ops->nw_endgrent(b);
        }
 }
 
-static void nwrap_module_setpwent(struct nwrap_backend *b)
+void endgrent(void)
 {
-       if (!b->fns->_nss_setpwent) {
+       if (!nss_wrapper_enabled()) {
+               libc_endgrent();
                return;
        }
 
-       b->fns->_nss_setpwent();
+       nwrap_endgrent();
 }
 
-static struct passwd *nwrap_module_getpwent(struct nwrap_backend *b)
+/****************************************************************************
+ *   GETGROUPLIST
+ ***************************************************************************/
+
+#ifdef HAVE_GETGROUPLIST
+static int nwrap_getgrouplist(const char *user, gid_t group,
+                             gid_t *groups, int *ngroups)
 {
-       static struct passwd pwd;
-       static char buf[1000];
-       NSS_STATUS status;
+       struct group *grp;
+       gid_t *groups_tmp;
+       int count = 1;
 
-       if (!b->fns->_nss_getpwent_r) {
-               return NULL;
-       }
+       NWRAP_LOG(NWRAP_LOG_DEBUG, "getgrouplist called for %s", user);
 
-       status = b->fns->_nss_getpwent_r(&pwd, buf, sizeof(buf), &errno);
-       if (status == NSS_STATUS_NOTFOUND) {
-               return NULL;
-       }
-       if (status != NSS_STATUS_SUCCESS) {
-               return NULL;
+       groups_tmp = (gid_t *)malloc(count * sizeof(gid_t));
+       if (!groups_tmp) {
+               NWRAP_LOG(NWRAP_LOG_ERROR, "Out of memory");
+               errno = ENOMEM;
+               return -1;
        }
-       return &pwd;
-}
+       groups_tmp[0] = group;
 
-static int nwrap_module_getpwent_r(struct nwrap_backend *b,
-                                  struct passwd *pwdst, char *buf,
-                                  size_t buflen, struct passwd **pwdstp)
-{
-       int ret;
+       nwrap_setgrent();
+       while ((grp = nwrap_getgrent()) != NULL) {
+               int i = 0;
 
-       if (!b->fns->_nss_getpwent_r) {
-               return ENOENT;
-       }
+               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                         "Inspecting %s for group membership",
+                         grp->gr_name);
 
-       ret = b->fns->_nss_getpwent_r(pwdst, buf, buflen, &errno);
-       switch (ret) {
-       case NSS_STATUS_SUCCESS:
-               return 0;
-       case NSS_STATUS_NOTFOUND:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ENOENT;
-       case NSS_STATUS_TRYAGAIN:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ERANGE;
-       default:
-               if (errno != 0) {
-                       return errno;
+               for (i=0; grp->gr_mem && grp->gr_mem[i] != NULL; i++) {
+
+                       if (group != grp->gr_gid &&
+                           (strcmp(user, grp->gr_mem[i]) == 0)) {
+
+                               NWRAP_LOG(NWRAP_LOG_DEBUG,
+                                         "%s is member of %s",
+                                         user,
+                                         grp->gr_name);
+
+                               groups_tmp = (gid_t *)realloc(groups_tmp, (count + 1) * sizeof(gid_t));
+                               if (!groups_tmp) {
+                                       NWRAP_LOG(NWRAP_LOG_ERROR,
+                                                 "Out of memory");
+                                       errno = ENOMEM;
+                                       return -1;
+                               }
+                               groups_tmp[count] = grp->gr_gid;
+
+                               count++;
+                       }
                }
-               return ret;
        }
+
+       nwrap_endgrent();
+
+       NWRAP_LOG(NWRAP_LOG_DEBUG,
+                 "%s is member of %d groups",
+                 user, *ngroups);
+
+       if (*ngroups < count) {
+               *ngroups = count;
+               free(groups_tmp);
+               return -1;
+       }
+
+       *ngroups = count;
+       memcpy(groups, groups_tmp, count * sizeof(gid_t));
+       free(groups_tmp);
+
+       return count;
 }
 
-static void nwrap_module_endpwent(struct nwrap_backend *b)
+int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups)
 {
-       if (!b->fns->_nss_endpwent) {
-               return;
+       if (!nss_wrapper_enabled()) {
+               return libc_getgrouplist(user, group, groups, ngroups);
        }
 
-       b->fns->_nss_endpwent();
+       return nwrap_getgrouplist(user, group, groups, ngroups);
 }
+#endif
 
-static int nwrap_module_initgroups(struct nwrap_backend *b,
-                                  const char *user, gid_t group)
+/**********************************************************
+ * SHADOW
+ **********************************************************/
+
+#if defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM)
+
+#ifdef HAVE_SETSPENT
+static void nwrap_setspent(void)
 {
-       gid_t *groups;
-       long int start;
-       long int size;
+       nwrap_files_setspent();
+}
 
-       if (!b->fns->_nss_initgroups) {
-               return NSS_STATUS_UNAVAIL;
+void setspent(void)
+{
+       if (!nss_wrapper_shadow_enabled()) {
+               return;
        }
 
-       return b->fns->_nss_initgroups(user, group, &start, &size, &groups, 0, &errno);
+       nwrap_setspent();
 }
 
-static struct group *nwrap_module_getgrnam(struct nwrap_backend *b,
-                                          const char *name)
+static struct spwd *nwrap_getspent(void)
 {
-       static struct group grp;
-       static char *buf;
-       static int buflen = 1000;
-       NSS_STATUS status;
+       return nwrap_files_getspent();
+}
 
-       if (!b->fns->_nss_getgrnam_r) {
+struct spwd *getspent(void)
+{
+       if (!nss_wrapper_shadow_enabled()) {
                return NULL;
        }
 
-       if (!buf) {
-               buf = (char *)malloc(buflen);
-       }
-again:
-       status = b->fns->_nss_getgrnam_r(name, &grp, buf, buflen, &errno);
-       if (status == NSS_STATUS_TRYAGAIN) {
-               buflen *= 2;
-               buf = (char *)realloc(buf, buflen);
-               if (!buf) {
-                       return NULL;
-               }
-               goto again;
-       }
-       if (status == NSS_STATUS_NOTFOUND) {
-               SAFE_FREE(buf);
-               return NULL;
-       }
-       if (status != NSS_STATUS_SUCCESS) {
-               SAFE_FREE(buf);
-               return NULL;
-       }
-       return &grp;
+       return nwrap_getspent();
 }
 
-static int nwrap_module_getgrnam_r(struct nwrap_backend *b,
-                                  const char *name, struct group *grdst,
-                                  char *buf, size_t buflen, struct group **grdstp)
+static void nwrap_endspent(void)
 {
-       int ret;
+       nwrap_files_endspent();
+}
 
-       if (!b->fns->_nss_getgrnam_r) {
-               return ENOENT;
+void endspent(void)
+{
+       if (!nss_wrapper_shadow_enabled()) {
+               return;
        }
 
-       ret = b->fns->_nss_getgrnam_r(name, grdst, buf, buflen, &errno);
-       switch (ret) {
-       case NSS_STATUS_SUCCESS:
-               return 0;
-       case NSS_STATUS_NOTFOUND:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ENOENT;
-       case NSS_STATUS_TRYAGAIN:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ERANGE;
-       default:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ret;
-       }
+       nwrap_endspent();
 }
+#endif /* HAVE_SETSPENT */
 
-static struct group *nwrap_module_getgrgid(struct nwrap_backend *b,
-                                          gid_t gid)
+static struct spwd *nwrap_getspnam(const char *name)
 {
-       static struct group grp;
-       static char *buf;
-       static int buflen = 1000;
-       NSS_STATUS status;
+       return nwrap_files_getspnam(name);
+}
 
-       if (!b->fns->_nss_getgrgid_r) {
+struct spwd *getspnam(const char *name)
+{
+       if (!nss_wrapper_shadow_enabled()) {
                return NULL;
        }
 
-       if (!buf) {
-               buf = (char *)malloc(buflen);
-       }
-
-again:
-       status = b->fns->_nss_getgrgid_r(gid, &grp, buf, buflen, &errno);
-       if (status == NSS_STATUS_TRYAGAIN) {
-               buflen *= 2;
-               buf = (char *)realloc(buf, buflen);
-               if (!buf) {
-                       return NULL;
-               }
-               goto again;
-       }
-       if (status == NSS_STATUS_NOTFOUND) {
-               SAFE_FREE(buf);
-               return NULL;
-       }
-       if (status != NSS_STATUS_SUCCESS) {
-               SAFE_FREE(buf);
-               return NULL;
-       }
-       return &grp;
+       return nwrap_getspnam(name);
 }
 
-static int nwrap_module_getgrgid_r(struct nwrap_backend *b,
-                                  gid_t gid, struct group *grdst,
-                                  char *buf, size_t buflen, struct group **grdstp)
-{
-       int ret;
+#endif /* defined(HAVE_SHADOW_H) && defined(HAVE_GETSPNAM) */
 
-       if (!b->fns->_nss_getgrgid_r) {
-               return ENOENT;
-       }
+/**********************************************************
+ * NETDB
+ **********************************************************/
 
-       ret = b->fns->_nss_getgrgid_r(gid, grdst, buf, buflen, &errno);
-       switch (ret) {
-       case NSS_STATUS_SUCCESS:
+static void nwrap_sethostent(int stayopen) {
+       (void) stayopen; /* ignored */
+
+       nwrap_files_sethostent();
+}
+
+#ifdef HAVE_SOLARIS_SETHOSTENT
+int sethostent(int stayopen)
+{
+       if (!nss_wrapper_hosts_enabled()) {
+               libc_sethostent(stayopen);
                return 0;
-       case NSS_STATUS_NOTFOUND:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ENOENT;
-       case NSS_STATUS_TRYAGAIN:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ERANGE;
-       default:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ret;
        }
-}
 
-static void nwrap_module_setgrent(struct nwrap_backend *b)
+       nwrap_sethostent(stayopen);
+
+       return 0;
+}
+#else /* HAVE_SOLARIS_SETHOSTENT */
+void sethostent(int stayopen)
 {
-       if (!b->fns->_nss_setgrent) {
+       if (!nss_wrapper_hosts_enabled()) {
+               libc_sethostent(stayopen);
                return;
        }
 
-       b->fns->_nss_setgrent();
+       nwrap_sethostent(stayopen);
 }
+#endif /* HAVE_SOLARIS_SETHOSTENT */
 
-static struct group *nwrap_module_getgrent(struct nwrap_backend *b)
+static struct hostent *nwrap_gethostent(void)
 {
-       static struct group grp;
-       static char *buf;
-       static int buflen = 1024;
-       NSS_STATUS status;
+       return nwrap_files_gethostent();
+}
 
-       if (!b->fns->_nss_getgrent_r) {
-               return NULL;
+struct hostent *gethostent(void) {
+       if (!nss_wrapper_hosts_enabled()) {
+               return libc_gethostent();
        }
 
-       if (!buf) {
-               buf = (char *)malloc(buflen);
-       }
+       return nwrap_gethostent();
+}
 
-again:
-       status = b->fns->_nss_getgrent_r(&grp, buf, buflen, &errno);
-       if (status == NSS_STATUS_TRYAGAIN) {
-               buflen *= 2;
-               buf = (char *)realloc(buf, buflen);
-               if (!buf) {
-                       return NULL;
-               }
-               goto again;
-       }
-       if (status == NSS_STATUS_NOTFOUND) {
-               SAFE_FREE(buf);
-               return NULL;
-       }
-       if (status != NSS_STATUS_SUCCESS) {
-               SAFE_FREE(buf);
-               return NULL;
-       }
-       return &grp;
+static void nwrap_endhostent(void) {
+       nwrap_files_endhostent();
 }
 
-static int nwrap_module_getgrent_r(struct nwrap_backend *b,
-                                  struct group *grdst, char *buf,
-                                  size_t buflen, struct group **grdstp)
+#ifdef HAVE_SOLARIS_ENDHOSTENT
+int endhostent(void)
 {
-       int ret;
-
-       if (!b->fns->_nss_getgrent_r) {
-               return ENOENT;
-       }
-
-       ret = b->fns->_nss_getgrent_r(grdst, buf, buflen, &errno);
-       switch (ret) {
-       case NSS_STATUS_SUCCESS:
+       if (!nss_wrapper_hosts_enabled()) {
+               libc_endhostent();
                return 0;
-       case NSS_STATUS_NOTFOUND:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ENOENT;
-       case NSS_STATUS_TRYAGAIN:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ERANGE;
-       default:
-               if (errno != 0) {
-                       return errno;
-               }
-               return ret;
        }
-}
 
-static void nwrap_module_endgrent(struct nwrap_backend *b)
+       nwrap_endhostent();
+
+       return 0;
+}
+#else /* HAVE_SOLARIS_ENDHOSTENT */
+void endhostent(void)
 {
-       if (!b->fns->_nss_endgrent) {
+       if (!nss_wrapper_hosts_enabled()) {
+               libc_endhostent();
                return;
        }
 
-       b->fns->_nss_endgrent();
+       nwrap_endhostent();
 }
+#endif /* HAVE_SOLARIS_ENDHOSTENT */
 
-/*
- * PUBLIC interface
- */
-
-_PUBLIC_ struct passwd *nwrap_getpwnam(const char *name)
+#ifdef BSD
+/* BSD implementation stores data in thread local storage but GLIBC does not */
+static __thread struct hostent user_he;
+static __thread struct nwrap_vector user_addrlist;
+#else
+static struct hostent user_he;
+static struct nwrap_vector user_addrlist;
+#endif /* BSD */
+static struct hostent *nwrap_gethostbyname(const char *name)
 {
-       int i;
-       struct passwd *pwd;
-
-       if (!nwrap_enabled()) {
-               return real_getpwnam(name);
-       }
-
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               pwd = b->ops->nw_getpwnam(b, name);
-               if (pwd) {
-                       return pwd;
-               }
+       if (nwrap_files_gethostbyname(name, AF_UNSPEC, &user_he, &user_addrlist) == -1) {
+               return NULL;
        }
-
-       return NULL;
+       return &user_he;
 }
 
-_PUBLIC_ int nwrap_getpwnam_r(const char *name, struct passwd *pwdst,
-                             char *buf, size_t buflen, struct passwd **pwdstp)
+struct hostent *gethostbyname(const char *name)
 {
-       int i,ret;
-
-       if (!nwrap_enabled()) {
-               return real_getpwnam_r(name, pwdst, buf, buflen, pwdstp);
-       }
-
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               ret = b->ops->nw_getpwnam_r(b, name, pwdst, buf, buflen, pwdstp);
-               if (ret == ENOENT) {
-                       continue;
-               }
-               return ret;
+       if (!nss_wrapper_hosts_enabled()) {
+               return libc_gethostbyname(name);
        }
 
-       return ENOENT;
+       return nwrap_gethostbyname(name);
 }
 
-_PUBLIC_ struct passwd *nwrap_getpwuid(uid_t uid)
+/* This is a GNU extension - Also can be found on BSD systems */
+#ifdef HAVE_GETHOSTBYNAME2
+#ifdef BSD
+/* BSD implementation stores data in  thread local storage but GLIBC not */
+static __thread struct hostent user_he2;
+static __thread struct nwrap_vector user_addrlist2;
+#else
+static struct hostent user_he2;
+static struct nwrap_vector user_addrlist2;
+#endif /* BSD */
+static struct hostent *nwrap_gethostbyname2(const char *name, int af)
 {
-       int i;
-       struct passwd *pwd;
-
-       if (!nwrap_enabled()) {
-               return real_getpwuid(uid);
+       if (nwrap_files_gethostbyname(name, af, &user_he2, &user_addrlist2) == -1) {
+               return NULL;
        }
+       return &user_he2;
+}
 
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               pwd = b->ops->nw_getpwuid(b, uid);
-               if (pwd) {
-                       return pwd;
-               }
+struct hostent *gethostbyname2(const char *name, int af)
+{
+       if (!nss_wrapper_hosts_enabled()) {
+               return libc_gethostbyname2(name, af);
        }
 
-       return NULL;
+       return nwrap_gethostbyname2(name, af);
 }
+#endif
 
-_PUBLIC_ int nwrap_getpwuid_r(uid_t uid, struct passwd *pwdst,
-                             char *buf, size_t buflen, struct passwd **pwdstp)
+static struct hostent *nwrap_gethostbyaddr(const void *addr,
+                                          socklen_t len, int type)
 {
-       int i,ret;
-
-       if (!nwrap_enabled()) {
-               return real_getpwuid_r(uid, pwdst, buf, buflen, pwdstp);
-       }
+       return nwrap_files_gethostbyaddr(addr, len, type);
+}
 
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               ret = b->ops->nw_getpwuid_r(b, uid, pwdst, buf, buflen, pwdstp);
-               if (ret == ENOENT) {
-                       continue;
-               }
-               return ret;
+struct hostent *gethostbyaddr(const void *addr,
+                             socklen_t len, int type)
+{
+       if (!nss_wrapper_hosts_enabled()) {
+               return libc_gethostbyaddr(addr, len, type);
        }
 
-       return ENOENT;
+       return nwrap_gethostbyaddr(addr, len, type);
 }
 
-_PUBLIC_ void nwrap_setpwent(void)
+static const struct addrinfo default_hints =
 {
-       int i;
+       .ai_flags = AI_ADDRCONFIG|AI_V4MAPPED,
+       .ai_family = AF_UNSPEC,
+       .ai_socktype = 0,
+       .ai_protocol = 0,
+       .ai_addrlen = 0,
+       .ai_addr = NULL,
+       .ai_canonname = NULL,
+       .ai_next = NULL
+};
 
-       if (!nwrap_enabled()) {
-               real_setpwent();
-               return;
-       }
+static int nwrap_convert_he_ai(const struct hostent *he,
+                              unsigned short port,
+                              const struct addrinfo *hints,
+                              struct addrinfo **pai,
+                              bool skip_canonname)
+{
+       struct addrinfo *ai;
+       socklen_t socklen;
 
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               b->ops->nw_setpwent(b);
+       if (he == NULL) {
+               return EAI_MEMORY;
        }
-}
 
-_PUBLIC_ struct passwd *nwrap_getpwent(void)
-{
-       int i;
-       struct passwd *pwd;
+       switch (he->h_addrtype) {
+               case AF_INET:
+                       socklen = sizeof(struct sockaddr_in);
+                       break;
+#ifdef HAVE_IPV6
+               case AF_INET6:
+                       socklen = sizeof(struct sockaddr_in6);
+                       break;
+#endif
+               default:
+                       return EAI_FAMILY;
+       }
 
-       if (!nwrap_enabled()) {
-               return real_getpwent();
+       ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) + socklen);
+       if (ai == NULL) {
+               return EAI_MEMORY;
        }
 
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               pwd = b->ops->nw_getpwent(b);
-               if (pwd) {
-                       return pwd;
+       ai->ai_flags = hints->ai_flags;
+       ai->ai_family = he->h_addrtype;
+       ai->ai_socktype = hints->ai_socktype;
+       ai->ai_protocol = hints->ai_protocol;
+       ai->ai_canonname = NULL;
+
+       if (ai->ai_socktype == 0) {
+               ai->ai_socktype = SOCK_DGRAM;
+       }
+       if (ai->ai_protocol == 0) {
+               if (ai->ai_socktype == SOCK_DGRAM) {
+                       ai->ai_protocol = IPPROTO_UDP;
+               } else if (ai->ai_socktype == SOCK_STREAM) {
+                       ai->ai_protocol = IPPROTO_TCP;
                }
        }
 
-       return NULL;
-}
+       ai->ai_addrlen = socklen;
+       ai->ai_addr = (void *)(ai + 1);
 
-_PUBLIC_ int nwrap_getpwent_r(struct passwd *pwdst, char *buf,
-                             size_t buflen, struct passwd **pwdstp)
-{
-       int i,ret;
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+       ai->ai_addr->sa_len = socklen;
+#endif
+       ai->ai_addr->sa_family = he->h_addrtype;
+
+       switch (he->h_addrtype) {
+               case AF_INET:
+               {
+                       struct sockaddr_in *sinp =
+                               (struct sockaddr_in *) ai->ai_addr;
+
+                       memset(sinp, 0, sizeof(struct sockaddr_in));
+
+                       sinp->sin_port = htons(port);
+                       sinp->sin_family = AF_INET;
+
+                       memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
+                       memcpy(&sinp->sin_addr, he->h_addr_list[0], he->h_length);
 
-       if (!nwrap_enabled()) {
-#ifdef SOLARIS_GETPWENT_R
-               struct passwd *pw;
-               pw = real_getpwent_r(pwdst, buf, buflen);
-               if (!pw) {
-                       if (errno == 0) {
-                               return ENOENT;
-                       }
-                       return errno;
                }
-               if (pwdstp) {
-                       *pwdstp = pw;
+               break;
+#ifdef HAVE_IPV6
+               case AF_INET6:
+               {
+                       struct sockaddr_in6 *sin6p =
+                               (struct sockaddr_in6 *) ai->ai_addr;
+
+                       memset(sin6p, 0, sizeof(struct sockaddr_in6));
+
+                       sin6p->sin6_port = htons(port);
+                       sin6p->sin6_family = AF_INET6;
+
+                       memcpy(&sin6p->sin6_addr,
+                              he->h_addr_list[0],
+                              he->h_length);
                }
-               return 0;
-#else
-               return real_getpwent_r(pwdst, buf, buflen, pwdstp);
+               break;
 #endif
        }
 
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               ret = b->ops->nw_getpwent_r(b, pwdst, buf, buflen, pwdstp);
-               if (ret == ENOENT) {
-                       continue;
+       ai->ai_next = NULL;
+
+       if (he->h_name && !skip_canonname) {
+               ai->ai_canonname = strdup(he->h_name);
+               if (ai->ai_canonname == NULL) {
+                       freeaddrinfo(ai);
+                       return EAI_MEMORY;
                }
-               return ret;
        }
 
-       return ENOENT;
+       *pai = ai;
+       return 0;
 }
 
-_PUBLIC_ void nwrap_endpwent(void)
+static int nwrap_getaddrinfo(const char *node,
+                            const char *service,
+                            const struct addrinfo *hints,
+                            struct addrinfo **res)
 {
-       int i;
+       struct addrinfo *ai = NULL;
+       unsigned short port = 0;
+       struct {
+               int family;
+               union {
+                       struct in_addr v4;
+#ifdef HAVE_IPV6
+                       struct in6_addr v6;
+               } in;
+#endif
+       } addr = {
+               .family = AF_UNSPEC,
+       };
+       int rc;
 
-       if (!nwrap_enabled()) {
-               real_endpwent();
-               return;
+       if (node == NULL && service == NULL) {
+               return EAI_NONAME;
        }
 
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               b->ops->nw_endpwent(b);
+       if (hints == NULL) {
+               hints = &default_hints;
        }
-}
 
-_PUBLIC_ int nwrap_initgroups(const char *user, gid_t group)
-{
-       int i;
-
-       if (!nwrap_enabled()) {
-               return real_initgroups(user, group);
+        /* EAI_BADFLAGS
+              hints.ai_flags   contains   invalid  flags;  or,  hints.ai_flags
+              included AI_CANONNAME and name was NULL.
+       */
+       if ((hints->ai_flags & AI_CANONNAME) && (node == NULL)) {
+               return EAI_BADFLAGS;
        }
 
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               return b->ops->nw_initgroups(b, user, group);
+       /* If no node has been specified, let glibc deal with it */
+       if (node == NULL) {
+               int ret;
+               struct addrinfo *p = NULL;
+
+               ret = libc_getaddrinfo(node, service, hints, &p);
+
+               if (ret == 0) {
+                       *res = p;
+               }
+               return ret;
        }
 
-       errno = ENOENT;
-       return -1;
-}
+       if (service != NULL && service[0] != '\0') {
+               const char *proto = NULL;
+               struct servent *s;
+               char *end_ptr;
+               long sl;
 
-_PUBLIC_ struct group *nwrap_getgrnam(const char *name)
-{
-       int i;
-       struct group *grp;
+               errno = 0;
+               sl = strtol(service, &end_ptr, 10);
 
-       if (!nwrap_enabled()) {
-               return real_getgrnam(name);
-       }
+               if (*end_ptr == '\0') {
+                       port = sl;
+                       goto valid_port;
+               } else if (hints->ai_flags & AI_NUMERICSERV) {
+                       return EAI_NONAME;
+               }
 
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               grp = b->ops->nw_getgrnam(b, name);
-               if (grp) {
-                       return grp;
+               if (hints->ai_protocol != 0) {
+                       struct protoent *pent;
+
+                       pent = getprotobynumber(hints->ai_protocol);
+                       if (pent != NULL) {
+                               proto = pent->p_name;
+                       }
                }
-       }
 
-       return NULL;
-}
+               s = getservbyname(service, proto);
+               if (s == NULL) {
+                       return EAI_NONAME;
+               }
+               port = ntohs(s->s_port);
+       }
 
-_PUBLIC_ int nwrap_getgrnam_r(const char *name, struct group *grdst,
-                             char *buf, size_t buflen, struct group **grdstp)
-{
-       int i,ret;
+valid_port:
 
-       if (!nwrap_enabled()) {
-               return real_getgrnam_r(name, grdst, buf, buflen, grdstp);
+       rc = inet_pton(AF_INET, node, &addr.in.v4);
+       if (rc == 1) {
+               addr.family = AF_INET;
+       }
+#ifdef HAVE_IPV6
+       if (addr.family == AF_UNSPEC) {
+               rc = inet_pton(AF_INET6, node, &addr.in.v6);
+               if (rc == 1) {
+                       addr.family = AF_INET6;
+               }
        }
+#endif
 
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               ret = b->ops->nw_getgrnam_r(b, name, grdst, buf, buflen, grdstp);
-               if (ret == ENOENT) {
-                       continue;
+       if (addr.family == AF_UNSPEC) {
+              if (hints->ai_flags & AI_NUMERICHOST) {
+                       return EAI_NONAME;
                }
-               return ret;
+       } else if ((hints->ai_family != AF_UNSPEC) &&
+                  (hints->ai_family != addr.family))
+       {
+               return EAI_ADDRFAMILY;
+       }
+
+       rc = nwrap_files_getaddrinfo(node, port, hints, &ai);
+       if (rc != 0) {
+               int ret;
+               struct addrinfo *p = NULL;
+
+               ret = libc_getaddrinfo(node, service, hints, &p);
+
+               if (ret == 0) {
+                       /*
+                        * nwrap_files_getaddrinfo failed, but libc was
+                        * successful -- use the result from libc.
+                        */
+                       *res = p;
+                       return 0;
+               }
+
+               return rc;
        }
 
-       return ENOENT;
-}
+       /*
+        * If the socktype was not specified, duplicate
+        * each ai returned, so that we have variants for
+        * both UDP and TCP.
+        */
+       if (hints->ai_socktype == 0) {
+               struct addrinfo *ai_cur;
 
-_PUBLIC_ struct group *nwrap_getgrgid(gid_t gid)
-{
-       int i;
-       struct group *grp;
+               /* freeaddrinfo() frees ai_canonname and ai so allocate them */
+               for (ai_cur = ai; ai_cur != NULL; ai_cur = ai_cur->ai_next) {
+                       struct addrinfo *ai_new;
 
-       if (!nwrap_enabled()) {
-               return real_getgrgid(gid);
-       }
+                       /* duplicate the current entry */
 
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               grp = b->ops->nw_getgrgid(b, gid);
-               if (grp) {
-                       return grp;
-               }
-       }
+                       ai_new = malloc(sizeof(struct addrinfo));
+                       if (ai_new == NULL) {
+                               freeaddrinfo(ai);
+                               return EAI_MEMORY;
+                       }
 
-       return NULL;
-}
+                       memcpy(ai_new, ai_cur, sizeof(struct addrinfo));
+                       ai_new->ai_next = NULL;
 
-_PUBLIC_ int nwrap_getgrgid_r(gid_t gid, struct group *grdst,
-                             char *buf, size_t buflen, struct group **grdstp)
-{
-       int i,ret;
+                       /* We need a deep copy or freeaddrinfo() will blow up */
+                       if (ai_cur->ai_canonname != NULL) {
+                               ai_new->ai_canonname =
+                                       strdup(ai_cur->ai_canonname);
+                       }
 
-       if (!nwrap_enabled()) {
-               return real_getgrgid_r(gid, grdst, buf, buflen, grdstp);
-       }
+                       if (ai_cur->ai_socktype == SOCK_DGRAM) {
+                               ai_new->ai_socktype = SOCK_STREAM;
+                       } else if (ai_cur->ai_socktype == SOCK_STREAM) {
+                               ai_new->ai_socktype = SOCK_DGRAM;
+                       }
+                       if (ai_cur->ai_protocol == IPPROTO_TCP) {
+                               ai_new->ai_protocol = IPPROTO_UDP;
+                       } else if (ai_cur->ai_protocol == IPPROTO_UDP) {
+                               ai_new->ai_protocol = IPPROTO_TCP;
+                       }
 
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               ret = b->ops->nw_getgrgid_r(b, gid, grdst, buf, buflen, grdstp);
-               if (ret == ENOENT) {
-                       continue;
-               }
-               return ret;
-       }
+                       /* now insert the new entry */
 
-       return ENOENT;
-}
+                       ai_new->ai_next = ai_cur->ai_next;
+                       ai_cur->ai_next = ai_new;
 
-_PUBLIC_ void nwrap_setgrent(void)
-{
-       int i;
+                       /* and move on (don't duplicate the new entry) */
 
-       if (!nwrap_enabled()) {
-               real_setgrent();
-               return;
+                       ai_cur = ai_new;
+               }
        }
 
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               b->ops->nw_setgrent(b);
-       }
+       *res = ai;
+
+       return 0;
 }
 
-_PUBLIC_ struct group *nwrap_getgrent(void)
+int getaddrinfo(const char *node, const char *service,
+               const struct addrinfo *hints,
+               struct addrinfo **res)
 {
-       int i;
-       struct group *grp;
-
-       if (!nwrap_enabled()) {
-               return real_getgrent();
-       }
-
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               grp = b->ops->nw_getgrent(b);
-               if (grp) {
-                       return grp;
-               }
+       if (!nss_wrapper_hosts_enabled()) {
+               return libc_getaddrinfo(node, service, hints, res);
        }
 
-       return NULL;
+       return nwrap_getaddrinfo(node, service, hints, res);
 }
 
-_PUBLIC_ int nwrap_getgrent_r(struct group *grdst, char *buf,
-                             size_t buflen, struct group **grdstp)
+static int nwrap_getnameinfo(const struct sockaddr *sa, socklen_t salen,
+                            char *host, size_t hostlen,
+                            char *serv, size_t servlen,
+                            int flags)
 {
-       int i,ret;
+       struct hostent *he;
+       struct servent *service;
+       const char *proto;
+       const void *addr;
+       socklen_t addrlen;
+       uint16_t port;
+       sa_family_t type;
+
+       if (sa == NULL || salen < sizeof(sa_family_t)) {
+               return EAI_FAMILY;
+       }
+
+       if ((flags & NI_NAMEREQD) && host == NULL && serv == NULL) {
+               return EAI_NONAME;
+       }
+
+       type = sa->sa_family;
+       switch (type) {
+       case AF_INET:
+               if (salen < sizeof(struct sockaddr_in))
+                       return EAI_FAMILY;
+               addr = &((const struct sockaddr_in *)sa)->sin_addr;
+               addrlen = sizeof(((const struct sockaddr_in *)sa)->sin_addr);
+               port = ntohs(((const struct sockaddr_in *)sa)->sin_port);
+               break;
+#ifdef HAVE_IPV6
+       case AF_INET6:
+               if (salen < sizeof(struct sockaddr_in6))
+                       return EAI_FAMILY;
+               addr = &((const struct sockaddr_in6 *)sa)->sin6_addr;
+               addrlen = sizeof(((const struct sockaddr_in6 *)sa)->sin6_addr);
+               port = ntohs(((const struct sockaddr_in6 *)sa)->sin6_port);
+               break;
+#endif
+       default:
+               return EAI_FAMILY;
+       }
 
-       if (!nwrap_enabled()) {
-#ifdef SOLARIS_GETGRENT_R
-               struct group *gr;
-               gr = real_getgrent_r(grdst, buf, buflen);
-               if (!gr) {
-                       if (errno == 0) {
-                               return ENOENT;
-                       }
-                       return errno;
+       if (host != NULL) {
+               he = NULL;
+               if ((flags & NI_NUMERICHOST) == 0) {
+                       he = nwrap_files_gethostbyaddr(addr, addrlen, type);
+                       if ((flags & NI_NAMEREQD) && (he == NULL || he->h_name == NULL))
+                               return EAI_NONAME;
                }
-               if (grdstp) {
-                       *grdstp = gr;
+               if (he != NULL && he->h_name != NULL) {
+                       if (strlen(he->h_name) >= hostlen)
+                               return EAI_OVERFLOW;
+                       strcpy(host, he->h_name);
+                       if (flags & NI_NOFQDN)
+                               host[strcspn(host, ".")] = '\0';
+               } else {
+                       if (inet_ntop(type, addr, host, hostlen) == NULL)
+                               return (errno == ENOSPC) ? EAI_OVERFLOW : EAI_FAIL;
                }
-               return 0;
-#else
-               return real_getgrent_r(grdst, buf, buflen, grdstp);
-#endif
        }
 
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               ret = b->ops->nw_getgrent_r(b, grdst, buf, buflen, grdstp);
-               if (ret == ENOENT) {
-                       continue;
+       if (serv != NULL) {
+               service = NULL;
+               if ((flags & NI_NUMERICSERV) == 0) {
+                       proto = (flags & NI_DGRAM) ? "udp" : "tcp";
+                       service = getservbyport(htons(port), proto);
+               }
+               if (service != NULL) {
+                       if (strlen(service->s_name) >= servlen)
+                               return EAI_OVERFLOW;
+                       strcpy(serv, service->s_name);
+               } else {
+                       if (snprintf(serv, servlen, "%u", port) >= (int) servlen)
+                               return EAI_OVERFLOW;
                }
-               return ret;
        }
 
-       return ENOENT;
+       return 0;
 }
 
-_PUBLIC_ void nwrap_endgrent(void)
+#ifdef HAVE_LINUX_GETNAMEINFO
+int getnameinfo(const struct sockaddr *sa, socklen_t salen,
+               char *host, socklen_t hostlen,
+               char *serv, socklen_t servlen,
+               int flags)
+#elif defined(HAVE_LINUX_GETNAMEINFO_UNSIGNED)
+int getnameinfo(const struct sockaddr *sa, socklen_t salen,
+               char *host, socklen_t hostlen,
+               char *serv, socklen_t servlen,
+               unsigned int flags)
+#else
+int getnameinfo(const struct sockaddr *sa, socklen_t salen,
+               char *host, size_t hostlen,
+               char *serv, size_t servlen,
+               int flags)
+#endif
 {
-       int i;
-
-       if (!nwrap_enabled()) {
-               real_endgrent();
-               return;
+       if (!nss_wrapper_hosts_enabled()) {
+               return libc_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
        }
 
-       for (i=0; i < nwrap_main_global->num_backends; i++) {
-               struct nwrap_backend *b = &nwrap_main_global->backends[i];
-               b->ops->nw_endgrent(b);
-       }
+       return nwrap_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
 }
 
-_PUBLIC_ int nwrap_getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups)
+static int nwrap_gethostname(char *name, size_t len)
 {
-       struct group *grp;
-       gid_t *groups_tmp;
-       int count = 1;
-       const char *name_of_group = "";
-
-       if (!nwrap_enabled()) {
-               return real_getgrouplist(user, group, groups, ngroups);
-       }
+       const char *hostname = getenv("NSS_WRAPPER_HOSTNAME");
 
-       NWRAP_DEBUG(("%s: getgrouplist called for %s\n", __location__, user));
-
-       groups_tmp = (gid_t *)malloc(count * sizeof(gid_t));
-       if (!groups_tmp) {
-               NWRAP_ERROR(("%s:calloc failed\n",__location__));
-               errno = ENOMEM;
+       if (strlen(hostname) >= len) {
+               errno = ENAMETOOLONG;
                return -1;
        }
+       snprintf(name, len, "%s", hostname);
 
-       memcpy(groups_tmp, &group, sizeof(gid_t));
+       return 0;
+}
 
-       grp = nwrap_getgrgid(group);
-       if (grp) {
-               name_of_group = grp->gr_name;
+#ifdef HAVE_SOLARIS_GETHOSTNAME
+int gethostname(char *name, int len)
+#else /* HAVE_SOLARIS_GETHOSTNAME */
+int gethostname(char *name, size_t len)
+#endif /* HAVE_SOLARIS_GETHOSTNAME */
+{
+       if (!nwrap_hostname_enabled()) {
+               return libc_gethostname(name, len);
        }
 
-       nwrap_setgrent();
-       while ((grp = nwrap_getgrent()) != NULL) {
-               int i = 0;
+       return nwrap_gethostname(name, len);
+}
 
-               NWRAP_VERBOSE(("%s: inspecting %s for group membership\n",
-                              __location__, grp->gr_name));
+/****************************
+ * DESTRUCTOR
+ ***************************/
 
-               for (i=0; grp->gr_mem && grp->gr_mem[i] != NULL; i++) {
+/*
+ * This function is called when the library is unloaded and makes sure that
+ * sockets get closed and the unix file for the socket are unlinked.
+ */
+void nwrap_destructor(void)
+{
+       int i;
 
-                       if ((strcmp(user, grp->gr_mem[i]) == 0) &&
-                           (strcmp(name_of_group, grp->gr_name) != 0)) {
+       NWRAP_LOCK_ALL;
+       if (nwrap_main_global != NULL) {
+               struct nwrap_main *m = nwrap_main_global;
 
-                               NWRAP_DEBUG(("%s: %s is member of %s\n",
-                                       __location__, user, grp->gr_name));
+               /* libc */
+               SAFE_FREE(m->libc->fns);
+               if (m->libc->handle != NULL) {
+                       dlclose(m->libc->handle);
+               }
+               if (m->libc->nsl_handle != NULL) {
+                       dlclose(m->libc->nsl_handle);
+               }
+               if (m->libc->sock_handle != NULL) {
+                       dlclose(m->libc->sock_handle);
+               }
+               SAFE_FREE(m->libc);
 
-                               groups_tmp = (gid_t *)realloc(groups_tmp, (count + 1) * sizeof(gid_t));
-                               if (!groups_tmp) {
-                                       NWRAP_ERROR(("%s:calloc failed\n",__location__));
-                                       errno = ENOMEM;
-                                       return -1;
-                               }
+               /* backends */
+               for (i = 0; i < m->num_backends; i++) {
+                       struct nwrap_backend *b = &(m->backends[i]);
 
-                               memcpy(&groups_tmp[count], &grp->gr_gid, sizeof(gid_t));
-                               count++;
+                       if (b->so_handle != NULL) {
+                               dlclose(b->so_handle);
                        }
+                       SAFE_FREE(b->fns);
                }
+               SAFE_FREE(m->backends);
        }
 
-       nwrap_endgrent();
+       if (nwrap_pw_global.cache != NULL) {
+               struct nwrap_cache *c = nwrap_pw_global.cache;
 
-       NWRAP_VERBOSE(("%s: %s is member of %d groups: %d\n",
-                      __location__, user, *ngroups));
+               nwrap_files_cache_unload(c);
+               if (c->fd >= 0) {
+                       fclose(c->fp);
+                       c->fd = -1;
+               }
 
-       if (*ngroups < count) {
-               *ngroups = count;
-               free(groups_tmp);
-               return -1;
+               SAFE_FREE(nwrap_pw_global.list);
+               nwrap_pw_global.num = 0;
        }
 
-       *ngroups = count;
-       memcpy(groups, groups_tmp, count * sizeof(gid_t));
-       free(groups_tmp);
+       if (nwrap_gr_global.cache != NULL) {
+               struct nwrap_cache *c = nwrap_gr_global.cache;
 
-       return count;
+               nwrap_files_cache_unload(c);
+               if (c->fd >= 0) {
+                       fclose(c->fp);
+                       c->fd = -1;
+               }
+
+               SAFE_FREE(nwrap_gr_global.list);
+               nwrap_pw_global.num = 0;
+       }
+
+       if (nwrap_he_global.cache != NULL) {
+               struct nwrap_cache *c = nwrap_he_global.cache;
+
+               nwrap_files_cache_unload(c);
+               if (c->fd >= 0) {
+                       fclose(c->fp);
+                       c->fd = -1;
+               }
+
+               nwrap_he_global.num = 0;
+       }
+
+       hdestroy();
+       NWRAP_UNLOCK_ALL;
 }