summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur de Jong <arthur@arthurdejong.org>2013-12-21 21:39:50 +0100
committerArthur de Jong <arthur@arthurdejong.org>2013-12-21 21:39:50 +0100
commit3ce5ef9d87d6c5169bb5c7bbbc8310387c564f8b (patch)
tree72980b885d5082009b3d22b6aeea7eec6baa2e44
parentb9ec6df4ab3b7a1fc16dc894f03f538dafb879fb (diff)
parent19f3cc3a0ec8926a140e45b4b026cefa4e8c8894 (diff)
Make dn2uid cache tuneable
This introduces a new cache configuration option that allows setting positive and negative cache lifetimes for the dn2uid cache.
-rw-r--r--man/nslcd.conf.5.xml33
-rw-r--r--nslcd/cfg.c94
-rw-r--r--nslcd/cfg.h4
-rw-r--r--nslcd/passwd.c30
-rw-r--r--tests/test_cfg.c5
5 files changed, 156 insertions, 10 deletions
diff --git a/man/nslcd.conf.5.xml b/man/nslcd.conf.5.xml
index 921a8eb..778160e 100644
--- a/man/nslcd.conf.5.xml
+++ b/man/nslcd.conf.5.xml
@@ -844,6 +844,39 @@
</listitem>
</varlistentry>
+ <varlistentry id="cache"> <!-- since 0.9.3 -->
+ <term><option>cache</option>
+ <replaceable>CACHE</replaceable>
+ <replaceable>TIME</replaceable>
+ <optional><replaceable>TIME</replaceable></optional></term>
+ <listitem>
+ <para>
+ Configure the time entries are kept in the specified cache.
+ </para>
+ <para>
+ The first <replaceable>TIME</replaceable> value specifies the time
+ to keep found entries in the cache.
+ The second <replaceable>TIME</replaceable> value specifies to the
+ time to remember that a particular entry was not found.
+ If the second parameter is absent, it is assumed to be the same as
+ the first.
+ </para>
+ <para>
+ Time values are specified as a number followed by an
+ <literal>s</literal> for seconds, <literal>m</literal> for minutes,
+ <literal>h</literal> for hours or <literal>d</literal> for days.
+ Use <literal>0</literal> or <literal>off</literal> to disable the
+ cache.
+ </para>
+ <para>
+ Currently, only the <literal>dn2uid</literal> cache is supported
+ that is used to remember DN to username lookups that are used when the
+ <literal>member</literal> attribute is used.
+ The default time value for this cache is <literal>15m</literal>.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect2>
diff --git a/nslcd/cfg.c b/nslcd/cfg.c
index e3dd418..e1857d5 100644
--- a/nslcd/cfg.c
+++ b/nslcd/cfg.c
@@ -213,6 +213,63 @@ static const char *print_boolean(int bool)
else return "no";
}
+#define TIME_MINUTES 60
+#define TIME_HOURS (60 * 60)
+#define TIME_DAYS (60 * 60 * 24)
+
+static time_t parse_time(const char *filename, int lnr, const char *value)
+{
+ time_t t;
+ char *tmp = NULL;
+ if (strcasecmp(value, "off") == 0)
+ return 0;
+ errno = 0;
+ t = strtol(value, &tmp, 10);
+ if (errno != 0)
+ {
+ log_log(LOG_ERR, "%s:%d: value out of range: '%s'",
+ filename, lnr, value);
+ exit(EXIT_FAILURE);
+ }
+ if ((strcasecmp(tmp, "") == 0) || (strcasecmp(tmp, "s") == 0))
+ return t;
+ else if (strcasecmp(tmp, "m") == 0)
+ return t * TIME_MINUTES;
+ else if (strcasecmp(tmp, "h") == 0)
+ return t * TIME_HOURS;
+ else if (strcasecmp(tmp, "d") == 0)
+ return t * TIME_DAYS;
+ else
+ {
+ log_log(LOG_ERR, "%s:%d: invalid time value: '%s'",
+ filename, lnr, value);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static time_t get_time(const char *filename, int lnr,
+ const char *keyword, char **line)
+{
+ char token[32];
+ check_argumentcount(filename, lnr, keyword,
+ get_token(line, token, sizeof(token)) != NULL);
+ return parse_time(filename, lnr, token);
+}
+
+static void print_time(time_t t, char *buffer, size_t buflen)
+{
+ if (t == 0)
+ mysnprintf(buffer, buflen, "off");
+ else if ((t % TIME_DAYS) == 0)
+ mysnprintf(buffer, buflen, "%ldd", (long)(t / TIME_DAYS));
+ else if ((t % TIME_HOURS) == 0)
+ mysnprintf(buffer, buflen, "%ldh", (long)(t / TIME_HOURS));
+ else if ((t % TIME_MINUTES) == 0)
+ mysnprintf(buffer, buflen, "%ldm", (long)(t / TIME_MINUTES));
+ else
+ mysnprintf(buffer, buflen, "%lds", (long)t);
+}
+
static void handle_uid(const char *filename, int lnr,
const char *keyword, char *line,
struct ldap_config *cfg)
@@ -973,6 +1030,34 @@ static void handle_reconnect_invalidate(
}
}
+static void handle_cache(const char *filename, int lnr,
+ const char *keyword, char *line,
+ struct ldap_config *cfg)
+{
+ char cache[16];
+ time_t value1, value2;
+ /* get cache map and values */
+ check_argumentcount(filename, lnr, keyword,
+ get_token(&line, cache, sizeof(cache)) != NULL);
+ value1 = get_time(filename, lnr, keyword, &line);
+ if ((line != NULL) && (*line != '\0'))
+ value2 = get_time(filename, lnr, keyword, &line);
+ else
+ value2 = value1;
+ get_eol(filename, lnr, keyword, &line);
+ /* check the cache */
+ if (strcasecmp(cache, "dn2uid") == 0)
+ {
+ cfg->cache_dn2uid_positive = value1;
+ cfg->cache_dn2uid_negative = value2;
+ }
+ else
+ {
+ log_log(LOG_ERR, "%s:%d: unknown cache: '%s'", filename, lnr, cache);
+ exit(EXIT_FAILURE);
+ }
+}
+
/* This function tries to get the LDAP search base from the LDAP server.
Note that this returns a string that has been allocated with strdup().
For this to work the myldap module needs enough configuration information
@@ -1106,6 +1191,8 @@ static void cfg_defaults(struct ldap_config *cfg)
cfg->pam_password_prohibit_message = NULL;
for (i = 0; i < LM_NONE; i++)
cfg->reconnect_invalidate[i] = 0;
+ cfg->cache_dn2uid_positive = 15 * TIME_MINUTES;
+ cfg->cache_dn2uid_negative = 15 * TIME_MINUTES;
}
static void cfg_read(const char *filename, struct ldap_config *cfg)
@@ -1441,6 +1528,10 @@ static void cfg_read(const char *filename, struct ldap_config *cfg)
{
handle_reconnect_invalidate(filename, lnr, keyword, line, cfg);
}
+ else if (strcasecmp(keyword, "cache") == 0)
+ {
+ handle_cache(filename, lnr, keyword, line, cfg);
+ }
#ifdef ENABLE_CONFIGFILE_CHECKING
/* fallthrough */
else
@@ -1702,6 +1793,9 @@ static void cfg_dump(void)
}
if (buffer[0] != '\0')
log_log(LOG_DEBUG, "CFG: reconnect_invalidate %s", buffer);
+ print_time(nslcd_cfg->cache_dn2uid_positive, buffer, sizeof(buffer) / 2);
+ print_time(nslcd_cfg->cache_dn2uid_positive, buffer + (sizeof(buffer) / 2), sizeof(buffer) / 2);
+ log_log(LOG_DEBUG, "CFG: cache dn2uid %s %s", buffer, buffer + (sizeof(buffer) / 2));
}
void cfg_init(const char *fname)
diff --git a/nslcd/cfg.h b/nslcd/cfg.h
index 559d1da..2fade8b 100644
--- a/nslcd/cfg.h
+++ b/nslcd/cfg.h
@@ -31,6 +31,7 @@
#include <lber.h>
#include <ldap.h>
#include <regex.h>
+#include <time.h>
#include "compat/attrs.h"
#include "common/set.h"
@@ -127,6 +128,9 @@ struct ldap_config {
char *pam_authz_searches[NSS_LDAP_CONFIG_MAX_AUTHZ_SEARCHES]; /* the searches that should be performed to do autorisation checks */
char *pam_password_prohibit_message; /* whether password changing should be denied and user prompted with this message */
char reconnect_invalidate[LM_NONE]; /* set to 1 if the corresponding map should be invalidated */
+
+ time_t cache_dn2uid_positive;
+ time_t cache_dn2uid_negative;
};
/* this is a pointer to the global configuration, it should be available
diff --git a/nslcd/passwd.c b/nslcd/passwd.c
index 2ad53a3..e9cf950 100644
--- a/nslcd/passwd.c
+++ b/nslcd/passwd.c
@@ -162,7 +162,6 @@ struct dn2uid_cache_entry {
time_t timestamp;
char *uid;
};
-#define DN2UID_CACHE_TIMEOUT (15 * 60)
/* checks whether the entry has a valid uidNumber attribute
(>= nss_min_uid) */
@@ -279,23 +278,36 @@ char *dn2uid(MYLDAP_SESSION *session, const char *dn, char *buf, size_t buflen)
return NULL;
return buf;
}
+ /* if we don't use the cache, just lookup and return */
+ if ((nslcd_cfg->cache_dn2uid_positive == 0) && (nslcd_cfg->cache_dn2uid_negative == 0))
+ return lookup_dn2uid(session, dn, NULL, buf, buflen);
/* see if we have a cached entry */
pthread_mutex_lock(&dn2uid_cache_mutex);
if (dn2uid_cache == NULL)
dn2uid_cache = dict_new();
if ((dn2uid_cache != NULL) && ((cacheentry = dict_get(dn2uid_cache, dn)) != NULL))
{
- /* if the cached entry is still valid, return that */
- if (time(NULL) < (cacheentry->timestamp + DN2UID_CACHE_TIMEOUT))
+ if ((cacheentry->uid != NULL) && (strlen(cacheentry->uid) < buflen))
{
- if ((cacheentry->uid != NULL) && (strlen(cacheentry->uid) < buflen))
+ /* if the cached entry is still valid, return that */
+ if ((nslcd_cfg->cache_dn2uid_positive > 0) &&
+ (time(NULL) < (cacheentry->timestamp + nslcd_cfg->cache_dn2uid_positive)))
+ {
strcpy(buf, cacheentry->uid);
- else
- buf = NULL;
- pthread_mutex_unlock(&dn2uid_cache_mutex);
- return buf;
+ pthread_mutex_unlock(&dn2uid_cache_mutex);
+ return buf;
+ }
+ }
+ else
+ {
+ if ((nslcd_cfg->cache_dn2uid_negative > 0) &&
+ (time(NULL) < (cacheentry->timestamp + nslcd_cfg->cache_dn2uid_negative)))
+ /* if the cached entry is still valid, return that */
+ {
+ pthread_mutex_unlock(&dn2uid_cache_mutex);
+ return NULL;
+ }
}
- /* leave the entry intact, just replace the uid below */
}
pthread_mutex_unlock(&dn2uid_cache_mutex);
/* look up the uid using an LDAP query */
diff --git a/tests/test_cfg.c b/tests/test_cfg.c
index 3fce39c..6f36460 100644
--- a/tests/test_cfg.c
+++ b/tests/test_cfg.c
@@ -206,7 +206,8 @@ static void test_read(void)
"map passwd homeDirectory \"${homeDirectory:-/home/$uid}\"\n"
"filter group (&(objeclClass=posixGroup)(gid=1*))\n"
"\n"
- "scope passwd one\n");
+ "scope passwd one\n"
+ "cache dn2uid 10m 1s\n");
fclose(fp);
/* parse the file */
cfg_defaults(&cfg);
@@ -224,6 +225,8 @@ static void test_read(void)
assertstreq(attmap_passwd_uid, "sAMAccountName");
assertstreq(group_filter, "(&(objeclClass=posixGroup)(gid=1*))");
assert(passwd_scope == LDAP_SCOPE_ONELEVEL);
+ assert(cfg.cache_dn2uid_positive == 10 * 60);
+ assert(cfg.cache_dn2uid_negative == 1);
/* remove temporary file */
remove("temp.cfg");
}