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