Back to index

glibc  2.9
nscd.c
Go to the documentation of this file.
00001 /* Copyright (c) 1998-2006, 2007, 2008 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
00004 
00005    This program is free software; you can redistribute it and/or modify
00006    it under the terms of the GNU General Public License as published
00007    by the Free Software Foundation; version 2 of the License, or
00008    (at your option) any later version.
00009 
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013    GNU General Public License for more details.
00014 
00015    You should have received a copy of the GNU General Public License
00016    along with this program; if not, write to the Free Software Foundation,
00017    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00018 
00019 /* nscd - Name Service Cache Daemon. Caches passwd, group, and hosts.  */
00020 
00021 #include <argp.h>
00022 #include <assert.h>
00023 #include <dirent.h>
00024 #include <errno.h>
00025 #include <error.h>
00026 #include <fcntl.h>
00027 #include <libintl.h>
00028 #include <locale.h>
00029 #include <paths.h>
00030 #include <pthread.h>
00031 #include <signal.h>
00032 #include <stdbool.h>
00033 #include <stdio.h>
00034 #include <stdlib.h>
00035 #include <string.h>
00036 #include <syslog.h>
00037 #include <unistd.h>
00038 #include <sys/mman.h>
00039 #include <sys/socket.h>
00040 #include <sys/stat.h>
00041 #include <sys/uio.h>
00042 #include <sys/un.h>
00043 
00044 #include "dbg_log.h"
00045 #include "nscd.h"
00046 #include "selinux.h"
00047 #include "../nss/nsswitch.h"
00048 #include <device-nrs.h>
00049 
00050 /* Get libc version number.  */
00051 #include <version.h>
00052 
00053 #define PACKAGE _libc_intl_domainname
00054 
00055 /* Structure used by main() thread to keep track of the number of
00056    active threads.  Used to limit how many threads it will create
00057    and under a shutdown condition to wait till all in-progress
00058    requests have finished before "turning off the lights".  */
00059 
00060 typedef struct
00061 {
00062   int             num_active;
00063   pthread_cond_t  thread_exit_cv;
00064   pthread_mutex_t mutex;
00065 } thread_info_t;
00066 
00067 thread_info_t thread_info;
00068 
00069 int do_shutdown;
00070 int disabled_passwd;
00071 int disabled_group;
00072 int go_background = 1;
00073 
00074 static const char *conffile = _PATH_NSCDCONF;
00075 
00076 time_t start_time;
00077 
00078 uintptr_t pagesize_m1;
00079 
00080 int paranoia;
00081 time_t restart_time;
00082 time_t restart_interval = RESTART_INTERVAL;
00083 const char *oldcwd;
00084 uid_t old_uid;
00085 gid_t old_gid;
00086 
00087 static int check_pid (const char *file);
00088 static int write_pid (const char *file);
00089 
00090 /* Name and version of program.  */
00091 static void print_version (FILE *stream, struct argp_state *state);
00092 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
00093 
00094 /* Definitions of arguments for argp functions.  */
00095 static const struct argp_option options[] =
00096 {
00097   { "config-file", 'f', N_("NAME"), 0,
00098     N_("Read configuration data from NAME") },
00099   { "debug", 'd', NULL, 0,
00100     N_("Do not fork and display messages on the current tty") },
00101   { "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") },
00102   { "shutdown", 'K', NULL, 0, N_("Shut the server down") },
00103   { "statistics", 'g', NULL, 0, N_("Print current configuration statistics") },
00104   { "invalidate", 'i', N_("TABLE"), 0,
00105     N_("Invalidate the specified cache") },
00106   { "secure", 'S', N_("TABLE,yes"), OPTION_HIDDEN,
00107     N_("Use separate cache for each user")},
00108   { NULL, 0, NULL, 0, NULL }
00109 };
00110 
00111 /* Short description of program.  */
00112 static const char doc[] = N_("Name Service Cache Daemon.");
00113 
00114 /* Prototype for option handler.  */
00115 static error_t parse_opt (int key, char *arg, struct argp_state *state);
00116 
00117 /* Data structure to communicate with argp functions.  */
00118 static struct argp argp =
00119 {
00120   options, parse_opt, NULL, doc,
00121 };
00122 
00123 /* True if only statistics are requested.  */
00124 static bool get_stats;
00125 
00126 int
00127 main (int argc, char **argv)
00128 {
00129   int remaining;
00130 
00131   /* Set locale via LC_ALL.  */
00132   setlocale (LC_ALL, "");
00133   /* Set the text message domain.  */
00134   textdomain (PACKAGE);
00135 
00136   /* Determine if the kernel has SELinux support.  */
00137   nscd_selinux_enabled (&selinux_enabled);
00138 
00139   /* Parse and process arguments.  */
00140   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
00141 
00142   if (remaining != argc)
00143     {
00144       error (0, 0, gettext ("wrong number of arguments"));
00145       argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
00146       exit (1);
00147     }
00148 
00149   /* Read the configuration file.  */
00150   if (nscd_parse_file (conffile, dbs) != 0)
00151     /* We couldn't read the configuration file.  We don't start the
00152        server.  */
00153     error (EXIT_FAILURE, 0,
00154           _("failure while reading configuration file; this is fatal"));
00155 
00156   /* Do we only get statistics?  */
00157   if (get_stats)
00158     /* Does not return.  */
00159     receive_print_stats ();
00160 
00161   /* Check if we are already running. */
00162   if (check_pid (_PATH_NSCDPID))
00163     error (EXIT_FAILURE, 0, _("already running"));
00164 
00165   /* Remember when we started.  */
00166   start_time = time (NULL);
00167 
00168   /* Determine page size.  */
00169   pagesize_m1 = getpagesize () - 1;
00170 
00171   /* Behave like a daemon.  */
00172   if (go_background)
00173     {
00174       int i;
00175 
00176       pid_t pid = fork ();
00177       if (pid == -1)
00178        error (EXIT_FAILURE, errno, _("cannot fork"));
00179       if (pid != 0)
00180        exit (0);
00181 
00182       int nullfd = open (_PATH_DEVNULL, O_RDWR);
00183       if (nullfd != -1)
00184        {
00185          struct stat64 st;
00186 
00187          if (fstat64 (nullfd, &st) == 0 && S_ISCHR (st.st_mode) != 0
00188 #if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
00189              && st.st_rdev == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR)
00190 #endif
00191              )
00192            {
00193              /* It is the /dev/null special device alright.  */
00194              (void) dup2 (nullfd, STDIN_FILENO);
00195              (void) dup2 (nullfd, STDOUT_FILENO);
00196              (void) dup2 (nullfd, STDERR_FILENO);
00197 
00198              if (nullfd > 2)
00199               close (nullfd);
00200            }
00201          else
00202            {
00203              /* Ugh, somebody is trying to play a trick on us.  */
00204              close (nullfd);
00205              nullfd = -1;
00206            }
00207        }
00208       int min_close_fd = nullfd == -1 ? 0 : STDERR_FILENO + 1;
00209 
00210       DIR *d = opendir ("/proc/self/fd");
00211       if (d != NULL)
00212        {
00213          struct dirent64 *dirent;
00214          int dfdn = dirfd (d);
00215 
00216          while ((dirent = readdir64 (d)) != NULL)
00217            {
00218              char *endp;
00219              long int fdn = strtol (dirent->d_name, &endp, 10);
00220 
00221              if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd)
00222               close ((int) fdn);
00223            }
00224 
00225          closedir (d);
00226        }
00227       else
00228        for (i = min_close_fd; i < getdtablesize (); i++)
00229          close (i);
00230 
00231       pid = fork ();
00232       if (pid == -1)
00233        error (EXIT_FAILURE, errno, _("cannot fork"));
00234       if (pid != 0)
00235        exit (0);
00236 
00237       setsid ();
00238 
00239       if (chdir ("/") != 0)
00240        error (EXIT_FAILURE, errno,
00241               _("cannot change current working directory to \"/\""));
00242 
00243       openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
00244 
00245       if (write_pid (_PATH_NSCDPID) < 0)
00246         dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
00247 
00248       if (!init_logfile ())
00249        dbg_log (_("Could not create log file"));
00250 
00251       /* Ignore job control signals.  */
00252       signal (SIGTTOU, SIG_IGN);
00253       signal (SIGTTIN, SIG_IGN);
00254       signal (SIGTSTP, SIG_IGN);
00255     }
00256   else
00257     /* In foreground mode we are not paranoid.  */
00258     paranoia = 0;
00259 
00260   /* Start the SELinux AVC.  */
00261   if (selinux_enabled)
00262     nscd_avc_init ();
00263 
00264   signal (SIGINT, termination_handler);
00265   signal (SIGQUIT, termination_handler);
00266   signal (SIGTERM, termination_handler);
00267   signal (SIGPIPE, SIG_IGN);
00268 
00269   /* Cleanup files created by a previous 'bind'.  */
00270   unlink (_PATH_NSCDSOCKET);
00271 
00272   /* Make sure we do not get recursive calls.  */
00273   __nss_disable_nscd ();
00274 
00275   /* Init databases.  */
00276   nscd_init ();
00277 
00278   /* Handle incoming requests */
00279   start_threads ();
00280 
00281   return 0;
00282 }
00283 
00284 
00285 /* Handle program arguments.  */
00286 static error_t
00287 parse_opt (int key, char *arg, struct argp_state *state)
00288 {
00289   switch (key)
00290     {
00291     case 'd':
00292       ++debug_level;
00293       go_background = 0;
00294       break;
00295 
00296     case 'f':
00297       conffile = arg;
00298       break;
00299 
00300     case 'K':
00301       if (getuid () != 0)
00302        error (4, 0, _("Only root is allowed to use this option!"));
00303       {
00304        int sock = nscd_open_socket ();
00305 
00306        if (sock == -1)
00307          exit (EXIT_FAILURE);
00308 
00309        request_header req;
00310        req.version = NSCD_VERSION;
00311        req.type = SHUTDOWN;
00312        req.key_len = 0;
00313 
00314        ssize_t nbytes = TEMP_FAILURE_RETRY (send (sock, &req,
00315                                              sizeof (request_header),
00316                                              MSG_NOSIGNAL));
00317        close (sock);
00318        exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
00319       }
00320 
00321     case 'g':
00322       get_stats = true;
00323       break;
00324 
00325     case 'i':
00326       if (getuid () != 0)
00327        error (4, 0, _("Only root is allowed to use this option!"));
00328       else
00329        {
00330          int sock = nscd_open_socket ();
00331 
00332          if (sock == -1)
00333            exit (EXIT_FAILURE);
00334 
00335          dbtype cnt;
00336          for (cnt = pwddb; cnt < lastdb; ++cnt)
00337            if (strcmp (arg, dbnames[cnt]) == 0)
00338              break;
00339 
00340          if (cnt == lastdb)
00341            return ARGP_ERR_UNKNOWN;
00342 
00343          size_t arg_len = strlen (arg) + 1;
00344          struct
00345          {
00346            request_header req;
00347            char arg[arg_len];
00348          } reqdata;
00349 
00350          reqdata.req.key_len = strlen (arg) + 1;
00351          reqdata.req.version = NSCD_VERSION;
00352          reqdata.req.type = INVALIDATE;
00353          memcpy (reqdata.arg, arg, arg_len);
00354 
00355          ssize_t nbytes = TEMP_FAILURE_RETRY (send (sock, &reqdata,
00356                                                sizeof (request_header)
00357                                                + arg_len,
00358                                                MSG_NOSIGNAL));
00359 
00360          if (nbytes != sizeof (request_header) + arg_len)
00361            {
00362              int err = errno;
00363              close (sock);
00364              error (EXIT_FAILURE, err, _("write incomplete"));
00365            }
00366 
00367          /* Wait for ack.  Older nscd just closed the socket when
00368             prune_cache finished, silently ignore that.  */
00369          int32_t resp = 0;
00370          nbytes = TEMP_FAILURE_RETRY (read (sock, &resp, sizeof (resp)));
00371          if (nbytes != 0 && nbytes != sizeof (resp))
00372            {
00373              int err = errno;
00374              close (sock);
00375              error (EXIT_FAILURE, err, _("cannot read invalidate ACK"));
00376            }
00377 
00378          close (sock);
00379 
00380          if (resp != 0)
00381            error (EXIT_FAILURE, resp, _("invalidation failed"));
00382 
00383          exit (0);
00384        }
00385 
00386     case 't':
00387       nthreads = atol (arg);
00388       break;
00389 
00390     case 'S':
00391       error (0, 0, _("secure services not implemented anymore"));
00392       break;
00393 
00394     default:
00395       return ARGP_ERR_UNKNOWN;
00396     }
00397 
00398   return 0;
00399 }
00400 
00401 /* Print the version information.  */
00402 static void
00403 print_version (FILE *stream, struct argp_state *state)
00404 {
00405   fprintf (stream, "nscd (GNU %s) %s\n", PACKAGE, VERSION);
00406   fprintf (stream, gettext ("\
00407 Copyright (C) %s Free Software Foundation, Inc.\n\
00408 This is free software; see the source for copying conditions.  There is NO\n\
00409 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
00410 "), "2008");
00411   fprintf (stream, gettext ("Written by %s.\n"),
00412           "Thorsten Kukuk and Ulrich Drepper");
00413 }
00414 
00415 
00416 /* Create a socket connected to a name.  */
00417 int
00418 nscd_open_socket (void)
00419 {
00420   struct sockaddr_un addr;
00421   int sock;
00422 
00423   sock = socket (PF_UNIX, SOCK_STREAM, 0);
00424   if (sock < 0)
00425     return -1;
00426 
00427   addr.sun_family = AF_UNIX;
00428   assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
00429   strcpy (addr.sun_path, _PATH_NSCDSOCKET);
00430   if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
00431     {
00432       close (sock);
00433       return -1;
00434     }
00435 
00436   return sock;
00437 }
00438 
00439 
00440 /* Cleanup.  */
00441 void
00442 termination_handler (int signum)
00443 {
00444   close_sockets ();
00445 
00446   /* Clean up the file created by 'bind'.  */
00447   unlink (_PATH_NSCDSOCKET);
00448 
00449   /* Clean up pid file.  */
00450   unlink (_PATH_NSCDPID);
00451 
00452   // XXX Terminate threads.
00453 
00454   /* Synchronize memory.  */
00455   for (int cnt = 0; cnt < lastdb; ++cnt)
00456     {
00457       if (!dbs[cnt].enabled)
00458        continue;
00459 
00460       /* Make sure nobody keeps using the database.  */
00461       dbs[cnt].head->timestamp = 0;
00462 
00463       if (dbs[cnt].persistent)
00464        // XXX async OK?
00465        msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
00466     }
00467 
00468   /* Shutdown the SELinux AVC.  */
00469   if (selinux_enabled)
00470     nscd_avc_destroy ();
00471 
00472   _exit (EXIT_SUCCESS);
00473 }
00474 
00475 /* Returns 1 if the process in pid file FILE is running, 0 if not.  */
00476 static int
00477 check_pid (const char *file)
00478 {
00479   FILE *fp;
00480 
00481   fp = fopen (file, "r");
00482   if (fp)
00483     {
00484       pid_t pid;
00485       int n;
00486 
00487       n = fscanf (fp, "%d", &pid);
00488       fclose (fp);
00489 
00490       /* If we cannot parse the file default to assuming nscd runs.
00491         If the PID is alive, assume it is running.  That all unless
00492         the PID is the same as the current process' since tha latter
00493         can mean we re-exec.  */
00494       if ((n != 1 || kill (pid, 0) == 0) && pid != getpid ())
00495         return 1;
00496     }
00497 
00498   return 0;
00499 }
00500 
00501 /* Write the current process id to the file FILE.
00502    Returns 0 if successful, -1 if not.  */
00503 static int
00504 write_pid (const char *file)
00505 {
00506   FILE *fp;
00507 
00508   fp = fopen (file, "w");
00509   if (fp == NULL)
00510     return -1;
00511 
00512   fprintf (fp, "%d\n", getpid ());
00513 
00514   int result = fflush (fp) || ferror (fp) ? -1 : 0;
00515 
00516   fclose (fp);
00517 
00518   return result;
00519 }