Changes and additions migrated from cvs.devel.redhat.com:/cvs/devel/glibc to fedora...
[glibc.git] / nscd / nscd.c
blob7f8f58fde6fed2d9aeccfbb45457ab9265ef3a3f
1 /* Copyright (c) 1998-2003, 2004 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 static int check_pid (const char *file);
83 static int write_pid (const char *file);
85 /* Name and version of program. */
86 static void print_version (FILE *stream, struct argp_state *state);
87 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
89 /* Definitions of arguments for argp functions. */
90 static const struct argp_option options[] =
92 { "config-file", 'f', N_("NAME"), 0,
93 N_("Read configuration data from NAME") },
94 { "debug", 'd', NULL, 0,
95 N_("Do not fork and display messages on the current tty") },
96 { "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") },
97 { "shutdown", 'K', NULL, 0, N_("Shut the server down") },
98 { "statistic", 'g', NULL, 0, N_("Print current configuration statistic") },
99 { "invalidate", 'i', N_("TABLE"), 0,
100 N_("Invalidate the specified cache") },
101 { "secure", 'S', N_("TABLE,yes"), OPTION_HIDDEN,
102 N_("Use separate cache for each user")},
103 { NULL, 0, NULL, 0, NULL }
106 /* Short description of program. */
107 static const char doc[] = N_("Name Service Cache Daemon.");
109 /* Prototype for option handler. */
110 static error_t parse_opt (int key, char *arg, struct argp_state *state);
112 /* Data structure to communicate with argp functions. */
113 static struct argp argp =
115 options, parse_opt, NULL, doc,
118 /* The SIGHUP handler is extern to this file */
119 extern void sighup_handler(int signum);
121 /* True if only statistics are requested. */
122 static bool get_stats;
124 #ifdef atomic_init_nscd
125 atomic_init_nscd
126 #endif
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 (EXIT_FAILURE);
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 chdir ("/");
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);
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 (SIGHUP, sighup_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 (EXIT_FAILURE, 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 (write (sock, &req,
315 sizeof (request_header)));
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 (EXIT_FAILURE, 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 ssize_t nbytes;
336 struct iovec iov[2];
338 if (strcmp (arg, "passwd") == 0)
339 req.key_len = sizeof "passwd";
340 else if (strcmp (arg, "group") == 0)
341 req.key_len = sizeof "group";
342 else if (strcmp (arg, "hosts") == 0)
343 req.key_len = sizeof "hosts";
344 else
345 return ARGP_ERR_UNKNOWN;
347 req.version = NSCD_VERSION;
348 req.type = INVALIDATE;
350 iov[0].iov_base = &req;
351 iov[0].iov_len = sizeof (req);
352 iov[1].iov_base = arg;
353 iov[1].iov_len = req.key_len;
355 nbytes = TEMP_FAILURE_RETRY (writev (sock, iov, 2));
357 close (sock);
359 exit (nbytes != iov[0].iov_len + iov[1].iov_len
360 ? EXIT_FAILURE : EXIT_SUCCESS);
363 case 't':
364 nthreads = atol (arg);
365 break;
367 case 'S':
368 #if 0
369 if (strcmp (arg, "passwd,yes") == 0)
370 secure_in_use = dbs[pwddb].secure = 1;
371 else if (strcmp (arg, "group,yes") == 0)
372 secure_in_use = dbs[grpdb].secure = 1;
373 else if (strcmp (arg, "hosts,yes") == 0)
374 secure_in_use = dbs[hstdb].secure = 1;
375 #else
376 error (0, 0, _("secure services not implemented anymore"));
377 #endif
378 break;
380 default:
381 return ARGP_ERR_UNKNOWN;
384 return 0;
387 /* Print the version information. */
388 static void
389 print_version (FILE *stream, struct argp_state *state)
391 fprintf (stream, "nscd (GNU %s) %s\n", PACKAGE, VERSION);
392 fprintf (stream, gettext ("\
393 Copyright (C) %s Free Software Foundation, Inc.\n\
394 This is free software; see the source for copying conditions. There is NO\n\
395 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
396 "), "2004");
397 fprintf (stream, gettext ("Written by %s.\n"),
398 "Thorsten Kukuk and Ulrich Drepper");
402 /* Create a socket connected to a name. */
404 nscd_open_socket (void)
406 struct sockaddr_un addr;
407 int sock;
409 sock = socket (PF_UNIX, SOCK_STREAM, 0);
410 if (sock < 0)
411 return -1;
413 addr.sun_family = AF_UNIX;
414 assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
415 strcpy (addr.sun_path, _PATH_NSCDSOCKET);
416 if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
418 close (sock);
419 return -1;
422 return sock;
425 /* Cleanup. */
426 void
427 termination_handler (int signum)
429 close_sockets ();
431 /* Clean up the file created by 'bind'. */
432 unlink (_PATH_NSCDSOCKET);
434 /* Clean up pid file. */
435 unlink (_PATH_NSCDPID);
437 // XXX Terminate threads.
439 /* Synchronize memory. */
440 for (int cnt = 0; cnt < lastdb; ++cnt)
441 if (dbs[cnt].persistent)
442 // XXX async OK?
443 msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
445 /* Shutdown the SELinux AVC. */
446 if (selinux_enabled)
447 nscd_avc_destroy ();
449 _exit (EXIT_SUCCESS);
452 /* Returns 1 if the process in pid file FILE is running, 0 if not. */
453 static int
454 check_pid (const char *file)
456 FILE *fp;
458 fp = fopen (file, "r");
459 if (fp)
461 pid_t pid;
462 int n;
464 n = fscanf (fp, "%d", &pid);
465 fclose (fp);
467 if (n != 1 || kill (pid, 0) == 0)
468 return 1;
471 return 0;
474 /* Write the current process id to the file FILE.
475 Returns 0 if successful, -1 if not. */
476 static int
477 write_pid (const char *file)
479 FILE *fp;
481 fp = fopen (file, "w");
482 if (fp == NULL)
483 return -1;
485 fprintf (fp, "%d\n", getpid ());
486 if (fflush (fp) || ferror (fp))
487 return -1;
489 fclose (fp);
491 return 0;
495 /* This is an ugly hack which prevents getaddrinfo from being dragged
496 into nscd. There currently is no special getaddrinfo version for
497 use in nscd. In case it should be necessary such a version must be
498 created and this dummy version should be removed. */
499 void
500 getaddrinfo (void)
502 abort ();