* sysdeps/hppa/bits/setjmp.h: Add _BITS_SETJMP_H preprocessor
[glibc.git] / nscd / nscd.c
blobf96f44e0baf92ddb6fdb706abc4852dd6bded8fe
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 /* True if only statistics are requested. */
126 static bool get_stats;
129 main (int argc, char **argv)
131 int remaining;
133 /* Set locale via LC_ALL. */
134 setlocale (LC_ALL, "");
135 /* Set the text message domain. */
136 textdomain (PACKAGE);
138 /* Determine if the kernel has SELinux support. */
139 nscd_selinux_enabled (&selinux_enabled);
141 /* Parse and process arguments. */
142 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
144 if (remaining != argc)
146 error (0, 0, gettext ("wrong number of arguments"));
147 argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
148 exit (1);
151 /* Read the configuration file. */
152 if (nscd_parse_file (conffile, dbs) != 0)
153 /* We couldn't read the configuration file. We don't start the
154 server. */
155 error (EXIT_FAILURE, 0,
156 _("failure while reading configuration file; this is fatal"));
158 /* Do we only get statistics? */
159 if (get_stats)
160 /* Does not return. */
161 receive_print_stats ();
163 /* Check if we are already running. */
164 if (check_pid (_PATH_NSCDPID))
165 error (EXIT_FAILURE, 0, _("already running"));
167 /* Remember when we started. */
168 start_time = time (NULL);
170 /* Determine page size. */
171 pagesize_m1 = getpagesize () - 1;
173 /* Behave like a daemon. */
174 if (go_background)
176 int i;
178 pid_t pid = fork ();
179 if (pid == -1)
180 error (EXIT_FAILURE, errno, _("cannot fork"));
181 if (pid != 0)
182 exit (0);
184 int nullfd = open (_PATH_DEVNULL, O_RDWR);
185 if (nullfd != -1)
187 struct stat64 st;
189 if (fstat64 (nullfd, &st) == 0 && S_ISCHR (st.st_mode) != 0
190 #if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
191 && st.st_rdev == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR)
192 #endif
195 /* It is the /dev/null special device alright. */
196 (void) dup2 (nullfd, STDIN_FILENO);
197 (void) dup2 (nullfd, STDOUT_FILENO);
198 (void) dup2 (nullfd, STDERR_FILENO);
200 if (nullfd > 2)
201 close (nullfd);
203 else
205 /* Ugh, somebody is trying to play a trick on us. */
206 close (nullfd);
207 nullfd = -1;
210 int min_close_fd = nullfd == -1 ? 0 : STDERR_FILENO + 1;
212 DIR *d = opendir ("/proc/self/fd");
213 if (d != NULL)
215 struct dirent64 *dirent;
216 int dfdn = dirfd (d);
218 while ((dirent = readdir64 (d)) != NULL)
220 char *endp;
221 long int fdn = strtol (dirent->d_name, &endp, 10);
223 if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd)
224 close ((int) fdn);
227 closedir (d);
229 else
230 for (i = min_close_fd; i < getdtablesize (); i++)
231 close (i);
233 pid = fork ();
234 if (pid == -1)
235 error (EXIT_FAILURE, errno, _("cannot fork"));
236 if (pid != 0)
237 exit (0);
239 setsid ();
241 if (chdir ("/") != 0)
242 error (EXIT_FAILURE, errno,
243 _("cannot change current working cirectory to \"/\""));
245 openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
247 if (write_pid (_PATH_NSCDPID) < 0)
248 dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
250 if (!init_logfile ())
251 dbg_log (_("Could not create log file"));
253 /* Ignore job control signals. */
254 signal (SIGTTOU, SIG_IGN);
255 signal (SIGTTIN, SIG_IGN);
256 signal (SIGTSTP, SIG_IGN);
258 else
259 /* In foreground mode we are not paranoid. */
260 paranoia = 0;
262 /* Start the SELinux AVC. */
263 if (selinux_enabled)
264 nscd_avc_init ();
266 signal (SIGINT, termination_handler);
267 signal (SIGQUIT, termination_handler);
268 signal (SIGTERM, termination_handler);
269 signal (SIGPIPE, SIG_IGN);
271 /* Cleanup files created by a previous 'bind'. */
272 unlink (_PATH_NSCDSOCKET);
274 /* Make sure we do not get recursive calls. */
275 __nss_disable_nscd ();
277 /* Init databases. */
278 nscd_init ();
280 /* Handle incoming requests */
281 start_threads ();
283 return 0;
287 /* Handle program arguments. */
288 static error_t
289 parse_opt (int key, char *arg, struct argp_state *state)
291 switch (key)
293 case 'd':
294 ++debug_level;
295 go_background = 0;
296 break;
298 case 'f':
299 conffile = arg;
300 break;
302 case 'K':
303 if (getuid () != 0)
304 error (4, 0, _("Only root is allowed to use this option!"));
306 int sock = nscd_open_socket ();
307 request_header req;
308 ssize_t nbytes;
310 if (sock == -1)
311 exit (EXIT_FAILURE);
313 req.version = NSCD_VERSION;
314 req.type = SHUTDOWN;
315 req.key_len = 0;
316 nbytes = TEMP_FAILURE_RETRY (send (sock, &req,
317 sizeof (request_header),
318 MSG_NOSIGNAL));
319 close (sock);
320 exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
323 case 'g':
324 get_stats = true;
325 break;
327 case 'i':
328 if (getuid () != 0)
329 error (4, 0, _("Only root is allowed to use this option!"));
330 else
332 int sock = nscd_open_socket ();
334 if (sock == -1)
335 exit (EXIT_FAILURE);
337 request_header req;
338 ssize_t nbytes;
339 struct iovec iov[2];
341 if (strcmp (arg, "passwd") == 0)
342 req.key_len = sizeof "passwd";
343 else if (strcmp (arg, "group") == 0)
344 req.key_len = sizeof "group";
345 else if (strcmp (arg, "hosts") == 0)
346 req.key_len = sizeof "hosts";
347 else
348 return ARGP_ERR_UNKNOWN;
350 req.version = NSCD_VERSION;
351 req.type = INVALIDATE;
353 iov[0].iov_base = &req;
354 iov[0].iov_len = sizeof (req);
355 iov[1].iov_base = arg;
356 iov[1].iov_len = req.key_len;
358 nbytes = TEMP_FAILURE_RETRY (writev (sock, iov, 2));
360 close (sock);
362 exit (nbytes != iov[0].iov_len + iov[1].iov_len
363 ? EXIT_FAILURE : EXIT_SUCCESS);
366 case 't':
367 nthreads = atol (arg);
368 break;
370 case 'S':
371 #if 0
372 if (strcmp (arg, "passwd,yes") == 0)
373 secure_in_use = dbs[pwddb].secure = 1;
374 else if (strcmp (arg, "group,yes") == 0)
375 secure_in_use = dbs[grpdb].secure = 1;
376 else if (strcmp (arg, "hosts,yes") == 0)
377 secure_in_use = dbs[hstdb].secure = 1;
378 #else
379 error (0, 0, _("secure services not implemented anymore"));
380 #endif
381 break;
383 default:
384 return ARGP_ERR_UNKNOWN;
387 return 0;
390 /* Print the version information. */
391 static void
392 print_version (FILE *stream, struct argp_state *state)
394 fprintf (stream, "nscd (GNU %s) %s\n", PACKAGE, VERSION);
395 fprintf (stream, gettext ("\
396 Copyright (C) %s Free Software Foundation, Inc.\n\
397 This is free software; see the source for copying conditions. There is NO\n\
398 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
399 "), "2005");
400 fprintf (stream, gettext ("Written by %s.\n"),
401 "Thorsten Kukuk and Ulrich Drepper");
405 /* Create a socket connected to a name. */
407 nscd_open_socket (void)
409 struct sockaddr_un addr;
410 int sock;
412 sock = socket (PF_UNIX, SOCK_STREAM, 0);
413 if (sock < 0)
414 return -1;
416 addr.sun_family = AF_UNIX;
417 assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
418 strcpy (addr.sun_path, _PATH_NSCDSOCKET);
419 if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
421 close (sock);
422 return -1;
425 return sock;
429 /* Cleanup. */
430 void
431 termination_handler (int signum)
433 close_sockets ();
435 /* Clean up the file created by 'bind'. */
436 unlink (_PATH_NSCDSOCKET);
438 /* Clean up pid file. */
439 unlink (_PATH_NSCDPID);
441 // XXX Terminate threads.
443 /* Synchronize memory. */
444 for (int cnt = 0; cnt < lastdb; ++cnt)
446 if (!dbs[cnt].enabled)
447 continue;
449 /* Make sure nobody keeps using the database. */
450 dbs[cnt].head->timestamp = 0;
452 if (dbs[cnt].persistent)
453 // XXX async OK?
454 msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
457 /* Shutdown the SELinux AVC. */
458 if (selinux_enabled)
459 nscd_avc_destroy ();
461 _exit (EXIT_SUCCESS);
464 /* Returns 1 if the process in pid file FILE is running, 0 if not. */
465 static int
466 check_pid (const char *file)
468 FILE *fp;
470 fp = fopen (file, "r");
471 if (fp)
473 pid_t pid;
474 int n;
476 n = fscanf (fp, "%d", &pid);
477 fclose (fp);
479 /* If we cannot parse the file default to assuming nscd runs.
480 If the PID is alive, assume it is running. That all unless
481 the PID is the same as the current process' since tha latter
482 can mean we re-exec. */
483 if ((n != 1 || kill (pid, 0) == 0) && pid != getpid ())
484 return 1;
487 return 0;
490 /* Write the current process id to the file FILE.
491 Returns 0 if successful, -1 if not. */
492 static int
493 write_pid (const char *file)
495 FILE *fp;
497 fp = fopen (file, "w");
498 if (fp == NULL)
499 return -1;
501 fprintf (fp, "%d\n", getpid ());
502 if (fflush (fp) || ferror (fp))
503 return -1;
505 fclose (fp);
507 return 0;