From e1b0399ee018d217cd50267cef03c28dfdb32fbf Mon Sep 17 00:00:00 2001 From: Arthur de Jong Date: Fri, 26 Jul 2013 14:59:43 +0200 Subject: Rename nscd_invalidate option to reconnect_invalidate This also renames the internal nscd module to invalidator for both nslcd and pynslcd. The new invalidator module is now no longer nscd-specific. --- man/nslcd.conf.5.xml | 4 +- nslcd/Makefile.am | 2 +- nslcd/cfg.c | 14 +-- nslcd/cfg.h | 2 +- nslcd/common.h | 8 +- nslcd/invalidator.c | 265 +++++++++++++++++++++++++++++++++++++++++++++++++ nslcd/myldap.c | 4 +- nslcd/nscd.c | 262 ------------------------------------------------ nslcd/nslcd.c | 6 +- pynslcd/Makefile.am | 2 +- pynslcd/cfg.py | 8 +- pynslcd/invalidator.py | 112 +++++++++++++++++++++ pynslcd/nscd.py | 112 --------------------- pynslcd/pynslcd.py | 8 +- pynslcd/search.py | 4 +- tests/Makefile.am | 2 +- 16 files changed, 409 insertions(+), 406 deletions(-) create mode 100644 nslcd/invalidator.c delete mode 100644 nslcd/nscd.c create mode 100644 pynslcd/invalidator.py delete mode 100644 pynslcd/nscd.py diff --git a/man/nslcd.conf.5.xml b/man/nslcd.conf.5.xml index 9435e3a..482ad7b 100644 --- a/man/nslcd.conf.5.xml +++ b/man/nslcd.conf.5.xml @@ -818,8 +818,8 @@ - - + + DB,DB,... diff --git a/nslcd/Makefile.am b/nslcd/Makefile.am index 60560f8..4346f21 100644 --- a/nslcd/Makefile.am +++ b/nslcd/Makefile.am @@ -30,7 +30,7 @@ nslcd_SOURCES = nslcd.c ../nslcd.h ../common/nslcd-prot.h \ myldap.c myldap.h \ cfg.c cfg.h \ attmap.c attmap.h \ - nsswitch.c nscd.c \ + nsswitch.c invalidator.c \ config.c alias.c ether.c group.c host.c netgroup.c network.c \ passwd.c protocol.c rpc.c service.c shadow.c pam.c usermod.c nslcd_LDADD = ../common/libtio.a ../common/libdict.a \ diff --git a/nslcd/cfg.c b/nslcd/cfg.c index 381ddec..ec1237c 100644 --- a/nslcd/cfg.c +++ b/nslcd/cfg.c @@ -934,7 +934,7 @@ static void handle_pam_password_prohibit_message( cfg->pam_password_prohibit_message = value; } -static void handle_nscd_invalidate( +static void handle_reconnect_invalidate( const char *filename, int lnr, const char *keyword, char *line, struct ldap_config *cfg) { @@ -963,7 +963,7 @@ static void handle_nscd_invalidate( log_log(LOG_ERR, "%s:%d: unknown map: '%s'", filename, lnr, name); exit(EXIT_FAILURE); } - cfg->nscd_invalidate[map] = 1; + cfg->reconnect_invalidate[map] = 1; } } } @@ -1100,7 +1100,7 @@ static void cfg_defaults(struct ldap_config *cfg) cfg->pam_authz_searches[i] = NULL; cfg->pam_password_prohibit_message = NULL; for (i = 0; i < LM_NONE; i++) - cfg->nscd_invalidate[i] = 0; + cfg->reconnect_invalidate[i] = 0; } static void cfg_read(const char *filename, struct ldap_config *cfg) @@ -1432,9 +1432,9 @@ static void cfg_read(const char *filename, struct ldap_config *cfg) { handle_pam_password_prohibit_message(filename, lnr, keyword, line, cfg); } - else if (strcasecmp(keyword, "nscd_invalidate") == 0) + else if (strcasecmp(keyword, "reconnect_invalidate") == 0) { - handle_nscd_invalidate(filename, lnr, keyword, line, cfg); + handle_reconnect_invalidate(filename, lnr, keyword, line, cfg); } #ifdef ENABLE_CONFIGFILE_CHECKING /* fallthrough */ @@ -1689,14 +1689,14 @@ static void cfg_dump(void) /* build a comma-separated list */ buffer[0] = '\0'; for (i = 0; i < LM_NONE ; i++) - if (nslcd_cfg->nscd_invalidate[i]) + if (nslcd_cfg->reconnect_invalidate[i]) { if (buffer[0] != '\0') strncat(buffer, ",", sizeof(buffer) - 1 - strlen(buffer)); strncat(buffer, print_map(i), sizeof(buffer) - 1 - strlen(buffer)); } if (buffer[0] != '\0') - log_log(LOG_DEBUG, "CFG: nscd_invalidate %s", buffer); + log_log(LOG_DEBUG, "CFG: reconnect_invalidate %s", buffer); } void cfg_init(const char *fname) diff --git a/nslcd/cfg.h b/nslcd/cfg.h index 4ec31ff..65b2058 100644 --- a/nslcd/cfg.h +++ b/nslcd/cfg.h @@ -126,7 +126,7 @@ struct ldap_config { int ignorecase; /* whether or not case should be ignored in lookups */ 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 nscd_invalidate[LM_NONE]; /* set to 1 if the corresponding map should be invalidated */ + char reconnect_invalidate[LM_NONE]; /* set to 1 if the corresponding map should be invalidated */ }; /* this is a pointer to the global configuration, it should be available diff --git a/nslcd/common.h b/nslcd/common.h index a6c2c4d..fce92f6 100644 --- a/nslcd/common.h +++ b/nslcd/common.h @@ -141,11 +141,11 @@ void nsswitch_check_reload(void); int nsswitch_shadow_uses_ldap(void); /* start a child process that holds onto the original privileges with the - sole purpose of running nscd -i commands */ -int nscd_start_invalidator(void); + purpose of running external cache invalidation commands */ +int invalidator_start(void); -/* signal nscd to invalidate the selected map */ -void nscd_invalidate(enum ldap_map_selector map); +/* signal invalidator to invalidate the selected external cache */ +void invalidator_do(enum ldap_map_selector map); /* fallback definition of HOST_NAME_MAX */ #ifndef HOST_NAME_MAX diff --git a/nslcd/invalidator.c b/nslcd/invalidator.c new file mode 100644 index 0000000..03584eb --- /dev/null +++ b/nslcd/invalidator.c @@ -0,0 +1,265 @@ +/* + invalidator.c - functions for invalidating external caches + + Copyright (C) 2013 Arthur de Jong + + This 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. + + This 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 this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "log.h" + +/* the write end of a pipe that is used to signal the child process + to invalidate the cache */ +static int signalfd = -1; + +/* we have our own implementation because nscd could use different names */ +static const char *map2name(enum ldap_map_selector map) +{ + switch (map) + { + case LM_ALIASES: return "aliases"; + case LM_ETHERS: return "ethers"; + case LM_GROUP: return "group"; + case LM_HOSTS: return "hosts"; + case LM_NETGROUP: return "netgroup"; + case LM_NETWORKS: return "networks"; + case LM_PASSWD: return "passwd"; + case LM_PROTOCOLS: return "protocols"; + case LM_RPC: return "rpc"; + case LM_SERVICES: return "services"; + case LM_SHADOW: return "shadow"; + case LM_NFSIDMAP: return "nfsidmap"; + case LM_NONE: + default: return NULL; + } +} + +/* invalidate the specified database */ +static void exec_invalidate(const char *db) +{ + pid_t cpid; + int i, status; + char *argv[4]; + char cmdline[80]; +#ifdef HAVE_EXECVPE + char *newenviron[] = { NULL }; +#endif + /* build command line */ + if (strcmp(db, "nfsidmap") == 0) + { + argv[0] = "nfsidmap"; + argv[1] = "-c"; + argv[2] = NULL; + } + else + { + argv[0] = "nscd"; + argv[1] = "-i"; + argv[2] = (char *)db; + argv[3] = NULL; + } + mysnprintf(cmdline, 80, "%s %s%s%s", argv[0], argv[1], + argv[2] != NULL ? " " : "", argv[2] != NULL ? argv[2] : ""); + log_log(LOG_DEBUG, "invalidator: %s", cmdline); + /* do fork/exec */ + switch (cpid=fork()) + { + case 0: /* we are the child */ + /* close all file descriptors */ + i = sysconf(_SC_OPEN_MAX) - 1; + /* if the system does not have OPEN_MAX just close the first 32 and + hope we have closed enough */ + if (i < 0) + i = 32; + for (; i >= 0; i--) + close(i); + /* execute command */ +#ifdef HAVE_EXECVPE + execvpe(argv[0], argv, newenviron); +#else + execvp(argv[0], argv); +#endif + /* if we are here there has been an error */ + /* we can't log since we don't have any useful file descriptors */ + _exit(EXIT_FAILURE); + break; + case -1: /* we are the parent, but have an error */ + log_log(LOG_ERR, "invalidator: fork() failed: %s", strerror(errno)); + break; + default: /* we are the parent */ + /* wait for child exit */ + do + { + errno = 0; + i = waitpid(cpid, &status, 0); + } + while ((i < 0) && (errno == EINTR)); + if (i < 0) + log_log(LOG_ERR, "invalidator: waitpid(%d) failed: %s", (int)cpid, strerror(errno)); + else if (WIFEXITED(status)) + { + i = WEXITSTATUS(status); + if (i == 0) + log_log(LOG_DEBUG, "invalidator: %s (pid %d) success", + cmdline, (int)cpid); + else + log_log(LOG_DEBUG, "invalidator: %s (pid %d) failed (%d)", + cmdline, (int)cpid, i); + } + else if (WIFSIGNALED(status)) + { + i = WTERMSIG(status); + log_log(LOG_ERR, "invalidator: %s (pid %d) killed by %s (%d)", + cmdline, (int)cpid, signame(i), i); + } + else + log_log(LOG_ERR, "invalidator: %s (pid %d) had unknown failure", + cmdline, (int)cpid); + break; + } +} + +/* main loop for the invalidator process */ +static void handle_requests(int fd) +{ + int i; + uint8_t c; + const char *db; + log_log(LOG_DEBUG, "invalidator: starting"); + /* set up environment */ + (void)chdir("/"); + putenv("PATH=/usr/sbin:/usr/bin:/sbin:/bin"); + /* handle incoming requests */ + while (1) + { + i = read(fd, &c, sizeof(uint8_t)); + if (i == 0) + { + log_log(LOG_ERR, "invalidator: EOF"); + _exit(EXIT_SUCCESS); + } + else if (i < 0) + { + if (errno == EINTR) + log_log(LOG_DEBUG, "invalidator: read failed (ignored): %s", + strerror(errno)); + else + { + log_log(LOG_ERR, "invalidator: read failed: %s", strerror(errno)); + _exit(EXIT_SUCCESS); + } + } + else + { + db = map2name((enum ldap_map_selector)c); + if (db == NULL) + log_log(LOG_ERR, "invalidator: invalid db received"); + else + exec_invalidate(db); + } + } +} + +/* start a child process that holds onto the original privileges with the + purpose of running external cache invalidation commands */ +int invalidator_start(void) +{ + int pipefds[2]; + pid_t cpid; + int i; + /* set up a pipe for communication */ + if (pipe(pipefds) < 0) + { + log_log(LOG_ERR, "pipe() failed: %s", strerror(errno)); + return -1; + } + /* set O_NONBLOCK on the write end to ensure that a hanging invalidator + process does not bring down the rest of the application */ + if ((i = fcntl(pipefds[1], F_GETFL, 0)) < 0) + { + log_log(LOG_ERR, "fctnl(F_GETFL) failed: %s", strerror(errno)); + close(pipefds[0]); + close(pipefds[1]); + return -1; + } + if (fcntl(pipefds[1], F_SETFL, i | O_NONBLOCK) < 0) + { + log_log(LOG_ERR, "fctnl(F_SETFL,O_NONBLOCK) failed: %s", strerror(errno)); + close(pipefds[0]); + close(pipefds[1]); + return -1; + } + /* fork a child to perfrom the invalidate commands */ + cpid = fork(); + if (cpid < 0) + { + log_log(LOG_ERR, "fork() failed: %s", strerror(errno)); + close(pipefds[0]); + close(pipefds[1]); + return -1; + } + if (cpid == 0) + { + /* we are the child: close the write end and handle requests */ + close(pipefds[1]); + handle_requests(pipefds[0]); + /* the handle function should't return */ + _exit(EXIT_FAILURE); + } + /* we are the parent: close the read end and save the write end */ + close(pipefds[0]); + signalfd = pipefds[1]; + return 0; +} + +/* signal invalidator to invalidate the selected external cache */ +void invalidator_do(enum ldap_map_selector map) +{ + uint8_t c; + int rc; + if (signalfd < 0) + return; + /* LM_NONE is used to signal all maps condigured in reconnect_invalidate */ + if (map == LM_NONE) + { + for (map = 0; map < LM_NONE ; map++) + invalidator_do(map); + if (nslcd_cfg->reconnect_invalidate[map]) + return; + } + /* write a single byte which should be atomic and not fill the PIPE + buffer too soon on most platforms + (nslcd should already ignore SIGPIPE) */ + c = (uint8_t)map; + rc = write(signalfd, &c, sizeof(uint8_t)); + if (rc <= 0) + log_log(LOG_WARNING, "error signalling invalidator: %s", + strerror(errno)); +} diff --git a/nslcd/myldap.c b/nslcd/myldap.c index 243ec9e..b2258d4 100644 --- a/nslcd/myldap.c +++ b/nslcd/myldap.c @@ -1197,8 +1197,8 @@ static int do_retry_search(MYLDAP_SEARCH *search) if ((current_uri->lastfail > 0) || (search->session->current_uri != start_uri)) { log_log(LOG_INFO, "connected to LDAP server %s", current_uri->uri); - /* signal nscd cache invalidation */ - nscd_invalidate(LM_NONE); + /* signal external invalidation of configured caches */ + invalidator_do(LM_NONE); } /* update ok time */ current_uri->firstfail = 0; diff --git a/nslcd/nscd.c b/nslcd/nscd.c deleted file mode 100644 index 14a976c..0000000 --- a/nslcd/nscd.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - nscd.c - functions for invalidating the nscd cache - - Copyright (C) 2013 Arthur de Jong - - This 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. - - This 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 this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "log.h" - -/* the write end of a pipe that is used to signal the child process - to call nscd to invalidate the cache */ -static int signalfd = -1; - -/* we have our own implementation because nscd could use different names */ -static const char *map2name(enum ldap_map_selector map) -{ - switch (map) - { - case LM_ALIASES: return "aliases"; - case LM_ETHERS: return "ethers"; - case LM_GROUP: return "group"; - case LM_HOSTS: return "hosts"; - case LM_NETGROUP: return "netgroup"; - case LM_NETWORKS: return "networks"; - case LM_PASSWD: return "passwd"; - case LM_PROTOCOLS: return "protocols"; - case LM_RPC: return "rpc"; - case LM_SERVICES: return "services"; - case LM_SHADOW: return "shadow"; - case LM_NFSIDMAP: return "nfsidmap"; - case LM_NONE: - default: return NULL; - } -} - -/* invalidate the specified database in nscd */ -static void exec_invalidate(const char *db) -{ - pid_t cpid; - int i, status; - char *argv[4]; -#ifdef HAVE_EXECVPE - char *newenviron[] = { NULL }; -#endif - log_log(LOG_DEBUG, "nscd_invalidator: nscd -i %s", db); - /* do fork/exec */ - switch (cpid=fork()) - { - case 0: /* we are the child */ - /* close all file descriptors */ - i = sysconf(_SC_OPEN_MAX) - 1; - /* if the system does not have OPEN_MAX just close the first 32 and - hope we have closed enough */ - if (i < 0) - i = 32; - for (; i >= 0; i--) - close(i); - /* build command line */ - if (strcmp(db, "nfsidmap") == 0) - { - argv[0] = "nfsidmap"; - argv[1] = "-c"; - argv[2] = NULL; - } - else - { - argv[0] = "nscd"; - argv[1] = "-i"; - argv[2] = (char *)db; - argv[3] = NULL; - } - /* execute command */ -#ifdef HAVE_EXECVPE - execvpe(argv[0], argv, newenviron); -#else - execvp(argv[0], argv); -#endif - /* if we are here there has been an error */ - /* we can't log since we don't have any useful file descriptors */ - _exit(EXIT_FAILURE); - break; - case -1: /* we are the parent, but have an error */ - log_log(LOG_ERR, "nscd_invalidator: fork() failed: %s", strerror(errno)); - break; - default: /* we are the parent */ - /* wait for child exit */ - do - { - errno = 0; - i = waitpid(cpid, &status, 0); - } - while ((i < 0) && (errno == EINTR)); - if (i < 0) - log_log(LOG_ERR, "nscd_invalidator: waitpid(%d) failed: %s", (int)cpid, strerror(errno)); - else if (WIFEXITED(status)) - { - i = WEXITSTATUS(status); - if (i == 0) - log_log(LOG_DEBUG, "nscd_invalidator: nscd -i %s (pid %d) success", - db, (int)cpid); - else - log_log(LOG_DEBUG, "nscd_invalidator: nscd -i %s (pid %d) failed (%d)", - db, (int)cpid, i); - } - else if (WIFSIGNALED(status)) - { - i = WTERMSIG(status); - log_log(LOG_ERR, "nscd_invalidator: nscd -i %s (pid %d) killed by %s (%d)", - db, (int)cpid, signame(i), i); - } - else - log_log(LOG_ERR, "nscd_invalidator: nscd -i %s (pid %d) had unknown failure", - db, (int)cpid); - break; - } -} - -/* main loop for the invalidator process */ -static void nscd_handle_requests(int fd) -{ - int i; - uint8_t c; - const char *db; - log_log(LOG_DEBUG, "nscd_invalidator: starting"); - /* set up environment */ - (void)chdir("/"); - putenv("PATH=/usr/sbin:/usr/bin:/sbin:/bin"); - /* handle incoming requests */ - while (1) - { - i = read(fd, &c, sizeof(uint8_t)); - if (i == 0) - { - log_log(LOG_ERR, "nscd_invalidator: EOF"); - _exit(EXIT_SUCCESS); - } - else if (i < 0) - { - if (errno == EINTR) - log_log(LOG_DEBUG, "nscd_invalidator: read failed (ignored): %s", - strerror(errno)); - else - { - log_log(LOG_ERR, "nscd_invalidator: read failed: %s", strerror(errno)); - _exit(EXIT_SUCCESS); - } - } - else - { - db = map2name((enum ldap_map_selector)c); - if (db == NULL) - log_log(LOG_ERR, "nscd_invalidator: invalid db received"); - else - exec_invalidate(db); - } - } -} - -/* start a child process that holds onto the original privileges with the - sole purpose of running nscd -i commands */ -int nscd_start_invalidator(void) -{ - int pipefds[2]; - pid_t cpid; - int i; - /* set up a pipe for communication */ - if (pipe(pipefds) < 0) - { - log_log(LOG_ERR, "pipe() failed: %s", strerror(errno)); - return -1; - } - /* set O_NONBLOCK on the write end to ensure that a hanging invalidator - process does not bring down the rest of the application */ - if ((i = fcntl(pipefds[1], F_GETFL, 0)) < 0) - { - log_log(LOG_ERR, "fctnl(F_GETFL) failed: %s", strerror(errno)); - close(pipefds[0]); - close(pipefds[1]); - return -1; - } - if (fcntl(pipefds[1], F_SETFL, i | O_NONBLOCK) < 0) - { - log_log(LOG_ERR, "fctnl(F_SETFL,O_NONBLOCK) failed: %s", strerror(errno)); - close(pipefds[0]); - close(pipefds[1]); - return -1; - } - /* fork a child to perfrom the nscd invalidate commands */ - cpid = fork(); - if (cpid < 0) - { - log_log(LOG_ERR, "fork() failed: %s", strerror(errno)); - close(pipefds[0]); - close(pipefds[1]); - return -1; - } - if (cpid == 0) - { - /* we are the child: close the write end and handle requests */ - close(pipefds[1]); - nscd_handle_requests(pipefds[0]); - /* the handle function should't return */ - _exit(EXIT_FAILURE); - } - /* we are the parent: close the read end and save the write end */ - close(pipefds[0]); - signalfd = pipefds[1]; - return 0; -} - -/* signal nscd to invalidate the selected map */ -void nscd_invalidate(enum ldap_map_selector map) -{ - uint8_t c; - int rc; - if (signalfd < 0) - return; - /* LM_NONE is used to signal all maps condigured in nscd_invalidate */ - if (map == LM_NONE) - { - for (map = 0; map < LM_NONE ; map++) - if (nslcd_cfg->nscd_invalidate[map]) - nscd_invalidate(map); - return; - } - /* write a single byte which should be atomic and not fill the PIPE - buffer too soon on most platforms - (nslcd should already ignore SIGPIPE) */ - c = (uint8_t)map; - rc = write(signalfd, &c, sizeof(uint8_t)); - if (rc <= 0) - log_log(LOG_WARNING, "error signalling nscd invalidator: %s", - strerror(errno)); -} diff --git a/nslcd/nslcd.c b/nslcd/nslcd.c index 0816647..8cc29c6 100644 --- a/nslcd/nslcd.c +++ b/nslcd/nslcd.c @@ -706,12 +706,12 @@ int main(int argc, char *argv[]) if (!nslcd_debugging) log_startlogging(); log_log(LOG_INFO, "version %s starting", VERSION); - /* start subprocess to do nscd invalidating if nscd_invalidate is set */ + /* start subprocess to do invalidating if reconnect_invalidate is set */ for (i = 0; i < LM_NONE; i++) - if (nslcd_cfg->nscd_invalidate[i]) + if (nslcd_cfg->reconnect_invalidate[i]) break; if (i < LM_NONE) - nscd_start_invalidator(); + invalidator_start(); /* write pidfile */ create_pidfile(NSLCD_PIDFILE); /* install handler to close stuff off on exit and log notice */ diff --git a/pynslcd/Makefile.am b/pynslcd/Makefile.am index d0604f4..f96654b 100644 --- a/pynslcd/Makefile.am +++ b/pynslcd/Makefile.am @@ -20,7 +20,7 @@ pynslcddir = $(datadir)/pynslcd pynslcd_PYTHON = pynslcd.py attmap.py cache.py cfg.py common.py expr.py \ - mypidfile.py nscd.py search.py tio.py \ + mypidfile.py invalidator.py search.py tio.py \ alias.py ether.py group.py host.py netgroup.py network.py \ passwd.py protocol.py rpc.py service.py shadow.py pam.py \ usermod.py diff --git a/pynslcd/cfg.py b/pynslcd/cfg.py index 516eeb7..cf9c872 100644 --- a/pynslcd/cfg.py +++ b/pynslcd/cfg.py @@ -85,7 +85,7 @@ nss_nested_groups = False validnames = re.compile(r'^[a-z0-9._@$][a-z0-9._@$ \\~-]{0,98}[a-z0-9._@$~-]$', re.IGNORECASE) pam_authz_searches = [] pam_password_prohibit_message = None # FIXME: add support -nscd_invalidate = set() +reconnect_invalidate = set() # allowed boolean values @@ -311,15 +311,15 @@ def read(filename): flags = 0 | re.IGNORECASE if m.group('flags') == 'i' else 0 validnames = re.compile(m.group('value'), flags=flags) continue - # nscd_invalidate ,,... - m = re.match('nscd_invalidate\s+(?P\S.*)', + # reconnect_invalidate ,,... + m = re.match('reconnect_invalidate\s+(?P\S.*)', line, re.IGNORECASE) if m: dbs = re.split('[ ,]+', m.group('value').lower()) for db in dbs: if db not in maps.keys() + ['nfsidmap']: raise ParseError(filename, lineno, 'map %s unknown' % db) - nscd_invalidate.update(dbs) + reconnect_invalidate.update(dbs) continue # unrecognised line raise ParseError(filename, lineno, 'error parsing line %r' % line) diff --git a/pynslcd/invalidator.py b/pynslcd/invalidator.py new file mode 100644 index 0000000..98c0783 --- /dev/null +++ b/pynslcd/invalidator.py @@ -0,0 +1,112 @@ + +# invalidator.py - functions for invalidating external caches +# +# Copyright (C) 2013 Arthur de Jong +# +# This 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. +# +# This 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 this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +import fcntl +import logging +import os +import subprocess + +import cfg + + +# the file descriptor used for sending messages to the child process +signalfd = None + + +# mapping between map name and signal character +_db_to_char = dict( + aliases='A', ethers='E', group='G', hosts='H', netgroup='U', + networks='N', passwd='P', protocols='L', rpc='R', services='V', + shadow='S', nfsidmap='F', + ) +_char_to_db = dict((reversed(item) for item in _db_to_char.items())) + + +def exec_invalidate(*args): + cmd = ' '.join(args) + logging.debug('invalidator: %s', cmd) + try: + p = subprocess.Popen(args, bufsize=4096, close_fds=True, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + output, ignored = p.communicate() + if output: + output = ': %s' % output[:1024].strip() + if p.returncode == 0: + logging.debug('invalidator: %s (pid %d) success%s', + cmd, p.pid, output) + elif p.returncode > 0: + logging.debug('invalidator: %s (pid %d) failed (%d)%s', + cmd, p.pid, p.returncode, output) + else: # p.returncode < 0 + logging.error('invalidator: %s (pid %d) killed by signal %d%s', + cmd, p.pid, -p.returncode, output) + except: + logging.warn('invalidator: %s failed', cmd, exc_info=True) + + +def loop(fd): + # set process title + try: + import setproctitle + setproctitle.setproctitle('(invalidator)') + except ImportError: + pass + # set up clean environment + os.chdir('/') + os.environ['PATH'] = '/usr/sbin:/usr/bin:/sbin:/bin' + while True: + db = os.read(fd, 1) + if db == '': + break # close process down + db = _char_to_db.get(db, None) + if db == 'nfsidmap': + exec_invalidate('nfsidmap', '-c') + else if db: + exec_invalidate('nscd', '-i', db) + + +def start_invalidator(): + r, w = os.pipe() + # mark write end as non-blocking + flags = fcntl.fcntl(w, fcntl.F_GETFL) + fcntl.fcntl(w, fcntl.F_SETFL, flags | os.O_NONBLOCK) + cpid = os.fork() + if cpid == 0: + # we are the child + os.close(w) + loop(r) + os._exit(1) + # we are the parent + global signalfd + signalfd = w + os.close(r) + + +def invalidate(db=None): + if signalfd is None: + return # nothing to do + if db: + db = _db_to_char.get(db, '') + else: + db = ''.join(_db_to_char[x] for x in cfg.reconnect_invalidate) + try: + os.write(signalfd, db) + except: + logging.warn('requesting invalidation (%s) failed', db, exc_info=True) diff --git a/pynslcd/nscd.py b/pynslcd/nscd.py deleted file mode 100644 index 1cc05cf..0000000 --- a/pynslcd/nscd.py +++ /dev/null @@ -1,112 +0,0 @@ - -# nscd.py - functions for invalidating the nscd cache -# -# Copyright (C) 2013 Arthur de Jong -# -# This 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. -# -# This 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 this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -# 02110-1301 USA - -import fcntl -import logging -import os -import subprocess - -import cfg - - -# the file descriptor used for sending messages to the child process -signalfd = None - - -# mapping between map name and signal character -_db_to_char = dict( - aliases='A', ethers='E', group='G', hosts='H', netgroup='U', - networks='N', passwd='P', protocols='L', rpc='R', services='V', - shadow='S', nfsidmap='F', - ) -_char_to_db = dict((reversed(item) for item in _db_to_char.items())) - - -def exec_invalidate(*args): - cmd = ' '.join(args) - logging.debug('nscd_invalidator: %s', cmd) - try: - p = subprocess.Popen(args, bufsize=4096, close_fds=True, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - output, ignored = p.communicate() - if output: - output = ': %s' % output[:1024].strip() - if p.returncode == 0: - logging.debug('nscd_invalidator: %s (pid %d) success%s', - cmd, p.pid, output) - elif p.returncode > 0: - logging.debug('nscd_invalidator: %s (pid %d) failed (%d)%s', - cmd, p.pid, p.returncode, output) - else: # p.returncode < 0 - logging.error('nscd_invalidator: %s (pid %d) killed by signal %d%s', - cmd, p.pid, -p.returncode, output) - except: - logging.warn('nscd_invalidator: %s failed', cmd, exc_info=True) - - -def loop(fd): - # set process title - try: - import setproctitle - setproctitle.setproctitle('(nscd invalidator)') - except ImportError: - pass - # set up clean environment - os.chdir('/') - os.environ['PATH'] = '/usr/sbin:/usr/bin:/sbin:/bin' - while True: - db = os.read(fd, 1) - if db == '': - break # close process down - db = _char_to_db.get(db, None) - if db == 'nfsidmap': - exec_invalidate('nfsidmap', '-c') - else if db: - exec_invalidate('nscd', '-i', db) - - -def start_invalidator(): - r, w = os.pipe() - # mark write end as non-blocking - flags = fcntl.fcntl(w, fcntl.F_GETFL) - fcntl.fcntl(w, fcntl.F_SETFL, flags | os.O_NONBLOCK) - cpid = os.fork() - if cpid == 0: - # we are the child - os.close(w) - loop(r) - os._exit(1) - # we are the parent - global signalfd - signalfd = w - os.close(r) - - -def invalidate(db=None): - if signalfd is None: - return # nothing to do - if db: - db = _db_to_char.get(db, '') - else: - db = ''.join(_db_to_char[x] for x in cfg.nscd_invalidate) - try: - os.write(signalfd, db) - except: - logging.warn('requesting invalidation (%s) failed', db, exc_info=True) diff --git a/pynslcd/pynslcd.py b/pynslcd/pynslcd.py index df54bad..4a57155 100755 --- a/pynslcd/pynslcd.py +++ b/pynslcd/pynslcd.py @@ -35,7 +35,7 @@ import cfg import common import constants import mypidfile -import nscd +import invalidator import search @@ -323,9 +323,9 @@ if __name__ == '__main__': logging.getLogger().setLevel(min(level for method, level in cfg.logs)) logging.getLogger().removeHandler(stderrhandler) logging.info('version %s starting', constants.VERSION) - # start nscd sub-process if needed - if cfg.nscd_invalidate: - nscd.start_invalidator() + # start invalidator sub-process if needed + if cfg.invalidator_invalidate: + invalidator.start_invalidator() # create socket nslcd_serversocket = create_socket() # load supplementary groups diff --git a/pynslcd/search.py b/pynslcd/search.py index 3db6e9d..f8c82fb 100644 --- a/pynslcd/search.py +++ b/pynslcd/search.py @@ -55,9 +55,9 @@ class Connection(ldap.ldapobject.ReconnectLDAPObject): self.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_HARD) def reconnect_after_fail(self): - import nscd + import invalidator logging.info('connected to LDAP server %s', cfg.uri) - nscd.invalidate() + invalidator.invalidate() def search_s(self, *args, **kwargs): # wrapper function to keep the global server_error state diff --git a/tests/Makefile.am b/tests/Makefile.am index b8990a9..13d34fc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -55,7 +55,7 @@ test_getpeercred_SOURCES = test_getpeercred.c common.h test_getpeercred_LDADD = ../compat/libcompat.a # common objects that are included for the tests of nslcd functionality -common_nslcd_LDADD = ../nslcd/log.o ../nslcd/common.o ../nslcd/nscd.o \ +common_nslcd_LDADD = ../nslcd/log.o ../nslcd/common.o ../nslcd/invalidator.o \ ../nslcd/myldap.o ../nslcd/attmap.o ../nslcd/nsswitch.o \ ../nslcd/alias.o ../nslcd/ether.o ../nslcd/group.o \ ../nslcd/host.o ../nslcd/netgroup.o ../nslcd/network.o \ -- cgit v1.2.3