Blame nscd/nscd.c

Packit 6c4009
/* Copyright (c) 1998-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
Packit 6c4009
Packit 6c4009
   This program is free software; you can redistribute it and/or modify
Packit 6c4009
   it under the terms of the GNU General Public License as published
Packit 6c4009
   by the Free Software Foundation; version 2 of the License, or
Packit 6c4009
   (at your option) any later version.
Packit 6c4009
Packit 6c4009
   This program is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 6c4009
   GNU General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU General Public License
Packit 6c4009
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
/* nscd - Name Service Cache Daemon. Caches passwd, group, and hosts.  */
Packit 6c4009
Packit 6c4009
#include <argp.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <dirent.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <error.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <libintl.h>
Packit 6c4009
#include <locale.h>
Packit 6c4009
#include <paths.h>
Packit 6c4009
#include <pthread.h>
Packit 6c4009
#include <signal.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <syslog.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
#include <sys/socket.h>
Packit 6c4009
#include <sys/stat.h>
Packit 6c4009
#include <sys/uio.h>
Packit 6c4009
#include <sys/un.h>
Packit 6c4009
#include <sys/wait.h>
Packit 6c4009
#include <stdarg.h>
Packit 6c4009
Packit 6c4009
#include "dbg_log.h"
Packit 6c4009
#include "nscd.h"
Packit 6c4009
#include "selinux.h"
Packit 6c4009
#include "../nss/nsswitch.h"
Packit 6c4009
#include <device-nrs.h>
Packit 6c4009
#ifdef HAVE_INOTIFY
Packit 6c4009
# include <sys/inotify.h>
Packit 6c4009
#endif
Packit 6c4009
#include <kernel-features.h>
Packit 6c4009
Packit 6c4009
/* Get libc version number.  */
Packit 6c4009
#include <version.h>
Packit 6c4009
Packit 6c4009
#define PACKAGE _libc_intl_domainname
Packit 6c4009
Packit 6c4009
int do_shutdown;
Packit 6c4009
int disabled_passwd;
Packit 6c4009
int disabled_group;
Packit 6c4009
Packit 6c4009
typedef enum
Packit 6c4009
{
Packit 6c4009
  /* Running in background as daemon.  */
Packit 6c4009
  RUN_DAEMONIZE,
Packit 6c4009
  /* Running in foreground but otherwise behave like a daemon,
Packit 6c4009
     i.e., detach from terminal and use syslog.  This allows
Packit 6c4009
     better integration with services like systemd.  */
Packit 6c4009
  RUN_FOREGROUND,
Packit 6c4009
  /* Run in foreground in debug mode.  */
Packit 6c4009
  RUN_DEBUG
Packit 6c4009
} run_modes;
Packit 6c4009
Packit 6c4009
static run_modes run_mode = RUN_DAEMONIZE;
Packit 6c4009
Packit 6c4009
static const char *conffile = _PATH_NSCDCONF;
Packit 6c4009
Packit 6c4009
time_t start_time;
Packit 6c4009
Packit 6c4009
uintptr_t pagesize_m1;
Packit 6c4009
Packit 6c4009
int paranoia;
Packit 6c4009
time_t restart_time;
Packit 6c4009
time_t restart_interval = RESTART_INTERVAL;
Packit 6c4009
const char *oldcwd;
Packit 6c4009
uid_t old_uid;
Packit 6c4009
gid_t old_gid;
Packit 6c4009
Packit 6c4009
static int check_pid (const char *file);
Packit 6c4009
static int write_pid (const char *file);
Packit 6c4009
static int monitor_child (int fd);
Packit 6c4009
Packit 6c4009
/* Name and version of program.  */
Packit 6c4009
static void print_version (FILE *stream, struct argp_state *state);
Packit 6c4009
void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
Packit 6c4009
Packit 6c4009
/* Function to print some extra text in the help message.  */
Packit 6c4009
static char *more_help (int key, const char *text, void *input);
Packit 6c4009
Packit 6c4009
/* Definitions of arguments for argp functions.  */
Packit 6c4009
static const struct argp_option options[] =
Packit 6c4009
{
Packit 6c4009
  { "config-file", 'f', N_("NAME"), 0,
Packit 6c4009
    N_("Read configuration data from NAME") },
Packit 6c4009
  { "debug", 'd', NULL, 0,
Packit 6c4009
    N_("Do not fork and display messages on the current tty") },
Packit 6c4009
  { "foreground", 'F', NULL, 0,
Packit 6c4009
    N_("Do not fork, but otherwise behave like a daemon") },
Packit 6c4009
  { "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") },
