diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2013-12-21 21:39:50 +0100 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2013-12-21 21:39:50 +0100 |
commit | 3ce5ef9d87d6c5169bb5c7bbbc8310387c564f8b (patch) | |
tree | 72980b885d5082009b3d22b6aeea7eec6baa2e44 | |
parent | b9ec6df4ab3b7a1fc16dc894f03f538dafb879fb (diff) | |
parent | 19f3cc3a0ec8926a140e45b4b026cefa4e8c8894 (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.xml | 33 | ||||
-rw-r--r-- | nslcd/cfg.c | 94 | ||||
-rw-r--r-- | nslcd/cfg.h | 4 | ||||
-rw-r--r-- | nslcd/passwd.c | 30 | ||||
-rw-r--r-- | tests/test_cfg.c | 5 |
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"); } |