mirror of
https://sourceware.org/git/glibc.git
synced 2024-12-24 03:31:07 +00:00
<nss_action.h>: New abstraction for combining NSS modules and NSS actions
nss_action manages a set of lists of actions; these are the portions of the lines in nsswitch.conf to the right of the colons, like "dns [!UNAVAIL=return] files". Each permutation of actions and conditionals is cached for reuse, which limits memory growth, and refers to the static list of modules managed by nss_modules. Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
This commit is contained in:
parent
171689dac7
commit
fd5b9b4458
@ -30,7 +30,8 @@ routines = nsswitch getnssent getnssent_r digits_dots \
|
||||
$(addsuffix -lookup,$(databases)) \
|
||||
compat-lookup nss_hash nss_files_fopen \
|
||||
nss_readline nss_parse_line_result \
|
||||
nss_fgetent_r nss_module
|
||||
nss_fgetent_r nss_module nss_action \
|
||||
nss_action_parse
|
||||
|
||||
# These are the databases that go through nss dispatch.
|
||||
# Caution: if you add a database here, you must add its real name
|
||||
|
116
nss/nss_action.c
Normal file
116
nss/nss_action.c
Normal file
@ -0,0 +1,116 @@
|
||||
/* NSS actions, elements in a nsswitch.conf configuration line.
|
||||
Copyright (c) 2020 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <nss_action.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <libc-lock.h>
|
||||
|
||||
/* Maintain a global list of NSS action lists. Since most databases
|
||||
use the same list of actions, this list is usually short.
|
||||
Deduplication in __nss_action_allocate ensures that the list does
|
||||
not grow without bounds. */
|
||||
|
||||
struct nss_action_list_wrapper
|
||||
{
|
||||
/* The next element of the list. */
|
||||
struct nss_action_list_wrapper *next;
|
||||
|
||||
/* Number of elements in the list (excluding the terminator). */
|
||||
size_t count;
|
||||
|
||||
/* NULL-terminated list of actions. */
|
||||
struct nss_action actions[];
|
||||
};
|
||||
|
||||
/* Toplevel list of allocated NSS action lists. */
|
||||
static struct nss_action_list_wrapper *nss_actions;
|
||||
|
||||
/* Lock covers the nss_actions list. */
|
||||
__libc_lock_define (static, nss_actions_lock);
|
||||
|
||||
/* Returns true if the actions are equal (same module, same actions
|
||||
array). */
|
||||
static bool
|
||||
actions_equal (const struct nss_action *a, const struct nss_action *b)
|
||||
{
|
||||
return a->module == b->module && a->action_bits == b->action_bits;
|
||||
}
|
||||
|
||||
|
||||
/* Returns true if COUNT actions at A and B are equal (according to
|
||||
actions_equal above). Caller must ensure that either A or B have at
|
||||
least COUNT actions. */
|
||||
static bool
|
||||
action_lists_equal (const struct nss_action *a, const struct nss_action *b,
|
||||
size_t count)
|
||||
{
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
if (!actions_equal (a + i, b + i))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Returns a pre-allocated action list for COUNT actions at ACTIONS,
|
||||
or NULL if no such list exists. */
|
||||
static nss_action_list
|
||||
find_allocated (struct nss_action *actions, size_t count)
|
||||
{
|
||||
for (struct nss_action_list_wrapper *p = nss_actions; p != NULL; p = p->next)
|
||||
if (p->count == count && action_lists_equal (p->actions, actions, count))
|
||||
return p->actions;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nss_action_list
|
||||
__nss_action_allocate (struct nss_action *actions, size_t count)
|
||||
{
|
||||
nss_action_list result = NULL;
|
||||
__libc_lock_lock (nss_actions_lock);
|
||||
|
||||
result = find_allocated (actions, count);
|
||||
if (result == NULL)
|
||||
{
|
||||
struct nss_action_list_wrapper *wrapper
|
||||
= malloc (sizeof (*wrapper) + sizeof (*actions) * count);
|
||||
if (wrapper != NULL)
|
||||
{
|
||||
wrapper->next = nss_actions;
|
||||
wrapper->count = count;
|
||||
memcpy (wrapper->actions, actions, sizeof (*actions) * count);
|
||||
nss_actions = wrapper;
|
||||
result = wrapper->actions;
|
||||
}
|
||||
}
|
||||
|
||||
__libc_lock_unlock (nss_actions_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
void __libc_freeres_fn_section
|
||||
__nss_action_freeres (void)
|
||||
{
|
||||
struct nss_action_list_wrapper *current = nss_actions;
|
||||
while (current != NULL)
|
||||
{
|
||||
struct nss_action_list_wrapper *next = current->next;
|
||||
free (current);
|
||||
current = next;
|
||||
}
|
||||
nss_actions = NULL;
|
||||
}
|
108
nss/nss_action.h
Normal file
108
nss/nss_action.h
Normal file
@ -0,0 +1,108 @@
|
||||
/* NSS actions, elements in a nsswitch.conf configuration line.
|
||||
Copyright (c) 2020 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _NSS_ACTION_H
|
||||
#define _NSS_ACTION_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/* See nss_database.h for a summary of how this relates. */
|
||||
|
||||
#include "nsswitch.h" /* For lookup_actions. */
|
||||
|
||||
struct nss_module;
|
||||
|
||||
/* A NSS action pairs a service module with the action for each result
|
||||
state. */
|
||||
struct nss_action
|
||||
{
|
||||
/* The service module that provides the functionality (potentially
|
||||
not yet loaded). */
|
||||
struct nss_module *module;
|
||||
|
||||
/* Action according to result. Two bits for each lookup_actions
|
||||
value (from nsswitch.h), indexed by enum nss_status (from nss.h). */
|
||||
unsigned int action_bits;
|
||||
};
|
||||
|
||||
/* Value to add to first nss_status value to get zero. */
|
||||
#define NSS_STATUS_BIAS 2
|
||||
/* Number of bits per lookup action. */
|
||||
#define NSS_BPL 2
|
||||
#define NSS_BPL_MASK ((1 << NSS_BPL) - 1)
|
||||
|
||||
/* Index in actions of an NSS status. Note that in nss/nss.h the
|
||||
status starts at -2, and we shift that up to zero by adding 2.
|
||||
Thus for example NSS_STATUS_TRYAGAIN, which is -2, would index into
|
||||
the 0th bit place as expected. */
|
||||
static inline int
|
||||
nss_actions_bits_index (enum nss_status status)
|
||||
{
|
||||
return NSS_BPL * (NSS_STATUS_BIAS + status);
|
||||
}
|
||||
|
||||
/* Returns the lookup_action value for STATUS in ACTION. */
|
||||
static inline lookup_actions
|
||||
nss_action_get (const struct nss_action *action, enum nss_status status)
|
||||
{
|
||||
return ((action->action_bits >> nss_actions_bits_index (status))
|
||||
& NSS_BPL_MASK);
|
||||
}
|
||||
|
||||
/* Sets the lookup_action value for STATUS in ACTION. */
|
||||
static inline void
|
||||
nss_action_set (struct nss_action *action,
|
||||
enum nss_status status, lookup_actions actions)
|
||||
{
|
||||
int offset = nss_actions_bits_index (status);
|
||||
unsigned int mask = NSS_BPL_MASK << offset;
|
||||
action->action_bits = ((action->action_bits & ~mask)
|
||||
| ((unsigned int) actions << offset));
|
||||
}
|
||||
|
||||
static inline void
|
||||
nss_action_set_all (struct nss_action *action, lookup_actions actions)
|
||||
{
|
||||
unsigned int bits = actions & NSS_BPL_MASK;
|
||||
action->action_bits = ( bits
|
||||
| (bits << (NSS_BPL * 1))
|
||||
| (bits << (NSS_BPL * 2))
|
||||
| (bits << (NSS_BPL * 3))
|
||||
| (bits << (NSS_BPL * 4))
|
||||
);
|
||||
}
|
||||
|
||||
/* A list of struct nss_action objects in array terminated by an
|
||||
action with a NULL module. */
|
||||
typedef struct nss_action *nss_action_list;
|
||||
|
||||
/* Returns a pointer to an allocated NSS action list that has COUNT
|
||||
actions that matches the array at ACTIONS. */
|
||||
nss_action_list __nss_action_allocate (struct nss_action *actions,
|
||||
size_t count) attribute_hidden;
|
||||
|
||||
/* Returns a pointer to a list allocated by __nss_action_allocate, or
|
||||
NULL on error. ENOMEM means a (temporary) memory allocation error,
|
||||
EINVAL means that LINE is syntactically invalid. */
|
||||
nss_action_list __nss_action_parse (const char *line);
|
||||
|
||||
/* Called from __libc_freeres. */
|
||||
void __nss_action_freeres (void) attribute_hidden;
|
||||
|
||||
|
||||
#endif /* _NSS_ACTION_H */
|
197
nss/nss_action_parse.c
Normal file
197
nss/nss_action_parse.c
Normal file
@ -0,0 +1,197 @@
|
||||
/* Parse a service line from nsswitch.conf.
|
||||
Copyright (c) 1996-2020 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "nss_action.h"
|
||||
#include "nss_module.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Staging area during parsing. */
|
||||
#define DYNARRAY_STRUCT action_list
|
||||
#define DYNARRAY_ELEMENT struct nss_action
|
||||
#define DYNARRAY_PREFIX action_list_
|
||||
#include <malloc/dynarray-skeleton.c>
|
||||
|
||||
/* Skip whitespace in line[]. */
|
||||
#define SKIP_WS() \
|
||||
while (line[0] != '\0' && isspace (line[0])) \
|
||||
++line;
|
||||
|
||||
/* Read the source names:
|
||||
`( <source> ( "[" "!"? (<status> "=" <action> )+ "]" )? )*'
|
||||
*/
|
||||
static bool
|
||||
nss_action_parse (const char *line, struct action_list *result)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
SKIP_WS ();
|
||||
if (line[0] == '\0')
|
||||
/* No more sources specified. */
|
||||
return true;
|
||||
|
||||
/* Read <source> identifier. */
|
||||
const char *name = line;
|
||||
while (line[0] != '\0' && !isspace (line[0]) && line[0] != '[')
|
||||
++line;
|
||||
if (name == line)
|
||||
return true;
|
||||
|
||||
struct nss_action new_service
|
||||
= { .module = __nss_module_allocate (name, line - name), };
|
||||
if (new_service.module == NULL)
|
||||
{
|
||||
/* Memory allocation error. */
|
||||
action_list_mark_failed (result);
|
||||
return false;
|
||||
}
|
||||
nss_action_set_all (&new_service, NSS_ACTION_CONTINUE);
|
||||
nss_action_set (&new_service, NSS_STATUS_SUCCESS, NSS_ACTION_RETURN);
|
||||
nss_action_set (&new_service, NSS_STATUS_RETURN, NSS_ACTION_RETURN);
|
||||
|
||||
SKIP_WS ();
|
||||
|
||||
if (line[0] == '[')
|
||||
{
|
||||
/* Read criterions. */
|
||||
|
||||
/* Skip the '['. */
|
||||
++line;
|
||||
SKIP_WS ();
|
||||
|
||||
do
|
||||
{
|
||||
int not;
|
||||
enum nss_status status;
|
||||
lookup_actions action;
|
||||
|
||||
/* Grok ! before name to mean all statuses but that one. */
|
||||
not = line[0] == '!';
|
||||
if (not)
|
||||
++line;
|
||||
|
||||
/* Read status name. */
|
||||
name = line;
|
||||
while (line[0] != '\0' && !isspace (line[0]) && line[0] != '='
|
||||
&& line[0] != ']')
|
||||
++line;
|
||||
|
||||
/* Compare with known statuses. */
|
||||
if (line - name == 7)
|
||||
{
|
||||
if (__strncasecmp (name, "SUCCESS", 7) == 0)
|
||||
status = NSS_STATUS_SUCCESS;
|
||||
else if (__strncasecmp (name, "UNAVAIL", 7) == 0)
|
||||
status = NSS_STATUS_UNAVAIL;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else if (line - name == 8)
|
||||
{
|
||||
if (__strncasecmp (name, "NOTFOUND", 8) == 0)
|
||||
status = NSS_STATUS_NOTFOUND;
|
||||
else if (__strncasecmp (name, "TRYAGAIN", 8) == 0)
|
||||
status = NSS_STATUS_TRYAGAIN;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
SKIP_WS ();
|
||||
if (line[0] != '=')
|
||||
return false;
|
||||
|
||||
/* Skip the '='. */
|
||||
++line;
|
||||
SKIP_WS ();
|
||||
name = line;
|
||||
while (line[0] != '\0' && !isspace (line[0]) && line[0] != '='
|
||||
&& line[0] != ']')
|
||||
++line;
|
||||
|
||||
if (line - name == 6 && __strncasecmp (name, "RETURN", 6) == 0)
|
||||
action = NSS_ACTION_RETURN;
|
||||
else if (line - name == 8
|
||||
&& __strncasecmp (name, "CONTINUE", 8) == 0)
|
||||
action = NSS_ACTION_CONTINUE;
|
||||
else if (line - name == 5
|
||||
&& __strncasecmp (name, "MERGE", 5) == 0)
|
||||
action = NSS_ACTION_MERGE;
|
||||
else
|
||||
return false;
|
||||
|
||||
if (not)
|
||||
{
|
||||
/* Save the current action setting for this status,
|
||||
set them all to the given action, and reset this one. */
|
||||
const lookup_actions save
|
||||
= nss_action_get (&new_service, status);
|
||||
nss_action_set_all (&new_service, action);
|
||||
nss_action_set (&new_service, status, save);
|
||||
}
|
||||
else
|
||||
nss_action_set (&new_service, status, action);
|
||||
|
||||
SKIP_WS ();
|
||||
}
|
||||
while (line[0] != ']');
|
||||
|
||||
/* Skip the ']'. */
|
||||
++line;
|
||||
}
|
||||
|
||||
action_list_add (result, new_service);
|
||||
}
|
||||
}
|
||||
|
||||
nss_action_list
|
||||
__nss_action_parse (const char *line)
|
||||
{
|
||||
struct action_list list;
|
||||
action_list_init (&list);
|
||||
if (nss_action_parse (line, &list))
|
||||
{
|
||||
size_t size = action_list_size (&list);
|
||||
nss_action_list result
|
||||
= malloc (sizeof (*result) * (size + 1));
|
||||
if (result == NULL)
|
||||
{
|
||||
action_list_free (&list);
|
||||
return NULL;
|
||||
}
|
||||
memcpy (result, action_list_begin (&list), sizeof (*result) * size);
|
||||
/* Sentinel. */
|
||||
result[size].module = NULL;
|
||||
return result;
|
||||
}
|
||||
else if (action_list_has_failed (&list))
|
||||
{
|
||||
/* Memory allocation error. */
|
||||
__set_errno (ENOMEM);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Parse error. */
|
||||
__set_errno (EINVAL);
|
||||
return NULL;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user