|
Packit |
6bd9ab |
/*
|
|
Packit |
6bd9ab |
invalidator.c - functions for invalidating external caches
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
Copyright (C) 2013-2014 Arthur de Jong
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
This library is free software; you can redistribute it and/or
|
|
Packit |
6bd9ab |
modify it under the terms of the GNU Lesser General Public
|
|
Packit |
6bd9ab |
License as published by the Free Software Foundation; either
|
|
Packit |
6bd9ab |
version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
This library is distributed in the hope that it will be useful,
|
|
Packit |
6bd9ab |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
6bd9ab |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
6bd9ab |
Lesser General Public License for more details.
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
You should have received a copy of the GNU Lesser General Public
|
|
Packit |
6bd9ab |
License along with this library; if not, write to the Free Software
|
|
Packit |
6bd9ab |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit |
6bd9ab |
02110-1301 USA
|
|
Packit |
6bd9ab |
*/
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#include "config.h"
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#include <stdio.h>
|
|
Packit |
6bd9ab |
#include <string.h>
|
|
Packit |
6bd9ab |
#include <ctype.h>
|
|
Packit |
6bd9ab |
#include <errno.h>
|
|
Packit |
6bd9ab |
#include <unistd.h>
|
|
Packit |
6bd9ab |
#include <sys/types.h>
|
|
Packit |
6bd9ab |
#include <fcntl.h>
|
|
Packit |
6bd9ab |
#include <signal.h>
|
|
Packit |
6bd9ab |
#include <sys/types.h>
|
|
Packit |
6bd9ab |
#include <sys/wait.h>
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
#include "common.h"
|
|
Packit |
6bd9ab |
#include "log.h"
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* the write end of a pipe that is used to signal the child process
|
|
Packit |
6bd9ab |
to invalidate the cache */
|
|
Packit |
6bd9ab |
static int signalfd = -1;
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* we have our own implementation because nscd could use different names */
|
|
Packit |
6bd9ab |
static const char *map2name(enum ldap_map_selector map)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
switch (map)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
case LM_ALIASES: return "aliases";
|
|
Packit |
6bd9ab |
case LM_ETHERS: return "ethers";
|
|
Packit |
6bd9ab |
case LM_GROUP: return "group";
|
|
Packit |
6bd9ab |
case LM_HOSTS: return "hosts";
|
|
Packit |
6bd9ab |
case LM_NETGROUP: return "netgroup";
|
|
Packit |
6bd9ab |
case LM_NETWORKS: return "networks";
|
|
Packit |
6bd9ab |
case LM_PASSWD: return "passwd";
|
|
Packit |
6bd9ab |
case LM_PROTOCOLS: return "protocols";
|
|
Packit |
6bd9ab |
case LM_RPC: return "rpc";
|
|
Packit |
6bd9ab |
case LM_SERVICES: return "services";
|
|
Packit |
6bd9ab |
case LM_SHADOW: return "shadow";
|
|
Packit |
6bd9ab |
case LM_NFSIDMAP: return "nfsidmap";
|
|
Packit |
6bd9ab |
case LM_NONE:
|
|
Packit |
6bd9ab |
default: return NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* invalidate the specified database */
|
|
Packit |
6bd9ab |
static void exec_invalidate(const char *db)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
pid_t cpid;
|
|
Packit |
6bd9ab |
int i, status;
|
|
Packit |
6bd9ab |
char *argv[4];
|
|
Packit |
6bd9ab |
char cmdline[80];
|
|
Packit |
6bd9ab |
#ifdef HAVE_EXECVPE
|
|
Packit |
6bd9ab |
char *newenviron[] = { NULL };
|
|
Packit |
6bd9ab |
#endif
|
|
Packit |
6bd9ab |
/* build command line */
|
|
Packit |
6bd9ab |
if (strcmp(db, "nfsidmap") == 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
argv[0] = "nfsidmap";
|
|
Packit |
6bd9ab |
argv[1] = "-c";
|
|
Packit |
6bd9ab |
argv[2] = NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
argv[0] = "nscd";
|
|
Packit |
6bd9ab |
argv[1] = "-i";
|
|
Packit |
6bd9ab |
argv[2] = (char *)db;
|
|
Packit |
6bd9ab |
argv[3] = NULL;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
if (mysnprintf(cmdline, 80, "%s %s%s%s", argv[0], argv[1],
|
|
Packit |
6bd9ab |
argv[2] != NULL ? " " : "", argv[2] != NULL ? argv[2] : ""))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "exec_invalidate(): cmdline buffer too small");
|
|
Packit |
6bd9ab |
return;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "invalidator: %s", cmdline);
|
|
Packit |
6bd9ab |
/* do fork/exec */
|
|
Packit |
6bd9ab |
switch (cpid=fork())
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
case 0: /* we are the child */
|
|
Packit |
6bd9ab |
/* close all file descriptors */
|
|
Packit |
6bd9ab |
i = sysconf(_SC_OPEN_MAX) - 1;
|
|
Packit |
6bd9ab |
/* if the system does not have OPEN_MAX just close the first 32 and
|
|
Packit |
6bd9ab |
hope we have closed enough */
|
|
Packit |
6bd9ab |
if (i < 0)
|
|
Packit |
6bd9ab |
i = 32;
|
|
Packit |
6bd9ab |
for (; i >= 0; i--)
|
|
Packit |
6bd9ab |
close(i);
|
|
Packit |
6bd9ab |
/* execute command */
|
|
Packit |
6bd9ab |
#ifdef HAVE_EXECVPE
|
|
Packit |
6bd9ab |
execvpe(argv[0], argv, newenviron);
|
|
Packit |
6bd9ab |
#else
|
|
Packit |
6bd9ab |
execvp(argv[0], argv);
|
|
Packit |
6bd9ab |
#endif
|
|
Packit |
6bd9ab |
/* if we are here there has been an error */
|
|
Packit |
6bd9ab |
/* we can't log since we don't have any useful file descriptors */
|
|
Packit |
6bd9ab |
_exit(EXIT_FAILURE);
|
|
Packit |
6bd9ab |
break;
|
|
Packit |
6bd9ab |
case -1: /* we are the parent, but have an error */
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "invalidator: fork() failed: %s", strerror(errno));
|
|
Packit |
6bd9ab |
break;
|
|
Packit |
6bd9ab |
default: /* we are the parent */
|
|
Packit |
6bd9ab |
/* wait for child exit */
|
|
Packit |
6bd9ab |
do
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
errno = 0;
|
|
Packit |
6bd9ab |
i = waitpid(cpid, &status, 0);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
while ((i < 0) && (errno == EINTR));
|
|
Packit |
6bd9ab |
if (i < 0)
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "invalidator: waitpid(%d) failed: %s", (int)cpid, strerror(errno));
|
|
Packit |
6bd9ab |
else if (WIFEXITED(status))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
i = WEXITSTATUS(status);
|
|
Packit |
6bd9ab |
if (i == 0)
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "invalidator: %s (pid %d) success",
|
|
Packit |
6bd9ab |
cmdline, (int)cpid);
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "invalidator: %s (pid %d) failed (%d)",
|
|
Packit |
6bd9ab |
cmdline, (int)cpid, i);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else if (WIFSIGNALED(status))
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
i = WTERMSIG(status);
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "invalidator: %s (pid %d) killed by %s (%d)",
|
|
Packit |
6bd9ab |
cmdline, (int)cpid, signame(i), i);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "invalidator: %s (pid %d) had unknown failure",
|
|
Packit |
6bd9ab |
cmdline, (int)cpid);
|
|
Packit |
6bd9ab |
break;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* main loop for the invalidator process */
|
|
Packit |
6bd9ab |
static void handle_requests(int fd)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int i;
|
|
Packit |
6bd9ab |
uint8_t c;
|
|
Packit |
6bd9ab |
const char *db;
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "invalidator: starting");
|
|
Packit |
6bd9ab |
/* set up environment */
|
|
Packit |
6bd9ab |
(void)chdir("/");
|
|
Packit |
6bd9ab |
putenv("PATH=/usr/sbin:/usr/bin:/sbin:/bin");
|
|
Packit |
6bd9ab |
/* handle incoming requests */
|
|
Packit |
6bd9ab |
while (1)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
i = read(fd, &c, sizeof(uint8_t));
|
|
Packit |
6bd9ab |
if (i == 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "invalidator: EOF");
|
|
Packit |
6bd9ab |
_exit(EXIT_SUCCESS);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else if (i < 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
if (errno == EINTR)
|
|
Packit |
6bd9ab |
log_log(LOG_DEBUG, "invalidator: read failed (ignored): %s",
|
|
Packit |
6bd9ab |
strerror(errno));
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "invalidator: read failed: %s", strerror(errno));
|
|
Packit |
6bd9ab |
_exit(EXIT_SUCCESS);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
db = map2name((enum ldap_map_selector)c);
|
|
Packit |
6bd9ab |
if (db == NULL)
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "invalidator: invalid db received");
|
|
Packit |
6bd9ab |
else
|
|
Packit |
6bd9ab |
exec_invalidate(db);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* start a child process that holds onto the original privileges with the
|
|
Packit |
6bd9ab |
purpose of running external cache invalidation commands */
|
|
Packit |
6bd9ab |
int invalidator_start(void)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
int pipefds[2];
|
|
Packit |
6bd9ab |
pid_t cpid;
|
|
Packit |
6bd9ab |
int i;
|
|
Packit |
6bd9ab |
/* set up a pipe for communication */
|
|
Packit |
6bd9ab |
if (pipe(pipefds) < 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "pipe() failed: %s", strerror(errno));
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* set O_NONBLOCK on the write end to ensure that a hanging invalidator
|
|
Packit |
6bd9ab |
process does not bring down the rest of the application */
|
|
Packit |
6bd9ab |
if ((i = fcntl(pipefds[1], F_GETFL, 0)) < 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "fctnl(F_GETFL) failed: %s", strerror(errno));
|
|
Packit |
6bd9ab |
close(pipefds[0]);
|
|
Packit |
6bd9ab |
close(pipefds[1]);
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
if (fcntl(pipefds[1], F_SETFL, i | O_NONBLOCK) < 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "fctnl(F_SETFL,O_NONBLOCK) failed: %s", strerror(errno));
|
|
Packit |
6bd9ab |
close(pipefds[0]);
|
|
Packit |
6bd9ab |
close(pipefds[1]);
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* fork a child to perfrom the invalidate commands */
|
|
Packit |
6bd9ab |
cpid = fork();
|
|
Packit |
6bd9ab |
if (cpid < 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
log_log(LOG_ERR, "fork() failed: %s", strerror(errno));
|
|
Packit |
6bd9ab |
close(pipefds[0]);
|
|
Packit |
6bd9ab |
close(pipefds[1]);
|
|
Packit |
6bd9ab |
return -1;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
if (cpid == 0)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
/* we are the child: close the write end and handle requests */
|
|
Packit |
6bd9ab |
close(pipefds[1]);
|
|
Packit |
6bd9ab |
handle_requests(pipefds[0]);
|
|
Packit |
6bd9ab |
/* the handle function should't return */
|
|
Packit |
6bd9ab |
_exit(EXIT_FAILURE);
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* we are the parent: close the read end and save the write end */
|
|
Packit |
6bd9ab |
close(pipefds[0]);
|
|
Packit |
6bd9ab |
signalfd = pipefds[1];
|
|
Packit |
6bd9ab |
return 0;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
|
|
Packit |
6bd9ab |
/* signal invalidator to invalidate the selected external cache */
|
|
Packit |
6bd9ab |
void invalidator_do(enum ldap_map_selector map)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
uint8_t c;
|
|
Packit |
6bd9ab |
int rc;
|
|
Packit |
6bd9ab |
if (signalfd < 0)
|
|
Packit |
6bd9ab |
return;
|
|
Packit |
6bd9ab |
/* LM_NONE is used to signal all maps condigured in reconnect_invalidate */
|
|
Packit |
6bd9ab |
if (map == LM_NONE)
|
|
Packit |
6bd9ab |
{
|
|
Packit |
6bd9ab |
for (map = 0; map < LM_NONE ; map++)
|
|
Packit |
6bd9ab |
if (nslcd_cfg->reconnect_invalidate[map])
|
|
Packit |
6bd9ab |
invalidator_do(map);
|
|
Packit |
6bd9ab |
return;
|
|
Packit |
6bd9ab |
}
|
|
Packit |
6bd9ab |
/* write a single byte which should be atomic and not fill the PIPE
|
|
Packit |
6bd9ab |
buffer too soon on most platforms
|
|
Packit |
6bd9ab |
(nslcd should already ignore SIGPIPE) */
|
|
Packit |
6bd9ab |
c = (uint8_t)map;
|
|
Packit |
6bd9ab |
rc = write(signalfd, &c, sizeof(uint8_t));
|
|
Packit |
6bd9ab |
if (rc <= 0)
|
|
Packit |
6bd9ab |
log_log(LOG_WARNING, "error signalling invalidator: %s",
|
|
Packit |
6bd9ab |
strerror(errno));
|
|
Packit |
6bd9ab |
}
|