Updated to fedora-glibc-20050208T0948
[kopensolaris-gnu/glibc.git] / nscd / nscd.c
1 /* Copyright (c) 1998-2003, 2004, 2005 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 /* nscd - Name Service Cache Daemon. Caches passwd, group, and hosts.  */
21
22 #include <argp.h>
23 #include <assert.h>
24 #include <dirent.h>
25 #include <errno.h>
26 #include <error.h>
27 #include <fcntl.h>
28 #include <libintl.h>
29 #include <locale.h>
30 #include <paths.h>
31 #include <pthread.h>
32 #include <signal.h>
33 #include <stdbool.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <syslog.h>
38 #include <unistd.h>
39 #include <sys/mman.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <sys/uio.h>
43 #include <sys/un.h>
44
45 #include "dbg_log.h"
46 #include "nscd.h"
47 #include "selinux.h"
48 #include "../nss/nsswitch.h"
49 #include <device-nrs.h>
50
51 /* Get libc version number.  */
52 #include <version.h>
53
54 #define PACKAGE _libc_intl_domainname
55
56 /* Structure used by main() thread to keep track of the number of
57    active threads.  Used to limit how many threads it will create
58    and under a shutdown condition to wait till all in-progress
59    requests have finished before "turning off the lights".  */
60
61 typedef struct
62 {
63   int             num_active;
64   pthread_cond_t  thread_exit_cv;
65   pthread_mutex_t mutex;
66 } thread_info_t;
67
68 thread_info_t thread_info;
69
70 int do_shutdown;
71 int disabled_passwd;
72 int disabled_group;
73 int go_background = 1;
74
75 int secure_in_use;
76 static const char *conffile = _PATH_NSCDCONF;
77
78 time_t start_time;
79
80 uintptr_t pagesize_m1;
81
82 int paranoia;
83 time_t restart_time;
84 time_t restart_interval = RESTART_INTERVAL;
85 const char *oldcwd;
86 uid_t old_uid;
87 gid_t old_gid;
88
89 static int check_pid (const char *file);
90 static int write_pid (const char *file);
91
92 /* Name and version of program.  */
93 static void print_version (FILE *stream, struct argp_state *state);
94 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
95
96 /* Definitions of arguments for argp functions.  */
97 static const struct argp_option options[] =
98 {
99   { "config-file", 'f', N_("NAME"), 0,
100     N_("Read configuration data from NAME") },
101   { "debug", 'd', NULL, 0,
102     N_("Do not fork and display messages on the current tty") },
103   { "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") },
104   { "shutdown", 'K', NULL, 0, N_("Shut the server down") },
105   { "statistic", 'g', NULL, 0, N_("Print current configuration statistic") },
106   { "invalidate", 'i', N_("TABLE"), 0,
107     N_("Invalidate the specified cache") },
108   { "secure", 'S', N_("TABLE,yes"), OPTION_HIDDEN,
109     N_("Use separate cache for each user")},
110   { NULL, 0, NULL, 0, NULL }
111 };
112
113 /* Short description of program.  */
114 static const char doc[] = N_("Name Service Cache Daemon.");
115
116 /* Prototype for option handler.  */
117 static error_t parse_opt (int key, char *arg, struct argp_state *state);
118
119 /* Data structure to communicate with argp functions.  */
120 static struct argp argp =
121 {
122   options, parse_opt, NULL, doc,
123 };
124
125 /* The SIGHUP handler is extern to this file */
126 extern void sighup_handler(int signum);
127
128 /* True if only statistics are requested.  */
129 static bool get_stats;
130
131 #ifdef atomic_init_nscd
132 atomic_init_nscd
133 #endif
134
135 int
136 main (int argc, char **argv)
137 {
138   int remaining;
139
140   /* Set locale via LC_ALL.  */
141   setlocale (LC_ALL, "");
142   /* Set the text message domain.  */
143   textdomain (PACKAGE);
144
145   /* Determine if the kernel has SELinux support.  */
146   nscd_selinux_enabled (&selinux_enabled);
147
148   /* Parse and process arguments.  */
149   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
150
151   if (remaining != argc)
152     {
153       error (0, 0, gettext ("wrong number of arguments"));
154       argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
155       exit (EXIT_FAILURE);
156     }
157
158   /* Read the configuration file.  */
159   if (nscd_parse_file (conffile, dbs) != 0)
160     {
161       /* We couldn't read the configuration file.  We don't start the
162          server.  */
163       dbg_log (_("cannot read configuration file; this is fatal"));
164       exit (1);
165     }
166
167   /* Do we only get statistics?  */
168   if (get_stats)
169     /* Does not return.  */
170     receive_print_stats ();
171
172   /* Check if we are already running. */
173   if (check_pid (_PATH_NSCDPID))
174     error (EXIT_FAILURE, 0, _("already running"));
175
176   /* Remember when we started.  */
177   start_time = time (NULL);
178
179   /* Determine page size.  */
180   pagesize_m1 = getpagesize () - 1;
181
182   /* Behave like a daemon.  */
183   if (go_background)
184     {
185       int i;
186
187       pid_t pid = fork ();
188       if (pid == -1)
189         error (EXIT_FAILURE, errno, _("cannot fork"));
190       if (pid != 0)
191         exit (0);
192
193       int nullfd = open (_PATH_DEVNULL, O_RDWR);
194       if (nullfd != -1)
195         {
196           struct stat64 st;
197
198           if (fstat64 (nullfd, &st) == 0 && S_ISCHR (st.st_mode) != 0
199 #if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
200               && st.st_rdev == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR)
201 #endif
202               )
203             {
204               /* It is the /dev/null special device alright.  */
205               (void) dup2 (nullfd, STDIN_FILENO);
206               (void) dup2 (nullfd, STDOUT_FILENO);
207               (void) dup2 (nullfd, STDERR_FILENO);
208
209               if (nullfd > 2)
210                 close (nullfd);
211             }
212           else
213             {
214               /* Ugh, somebody is trying to play a trick on us.  */
215               close (nullfd);
216               nullfd = -1;
217             }
218         }
219       int min_close_fd = nullfd == -1 ? 0 : STDERR_FILENO + 1;
220
221       DIR *d = opendir ("/proc/self/fd");
222       if (d != NULL)
223         {
224           struct dirent64 *dirent;
225           int dfdn = dirfd (d);
226
227           while ((dirent = readdir64 (d)) != NULL)
228             {
229               char *endp;
230               long int fdn = strtol (dirent->d_name, &endp, 10);
231
232               if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd)
233                 close ((int) fdn);
234             }
235
236           closedir (d);
237         }
238       else
239         for (i = min_close_fd; i < getdtablesize (); i++)
240           close (i);
241
242       pid = fork ();
243       if (pid == -1)
244         error (EXIT_FAILURE, errno, _("cannot fork"));
245       if (pid != 0)
246         exit (0);
247
248       setsid ();
249
250       chdir ("/");
251
252       openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
253
254       if (write_pid (_PATH_NSCDPID) < 0)
255         dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
256
257       if (!init_logfile ())
258         dbg_log (_("Could not create log file"));
259
260       /* Ignore job control signals.  */
261       signal (SIGTTOU, SIG_IGN);
262       signal (SIGTTIN, SIG_IGN);
263       signal (SIGTSTP, SIG_IGN);
264     }
265   else
266     /* In foreground mode we are not paranoid.  */
267     paranoia = 0;
268
269   /* Start the SELinux AVC.  */
270   if (selinux_enabled)
271     nscd_avc_init ();
272
273   signal (SIGINT, termination_handler);
274   signal (SIGQUIT, termination_handler);
275   signal (SIGTERM, termination_handler);
276   signal (SIGHUP, sighup_handler);
277   signal (SIGPIPE, SIG_IGN);
278
279   /* Cleanup files created by a previous 'bind'.  */
280   unlink (_PATH_NSCDSOCKET);
281
282   /* Make sure we do not get recursive calls.  */
283   __nss_disable_nscd ();
284
285   /* Init databases.  */
286   nscd_init ();
287
288   /* Handle incoming requests */
289   start_threads ();
290
291   return 0;
292 }
293
294
295 /* Handle program arguments.  */
296 static error_t
297 parse_opt (int key, char *arg, struct argp_state *state)
298 {
299   switch (key)
300     {
301     case 'd':
302       ++debug_level;
303       go_background = 0;
304       break;
305
306     case 'f':
307       conffile = arg;
308       break;
309
310     case 'K':
311       if (getuid () != 0)
312         error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!"));
313       {
314         int sock = nscd_open_socket ();
315         request_header req;
316         ssize_t nbytes;
317
318         if (sock == -1)
319           exit (EXIT_FAILURE);
320
321         req.version = NSCD_VERSION;
322         req.type = SHUTDOWN;
323         req.key_len = 0;
324         nbytes = TEMP_FAILURE_RETRY (write (sock, &req,
325                                             sizeof (request_header)));
326         close (sock);
327         exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
328       }
329
330     case 'g':
331       get_stats = true;
332       break;
333
334     case 'i':
335       if (getuid () != 0)
336         error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!"));
337       else
338         {
339           int sock = nscd_open_socket ();
340
341           if (sock == -1)
342             exit (EXIT_FAILURE);
343
344           request_header req;
345           ssize_t nbytes;
346           struct iovec iov[2];
347
348           if (strcmp (arg, "passwd") == 0)
349             req.key_len = sizeof "passwd";
350           else if (strcmp (arg, "group") == 0)
351             req.key_len = sizeof "group";
352           else if (strcmp (arg, "hosts") == 0)
353             req.key_len = sizeof "hosts";
354           else
355             return ARGP_ERR_UNKNOWN;
356
357           req.version = NSCD_VERSION;
358           req.type = INVALIDATE;
359
360           iov[0].iov_base = &req;
361           iov[0].iov_len = sizeof (req);
362           iov[1].iov_base = arg;
363           iov[1].iov_len = req.key_len;
364
365           nbytes = TEMP_FAILURE_RETRY (writev (sock, iov, 2));
366
367           close (sock);
368
369           exit (nbytes != iov[0].iov_len + iov[1].iov_len
370                 ? EXIT_FAILURE : EXIT_SUCCESS);
371         }
372
373     case 't':
374       nthreads = atol (arg);
375       break;
376
377     case 'S':
378 #if 0
379       if (strcmp (arg, "passwd,yes") == 0)
380         secure_in_use = dbs[pwddb].secure = 1;
381       else if (strcmp (arg, "group,yes") == 0)
382         secure_in_use = dbs[grpdb].secure = 1;
383       else if (strcmp (arg, "hosts,yes") == 0)
384         secure_in_use = dbs[hstdb].secure = 1;
385 #else
386       error (0, 0, _("secure services not implemented anymore"));
387 #endif
388       break;
389
390     default:
391       return ARGP_ERR_UNKNOWN;
392     }
393
394   return 0;
395 }
396
397 /* Print the version information.  */
398 static void
399 print_version (FILE *stream, struct argp_state *state)
400 {
401   fprintf (stream, "nscd (GNU %s) %s\n", PACKAGE, VERSION);
402   fprintf (stream, gettext ("\
403 Copyright (C) %s Free Software Foundation, Inc.\n\
404 This is free software; see the source for copying conditions.  There is NO\n\
405 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
406 "), "2005");
407   fprintf (stream, gettext ("Written by %s.\n"),
408            "Thorsten Kukuk and Ulrich Drepper");
409 }
410
411
412 /* Create a socket connected to a name.  */
413 int
414 nscd_open_socket (void)
415 {
416   struct sockaddr_un addr;
417   int sock;
418
419   sock = socket (PF_UNIX, SOCK_STREAM, 0);
420   if (sock < 0)
421     return -1;
422
423   addr.sun_family = AF_UNIX;
424   assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
425   strcpy (addr.sun_path, _PATH_NSCDSOCKET);
426   if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
427     {
428       close (sock);
429       return -1;
430     }
431
432   return sock;
433 }
434
435
436 /* Cleanup.  */
437 void
438 termination_handler (int signum)
439 {
440   close_sockets ();
441
442   /* Clean up the file created by 'bind'.  */
443   unlink (_PATH_NSCDSOCKET);
444
445   /* Clean up pid file.  */
446   unlink (_PATH_NSCDPID);
447
448   // XXX Terminate threads.
449
450   /* Synchronize memory.  */
451   for (int cnt = 0; cnt < lastdb; ++cnt)
452     {
453       if (!dbs[cnt].enabled)
454         continue;
455
456       /* Make sure nobody keeps using the database.  */
457       dbs[cnt].head->timestamp = 0;
458
459       if (dbs[cnt].persistent)
460         // XXX async OK?
461         msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
462     }
463
464   /* Shutdown the SELinux AVC.  */
465   if (selinux_enabled)
466     nscd_avc_destroy ();
467
468   _exit (EXIT_SUCCESS);
469 }
470
471 /* Returns 1 if the process in pid file FILE is running, 0 if not.  */
472 static int
473 check_pid (const char *file)
474 {
475   FILE *fp;
476
477   fp = fopen (file, "r");
478   if (fp)
479     {
480       pid_t pid;
481       int n;
482
483       n = fscanf (fp, "%d", &pid);
484       fclose (fp);
485
486       /* If we cannot parse the file default to assuming nscd runs.
487          If the PID is alive, assume it is running.  That all unless
488          the PID is the same as the current process' since tha latter
489          can mean we re-exec.  */
490       if ((n != 1 || kill (pid, 0) == 0) && pid != getpid ())
491         return 1;
492     }
493
494   return 0;
495 }
496
497 /* Write the current process id to the file FILE.
498    Returns 0 if successful, -1 if not.  */
499 static int
500 write_pid (const char *file)
501 {
502   FILE *fp;
503
504   fp = fopen (file, "w");
505   if (fp == NULL)
506     return -1;
507
508   fprintf (fp, "%d\n", getpid ());
509   if (fflush (fp) || ferror (fp))
510     return -1;
511
512   fclose (fp);
513
514   return 0;
515 }