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