Arthur de Jong

Open Source / Free Software developer

summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur de Jong <arthur@arthurdejong.org>2013-03-09 16:55:47 +0100
committerArthur de Jong <arthur@arthurdejong.org>2013-03-09 19:26:38 +0100
commit008f8a95cd86782141849b41b34552eeef364128 (patch)
tree98043d6ae5057959003f2beb599846a554abd8fc
parent9a6f5b24424e65d62600b0a1388174646af686b9 (diff)
implement functionality to send a cache invalidation signal to nscd
-rw-r--r--configure.ac2
-rw-r--r--nslcd/Makefile.am4
-rw-r--r--nslcd/common.h8
-rw-r--r--nslcd/nscd.c240
-rw-r--r--tests/Makefile.am4
5 files changed, 253 insertions, 5 deletions
diff --git a/configure.ac b/configure.ac
index b55816a..9fcc3ac 100644
--- a/configure.ac
+++ b/configure.ac
@@ -588,7 +588,7 @@ then
AC_SEARCH_LIBS(dlopen, dl)
# check for availability of functions
- AC_CHECK_FUNCS(initgroups setgroups)
+ AC_CHECK_FUNCS(initgroups setgroups execvp execvpe)
AC_CHECK_FUNCS(getpeereid)
AC_CHECK_FUNCS(getpeerucred)
AC_CHECK_FUNCS(__nss_configure_lookup)
diff --git a/nslcd/Makefile.am b/nslcd/Makefile.am
index 4b866ae..d901d64 100644
--- a/nslcd/Makefile.am
+++ b/nslcd/Makefile.am
@@ -1,7 +1,7 @@
# Makefile.am - use automake to generate Makefile.in
#
# Copyright (C) 2006, 2007 West Consulting
-# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012 Arthur de Jong
+# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 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
@@ -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 \
+ nsswitch.c nscd.c \
alias.c config.c ether.c group.c host.c netgroup.c network.c \
passwd.c protocol.c rpc.c service.c shadow.c pam.c
nslcd_LDADD = ../common/libtio.a ../common/libdict.a \
diff --git a/nslcd/common.h b/nslcd/common.h
index fc44c43..2965de6 100644
--- a/nslcd/common.h
+++ b/nslcd/common.h
@@ -36,6 +36,7 @@
#include "common/tio.h"
#include "compat/attrs.h"
#include "myldap.h"
+#include "cfg.h"
/* macros for basic read and write operations, the following
ERROR_OUT* marcos define the action taken on errors
@@ -139,6 +140,13 @@ void nsswitch_check_reload(void);
/* check whether the nsswitch.conf file has LDAP as a naming source for db */
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);
+
+/* signal nscd to invalidate the selected map */
+void nscd_invalidate(enum ldap_map_selector map);
+
/* fallback definition of HOST_NAME_MAX */
#ifndef HOST_NAME_MAX
#ifdef _POSIX_HOST_NAME_MAX
diff --git a/nslcd/nscd.c b/nslcd/nscd.c
new file mode 100644
index 0000000..c707f77
--- /dev/null
+++ b/nslcd/nscd.c
@@ -0,0 +1,240 @@
+/*
+ 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 <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#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_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[] = { "nscd", "-i", NULL, NULL };
+#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);
+ /* execute command */
+ argv[2] = (char *)db;
+#ifdef HAVE_EXECVPE
+ execvpe("nscd", argv, newenviron);
+#else
+ execvp("nscd", 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", 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, cpid);
+ else
+ log_log(LOG_DEBUG, "nscd_invalidator: nscd -i %s (pid %d) failed (%d)",
+ db, 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, cpid, signame(i), i);
+ }
+ else
+ log_log(LOG_ERR, "nscd_invalidator: nscd -i %s (pid %d) had unknown failure",
+ db, 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 */
+ 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) == -1)
+ {
+ 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 == -1)
+ {
+ 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;
+ /* 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/tests/Makefile.am b/tests/Makefile.am
index 2dcc2e1..082e80d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,7 +1,7 @@
# Makefile.am - use automake to generate Makefile.in
#
# Copyright (C) 2006 West Consulting
-# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Arthur de Jong
+# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 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
@@ -54,7 +54,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 \
+common_nslcd_LDADD = ../nslcd/log.o ../nslcd/common.o ../nslcd/nscd.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 \