summaryrefslogtreecommitdiff
path: root/nslcd/invalidator.c
diff options
context:
space:
mode:
authorArthur de Jong <arthur@arthurdejong.org>2013-07-26 14:59:43 +0200
committerArthur de Jong <arthur@arthurdejong.org>2013-07-26 14:59:43 +0200
commite1b0399ee018d217cd50267cef03c28dfdb32fbf (patch)
tree56229ff26cd878176707d9672fe65148b6073c5a /nslcd/invalidator.c
parent6054499f9a9952593ccadc83182e01d39ff62f12 (diff)
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.
Diffstat (limited to 'nslcd/invalidator.c')
-rw-r--r--nslcd/invalidator.c265
1 files changed, 265 insertions, 0 deletions
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 <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 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));
+}