(to_wc): Mark as const. (to_mb): Likewise.
[glibc.git] / nscd / nscd.c
blob40c335aef7e2ebdf353480d5c49c32aea636085a
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 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 int secure_in_use;
74 static const char *conffile = _PATH_NSCDCONF;
76 time_t start_time;
78 uintptr_t pagesize_m1;
80 int paranoia;
81 time_t restart_time;
82 time_t restart_interval = RESTART_INTERVAL;
83 const char *oldcwd;
84 uid_t old_uid;
85 gid_t old_gid;
87 static int check_pid (const char *file);
88 static int write_pid (const char *file);
90 /* Name and version of program. */
91 static void print_version (FILE *stream, struct argp_state *state);
92 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
94 /* Definitions of arguments for argp functions. */
95 static const struct argp_option options[] =
97 { "config-file", 'f', N_("NAME"), 0,
98 N_("Read configuration data from NAME") },
99 { "debug", 'd', NULL, 0,
100 N_("Do not fork and display messages on the current tty") },
101 { "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") },
102 { "shutdown", 'K', NULL, 0, N_("Shut the server down") },
103 { "statistic", 'g', NULL, 0, N_("Print current configuration statistic") },
104 { "invalidate", 'i', N_("TABLE"), 0,
105 N_("Invalidate the specified cache") },
106 { "secure", 'S', N_("TABLE,yes"), OPTION_HIDDEN,
107 N_("Use separate cache for each user")},
108 { NULL, 0, NULL, 0, NULL }
111 /* Short description of program. */
112 static const char doc[] = N_("Name Service Cache Daemon.");
114 /* Prototype for option handler. */
115 static error_t parse_opt (int key, char *arg, struct argp_state *state);
117 /* Data structure to communicate with argp functions. */
118 static struct argp argp =
120 options, parse_opt, NULL, doc,
123 /* True if only statistics are requested. */
124 static bool get_stats;
127 main (int argc, char **argv)
129 int remaining;
131 /* Set locale via LC_ALL. */
132 setlocale (LC_ALL, "");
133 /* Set the text message domain. */
134 textdomain (PACKAGE);
136 /* Determine if the kernel has SELinux support. */
137 nscd_selinux_enabled (&selinux_enabled);
139 /* Parse and process arguments. */
140 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
142 if (remaining != argc)
144 error (0, 0, gettext ("wrong number of arguments"));
145 argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
146 exit (1);
149 /* Read the configuration file. */
150 if (nscd_parse_file (conffile, dbs) != 0)
151 /* We couldn't read the configuration file. We don't start the
152 server. */
153 error (EXIT_FAILURE, 0,
154 _("failure while reading configuration file; this is fatal"));
156 /* Do we only get statistics? */
157 if (get_stats)
158 /* Does not return. */
159 receive_print_stats ();
161 /* Check if we are already running. */
162 if (check_pid (_PATH_NSCDPID))
163 error (EXIT_FAILURE, 0, _("already running"));
165 /* Remember when we started. */
166 start_time = time (NULL);
168 /* Determine page size. */
169 pagesize_m1 = getpagesize () - 1;
171 /* Behave like a daemon. */
172 if (go_background)
174 int i;
176 pid_t pid = fork ();
177 if (pid == -1)
178 error (EXIT_FAILURE, errno, _("cannot fork"));
179 if (pid != 0)
180 exit (0);
182 int nullfd = open (_PATH_DEVNULL, O_RDWR);
183 if (nullfd != -1)
185 struct stat64 st;
187 if (fstat64 (nullfd, &st) == 0 && S_ISCHR (st.st_mode) != 0
188 #if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
189 && st.st_rdev == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR)
190 #endif
193 /* It is the /dev/null special device alright. */
194 (void) dup2 (nullfd, STDIN_FILENO);
195 (void) dup2 (nullfd, STDOUT_FILENO);
196 (void) dup2 (nullfd, STDERR_FILENO);
198 if (nullfd > 2)
199 close (nullfd);
201 else
203 /* Ugh, somebody is trying to play a trick on us. */
204 close (nullfd);
205 nullfd = -1;
208 int min_close_fd = nullfd == -1 ? 0 : STDERR_FILENO + 1;
210 DIR *d = opendir ("/proc/self/fd");
211 if (d != NULL)
213 struct dirent64 *dirent;
214 int dfdn = dirfd (d);
216 while ((dirent = readdir64 (d)) != NULL)
218 char *endp;
219 long int fdn = strtol (dirent->d_name, &endp, 10);
221 if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd)
222 close ((int) fdn);
225 closedir (d);
227 else
228 for (i = min_close_fd; i < getdtablesize (); i++)
229 close (i);
231 pid = fork ();
232 if (pid == -1)
233 error (EXIT_FAILURE, errno, _("cannot fork"));
234 if (pid != 0)
235 exit (0);
237 setsid ();
239 if (chdir ("/") != 0)
240 error (EXIT_FAILURE, errno,
241 _("cannot change current working cirectory to \"/\""));
243 openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
245 if (write_pid (_PATH_NSCDPID) < 0)
246 dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
248 if (!init_logfile ())
249 dbg_log (_("Could not create log file"));
251 /* Ignore job control signals. */
252 signal (SIGTTOU, SIG_IGN);
253 signal (SIGTTIN, SIG_IGN);
254 signal (SIGTSTP, SIG_IGN);
256 else
257 /* In foreground mode we are not paranoid. */
258 paranoia = 0;
260 /* Start the SELinux AVC. */
261 if (selinux_enabled)
262 nscd_avc_init ();
264 signal (SIGINT, termination_handler);
265 signal (SIGQUIT, termination_handler);
266 signal (SIGTERM, termination_handler);
267 signal (SIGPIPE, SIG_IGN);
269 /* Cleanup files created by a previous 'bind'. */
270 unlink (_PATH_NSCDSOCKET);
272 /* Make sure we do not get recursive calls. */
273 __nss_disable_nscd ();
275 /* Init databases. */
276 nscd_init ();
278 /* Handle incoming requests */
279 start_threads ();
281 return 0;
285 /* Handle program arguments. */
286 static error_t
287 parse_opt (int key, char *arg, struct argp_state *state)
289 switch (key)
291 case 'd':
292 ++debug_level;
293 go_background = 0;
294 break;
296 case 'f':
297 conffile = arg;
298 break;
300 case 'K':
301 if (getuid () != 0)
302 error (4, 0, _("Only root is allowed to use this option!"));
304 int sock = nscd_open_socket ();
305 request_header req;
306 ssize_t nbytes;
308 if (sock == -1)
309 exit (EXIT_FAILURE);
311 req.version = NSCD_VERSION;
312 req.type = SHUTDOWN;
313 req.key_len = 0;
314 nbytes = TEMP_FAILURE_RETRY (send (sock, &req,
315 sizeof (request_header),
316 MSG_NOSIGNAL));
317 close (sock);
318 exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
321 case 'g':
322 get_stats = true;
323 break;
325 case 'i':
326 if (getuid () != 0)
327 error (4, 0, _("Only root is allowed to use this option!"));
328 else
330 int sock = nscd_open_socket ();
332 if (sock == -1)
333 exit (EXIT_FAILURE);
335 request_header req;
336 ssize_t nbytes;
337 struct iovec iov[2];
339 if (strcmp (arg, "passwd") == 0)
340 req.key_len = sizeof "passwd";
341 else if (strcmp (arg, "group") == 0)
342 req.key_len = sizeof "group";
343 else if (strcmp (arg, "hosts") == 0)
344 req.key_len = sizeof "hosts";
345 else
346 return ARGP_ERR_UNKNOWN;
348 req.version = NSCD_VERSION;
349 req.type = INVALIDATE;
351 iov[0].iov_base = &req;
352 iov[0].iov_len = sizeof (req);
353 iov[1].iov_base = arg;
354 iov[1].iov_len = req.key_len;
356 nbytes = TEMP_FAILURE_RETRY (writev (sock, iov, 2));
358 close (sock);
360 exit (nbytes != iov[0].iov_len + iov[1].iov_len
361 ? EXIT_FAILURE : EXIT_SUCCESS);
364 case 't':
365 nthreads = atol (arg);
366 break;
368 case 'S':
369 #if 0
370 if (strcmp (arg, "passwd,yes") == 0)
371 secure_in_use = dbs[pwddb].secure = 1;
372 else if (strcmp (arg, "group,yes") == 0)
373 secure_in_use = dbs[grpdb].secure = 1;
374 else if (strcmp (arg, "hosts,yes") == 0)
375 secure_in_use = dbs[hstdb].secure = 1;
376 #else
377 error (0, 0, _("secure services not implemented anymore"));
378 #endif
379 break;
381 default:
382 return ARGP_ERR_UNKNOWN;
385 return 0;
388 /* Print the version information. */
389 static void
390 print_version (FILE *stream, struct argp_state *state)
392 fprintf (stream, "nscd (GNU %s) %s\n", PACKAGE, VERSION);
393 fprintf (stream, gettext ("\
394 Copyright (C) %s Free Software Foundation, Inc.\n\
395 This is free software; see the source for copying conditions. There is NO\n\
396 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
397 "), "2005");
398 fprintf (stream, gettext ("Written by %s.\n"),
399 "Thorsten Kukuk and Ulrich Drepper");
403 /* Create a socket connected to a name. */
405 nscd_open_socket (void)
407 struct sockaddr_un addr;
408 int sock;
410 sock = socket (PF_UNIX, SOCK_STREAM, 0);
411 if (sock < 0)
412 return -1;
414 addr.sun_family = AF_UNIX;
415 assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
416 strcpy (addr.sun_path, _PATH_NSCDSOCKET);
417 if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
419 close (sock);
420 return -1;
423 return sock;
427 /* Cleanup. */
428 void
429 termination_handler (int signum)
431 close_sockets ();
433 /* Clean up the file created by 'bind'. */
434 unlink (_PATH_NSCDSOCKET);
436 /* Clean up pid file. */
437 unlink (_PATH_NSCDPID);
439 // XXX Terminate threads.
441 /* Synchronize memory. */
442 for (int cnt = 0; cnt < lastdb; ++cnt)
444 if (!dbs[cnt].enabled)
445 continue;
447 /* Make sure nobody keeps using the database. */
448 dbs[cnt].head->timestamp = 0;
450 if (dbs[cnt].persistent)
451 // XXX async OK?
452 msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
455 /* Shutdown the SELinux AVC. */
456 if (selinux_enabled)
457 nscd_avc_destroy ();
459 _exit (EXIT_SUCCESS);
462 /* Returns 1 if the process in pid file FILE is running, 0 if not. */
463 static int
464 check_pid (const char *file)
466 FILE *fp;
468 fp = fopen (file, "r");
469 if (fp)
471 pid_t pid;
472 int n;
474 n = fscanf (fp, "%d", &pid);
475 fclose (fp);
477 /* If we cannot parse the file default to assuming nscd runs.
478 If the PID is alive, assume it is running. That all unless
479 the PID is the same as the current process' since tha latter
480 can mean we re-exec. */
481 if ((n != 1 || kill (pid, 0) == 0) && pid != getpid ())
482 return 1;
485 return 0;
488 /* Write the current process id to the file FILE.
489 Returns 0 if successful, -1 if not. */
490 static int
491 write_pid (const char *file)
493 FILE *fp;
495 fp = fopen (file, "w");
496 if (fp == NULL)
497 return -1;
499 fprintf (fp, "%d\n", getpid ());
500 if (fflush (fp) || ferror (fp))
501 return -1;
503 fclose (fp);
505 return 0;