Packit 6c4009
  { "shutdown", 'K', NULL, 0, N_("Shut the server down") },
Packit 6c4009
  { "statistics", 'g', NULL, 0, N_("Print current configuration statistics") },
Packit 6c4009
  { "invalidate", 'i', N_("TABLE"), 0,
Packit 6c4009
    N_("Invalidate the specified cache") },
Packit 6c4009
  { "secure", 'S', N_("TABLE,yes"), OPTION_HIDDEN,
Packit 6c4009
    N_("Use separate cache for each user")},
Packit 6c4009
  { NULL, 0, NULL, 0, NULL }
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Short description of program.  */
Packit 6c4009
static const char doc[] = N_("Name Service Cache Daemon.");
Packit 6c4009
Packit 6c4009
/* Prototype for option handler.  */
Packit 6c4009
static error_t parse_opt (int key, char *arg, struct argp_state *state);
Packit 6c4009
Packit 6c4009
/* Data structure to communicate with argp functions.  */
Packit 6c4009
static struct argp argp =
Packit 6c4009
{
Packit 6c4009
  options, parse_opt, NULL, doc, NULL, more_help
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* True if only statistics are requested.  */
Packit 6c4009
static bool get_stats;
Packit 6c4009
static int parent_fd = -1;
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
main (int argc, char **argv)
Packit 6c4009
{
Packit 6c4009
  int remaining;
Packit 6c4009
Packit 6c4009
  /* Set locale via LC_ALL.  */
Packit 6c4009
  setlocale (LC_ALL, "");
Packit 6c4009
  /* Set the text message domain.  */
Packit 6c4009
  textdomain (PACKAGE);
Packit 6c4009
Packit 6c4009
  /* Determine if the kernel has SELinux support.  */
Packit 6c4009
  nscd_selinux_enabled (&selinux_enabled);
Packit 6c4009
Packit 6c4009
  /* Parse and process arguments.  */
Packit 6c4009
  argp_parse (&argp, argc, argv, 0, &remaining, NULL);
Packit 6c4009
Packit 6c4009
  if (remaining != argc)
Packit 6c4009
    {
Packit 6c4009
      error (0, 0, gettext ("wrong number of arguments"));
Packit 6c4009
      argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
Packit 6c4009
      exit (1);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Read the configuration file.  */
Packit 6c4009
  if (nscd_parse_file (conffile, dbs) != 0)
Packit 6c4009
    /* We couldn't read the configuration file.  We don't start the
Packit 6c4009
       server.  */
Packit 6c4009
    error (EXIT_FAILURE, 0,
Packit 6c4009
	   _("failure while reading configuration file; this is fatal"));
Packit 6c4009
Packit 6c4009
  /* Do we only get statistics?  */
Packit 6c4009
  if (get_stats)
Packit 6c4009
    /* Does not return.  */
Packit 6c4009
    receive_print_stats ();
Packit 6c4009
Packit 6c4009
  /* Check if we are already running. */
Packit 6c4009
  if (check_pid (_PATH_NSCDPID))
Packit 6c4009
    error (EXIT_FAILURE, 0, _("already running"));
Packit 6c4009
Packit 6c4009
  /* Remember when we started.  */
Packit 6c4009
  start_time = time (NULL);
Packit 6c4009
Packit 6c4009
  /* Determine page size.  */
Packit 6c4009
  pagesize_m1 = getpagesize () - 1;
Packit 6c4009
Packit 6c4009
  if (run_mode == RUN_DAEMONIZE || run_mode == RUN_FOREGROUND)
Packit 6c4009
    {
Packit 6c4009
      int i;
Packit 6c4009
      pid_t pid;
Packit 6c4009
Packit 6c4009
      /* Behave like a daemon.  */
Packit 6c4009
      if (run_mode == RUN_DAEMONIZE)
Packit 6c4009
	{
Packit 6c4009
	  int fd[2];
Packit 6c4009
Packit 6c4009
	  if (pipe (fd) != 0)
Packit 6c4009
	    error (EXIT_FAILURE, errno,
Packit 6c4009
		   _("cannot create a pipe to talk to the child"));
Packit 6c4009
Packit 6c4009
	  pid = fork ();
Packit 6c4009
	  if (pid == -1)
Packit 6c4009
	    error (EXIT_FAILURE, errno, _("cannot fork"));
Packit 6c4009
	  if (pid != 0)
Packit 6c4009
	    {
Packit 6c4009
	      /* The parent only reads from the child.  */
Packit 6c4009
	      close (fd[1]);
Packit 6c4009
	      exit (monitor_child (fd[0]));
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      /* The child only writes to the parent.  */
Packit 6c4009
	      close (fd[0]);
Packit 6c4009
	      parent_fd = fd[1];
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      int nullfd = open (_PATH_DEVNULL, O_RDWR);
Packit 6c4009
      if (nullfd != -1)
Packit 6c4009
	{
Packit 6c4009
	  struct stat64 st;
Packit 6c4009
Packit 6c4009
	  if (fstat64 (nullfd, &st) == 0 && S_ISCHR (st.st_mode) != 0
Packit 6c4009
#if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
Packit 6c4009
	      && st.st_rdev == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR)
Packit 6c4009
#endif
Packit 6c4009
	      )
Packit 6c4009
	    {
Packit 6c4009
	      /* It is the /dev/null special device alright.  */
Packit 6c4009
	      (void) dup2 (nullfd, STDIN_FILENO);
Packit 6c4009
	      (void) dup2 (nullfd, STDOUT_FILENO);
Packit 6c4009
	      (void) dup2 (nullfd, STDERR_FILENO);
Packit 6c4009
Packit 6c4009
	      if (nullfd > 2)
Packit 6c4009
		close (nullfd);
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      /* Ugh, somebody is trying to play a trick on us.  */
Packit 6c4009
	      close (nullfd);
Packit 6c4009
	      nullfd = -1;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
      int min_close_fd = nullfd == -1 ? 0 : STDERR_FILENO + 1;
Packit 6c4009
Packit 6c4009
      DIR *d = opendir ("/proc/self/fd");
Packit 6c4009
      if (d != NULL)
Packit 6c4009
	{
Packit 6c4009
	  struct dirent64 *dirent;
Packit 6c4009
	  int dfdn = dirfd (d);
Packit 6c4009
Packit 6c4009
	  while ((dirent = readdir64 (d)) != NULL)
Packit 6c4009
	    {
Packit 6c4009
	      char *endp;
Packit 6c4009
	      long int fdn = strtol (dirent->d_name, &endp, 10);
Packit 6c4009
Packit 6c4009
	      if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd
Packit 6c4009
		  && fdn != parent_fd)
Packit 6c4009
		close ((int) fdn);
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  closedir (d);
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	for (i = min_close_fd; i < getdtablesize (); i++)
Packit 6c4009
	  if (i != parent_fd)
Packit 6c4009
	    close (i);
Packit 6c4009
Packit 6c4009
      setsid ();
Packit 6c4009
Packit 6c4009
      if (chdir ("/") != 0)
Packit 6c4009
	do_exit (EXIT_FAILURE, errno,
Packit 6c4009
		 _("cannot change current working directory to \"/\""));
Packit 6c4009
Packit 6c4009
      openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
Packit 6c4009
Packit 6c4009
      if (write_pid (_PATH_NSCDPID) < 0)
Packit 6c4009
	dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
Packit 6c4009
Packit 6c4009
      if (!init_logfile ())
Packit 6c4009
	dbg_log (_("Could not create log file"));
Packit 6c4009
Packit 6c4009
      /* Ignore job control signals.  */
Packit 6c4009
      signal (SIGTTOU, SIG_IGN);
Packit 6c4009
      signal (SIGTTIN, SIG_IGN);
Packit 6c4009
      signal (SIGTSTP, SIG_IGN);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    /* In debug mode we are not paranoid.  */
Packit 6c4009
    paranoia = 0;
Packit 6c4009
Packit 6c4009
  signal (SIGINT, termination_handler);
Packit 6c4009
  signal (SIGQUIT, termination_handler);
Packit 6c4009
  signal (SIGTERM, termination_handler);
Packit 6c4009
  signal (SIGPIPE, SIG_IGN);
Packit 6c4009
Packit 6c4009
  /* Cleanup files created by a previous 'bind'.  */
Packit 6c4009
  unlink (_PATH_NSCDSOCKET);
Packit 6c4009
Packit 6c4009
#ifdef HAVE_INOTIFY
Packit 6c4009
  /* Use inotify to recognize changed files.  */
Packit 6c4009
  inotify_fd = inotify_init1 (IN_NONBLOCK);
Packit 6c4009
# ifndef __ASSUME_IN_NONBLOCK
Packit 6c4009
  if (inotify_fd == -1 && errno == ENOSYS)
Packit 6c4009
    {
Packit 6c4009
      inotify_fd = inotify_init ();
Packit 6c4009
      if (inotify_fd != -1)
Packit 6c4009
	fcntl (inotify_fd, F_SETFL, O_RDONLY | O_NONBLOCK);
Packit 6c4009
    }
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#ifdef USE_NSCD
Packit 6c4009
  /* Make sure we do not get recursive calls.  */
Packit 6c4009
  __nss_disable_nscd (register_traced_file);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* Init databases.  */
Packit 6c4009
  nscd_init ();
Packit 6c4009
Packit 6c4009
  /* Start the SELinux AVC.  */
Packit 6c4009
  if (selinux_enabled)
Packit 6c4009
    nscd_avc_init ();
Packit 6c4009
Packit 6c4009
  /* Handle incoming requests */
Packit 6c4009
  start_threads ();
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static void __attribute__ ((noreturn))
Packit 6c4009
invalidate_db (const char *dbname)
Packit 6c4009
{
Packit 6c4009
  int sock = nscd_open_socket ();
Packit 6c4009
Packit 6c4009
  if (sock == -1)
Packit 6c4009
    exit (EXIT_FAILURE);
Packit 6c4009
Packit 6c4009
  size_t dbname_len = strlen (dbname) + 1;
Packit 6c4009
  size_t reqlen = sizeof (request_header) + dbname_len;
Packit 6c4009
  struct
Packit 6c4009
  {
Packit 6c4009
    request_header req;
Packit 6c4009
    char dbname[];
Packit 6c4009
  } *reqdata = alloca (reqlen);
Packit 6c4009
Packit 6c4009
  reqdata->req.key_len = dbname_len;
Packit 6c4009
  reqdata->req.version = NSCD_VERSION;
Packit 6c4009
  reqdata->req.type = INVALIDATE;
Packit 6c4009
  memcpy (reqdata->dbname, dbname, dbname_len);
Packit 6c4009
Packit 6c4009
  ssize_t nbytes = TEMP_FAILURE_RETRY (send (sock, reqdata, reqlen,
Packit 6c4009
					     MSG_NOSIGNAL));
Packit 6c4009
Packit 6c4009
  if (nbytes != reqlen)
Packit 6c4009
    {
Packit 6c4009
      int err = errno;
Packit 6c4009
      close (sock);
Packit 6c4009
      error (EXIT_FAILURE, err, _("write incomplete"));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Wait for ack.  Older nscd just closed the socket when
Packit 6c4009
     prune_cache finished, silently ignore that.  */
Packit 6c4009
  int32_t resp = 0;
Packit 6c4009
  nbytes = TEMP_FAILURE_RETRY (read (sock, &resp, sizeof (resp)));
Packit 6c4009
  if (nbytes != 0 && nbytes != sizeof (resp))
Packit 6c4009
    {
Packit 6c4009
      int err = errno;
Packit 6c4009
      close (sock);
Packit 6c4009
      error (EXIT_FAILURE, err, _("cannot read invalidate ACK"));
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  close (sock);
Packit 6c4009
Packit 6c4009
  if (resp != 0)
Packit 6c4009
    error (EXIT_FAILURE, resp, _("invalidation failed"));
Packit 6c4009
Packit 6c4009
  exit (0);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void __attribute__ ((noreturn))
Packit 6c4009
send_shutdown (void)
Packit 6c4009
{
Packit 6c4009
  int sock = nscd_open_socket ();
Packit 6c4009
Packit 6c4009
  if (sock == -1)
Packit 6c4009
    exit (EXIT_FAILURE);
Packit 6c4009
Packit 6c4009
  request_header req;
Packit 6c4009
  req.version = NSCD_VERSION;
Packit 6c4009
  req.type = SHUTDOWN;
Packit 6c4009
  req.key_len = 0;
Packit 6c4009
Packit 6c4009
  ssize_t nbytes = TEMP_FAILURE_RETRY (send (sock, &req, sizeof req,
Packit 6c4009
                                             MSG_NOSIGNAL));
Packit 6c4009
  close (sock);
Packit 6c4009
  exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Handle program arguments.  */
Packit 6c4009
static error_t
Packit 6c4009
parse_opt (int key, char *arg, struct argp_state *state)
Packit 6c4009
{
Packit 6c4009
  switch (key)
Packit 6c4009
    {
Packit 6c4009
    case 'd':
Packit 6c4009
      ++debug_level;
Packit 6c4009
      run_mode = RUN_DEBUG;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case 'F':
Packit 6c4009
      run_mode = RUN_FOREGROUND;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case 'f':
Packit 6c4009
      conffile = arg;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case 'K':
Packit 6c4009
      if (getuid () != 0)
Packit 6c4009
	error (4, 0, _("Only root is allowed to use this option!"));
Packit 6c4009
      else
Packit 6c4009
        send_shutdown ();
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case 'g':
Packit 6c4009
      get_stats = true;
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case 'i':
Packit 6c4009
      {
Packit 6c4009
        /* Validate the database name.  */
Packit 6c4009
Packit 6c4009
        dbtype cnt;
Packit 6c4009
        for (cnt = pwddb; cnt < lastdb; ++cnt)
Packit 6c4009
          if (strcmp (arg, dbnames[cnt]) == 0)
Packit 6c4009
            break;
Packit 6c4009
Packit 6c4009
        if (cnt == lastdb)
Packit 6c4009
          {
Packit 6c4009
            argp_error (state, _("'%s' is not a known database"), arg);
Packit 6c4009
            return EINVAL;
Packit 6c4009
          }
Packit 6c4009
      }
Packit 6c4009
      if (getuid () != 0)
Packit 6c4009
	error (4, 0, _("Only root is allowed to use this option!"));
Packit 6c4009
      else
Packit 6c4009
        invalidate_db (arg);
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case 't':
Packit 6c4009
      nthreads = atol (arg);
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    case 'S':
Packit 6c4009
      error (0, 0, _("secure services not implemented anymore"));
Packit 6c4009
      break;
Packit 6c4009
Packit 6c4009
    default:
Packit 6c4009
      return ARGP_ERR_UNKNOWN;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Print bug-reporting information in the help message.  */
Packit 6c4009
static char *
Packit 6c4009
more_help (int key, const char *text, void *input)
Packit 6c4009
{
Packit 6c4009
  switch (key)
Packit 6c4009
    {
Packit 6c4009
    case ARGP_KEY_HELP_EXTRA:
Packit 6c4009
      {
Packit 6c4009
	/* We print some extra information.  */
Packit 6c4009
Packit 6c4009
	char *tables = xstrdup (dbnames[0]);
Packit 6c4009
	for (dbtype i = 1; i < lastdb; ++i)
Packit 6c4009
	  {
Packit 6c4009
	    char *more_tables;
Packit 6c4009
	    if (asprintf (&more_tables, "%s %s", tables, dbnames[i]) < 0)
Packit 6c4009
	      more_tables = NULL;
Packit 6c4009
	    free (tables);
Packit 6c4009
	    if (more_tables == NULL)
Packit 6c4009
	      return NULL;
Packit 6c4009
	    tables = more_tables;
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
	char *tp;
Packit 6c4009
	if (asprintf (&tp, gettext ("\
Packit 6c4009
Supported tables:\n\
Packit 6c4009
%s\n\
Packit 6c4009
\n\
Packit 6c4009
For bug reporting instructions, please see:\n\
Packit 6c4009
%s.\n\
Packit 6c4009
"), tables, REPORT_BUGS_TO) < 0)
Packit 6c4009
	  tp = NULL;
Packit 6c4009
	free (tables);
Packit 6c4009
	return tp;
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
    default:
Packit 6c4009
      break;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return (char *) text;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Print the version information.  */
Packit 6c4009
static void
Packit 6c4009
print_version (FILE *stream, struct argp_state *state)
Packit 6c4009
{
Packit 6c4009
  fprintf (stream, "nscd %s%s\n", PKGVERSION, VERSION);
Packit 6c4009
  fprintf (stream, gettext ("\
Packit 6c4009
Copyright (C) %s Free Software Foundation, Inc.\n\
Packit 6c4009
This is free software; see the source for copying conditions.  There is NO\n\
Packit 6c4009
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
Packit 6c4009
"), "2018");
Packit 6c4009
  fprintf (stream, gettext ("Written by %s.\n"),
Packit 6c4009
	   "Thorsten Kukuk and Ulrich Drepper");
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Create a socket connected to a name.  */
Packit 6c4009
int
Packit 6c4009
nscd_open_socket (void)
Packit 6c4009
{
Packit 6c4009
  struct sockaddr_un addr;
Packit 6c4009
  int sock;
Packit 6c4009
Packit 6c4009
  sock = socket (PF_UNIX, SOCK_STREAM, 0);
Packit 6c4009
  if (sock < 0)
Packit 6c4009
    return -1;
Packit 6c4009
Packit 6c4009
  addr.sun_family = AF_UNIX;
Packit 6c4009
  assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
Packit 6c4009
  strcpy (addr.sun_path, _PATH_NSCDSOCKET);
Packit 6c4009
  if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
Packit 6c4009
    {
Packit 6c4009
      close (sock);
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return sock;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Cleanup.  */
Packit 6c4009
void
Packit 6c4009
termination_handler (int signum)
Packit 6c4009
{
Packit 6c4009
  close_sockets ();
Packit 6c4009
Packit 6c4009
  /* Clean up the file created by 'bind'.  */
Packit 6c4009
  unlink (_PATH_NSCDSOCKET);
Packit 6c4009
Packit 6c4009
  /* Clean up pid file.  */
Packit 6c4009
  unlink (_PATH_NSCDPID);
Packit 6c4009
Packit 6c4009
  // XXX Terminate threads.
Packit 6c4009
Packit 6c4009
  /* Synchronize memory.  */
Packit 6c4009
  for (int cnt = 0; cnt < lastdb; ++cnt)
Packit 6c4009
    {
Packit 6c4009
      if (!dbs[cnt].enabled || dbs[cnt].head == NULL)
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      /* Make sure nobody keeps using the database.  */
Packit 6c4009
      dbs[cnt].head->timestamp = 0;
Packit 6c4009
Packit 6c4009
      if (dbs[cnt].persistent)
Packit 6c4009
	// XXX async OK?
Packit 6c4009
	msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  _exit (EXIT_SUCCESS);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Returns 1 if the process in pid file FILE is running, 0 if not.  */
Packit 6c4009
static int
Packit 6c4009
check_pid (const char *file)
Packit 6c4009
{
Packit 6c4009
  FILE *fp;
Packit 6c4009
Packit 6c4009
  fp = fopen (file, "r");
Packit 6c4009
  if (fp)
Packit 6c4009
    {
Packit 6c4009
      pid_t pid;
Packit 6c4009
      int n;
Packit 6c4009
Packit 6c4009
      n = fscanf (fp, "%d", &pid;;
Packit 6c4009
      fclose (fp);
Packit 6c4009
Packit 6c4009
      /* If we cannot parse the file default to assuming nscd runs.
Packit 6c4009
	 If the PID is alive, assume it is running.  That all unless
Packit 6c4009
	 the PID is the same as the current process' since tha latter
Packit 6c4009
	 can mean we re-exec.  */
Packit 6c4009
      if ((n != 1 || kill (pid, 0) == 0) && pid != getpid ())
Packit 6c4009
	return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Write the current process id to the file FILE.
Packit 6c4009
   Returns 0 if successful, -1 if not.  */
Packit 6c4009
static int
Packit 6c4009
write_pid (const char *file)
Packit 6c4009
{
Packit 6c4009
  FILE *fp;
Packit 6c4009
Packit 6c4009
  fp = fopen (file, "w");
Packit 6c4009
  if (fp == NULL)
Packit 6c4009
    return -1;
Packit 6c4009
Packit 6c4009
  fprintf (fp, "%d\n", getpid ());
Packit 6c4009
Packit 6c4009
  int result = fflush (fp) || ferror (fp) ? -1 : 0;
Packit 6c4009
Packit 6c4009
  fclose (fp);
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
monitor_child (int fd)
Packit 6c4009
{
Packit 6c4009
  int child_ret = 0;
Packit 6c4009
  int ret = read (fd, &child_ret, sizeof (child_ret));
Packit 6c4009
Packit 6c4009
  /* The child terminated with an error, either via exit or some other abnormal
Packit 6c4009
     method, like a segfault.  */
Packit 6c4009
  if (ret <= 0 || child_ret != 0)
Packit 6c4009
    {
Packit 6c4009
      int status;
Packit 6c4009
      int err = wait (&status);
Packit 6c4009
Packit 6c4009
      if (err < 0)
Packit 6c4009
	{
Packit 6c4009
	  fprintf (stderr, _("'wait' failed\n"));
Packit 6c4009
	  return 1;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (WIFEXITED (status))
Packit 6c4009
	{
Packit 6c4009
	  child_ret = WEXITSTATUS (status);
Packit 6c4009
	  fprintf (stderr, _("child exited with status %d\n"), child_ret);
Packit 6c4009
	}
Packit 6c4009
      if (WIFSIGNALED (status))
Packit 6c4009
	{
Packit 6c4009
	  child_ret = WTERMSIG (status);
Packit 6c4009
	  fprintf (stderr, _("child terminated by signal %d\n"), child_ret);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* We have the child status, so exit with that code.  */
Packit 6c4009
  close (fd);
Packit 6c4009
Packit 6c4009
  return child_ret;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
do_exit (int child_ret, int errnum, const char *format, ...)
Packit 6c4009
{
Packit 6c4009
  if (parent_fd != -1)
Packit 6c4009
    {
Packit 6c4009
      int ret __attribute__ ((unused));
Packit 6c4009
      ret = write (parent_fd, &child_ret, sizeof (child_ret));
Packit 6c4009
      assert (ret == sizeof (child_ret));
Packit 6c4009
      close (parent_fd);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (format != NULL)
Packit 6c4009
    {
Packit 6c4009
      /* Emulate error() since we don't have a va_list variant for it.  */
Packit 6c4009
      va_list argp;
Packit 6c4009
Packit 6c4009
      fflush (stdout);
Packit 6c4009
Packit 6c4009
      fprintf (stderr, "%s: ", program_invocation_name);
Packit 6c4009
Packit 6c4009
      va_start (argp, format);
Packit 6c4009
      vfprintf (stderr, format, argp);
Packit 6c4009
      va_end (argp);
Packit 6c4009
Packit 6c4009
      fprintf (stderr, ": %s\n", strerror (errnum));
Packit 6c4009
      fflush (stderr);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Finally, exit.  */
Packit 6c4009
  exit (child_ret);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
notify_parent (int child_ret)
Packit 6c4009
{
Packit 6c4009
  if (parent_fd == -1)
Packit 6c4009
    return;
Packit 6c4009
Packit 6c4009
  int ret __attribute__ ((unused));
Packit 6c4009
  ret = write (parent_fd, &child_ret, sizeof (child_ret));
Packit 6c4009
  assert (ret == sizeof (child_ret));
Packit 6c4009
  close (parent_fd);
Packit 6c4009
  parent_fd = -1;
Packit 6c4009
}