summaryrefslogtreecommitdiff
path: root/nss
diff options
context:
space:
mode:
Diffstat (limited to 'nss')
-rw-r--r--nss/getXXbyYY_r.c110
-rw-r--r--nss/getnssent_r.c27
-rw-r--r--nss/nsswitch.c3
-rw-r--r--nss/nsswitch.h3
4 files changed, 137 insertions, 6 deletions
diff --git a/nss/getXXbyYY_r.c b/nss/getXXbyYY_r.c
index 113c687e06..93af2538ec 100644
--- a/nss/getXXbyYY_r.c
+++ b/nss/getXXbyYY_r.c
@@ -131,6 +131,52 @@
# define AF_VAL AF_INET
#endif
+
+/* Set defaults for merge functions that haven't been defined. */
+#ifndef DEEPCOPY_FN
+static inline int
+__copy_einval (LOOKUP_TYPE a,
+ const size_t b,
+ LOOKUP_TYPE *c,
+ char *d,
+ char **e)
+{
+ return EINVAL;
+}
+# define DEEPCOPY_FN __copy_einval
+#endif
+
+#ifndef MERGE_FN
+static inline int
+__merge_einval (LOOKUP_TYPE *a,
+ char *b,
+ char *c,
+ size_t d,
+ LOOKUP_TYPE *e,
+ char *f)
+{
+ return EINVAL;
+}
+# define MERGE_FN __merge_einval
+#endif
+
+#define CHECK_MERGE(err, status) \
+ ({ \
+ do \
+ { \
+ if (err) \
+ { \
+ __set_errno (err); \
+ if (err == ERANGE) \
+ status = NSS_STATUS_TRYAGAIN; \
+ else \
+ status = NSS_STATUS_UNAVAIL; \
+ break; \
+ } \
+ } \
+ while (0); \
+ })
+
/* Type of the lookup function we need here. */
typedef enum nss_status (*lookup_function) (ADD_PARAMS, LOOKUP_TYPE *, char *,
size_t, int * H_ERRNO_PARM
@@ -152,13 +198,16 @@ INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
static service_user *startp;
static lookup_function start_fct;
service_user *nip;
+ int do_merge = 0;
+ LOOKUP_TYPE mergegrp;
+ char *mergebuf = NULL;
+ char *endptr = NULL;
union
{
lookup_function l;
void *ptr;
} fct;
-
- int no_more;
+ int no_more, err;
enum nss_status status = NSS_STATUS_UNAVAIL;
#ifdef USE_NSCD
int nscd_status;
@@ -278,9 +327,66 @@ INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
&& errno == ERANGE)
break;
+ if (do_merge)
+ {
+
+ if (status == NSS_STATUS_SUCCESS)
+ {
+ /* The previous loop saved a buffer for merging.
+ Perform the merge now. */
+ err = MERGE_FN (&mergegrp, mergebuf, endptr, buflen, resbuf,
+ buffer);
+ CHECK_MERGE (err,status);
+ do_merge = 0;
+ }
+ else
+ {
+ /* If the result wasn't SUCCESS, copy the saved buffer back
+ into the result buffer and set the status back to
+ NSS_STATUS_SUCCESS to match the previous pass through the
+ loop.
+ * If the next action is CONTINUE, it will overwrite the value
+ currently in the buffer and return the new value.
+ * If the next action is RETURN, we'll return the previously-
+ acquired values.
+ * If the next action is MERGE, then it will be added to the
+ buffer saved from the previous source. */
+ err = DEEPCOPY_FN (mergegrp, buflen, resbuf, buffer, NULL);
+ CHECK_MERGE (err, status);
+ status = NSS_STATUS_SUCCESS;
+ }
+ }
+
+ /* If we were are configured to merge this value with the next one,
+ save the current value of the group struct. */
+ if (nss_next_action (nip, status) == NSS_ACTION_MERGE
+ && status == NSS_STATUS_SUCCESS)
+ {
+ /* Copy the current values into a buffer to be merged with the next
+ set of retrieved values. */
+ if (mergebuf == NULL)
+ {
+ /* Only allocate once and reuse it for as many merges as we need
+ to perform. */
+ mergebuf = malloc (buflen);
+ if (mergebuf == NULL)
+ {
+ __set_errno (ENOMEM);
+ status = NSS_STATUS_UNAVAIL;
+ break;
+ }
+ }
+
+ err = DEEPCOPY_FN (*resbuf, buflen, &mergegrp, mergebuf, &endptr);
+ CHECK_MERGE (err, status);
+ do_merge = 1;
+ }
+
no_more = __nss_next2 (&nip, REENTRANT_NAME_STRING,
REENTRANT2_NAME_STRING, &fct.ptr, status, 0);
}
+ free (mergebuf);
+ mergebuf = NULL;
#ifdef HANDLE_DIGITS_DOTS
done:
diff --git a/nss/getnssent_r.c b/nss/getnssent_r.c
index 456907b018..f5092482ef 100644
--- a/nss/getnssent_r.c
+++ b/nss/getnssent_r.c
@@ -79,7 +79,18 @@ __nss_setent (const char *func_name, db_lookup_function lookup_fct,
else
status = DL_CALL_FCT (fct.f, (0));
- no_more = __nss_next2 (nip, func_name, NULL, &fct.ptr, status, 0);
+
+ /* This is a special-case. When [SUCCESS=merge] is in play,
+ _nss_next2() will skip to the next database. Due to the
+ implementation of that function, we can't know whether we're
+ in an enumeration or an individual lookup, which behaves
+ differently with regards to merging. We'll treat SUCCESS as
+ an indication to start the enumeration at this database. */
+ if (nss_next_action (*nip, status) == NSS_ACTION_MERGE)
+ no_more = 1;
+ else
+ no_more = __nss_next2 (nip, func_name, NULL, &fct.ptr, status, 0);
+
if (is_last_nip)
*last_nip = *nip;
}
@@ -175,8 +186,18 @@ __nss_getent_r (const char *getent_func_name,
do
{
- no_more = __nss_next2 (nip, getent_func_name, NULL, &fct.ptr,
- status, 0);
+ /* This is a special-case. When [SUCCESS=merge] is in play,
+ _nss_next2() will skip to the next database. Due to the
+ implementation of that function, we can't know whether we're
+ in an enumeration or an individual lookup, which behaves
+ differently with regards to merging. We'll treat SUCCESS as
+ an indication to return the results here. */
+ if (status == NSS_STATUS_SUCCESS
+ && nss_next_action (*nip, status) == NSS_ACTION_MERGE)
+ no_more = 1;
+ else
+ no_more = __nss_next2 (nip, getent_func_name, NULL, &fct.ptr,
+ status, 0);
if (is_last_nip)
*last_nip = *nip;
diff --git a/nss/nsswitch.c b/nss/nsswitch.c
index bb644cb373..d7706506f0 100644
--- a/nss/nsswitch.c
+++ b/nss/nsswitch.c
@@ -712,6 +712,9 @@ nss_parse_service_list (const char *line)
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
goto finish;
diff --git a/nss/nsswitch.h b/nss/nsswitch.h
index 0074ee1d65..54c8b656f7 100644
--- a/nss/nsswitch.h
+++ b/nss/nsswitch.h
@@ -32,7 +32,8 @@
typedef enum
{
NSS_ACTION_CONTINUE,
- NSS_ACTION_RETURN
+ NSS_ACTION_RETURN,
+ NSS_ACTION_MERGE
} lookup_actions;