2004-08-13 Ulrich Drepper <drepper@redhat.com>
- * inet/getnetgrent_r.c (innetgr): We must get the lock used for
- the set/get/end interfaces since we call all these functions in
- succession and must not be interrupted doing this.
+ * nis/nss_nis/nis-netgrp.c: Remove locking by using data in struct
+ __netgrent object passed in instead of global variables.
+ Optimize.
+ * nis/nss_nisplus/nisplus-netgrp.c: Remove locking by using data
+ in struct __netgrent object passed in instead of global variables.
+ * inet/netgroup.h (struct __netgrent): Add service_user field.
+ Move cursor in anonymous union, add new field location to that
+ union.
+ * inet/getnetgrent_r.c: Extensive rewrite to really enable
+ concurrent uset of set/get/endnetgrent and innetgr.
Reported by Chuck Simmons.
* inet/netgroup.h (struct name_list): Replace name pointer with
/* Protect above variable against multiple uses at the same time. */
__libc_lock_define_initialized (static, lock)
-/* This handle for the NSS data base is shared between all
- set/get/endXXXent functions. */
-static service_user *nip;
-
/* The whole information for the set/get/endnetgrent functions are
kept in this structure. */
static struct __netgrent dataset;
current location if it's not nil. Return nonzero if there are no
services (left). */
static enum nss_status
-setup (void **fctp, const char *func_name, int all)
+setup (void **fctp, const char *func_name, int all, service_user **nipp)
{
/* Remember the first service_entry, it's always the same. */
static service_user *startp;
if (startp == NULL)
{
- no_more = __nss_netgroup_lookup (&nip, func_name, fctp);
- startp = no_more ? (service_user *) -1 : nip;
+ /* Executing this more than once at the same time must yield the
+ same result every time. So we need no locking. */
+ no_more = __nss_netgroup_lookup (nipp, func_name, fctp);
+ startp = no_more ? (service_user *) -1 : *nipp;
}
else if (startp == (service_user *) -1)
/* No services at all. */
return 1;
else
{
- if (all || !nip)
+ if (all || *nipp == NULL)
/* Reset to the beginning of the service list. */
- nip = startp;
+ *nipp = startp;
/* Look up the first function. */
- no_more = __nss_lookup (&nip, func_name, fctp);
+ no_more = __nss_lookup (nipp, func_name, fctp);
}
return no_more;
}
} fct;
enum nss_status status = NSS_STATUS_UNAVAIL;
struct name_list *new_elem;
- int no_more;
/* Cycle through all the services and run their setnetgrent functions. */
- no_more = setup (&fct.ptr, "setnetgrent", 1);
+ int no_more = setup (&fct.ptr, "setnetgrent", 1, &datap->nip);
while (! no_more)
{
/* Ignore status, we force check in `__nss_next'. */
status = (*fct.f) (group, datap);
- no_more = __nss_next (&nip, "setnetgrent", &fct.ptr, status, 0);
+ no_more = __nss_next (&datap->nip, "setnetgrent", &fct.ptr, status, 0);
}
/* Add the current group to the list of known groups. */
enum nss_status (*f) (struct __netgrent *);
void *ptr;
} fct;
- int no_more;
/* Remember which was the last used service. */
- old_nip = nip;
+ old_nip = datap->nip;
/* Cycle through all the services and run their endnetgrent functions. */
- no_more = setup (&fct.ptr, "endnetgrent", 1);
+ int no_more = setup (&fct.ptr, "endnetgrent", 1, &datap->nip);
while (! no_more)
{
/* Ignore status, we force check in `__nss_next'. */
(void) (*fct.f) (datap);
- no_more = (nip == old_nip
- || __nss_next (&nip, "endnetgrent", &fct.ptr, 0, 1));
+ no_more = (datap->nip == old_nip
+ || __nss_next (&datap->nip, "endnetgrent", &fct.ptr, 0, 1));
}
/* Now free list of all netgroup names from last run. */
enum nss_status (*f) (struct __netgrent *, char *, size_t, int *);
void *ptr;
} fct;
- int no_more;
/* Initialize status to return if no more functions are found. */
enum nss_status status = NSS_STATUS_NOTFOUND;
/* Run through available functions, starting with the same function last
run. We will repeat each function as long as it succeeds, and then go
on to the next service action. */
- no_more = setup (&fct.ptr, "getnetgrent_r", 0);
+ int no_more = setup (&fct.ptr, "getnetgrent_r", 0, &datap->nip);
while (! no_more)
{
status = (*fct.f) (datap, buffer, buflen, &errno);
}
}
- no_more = __nss_next (&nip, "getnetgrent_r", &fct.ptr, status, 0);
+ no_more = __nss_next (&datap->nip, "getnetgrent_r", &fct.ptr, status, 0);
}
if (status == NSS_STATUS_SUCCESS)
int (*f) (struct __netgrent *, char *, size_t, int *);
void *ptr;
} getfct;
- struct name_list *known = NULL;
- struct name_list *needed = NULL;
+ struct __netgrent entry;
int result = 0;
- int no_more;
const char *current_group = netgroup;
int real_entry = 0;
- __libc_lock_lock (lock);
+ memset (&entry, '\0', sizeof (entry));
/* Walk through the services until we found an answer or we shall
not work further. We can do some optimization here. Since all
the work during one walk through the service list. */
while (1)
{
- no_more = setup (&setfct.ptr, "setnetgrent", 1);
+ int no_more = setup (&setfct.ptr, "setnetgrent", 1, &entry.nip);
while (! no_more)
{
- enum nss_status status;
- struct __netgrent entry;
-
- /* Clear the space for the netgroup data. */
- __bzero (&entry, sizeof (entry));
-
/* Open netgroup. */
- status = (*setfct.f) (current_group, &entry);
+ enum nss_status status = (*setfct.f) (current_group, &entry);
+
if (status == NSS_STATUS_SUCCESS
- && __nss_lookup (&nip, "getnetgrent_r", &getfct.ptr) == 0)
+ && __nss_lookup (&entry.nip, "getnetgrent_r", &getfct.ptr) == 0)
{
char buffer[1024];
/* Make sure we haven't seen the name before. */
struct name_list *namep;
- for (namep = known; namep != NULL; namep = namep->next)
+ for (namep = entry.known_groups; namep != NULL;
+ namep = namep->next)
if (strcmp (entry.val.group, namep->name) == 0)
break;
if (namep == NULL
break;
}
- namep->next = needed;
+ namep->next = entry.needed_groups;
memcpy (namep->name, entry.val.group, group_len);
- needed = namep;
+ entry.needed_groups = namep;
}
}
else
}
/* Free all resources of the service. */
- if (__nss_lookup (&nip, "endnetgrent", &endfct.ptr) == 0)
+ if (__nss_lookup (&entry.nip, "endnetgrent", &endfct.ptr) == 0)
(*endfct.f) (&entry);
/* Look for the next service. */
- no_more = __nss_next (&nip, "setnetgrent",
+ no_more = __nss_next (&entry.nip, "setnetgrent",
&setfct.ptr, status, 0);
}
- if (result == 0 && needed != NULL)
+ if (result == 0 && entry.needed_groups != NULL)
{
- struct name_list *tmp = needed;
- needed = tmp->next;
- tmp->next = known;
- known = tmp;
- current_group = known->name;
+ struct name_list *tmp = entry.needed_groups;
+ entry.needed_groups = tmp->next;
+ tmp->next = entry.known_groups;
+ entry.known_groups = tmp;
+ current_group = entry.known_groups->name;
continue;
}
break;
}
- __libc_lock_unlock (lock);
-
/* Free the memory. */
- while (known != NULL)
- {
- struct name_list *tmp = known;
- known = known->next;
- free (tmp);
- }
- while (needed != NULL)
- {
- struct name_list *tmp = needed;
- needed = needed->next;
- free (tmp);
- }
+ free_memory (&entry);
- return result == 1;
+ return result;
}
libc_hidden_def (innetgr)
#ifndef _NETGROUP_H
#define _NETGROUP_H 1
+#include <nsswitch.h>
+
/* A netgroup can consist of names of other netgroups. We have to
track which netgroups were read and which still have to be read. */
struct name_list
functions. We must avoid global variables. */
char *data;
size_t data_size;
- char *cursor;
+ union
+ {
+ char *cursor;
+ unsigned long int position;
+ };
int first;
struct name_list *known_groups;
struct name_list *needed_groups;
+
+ /* This handle for the NSS data base is shared between all
+ set/get/endXXXent functions. */
+ service_user *nip;
};
-/* Copyright (C) 1996,1997,1999,2000,2002,2003 Free Software Foundation, Inc.
+/* Copyright (C) 1996,1997,1999,2000,2002,2003,2004
+ Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Thorsten Kukuk <kukuk@suse.de>, 1996.
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
-#include <nss.h>
+#include <assert.h>
#include <ctype.h>
#include <errno.h>
-#include <bits/libc-lock.h>
+#include <malloc.h>
#include <netdb.h>
+#include <nss.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "nss-nis.h"
-/* Locks the static variables in this file. */
-__libc_lock_define_initialized (static, lock)
-
-static char *data;
-static size_t data_size;
-static char *cursor;
-
extern enum nss_status
-_nss_netgroup_parseline (char **cursor, struct __netgrent *result,
+_nss_netgroup_parseline (char **cursor, struct __netgrent *netgrp,
char *buffer, size_t buflen, int *errnop);
static void
-internal_nis_endnetgrent (void)
+internal_nis_endnetgrent (struct __netgrent *netgrp)
{
- if (data != NULL)
+ if (netgrp->data != NULL)
{
- free (data);
- data = NULL;
- data_size = 0;
- cursor = NULL;
+ free (netgrp->data);
+ netgrp->data = NULL;
+ netgrp->data_size = 0;
+ netgrp->cursor = NULL;
}
}
enum nss_status
-_nss_nis_setnetgrent (const char *group, struct __netgrent *dummy)
+_nss_nis_setnetgrent (const char *group, struct __netgrent *netgrp)
{
char *domain;
- char *result;
- int len, group_len;
+ int len;
enum nss_status status;
status = NSS_STATUS_SUCCESS;
if (yp_get_default_domain (&domain))
return NSS_STATUS_UNAVAIL;
- __libc_lock_lock (lock);
+ internal_nis_endnetgrent (netgrp);
- internal_nis_endnetgrent ();
-
- group_len = strlen (group);
-
- status = yperr2nss (yp_match (domain, "netgroup", group, group_len,
- &result, &len));
+ status = yperr2nss (yp_match (domain, "netgroup", group, strlen (group),
+ &netgrp->data, &len));
if (status == NSS_STATUS_SUCCESS)
{
- if (len > 0 && (data = malloc (len + 1)) != NULL)
- {
- data_size = len;
- cursor = strncpy (data, result, len + 1);
- data[len] = '\0';
- free (result);
- }
- else
- status = NSS_STATUS_NOTFOUND;
+ /* Our implementation of yp_match already allocates a buffer
+ which is one byte larger than the value in LEN specifies
+ and the last byte is filled with NUL. So we can simply
+ use that buffer. */
+ assert (len > 0);
+ assert (malloc_usable_size (netgrp->data) >= len + 1);
+ assert (netgrp->data[len] == '\0');
+
+ netgrp->data_size = len;
+ netgrp->cursor = netgrp->data;
}
- __libc_lock_unlock (lock);
-
return status;
}
enum nss_status
-_nss_nis_endnetgrent (struct __netgrent *dummy)
+_nss_nis_endnetgrent (struct __netgrent *netgrp)
{
- __libc_lock_lock (lock);
-
- internal_nis_endnetgrent ();
-
- __libc_lock_unlock (lock);
+ internal_nis_endnetgrent (netgrp);
return NSS_STATUS_SUCCESS;
}
_nss_nis_getnetgrent_r (struct __netgrent *result, char *buffer, size_t buflen,
int *errnop)
{
- enum nss_status status;
-
- if (cursor == NULL)
+ if (result->cursor == NULL)
return NSS_STATUS_NOTFOUND;
- __libc_lock_lock (lock);
-
- status = _nss_netgroup_parseline (&cursor, result, buffer, buflen, errnop);
-
- __libc_lock_unlock (lock);
-
- return status;
+ return _nss_netgroup_parseline (&result->cursor, result, buffer, buflen,
+ errnop);
}
-/* Copyright (C) 1997, 2003 Free Software Foundation, Inc.
+/* Copyright (C) 1997, 2003, 2004 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1997.
#include <netdb.h>
#include <string.h>
#include <netgroup.h>
-#include <bits/libc-lock.h>
#include <rpcsvc/nis.h>
#include "nss-nisplus.h"
-__libc_lock_define_initialized (static, lock)
-
-static nis_result *data = NULL;
-static unsigned long data_size = 0;
-static unsigned long position = 0;
-
#define NISENTRYVAL(idx,col,res) \
((res)->objects.objects_val[(idx)].EN_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val)
#define NISENTRYLEN(idx,col,res) \
((res)->objects.objects_val[(idx)].EN_data.en_cols.en_cols_val[(col)].ec_value.ec_value_len)
-static enum nss_status
-_nss_nisplus_parse_netgroup (struct __netgrent *result, char *buffer,
- size_t buflen, int *errnop)
+enum nss_status
+_nss_nisplus_getnetgrent_r (struct __netgrent *result, char *buffer,
+ size_t buflen, int *errnop)
{
enum nss_status status;
/* Some sanity checks. */
- if (data == NULL || data_size == 0)
+ if (result->data == NULL || result->data_size == 0)
return NSS_STATUS_NOTFOUND;
- if (position == data_size)
+ if (result->position == result->data_size)
return result->first ? NSS_STATUS_NOTFOUND : NSS_STATUS_RETURN;
- if (NISENTRYLEN (position, 1, data) > 0)
+ unsigned int entrylen
+ = NISENTRYLEN (result->position, 1, (nis_result *) result->data);
+ if (entrylen > 0)
{
/* We have a list of other netgroups. */
result->type = group_val;
- if (NISENTRYLEN (position, 1, data) >= buflen)
+ if (entrylen >= buflen)
{
*errnop = ERANGE;
return NSS_STATUS_TRYAGAIN;
}
- strncpy (buffer, NISENTRYVAL (position, 1, data),
- NISENTRYLEN (position, 1, data));
- buffer[NISENTRYLEN (position, 1, data)] = '\0';
+ strncpy (buffer, NISENTRYVAL (result->position, 1,
+ (nis_result *) result->data),
+ entrylen);
+ buffer[entrylen] = '\0';
result->val.group = buffer;
- ++position;
+ ++result->position;
result->first = 0;
return NSS_STATUS_SUCCESS;
/* Before we can copy the entry to the private buffer we have to make
sure it is big enough. */
- if (NISENTRYLEN (position, 2, data) + NISENTRYLEN (position, 3, data) +
- NISENTRYLEN (position, 4, data) + 6 > buflen)
+ unsigned int hostlen
+ = NISENTRYLEN (result->position, 2, (nis_result *) result->data);
+ unsigned int userlen
+ = NISENTRYLEN (result->position, 3, (nis_result *) result->data);
+ unsigned int domainlen
+ = NISENTRYLEN (result->position, 4, (nis_result *) result->data);
+ if (hostlen + userlen + domainlen + 6 > buflen)
{
*errnop = ERANGE;
status = NSS_STATUS_TRYAGAIN;
result->type = triple_val;
- if (NISENTRYLEN (position, 2, data) == 0)
+ if (hostlen == 0)
result->val.triple.host = NULL;
else
{
result->val.triple.host = cp;
- cp = __stpncpy (cp, NISENTRYVAL (position, 2, data),
- NISENTRYLEN (position, 2, data));
+ cp = __stpncpy (cp, NISENTRYVAL (result->position, 2,
+ (nis_result *) result->data),
+ hostlen);
*cp++ = '\0';
}
- if (NISENTRYLEN (position, 3, data) == 0)
+ if (userlen == 0)
result->val.triple.user = NULL;
else
{
result->val.triple.user = cp;
- cp = __stpncpy (cp, NISENTRYVAL (position, 3, data),
- NISENTRYLEN (position, 3, data));
+ cp = __stpncpy (cp, NISENTRYVAL (result->position, 3,
+ (nis_result *) result->data),
+ userlen);
*cp++ = '\0';
}
- if (NISENTRYLEN (position, 4, data) == 0)
+ if (domainlen == 0)
result->val.triple.domain = NULL;
else
{
result->val.triple.domain = cp;
- cp = __stpncpy (cp, NISENTRYVAL (position, 4, data),
- NISENTRYLEN (position, 4, data));
+ cp = __stpncpy (cp, NISENTRYVAL (result->position, 4,
+ (nis_result *) result->data),
+ domainlen);
*cp = '\0';
}
status = NSS_STATUS_SUCCESS;
/* Remember where we stopped reading. */
- ++position;
+ ++result->position;
result->first = 0;
}
return status;
}
+static void
+internal_endnetgrent (struct __netgrent *netgrp)
+{
+ if (netgrp->data != NULL)
+ {
+ nis_freeresult ((nis_result *) netgrp->data);
+ netgrp->data = NULL;
+ netgrp->data_size = 0;
+ netgrp->position = 0;
+ }
+}
+
enum nss_status
-_nss_nisplus_setnetgrent (const char *group, struct __netgrent *dummy)
+_nss_nisplus_setnetgrent (const char *group, struct __netgrent *netgrp)
{
enum nss_status status;
char buf[strlen (group) + 30];
status = NSS_STATUS_SUCCESS;
- __libc_lock_lock (lock);
-
- if (data != NULL)
- {
- nis_freeresult (data);
- data = NULL;
- data_size = 0;
- position = 0;
- }
+ internal_endnetgrent (netgrp);
sprintf (buf, "[name=%s],netgroup.org_dir", group);
- data = nis_list (buf, EXPAND_NAME, NULL, NULL);
+ netgrp->data = (char *) nis_list (buf, EXPAND_NAME, NULL, NULL);
- if (data == NULL)
+ if (netgrp->data == NULL)
{
__set_errno (ENOMEM);
status = NSS_STATUS_TRYAGAIN;
}
- else if (niserr2nss (data->status) != NSS_STATUS_SUCCESS)
+ else if (niserr2nss (((nis_result *) netgrp->data)->status)
+ != NSS_STATUS_SUCCESS)
{
- status = niserr2nss (data->status);
- nis_freeresult (data);
- data = NULL;
+ status = niserr2nss (((nis_result *) netgrp->data)->status);
+
+ internal_endnetgrent (netgrp);
}
else
- data_size = data->objects.objects_len;
-
- __libc_lock_unlock (lock);
-
- return status;
-}
-
-enum nss_status
-_nss_nisplus_endnetgrent (struct __netgrent *dummy)
-{
- __libc_lock_lock (lock);
-
- if (data != NULL)
{
- nis_freeresult (data);
- data = NULL;
- data_size = 0;
- position = 0;
+ netgrp->data_size = ((nis_result *) netgrp->data)->objects.objects_len;
+ netgrp->position = 0;
+ netgrp->first = 1;
}
- __libc_lock_unlock (lock);
-
- return NSS_STATUS_SUCCESS;
+ return status;
}
enum nss_status
-_nss_nisplus_getnetgrent_r (struct __netgrent *result,
- char *buffer, size_t buflen, int *errnop)
+_nss_nisplus_endnetgrent (struct __netgrent *netgrp)
{
- enum nss_status status;
-
- __libc_lock_lock (lock);
-
- status = _nss_nisplus_parse_netgroup (result, buffer, buflen, errnop);
+ internal_endnetgrent (netgrp);
- __libc_lock_unlock (lock);
-
- return status;
+ return NSS_STATUS_SUCCESS;
}