2.3.5-5
[glibc.git] / nscd / nscd.c
blob3ae401f1f733f6db74e7dc832a8e472c08c94360
1 /* Copyright (c) 1998-2003, 2004, 2005 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
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.
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.
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. */
20 /* nscd - Name Service Cache Daemon. Caches passwd, group, and hosts. */
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>
45 #include "dbg_log.h"
46 #include "nscd.h"
47 #include "selinux.h"
48 #include "../nss/nsswitch.h"
49 #include <device-nrs.h>
51 /* Get libc version number. */
52 #include <version.h>
54 #define PACKAGE _libc_intl_domainname
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". */
61 typedef struct
63 int num_active;
64 pthread_cond_t thread_exit_cv;
65 pthread_mutex_t mutex;
66 } thread_info_t;
68 thread_info_t thread_info;
70 int do_shutdown;
71 int disabled_passwd;
72 int disabled_group;
73 int go_background = 1;
75 int secure_in_use;
76 static const char *conffile = _PATH_NSCDCONF;
78 time_t start_time;
80 uintptr_t pagesize_m1;
82 int paranoia;
83 time_t restart_time;
84 time_t restart_interval = RESTART_INTERVAL;
85 const char *oldcwd;
86 uid_t old_uid;
87 gid_t old_gid;
89 static int check_pid (const char *file);
90 static int write_pid (const char *file);
92 /* Name and version of program. */
93 static void print_version (FILE *stream, struct argp_state *state);
94 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
96 /* Definitions of arguments for argp functions. */
97 static const struct argp_option options[] =
99 { "config-file", 'f', N_("NAME"), 0,
100 N_("Read configuration data from NAME") },
101 { "debug", 'd', NULL, 0,
102 N_("Do not fork and display messages on the current tty") },
103 { "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") },
104 { "shutdown", 'K', NULL, 0, N_("Shut the server down") },
105 { "statistic", 'g', NULL, 0, N_("Print current configuration statistic") },
106 { "invalidate", 'i', N_("TABLE"), 0,
107 N_("Invalidate the specified cache") },
108 { "secure", 'S', N_("TABLE,yes"), OPTION_HIDDEN,
109 N_("Use separate cache for each user")},
110 { NULL, 0, NULL, 0, NULL }
113 /* Short description of program. */
114 static const char doc[] = N_("Name Service Cache Daemon.");
116 /* Prototype for option handler. */
117 static error_t parse_opt (int key, char *arg, struct argp_state *state);
119 /* Data structure to communicate with argp functions. */
120 static struct argp argp =
122 options, parse_opt, NULL, doc,
125 /* The SIGHUP handler is extern to this file */
126 extern void sighup_handler(int signum);
128 /* True if only statistics are requested. */
129 static bool get_stats;
131 #ifdef atomic_init_nscd
132 atomic_init_nscd
133 #endif
136 main (int argc, char **argv)
138 int remaining;
140 /* Set locale via LC_ALL. */
141 setlocale (LC_ALL, "");
142 /* Set the text message domain. */
143 textdomain (PACKAGE);
145 /* Determine if the kernel has SELinux support. */
146 nscd_selinux_enabled (&selinux_enabled);
148 /* Parse and process arguments. */
149 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
151 if (remaining != argc)
153 error (0, 0, gettext ("wrong number of arguments"));
154 argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
155 exit (EXIT_FAILURE);
158 /* Read the configuration file. */
159 if (nscd_parse_file (conffile, dbs) != 0)
161 /* We couldn't read the configuration file. We don't start the
162 server. */
163 dbg_log (_("cannot read configuration file; this is fatal"));
164 exit (1);
167 /* Do we only get statistics? */
168 if (get_stats)
169 /* Does not return. */
170 receive_print_stats ();
172 /* Check if we are already running. */
173 if (check_pid (_PATH_NSCDPID))
174 error (EXIT_FAILURE, 0, _("already running"));
176 /* Remember when we started. */
177 start_time = time (NULL);
179 /* Determine page size. */
180 pagesize_m1 = getpagesize () - 1;
182 /* Behave like a daemon. */
183 if (go_background)
185 int i;
187 pid_t pid = fork ();
188 if (pid == -1)
189 error (EXIT_FAILURE, errno, _("cannot fork"));
190 if (pid != 0)
191 exit (0);
193 int nullfd = open (_PATH_DEVNULL, O_RDWR);
194 if (nullfd != -1)
196 struct stat64 st;
198 if (fstat64 (nullfd, &st) == 0 && S_ISCHR (st.st_mode) != 0
199 #if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
200 && st.st_rdev == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR)
201 #endif
204 /* It is the /dev/null special device alright. */
205 (void) dup2 (nullfd, STDIN_FILENO);
206 (void) dup2 (nullfd, STDOUT_FILENO);
207 (void) dup2 (nullfd, STDERR_FILENO);
209 if (nullfd > 2)
210 close (nullfd);
212 else
214 /* Ugh, somebody is trying to play a trick on us. */
215 close (nullfd);
216 nullfd = -1;
219 int min_close_fd = nullfd == -1 ? 0 : STDERR_FILENO + 1;
221 DIR *d = opendir ("/proc/self/fd");
222 if (d != NULL)
224 struct dirent64 *dirent;
225 int dfdn = dirfd (d);
227 while ((dirent = readdir64 (d)) != NULL)
229 char *endp;
230 long int fdn = strtol (dirent->d_name, &endp, 10);
232 if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd)
233 close ((int) fdn);
236 closedir (d);
238 else
239 for (i = min_close_fd; i < getdtablesize (); i++)
240 close (i);
242 pid = fork ();
243 if (pid == -1)
244 error (EXIT_FAILURE, errno, _("cannot fork"));
245 if (pid != 0)
246 exit (0);
248 setsid ();
250 chdir ("/");
252 openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
254 if (write_pid (_PATH_NSCDPID) < 0)
255 dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
257 if (!init_logfile ())
258 dbg_log (_("Could not create log file"));
260 /* Ignore job control signals. */
261 signal (SIGTTOU, SIG_IGN);
262 signal (SIGTTIN, SIG_IGN);
263 signal (SIGTSTP, SIG_IGN);
265 else
266 /* In foreground mode we are not paranoid. */
267 paranoia = 0;
269 /* Start the SELinux AVC. */
270 if (selinux_enabled)
271 nscd_avc_init ();
273 signal (SIGINT, termination_handler);
274 signal (SIGQUIT, termination_handler);
275 signal (SIGTERM, termination_handler);
276 signal (SIGHUP, sighup_handler);
277 signal (SIGPIPE, SIG_IGN);
279 /* Cleanup files created by a previous 'bind'. */
280 unlink (_PATH_NSCDSOCKET);
282 /* Make sure we do not get recursive calls. */
283 __nss_disable_nscd ();
285 /* Init databases. */
286 nscd_init ();
288 /* Handle incoming requests */
289 start_threads ();
291 return 0;
295 /* Handle program arguments. */
296 static error_t
297 parse_opt (int key, char *arg, struct argp_state *state)
299 switch (key)
301 case 'd':
302 ++debug_level;
303 go_background = 0;
304 break;
306 case 'f':
307 conffile = arg;
308 break;
310 case 'K':
311 if (getuid () != 0)
312 error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!"));
314 int sock = nscd_open_socket ();
315 request_header req;
316 ssize_t nbytes;
318 if (sock == -1)
319 exit (EXIT_FAILURE);
321 req.version = NSCD_VERSION;
322 req.type = SHUTDOWN;
323 req.key_len = 0;
324 nbytes = TEMP_FAILURE_RETRY (write (sock, &req,
325 sizeof (request_header)));
326 close (sock);
327 exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
330 case 'g':
331 get_stats = true;
332 break;
334 case 'i':
335 if (getuid () != 0)
336 error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!"));
337 else
339 int sock = nscd_open_socket ();
341 if (sock == -1)
342 exit (EXIT_FAILURE);
344 request_header req;
345 ssize_t nbytes;
346 struct iovec iov[2];
348 if (strcmp (arg, "passwd") == 0)
349 req.key_len = sizeof "passwd";
350 else if (strcmp (arg, "group") == 0)
351 req.key_len = sizeof "group";
352 else if (strcmp (arg, "hosts") == 0)
353 req.key_len = sizeof "hosts";
354 else
355 return ARGP_ERR_UNKNOWN;
357 req.version = NSCD_VERSION;
358 req.type = INVALIDATE;
360 iov[0].iov_base = &req;
361 iov[0].iov_len = sizeof (req);
362 iov[1].iov_base = arg;
363 iov[1].iov_len = req.key_len;
365 nbytes = TEMP_FAILURE_RETRY (writev (sock, iov, 2));
367 close (sock);
369 exit (nbytes != iov[0].iov_len + iov[1].iov_len
370 ? EXIT_FAILURE : EXIT_SUCCESS);
373 case 't':
374 nthreads = atol (arg);
375 break;
377 case 'S':
378 #if 0
379 if (strcmp (arg, "passwd,yes") == 0)
380 secure_in_use = dbs[pwddb].secure = 1;
381 else if (strcmp (arg, "group,yes") == 0)
382 secure_in_use = dbs[grpdb].secure = 1;
383 else if (strcmp (arg, "hosts,yes") == 0)
384 secure_in_use = dbs[hstdb].secure = 1;
385 #else
386 error (0, 0, _("secure services not implemented anymore"));
387 #endif
388 break;
390 default:
391 return ARGP_ERR_UNKNOWN;
394 return 0;
397 /* Print the version information. */
398 static void
399 print_version (FILE *stream, struct argp_state *state)
401 fprintf (stream, "nscd (GNU %s) %s\n", PACKAGE, VERSION);
402 fprintf (stream, gettext ("\
403 Copyright (C) %s Free Software Foundation, Inc.\n\
404 This is free software; see the source for copying conditions. There is NO\n\
405 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
406 "), "2005");
407 fprintf (stream, gettext ("Written by %s.\n"),
408 "Thorsten Kukuk and Ulrich Drepper");
412 /* Create a socket connected to a name. */
414 nscd_open_socket (void)
416 struct sockaddr_un addr;
417 int sock;
419 sock = socket (PF_UNIX, SOCK_STREAM, 0);
420 if (sock < 0)
421 return -1;
423 addr.sun_family = AF_UNIX;
424 assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
425 strcpy (addr.sun_path, _PATH_NSCDSOCKET);
426 if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
428 close (sock);
429 return -1;
432 return sock;
436 /* Cleanup. */
437 void
438 termination_handler (int signum)
440 close_sockets ();
442 /* Clean up the file created by 'bind'. */
443 unlink (_PATH_NSCDSOCKET);
445 /* Clean up pid file. */
446 unlink (_PATH_NSCDPID);
448 // XXX Terminate threads.
450 /* Synchronize memory. */
451 for (int cnt = 0; cnt < lastdb; ++cnt)
453 if (!dbs[cnt].enabled)
454 continue;
456 /* Make sure nobody keeps using the database. */
457 dbs[cnt].head->timestamp = 0;
459 if (dbs[cnt].persistent)
460 // XXX async OK?
461 msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
464 /* Shutdown the SELinux AVC. */
465 if (selinux_enabled)
466 nscd_avc_destroy ();
468 _exit (EXIT_SUCCESS);
471 /* Returns 1 if the process in pid file FILE is running, 0 if not. */
472 static int
473 check_pid (const char *file)
475 FILE *fp;
477 fp = fopen (file, "r");
478 if (fp)
480 pid_t pid;
481 int n;
483 n = fscanf (fp, "%d", &pid);
484 fclose (fp);
486 /* If we cannot parse the file default to assuming nscd runs.
487 If the PID is alive, assume it is running. That all unless
488 the PID is the same as the current process' since tha latter
489 can mean we re-exec. */
490 if ((n != 1 || kill (pid, 0) == 0) && pid != getpid ())
491 return 1;
494 return 0;
497 /* Write the current process id to the file FILE.
498 Returns 0 if successful, -1 if not. */
499 static int
500 write_pid (const char *file)
502 FILE *fp;
504 fp = fopen (file, "w");
505 if (fp == NULL)
506 return -1;
508 fprintf (fp, "%d\n", getpid ());
509 if (fflush (fp) || ferror (fp))
510 return -1;
512 fclose (fp);
514 return 0;