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