Upload Tizen:Base source
[external/eglibc.git] / nscd / nscd.c
1 /* Copyright (c) 1998-2008, 2009, 2010, 2011 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 /* Function to print some extra text in the help message.  */
95 static char *more_help (int key, const char *text, void *input);
96
97 /* Definitions of arguments for argp functions.  */
98 static const struct argp_option options[] =
99 {
100   { "config-file", 'f', N_("NAME"), 0,
101     N_("Read configuration data from NAME") },
102   { "debug", 'd', NULL, 0,
103     N_("Do not fork and display messages on the current tty") },
104   { "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") },
105   { "shutdown", 'K', NULL, 0, N_("Shut the server down") },
106   { "statistics", 'g', NULL, 0, N_("Print current configuration statistics") },
107   { "invalidate", 'i', N_("TABLE"), 0,
108     N_("Invalidate the specified cache") },
109   { "secure", 'S', N_("TABLE,yes"), OPTION_HIDDEN,
110     N_("Use separate cache for each user")},
111   { NULL, 0, NULL, 0, NULL }
112 };
113
114 /* Short description of program.  */
115 static const char doc[] = N_("Name Service Cache Daemon.");
116
117 /* Prototype for option handler.  */
118 static error_t parse_opt (int key, char *arg, struct argp_state *state);
119
120 /* Data structure to communicate with argp functions.  */
121 static struct argp argp =
122 {
123   options, parse_opt, NULL, doc, NULL, more_help
124 };
125
126 /* True if only statistics are requested.  */
127 static bool get_stats;
128
129 int
130 main (int argc, char **argv)
131 {
132   int remaining;
133
134   /* Set locale via LC_ALL.  */
135   setlocale (LC_ALL, "");
136   /* Set the text message domain.  */
137   textdomain (PACKAGE);
138
139   /* Determine if the kernel has SELinux support.  */
140   nscd_selinux_enabled (&selinux_enabled);
141
142   /* Parse and process arguments.  */
143   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
144
145   if (remaining != argc)
146     {
147       error (0, 0, gettext ("wrong number of arguments"));
148       argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
149       exit (1);
150     }
151
152   /* Read the configuration file.  */
153   if (nscd_parse_file (conffile, dbs) != 0)
154     /* We couldn't read the configuration file.  We don't start the
155        server.  */
156     error (EXIT_FAILURE, 0,
157            _("failure while reading configuration file; this is fatal"));
158
159   /* Do we only get statistics?  */
160   if (get_stats)
161     /* Does not return.  */
162     receive_print_stats ();
163
164   /* Check if we are already running. */
165   if (check_pid (_PATH_NSCDPID))
166     error (EXIT_FAILURE, 0, _("already running"));
167
168   /* Remember when we started.  */
169   start_time = time (NULL);
170
171   /* Determine page size.  */
172   pagesize_m1 = getpagesize () - 1;
173
174   /* Behave like a daemon.  */
175   if (go_background)
176     {
177       int i;
178
179       pid_t pid = fork ();
180       if (pid == -1)
181         error (EXIT_FAILURE, errno, _("cannot fork"));
182       if (pid != 0)
183         exit (0);
184
185       int nullfd = open (_PATH_DEVNULL, O_RDWR);
186       if (nullfd != -1)
187         {
188           struct stat64 st;
189
190           if (fstat64 (nullfd, &st) == 0 && S_ISCHR (st.st_mode) != 0
191 #if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
192               && st.st_rdev == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR)
193 #endif
194               )
195             {
196               /* It is the /dev/null special device alright.  */
197               (void) dup2 (nullfd, STDIN_FILENO);
198               (void) dup2 (nullfd, STDOUT_FILENO);
199               (void) dup2 (nullfd, STDERR_FILENO);
200
201               if (nullfd > 2)
202                 close (nullfd);
203             }
204           else
205             {
206               /* Ugh, somebody is trying to play a trick on us.  */
207               close (nullfd);
208               nullfd = -1;
209             }
210         }
211       int min_close_fd = nullfd == -1 ? 0 : STDERR_FILENO + 1;
212
213       DIR *d = opendir ("/proc/self/fd");
214       if (d != NULL)
215         {
216           struct dirent64 *dirent;
217           int dfdn = dirfd (d);
218
219           while ((dirent = readdir64 (d)) != NULL)
220             {
221               char *endp;
222               long int fdn = strtol (dirent->d_name, &endp, 10);
223
224               if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd)
225                 close ((int) fdn);
226             }
227
228           closedir (d);
229         }
230       else
231         for (i = min_close_fd; i < getdtablesize (); i++)
232           close (i);
233
234       pid = fork ();
235       if (pid == -1)
236         error (EXIT_FAILURE, errno, _("cannot fork"));
237       if (pid != 0)
238         exit (0);
239
240       setsid ();
241
242       if (chdir ("/") != 0)
243         error (EXIT_FAILURE, errno,
244                _("cannot change current working directory to \"/\""));
245
246       openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
247
248       if (write_pid (_PATH_NSCDPID) < 0)
249         dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
250
251       if (!init_logfile ())
252         dbg_log (_("Could not create log file"));
253
254       /* Ignore job control signals.  */
255       signal (SIGTTOU, SIG_IGN);
256       signal (SIGTTIN, SIG_IGN);
257       signal (SIGTSTP, SIG_IGN);
258     }
259   else
260     /* In foreground mode we are not paranoid.  */
261     paranoia = 0;
262
263   /* Start the SELinux AVC.  */
264   if (selinux_enabled)
265     nscd_avc_init ();
266
267   signal (SIGINT, termination_handler);
268   signal (SIGQUIT, termination_handler);
269   signal (SIGTERM, termination_handler);
270   signal (SIGPIPE, SIG_IGN);
271
272   /* Cleanup files created by a previous 'bind'.  */
273   unlink (_PATH_NSCDSOCKET);
274
275   /* Make sure we do not get recursive calls.  */
276   __nss_disable_nscd ();
277
278   /* Init databases.  */
279   nscd_init ();
280
281   /* Handle incoming requests */
282   start_threads ();
283
284   return 0;
285 }
286
287
288 /* Handle program arguments.  */
289 static error_t
290 parse_opt (int key, char *arg, struct argp_state *state)
291 {
292   switch (key)
293     {
294     case 'd':
295       ++debug_level;
296       go_background = 0;
297       break;
298
299     case 'f':
300       conffile = arg;
301       break;
302
303     case 'K':
304       if (getuid () != 0)
305         error (4, 0, _("Only root is allowed to use this option!"));
306       {
307         int sock = nscd_open_socket ();
308
309         if (sock == -1)
310           exit (EXIT_FAILURE);
311
312         request_header req;
313         req.version = NSCD_VERSION;
314         req.type = SHUTDOWN;
315         req.key_len = 0;
316
317         ssize_t nbytes = TEMP_FAILURE_RETRY (send (sock, &req,
318                                                    sizeof (request_header),
319                                                    MSG_NOSIGNAL));
320         close (sock);
321         exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
322       }
323
324     case 'g':
325       get_stats = true;
326       break;
327
328     case 'i':
329       if (getuid () != 0)
330         error (4, 0, _("Only root is allowed to use this option!"));
331       else
332         {
333           int sock = nscd_open_socket ();
334
335           if (sock == -1)
336             exit (EXIT_FAILURE);
337
338           dbtype cnt;
339           for (cnt = pwddb; cnt < lastdb; ++cnt)
340             if (strcmp (arg, dbnames[cnt]) == 0)
341               break;
342
343           if (cnt == lastdb)
344             {
345               argp_error (state, _("'%s' is not a known database"), arg);
346               return EINVAL;
347             }
348
349           size_t arg_len = strlen (arg) + 1;
350           struct
351           {
352             request_header req;
353             char arg[arg_len];
354           } reqdata;
355
356           reqdata.req.key_len = strlen (arg) + 1;
357           reqdata.req.version = NSCD_VERSION;
358           reqdata.req.type = INVALIDATE;
359           memcpy (reqdata.arg, arg, arg_len);
360
361           ssize_t nbytes = TEMP_FAILURE_RETRY (send (sock, &reqdata,
362                                                      sizeof (request_header)
363                                                      + arg_len,
364                                                      MSG_NOSIGNAL));
365
366           if (nbytes != sizeof (request_header) + arg_len)
367             {
368               int err = errno;
369               close (sock);
370               error (EXIT_FAILURE, err, _("write incomplete"));
371             }
372
373           /* Wait for ack.  Older nscd just closed the socket when
374              prune_cache finished, silently ignore that.  */
375           int32_t resp = 0;
376           nbytes = TEMP_FAILURE_RETRY (read (sock, &resp, sizeof (resp)));
377           if (nbytes != 0 && nbytes != sizeof (resp))
378             {
379               int err = errno;
380               close (sock);
381               error (EXIT_FAILURE, err, _("cannot read invalidate ACK"));
382             }
383
384           close (sock);
385
386           if (resp != 0)
387             error (EXIT_FAILURE, resp, _("invalidation failed"));
388
389           exit (0);
390         }
391
392     case 't':
393       nthreads = atol (arg);
394       break;
395
396     case 'S':
397       error (0, 0, _("secure services not implemented anymore"));
398       break;
399
400     default:
401       return ARGP_ERR_UNKNOWN;
402     }
403
404   return 0;
405 }
406
407 /* Print bug-reporting information in the help message.  */
408 static char *
409 more_help (int key, const char *text, void *input)
410 {
411   char *tp = NULL;
412   switch (key)
413     {
414     case ARGP_KEY_HELP_EXTRA:
415       /* We print some extra information.  */
416       if (asprintf (&tp, gettext ("\
417 For bug reporting instructions, please see:\n\
418 %s.\n"), REPORT_BUGS_TO) < 0)
419         return NULL;
420       return tp;
421     default:
422       break;
423     }
424   return (char *) text;
425 }
426
427 /* Print the version information.  */
428 static void
429 print_version (FILE *stream, struct argp_state *state)
430 {
431   fprintf (stream, "nscd %s%s\n", PKGVERSION, VERSION);
432   fprintf (stream, gettext ("\
433 Copyright (C) %s Free Software Foundation, Inc.\n\
434 This is free software; see the source for copying conditions.  There is NO\n\
435 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
436 "), "2011");
437   fprintf (stream, gettext ("Written by %s.\n"),
438            "Thorsten Kukuk and Ulrich Drepper");
439 }
440
441
442 /* Create a socket connected to a name.  */
443 int
444 nscd_open_socket (void)
445 {
446   struct sockaddr_un addr;
447   int sock;
448
449   sock = socket (PF_UNIX, SOCK_STREAM, 0);
450   if (sock < 0)
451     return -1;
452
453   addr.sun_family = AF_UNIX;
454   assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
455   strcpy (addr.sun_path, _PATH_NSCDSOCKET);
456   if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
457     {
458       close (sock);
459       return -1;
460     }
461
462   return sock;
463 }
464
465
466 /* Cleanup.  */
467 void
468 termination_handler (int signum)
469 {
470   close_sockets ();
471
472   /* Clean up the file created by 'bind'.  */
473   unlink (_PATH_NSCDSOCKET);
474
475   /* Clean up pid file.  */
476   unlink (_PATH_NSCDPID);
477
478   // XXX Terminate threads.
479
480   /* Synchronize memory.  */
481   for (int cnt = 0; cnt < lastdb; ++cnt)
482     {
483       if (!dbs[cnt].enabled)
484         continue;
485
486       /* Make sure nobody keeps using the database.  */
487       dbs[cnt].head->timestamp = 0;
488
489       if (dbs[cnt].persistent)
490         // XXX async OK?
491         msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
492     }
493
494   _exit (EXIT_SUCCESS);
495 }
496
497 /* Returns 1 if the process in pid file FILE is running, 0 if not.  */
498 static int
499 check_pid (const char *file)
500 {
501   FILE *fp;
502
503   fp = fopen (file, "r");
504   if (fp)
505     {
506       pid_t pid;
507       int n;
508
509       n = fscanf (fp, "%d", &pid);
510       fclose (fp);
511
512       /* If we cannot parse the file default to assuming nscd runs.
513          If the PID is alive, assume it is running.  That all unless
514          the PID is the same as the current process' since tha latter
515          can mean we re-exec.  */
516       if ((n != 1 || kill (pid, 0) == 0) && pid != getpid ())
517         return 1;
518     }
519
520   return 0;
521 }
522
523 /* Write the current process id to the file FILE.
524    Returns 0 if successful, -1 if not.  */
525 static int
526 write_pid (const char *file)
527 {
528   FILE *fp;
529
530   fp = fopen (file, "w");
531   if (fp == NULL)
532     return -1;
533
534   fprintf (fp, "%d\n", getpid ());
535
536   int result = fflush (fp) || ferror (fp) ? -1 : 0;
537
538   fclose (fp);
539
540   return result;
541 }