* nscd/nscd_stat.c (receive_print_stats): Really print values of
[glibc.git] / nscd / nscd.c
bloba7aa96480445aada644eac7ef855b0c8cc7d9c2a
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)
154 /* We couldn't read the configuration file. We don't start the
155 server. */
156 dbg_log (_("cannot read configuration file; this is fatal"));
157 exit (1);
160 /* Do we only get statistics? */
161 if (get_stats)
162 /* Does not return. */
163 receive_print_stats ();
165 /* Check if we are already running. */
166 if (check_pid (_PATH_NSCDPID))
167 error (EXIT_FAILURE, 0, _("already running"));
169 /* Remember when we started. */
170 start_time = time (NULL);
172 /* Determine page size. */
173 pagesize_m1 = getpagesize () - 1;
175 /* Behave like a daemon. */
176 if (go_background)
178 int i;
180 pid_t pid = fork ();
181 if (pid == -1)
182 error (EXIT_FAILURE, errno, _("cannot fork"));
183 if (pid != 0)
184 exit (0);
186 int nullfd = open (_PATH_DEVNULL, O_RDWR);
187 if (nullfd != -1)
189 struct stat64 st;
191 if (fstat64 (nullfd, &st) == 0 && S_ISCHR (st.st_mode) != 0
192 #if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
193 && st.st_rdev == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR)
194 #endif
197 /* It is the /dev/null special device alright. */
198 (void) dup2 (nullfd, STDIN_FILENO);
199 (void) dup2 (nullfd, STDOUT_FILENO);
200 (void) dup2 (nullfd, STDERR_FILENO);
202 if (nullfd > 2)
203 close (nullfd);
205 else
207 /* Ugh, somebody is trying to play a trick on us. */
208 close (nullfd);
209 nullfd = -1;
212 int min_close_fd = nullfd == -1 ? 0 : STDERR_FILENO + 1;
214 DIR *d = opendir ("/proc/self/fd");
215 if (d != NULL)
217 struct dirent64 *dirent;
218 int dfdn = dirfd (d);
220 while ((dirent = readdir64 (d)) != NULL)
222 char *endp;
223 long int fdn = strtol (dirent->d_name, &endp, 10);
225 if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd)
226 close ((int) fdn);
229 closedir (d);
231 else
232 for (i = min_close_fd; i < getdtablesize (); i++)
233 close (i);
235 pid = fork ();
236 if (pid == -1)
237 error (EXIT_FAILURE, errno, _("cannot fork"));
238 if (pid != 0)
239 exit (0);
241 setsid ();
243 if (chdir ("/") != 0)
244 error (EXIT_FAILURE, errno,
245 _("cannot change current working cirectory to \"/\""));
247 openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
249 if (write_pid (_PATH_NSCDPID) < 0)
250 dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
252 if (!init_logfile ())
253 dbg_log (_("Could not create log file"));
255 /* Ignore job control signals. */
256 signal (SIGTTOU, SIG_IGN);
257 signal (SIGTTIN, SIG_IGN);
258 signal (SIGTSTP, SIG_IGN);
260 else
261 /* In foreground mode we are not paranoid. */
262 paranoia = 0;
264 /* Start the SELinux AVC. */
265 if (selinux_enabled)
266 nscd_avc_init ();
268 signal (SIGINT, termination_handler);
269 signal (SIGQUIT, termination_handler);
270 signal (SIGTERM, termination_handler);
271 signal (SIGPIPE, SIG_IGN);
273 /* Cleanup files created by a previous 'bind'. */
274 unlink (_PATH_NSCDSOCKET);
276 /* Make sure we do not get recursive calls. */
277 __nss_disable_nscd ();
279 /* Init databases. */
280 nscd_init ();
282 /* Handle incoming requests */
283 start_threads ();
285 return 0;
289 /* Handle program arguments. */
290 static error_t
291 parse_opt (int key, char *arg, struct argp_state *state)
293 switch (key)
295 case 'd':
296 ++debug_level;
297 go_background = 0;
298 break;
300 case 'f':
301 conffile = arg;
302 break;
304 case 'K':
305 if (getuid () != 0)
306 error (4, 0, _("Only root is allowed to use this option!"));
308 int sock = nscd_open_socket ();
309 request_header req;
310 ssize_t nbytes;
312 if (sock == -1)
313 exit (EXIT_FAILURE);
315 req.version = NSCD_VERSION;
316 req.type = SHUTDOWN;
317 req.key_len = 0;
318 nbytes = TEMP_FAILURE_RETRY (write (sock, &req,
319 sizeof (request_header)));
320 close (sock);
321 exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
324 case 'g':
325 get_stats = true;
326 break;
328 case 'i':
329 if (getuid () != 0)
330 error (4, 0, _("Only root is allowed to use this option!"));
331 else
333 int sock = nscd_open_socket ();
335 if (sock == -1)
336 exit (EXIT_FAILURE);
338 request_header req;
339 ssize_t nbytes;
340 struct iovec iov[2];
342 if (strcmp (arg, "passwd") == 0)
343 req.key_len = sizeof "passwd";
344 else if (strcmp (arg, "group") == 0)
345 req.key_len = sizeof "group";
346 else if (strcmp (arg, "hosts") == 0)
347 req.key_len = sizeof "hosts";
348 else
349 return ARGP_ERR_UNKNOWN;
351 req.version = NSCD_VERSION;
352 req.type = INVALIDATE;
354 iov[0].iov_base = &req;
355 iov[0].iov_len = sizeof (req);
356 iov[1].iov_base = arg;
357 iov[1].iov_len = req.key_len;
359 nbytes = TEMP_FAILURE_RETRY (writev (sock, iov, 2));
361 close (sock);
363 exit (nbytes != iov[0].iov_len + iov[1].iov_len
364 ? EXIT_FAILURE : EXIT_SUCCESS);
367 case 't':
368 nthreads = atol (arg);
369 break;
371 case 'S':
372 #if 0
373 if (strcmp (arg, "passwd,yes") == 0)
374 secure_in_use = dbs[pwddb].secure = 1;
375 else if (strcmp (arg, "group,yes") == 0)
376 secure_in_use = dbs[grpdb].secure = 1;
377 else if (strcmp (arg, "hosts,yes") == 0)
378 secure_in_use = dbs[hstdb].secure = 1;
379 #else
380 error (0, 0, _("secure services not implemented anymore"));
381 #endif
382 break;
384 default:
385 return ARGP_ERR_UNKNOWN;
388 return 0;
391 /* Print the version information. */
392 static void
393 print_version (FILE *stream, struct argp_state *state)
395 fprintf (stream, "nscd (GNU %s) %s\n", PACKAGE, VERSION);
396 fprintf (stream, gettext ("\
397 Copyright (C) %s Free Software Foundation, Inc.\n\
398 This is free software; see the source for copying conditions. There is NO\n\
399 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
400 "), "2005");
401 fprintf (stream, gettext ("Written by %s.\n"),
402 "Thorsten Kukuk and Ulrich Drepper");
406 /* Create a socket connected to a name. */
408 nscd_open_socket (void)
410 struct sockaddr_un addr;
411 int sock;
413 sock = socket (PF_UNIX, SOCK_STREAM, 0);
414 if (sock < 0)
415 return -1;
417 addr.sun_family = AF_UNIX;
418 assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
419 strcpy (addr.sun_path, _PATH_NSCDSOCKET);
420 if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
422 close (sock);
423 return -1;
426 return sock;
430 /* Cleanup. */
431 void
432 termination_handler (int signum)
434 close_sockets ();
436 /* Clean up the file created by 'bind'. */
437 unlink (_PATH_NSCDSOCKET);
439 /* Clean up pid file. */
440 unlink (_PATH_NSCDPID);
442 // XXX Terminate threads.
444 /* Synchronize memory. */
445 for (int cnt = 0; cnt < lastdb; ++cnt)
447 if (!dbs[cnt].enabled)
448 continue;
450 /* Make sure nobody keeps using the database. */
451 dbs[cnt].head->timestamp = 0;
453 if (dbs[cnt].persistent)
454 // XXX async OK?
455 msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
458 /* Shutdown the SELinux AVC. */
459 if (selinux_enabled)
460 nscd_avc_destroy ();
462 _exit (EXIT_SUCCESS);
465 /* Returns 1 if the process in pid file FILE is running, 0 if not. */
466 static int
467 check_pid (const char *file)
469 FILE *fp;
471 fp = fopen (file, "r");
472 if (fp)
474 pid_t pid;
475 int n;
477 n = fscanf (fp, "%d", &pid);
478 fclose (fp);
480 /* If we cannot parse the file default to assuming nscd runs.
481 If the PID is alive, assume it is running. That all unless
482 the PID is the same as the current process' since tha latter
483 can mean we re-exec. */
484 if ((n != 1 || kill (pid, 0) == 0) && pid != getpid ())
485 return 1;
488 return 0;
491 /* Write the current process id to the file FILE.
492 Returns 0 if successful, -1 if not. */
493 static int
494 write_pid (const char *file)
496 FILE *fp;
498 fp = fopen (file, "w");
499 if (fp == NULL)
500 return -1;
502 fprintf (fp, "%d\n", getpid ());
503 if (fflush (fp) || ferror (fp))
504 return -1;
506 fclose (fp);
508 return 0;