Blame nslcd/invalidator.c

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
}