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