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