]> git.ipfire.org Git - thirdparty/glibc.git/blob - nscd/nscd.c
Update.
[thirdparty/glibc.git] / nscd / nscd.c
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.
4
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.
9
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.
14
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. */
19
20 /* nscd - Name Service Cache Daemon. Caches passwd, group, and hosts. */
21
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>
44
45 #include "dbg_log.h"
46 #include "nscd.h"
47 #include "selinux.h"
48 #include "../nss/nsswitch.h"
49 #include <device-nrs.h>
50
51 /* Get libc version number. */
52 #include <version.h>
53
54 #define PACKAGE _libc_intl_domainname
55
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". */
60
61 typedef struct
62 {
63 int num_active;
64 pthread_cond_t thread_exit_cv;
65 pthread_mutex_t mutex;
66 } thread_info_t;
67
68 thread_info_t thread_info;
69
70 int do_shutdown;
71 int disabled_passwd;
72 int disabled_group;
73 int go_background = 1;
74
75 int secure_in_use;
76 static const char *conffile = _PATH_NSCDCONF;
77
78 time_t start_time;
79
80 uintptr_t pagesize_m1;
81
82 static int check_pid (const char *file);
83 static int write_pid (const char *file);
84
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;
88
89 /* Definitions of arguments for argp functions. */
90 static const struct argp_option options[] =
91 {
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"), 0, N_("Use separate cache for each user")},
102 { NULL, 0, NULL, 0, NULL }
103 };
104
105 /* Short description of program. */
106 static const char doc[] = N_("Name Service Cache Daemon.");
107
108 /* Prototype for option handler. */
109 static error_t parse_opt (int key, char *arg, struct argp_state *state);
110
111 /* Data structure to communicate with argp functions. */
112 static struct argp argp =
113 {
114 options, parse_opt, NULL, doc,
115 };
116
117 /* True if only statistics are requested. */
118 static bool get_stats;
119
120 int
121 main (int argc, char **argv)
122 {
123 int remaining;
124
125 /* Set locale via LC_ALL. */
126 setlocale (LC_ALL, "");
127 /* Set the text message domain. */
128 textdomain (PACKAGE);
129
130 /* Determine if the kernel has SELinux support. */
131 nscd_selinux_enabled (&selinux_enabled);
132
133 /* Parse and process arguments. */
134 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
135
136 if (remaining != argc)
137 {
138 error (0, 0, gettext ("wrong number of arguments"));
139 argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
140 exit (EXIT_FAILURE);
141 }
142
143 /* Read the configuration file. */
144 if (nscd_parse_file (conffile, dbs) != 0)
145 {
146 /* We couldn't read the configuration file. We don't start the
147 server. */
148 dbg_log (_("cannot read configuration file; this is fatal"));
149 exit (1);
150 }
151
152 /* Do we only get statistics? */
153 if (get_stats)
154 /* Does not return. */
155 receive_print_stats ();
156
157 /* Check if we are already running. */
158 if (check_pid (_PATH_NSCDPID))
159 error (EXIT_FAILURE, 0, _("already running"));
160
161 /* Remember when we started. */
162 start_time = time (NULL);
163
164 /* Determine page size. */
165 pagesize_m1 = getpagesize () - 1;
166
167 /* Behave like a daemon. */
168 if (go_background)
169 {
170 int i;
171
172 pid_t pid = fork ();
173 if (pid == -1)
174 error (EXIT_FAILURE, errno, _("cannot fork"));
175 if (pid != 0)
176 exit (0);
177
178 int nullfd = open (_PATH_DEVNULL, O_RDWR);
179 if (nullfd != -1)
180 {
181 struct stat64 st;
182
183 if (fstat64 (nullfd, &st) == 0 && S_ISCHR (st.st_mode) != 0
184 #if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
185 && st.st_rdev == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR)
186 #endif
187 )
188 {
189 /* It is the /dev/null special device alright. */
190 (void) dup2 (nullfd, STDIN_FILENO);
191 (void) dup2 (nullfd, STDOUT_FILENO);
192 (void) dup2 (nullfd, STDERR_FILENO);
193
194 if (nullfd > 2)
195 close (nullfd);
196 }
197 else
198 {
199 /* Ugh, somebody is trying to play a trick on us. */
200 close (nullfd);
201 nullfd = -1;
202 }
203 }
204 int min_close_fd = nullfd == -1 ? 0 : STDERR_FILENO + 1;
205
206 DIR *d = opendir ("/proc/self/fd");
207 if (d != NULL)
208 {
209 struct dirent64 *dirent;
210 int dfdn = dirfd (d);
211
212 while ((dirent = readdir64 (d)) != NULL)
213 {
214 char *endp;
215 long int fdn = strtol (dirent->d_name, &endp, 10);
216
217 if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd)
218 close ((int) fdn);
219 }
220
221 closedir (d);
222 }
223 else
224 for (i = min_close_fd; i < getdtablesize (); i++)
225 close (i);
226
227 pid = fork ();
228 if (pid == -1)
229 error (EXIT_FAILURE, errno, _("cannot fork"));
230 if (pid != 0)
231 exit (0);
232
233 setsid ();
234
235 chdir ("/");
236
237 openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
238
239 if (write_pid (_PATH_NSCDPID) < 0)
240 dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
241
242 if (!init_logfile ())
243 dbg_log (_("Could not create log file"));
244
245 /* Ignore job control signals. */
246 signal (SIGTTOU, SIG_IGN);
247 signal (SIGTTIN, SIG_IGN);
248 signal (SIGTSTP, SIG_IGN);
249 }
250
251 /* Start the SELinux AVC. */
252 if (selinux_enabled)
253 nscd_avc_init ();
254
255 signal (SIGINT, termination_handler);
256 signal (SIGQUIT, termination_handler);
257 signal (SIGTERM, termination_handler);
258 signal (SIGPIPE, SIG_IGN);
259
260 /* Cleanup files created by a previous 'bind'. */
261 unlink (_PATH_NSCDSOCKET);
262
263 /* Make sure we do not get recursive calls. */
264 __nss_disable_nscd ();
265
266 /* Init databases. */
267 nscd_init ();
268
269 /* Handle incoming requests */
270 start_threads ();
271
272 return 0;
273 }
274
275
276 /* Handle program arguments. */
277 static error_t
278 parse_opt (int key, char *arg, struct argp_state *state)
279 {
280 switch (key)
281 {
282 case 'd':
283 ++debug_level;
284 go_background = 0;
285 break;
286
287 case 'f':
288 conffile = arg;
289 break;
290
291 case 'K':
292 if (getuid () != 0)
293 error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!"));
294 {
295 int sock = nscd_open_socket ();
296 request_header req;
297 ssize_t nbytes;
298
299 if (sock == -1)
300 exit (EXIT_FAILURE);
301
302 req.version = NSCD_VERSION;
303 req.type = SHUTDOWN;
304 req.key_len = 0;
305 nbytes = TEMP_FAILURE_RETRY (write (sock, &req,
306 sizeof (request_header)));
307 close (sock);
308 exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
309 }
310
311 case 'g':
312 get_stats = true;
313 break;
314
315 case 'i':
316 if (getuid () != 0)
317 error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!"));
318 else
319 {
320 int sock = nscd_open_socket ();
321
322 if (sock == -1)
323 exit (EXIT_FAILURE);
324
325 request_header req;
326 ssize_t nbytes;
327 struct iovec iov[2];
328
329 if (strcmp (arg, "passwd") == 0)
330 req.key_len = sizeof "passwd";
331 else if (strcmp (arg, "group") == 0)
332 req.key_len = sizeof "group";
333 else if (strcmp (arg, "hosts") == 0)
334 req.key_len = sizeof "hosts";
335 else
336 return ARGP_ERR_UNKNOWN;
337
338 req.version = NSCD_VERSION;
339 req.type = INVALIDATE;
340
341 iov[0].iov_base = &req;
342 iov[0].iov_len = sizeof (req);
343 iov[1].iov_base = arg;
344 iov[1].iov_len = req.key_len;
345
346 nbytes = TEMP_FAILURE_RETRY (writev (sock, iov, 2));
347
348 close (sock);
349
350 exit (nbytes != iov[0].iov_len + iov[1].iov_len
351 ? EXIT_FAILURE : EXIT_SUCCESS);
352 }
353
354 case 't':
355 nthreads = atol (arg);
356 break;
357
358 case 'S':
359 if (strcmp (arg, "passwd,yes") == 0)
360 secure_in_use = dbs[pwddb].secure = 1;
361 else if (strcmp (arg, "group,yes") == 0)
362 secure_in_use = dbs[grpdb].secure = 1;
363 else if (strcmp (arg, "hosts,yes") == 0)
364 secure_in_use = dbs[hstdb].secure = 1;
365 break;
366
367 default:
368 return ARGP_ERR_UNKNOWN;
369 }
370
371 return 0;
372 }
373
374 /* Print the version information. */
375 static void
376 print_version (FILE *stream, struct argp_state *state)
377 {
378 fprintf (stream, "nscd (GNU %s) %s\n", PACKAGE, VERSION);
379 fprintf (stream, gettext ("\
380 Copyright (C) %s Free Software Foundation, Inc.\n\
381 This is free software; see the source for copying conditions. There is NO\n\
382 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
383 "), "2004");
384 fprintf (stream, gettext ("Written by %s.\n"),
385 "Thorsten Kukuk and Ulrich Drepper");
386 }
387
388
389 /* Create a socket connected to a name. */
390 int
391 nscd_open_socket (void)
392 {
393 struct sockaddr_un addr;
394 int sock;
395
396 sock = socket (PF_UNIX, SOCK_STREAM, 0);
397 if (sock < 0)
398 return -1;
399
400 addr.sun_family = AF_UNIX;
401 assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
402 strcpy (addr.sun_path, _PATH_NSCDSOCKET);
403 if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
404 {
405 close (sock);
406 return -1;
407 }
408
409 return sock;
410 }
411
412 /* Cleanup. */
413 void
414 termination_handler (int signum)
415 {
416 close_sockets ();
417
418 /* Clean up the file created by 'bind'. */
419 unlink (_PATH_NSCDSOCKET);
420
421 /* Clean up pid file. */
422 unlink (_PATH_NSCDPID);
423
424 // XXX Terminate threads.
425
426 /* Synchronize memory. */
427 for (int cnt = 0; cnt < lastdb; ++cnt)
428 if (dbs[cnt].persistent)
429 // XXX async OK?
430 msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
431
432 /* Shutdown the SELinux AVC. */
433 if (selinux_enabled)
434 nscd_avc_destroy ();
435
436 _exit (EXIT_SUCCESS);
437 }
438
439 /* Returns 1 if the process in pid file FILE is running, 0 if not. */
440 static int
441 check_pid (const char *file)
442 {
443 FILE *fp;
444
445 fp = fopen (file, "r");
446 if (fp)
447 {
448 pid_t pid;
449 int n;
450
451 n = fscanf (fp, "%d", &pid);
452 fclose (fp);
453
454 if (n != 1 || kill (pid, 0) == 0)
455 return 1;
456 }
457
458 return 0;
459 }
460
461 /* Write the current process id to the file FILE.
462 Returns 0 if successful, -1 if not. */
463 static int
464 write_pid (const char *file)
465 {
466 FILE *fp;
467
468 fp = fopen (file, "w");
469 if (fp == NULL)
470 return -1;
471
472 fprintf (fp, "%d\n", getpid ());
473 if (fflush (fp) || ferror (fp))
474 return -1;
475
476 fclose (fp);
477
478 return 0;
479 }
480
481
482 /* This is an ugly hack which prevents getaddrinfo from being dragged
483 into nscd. There currently is no special getaddrinfo version for
484 use in nscd. In case it should be necessary such a version must be
485 created and this dummy version should be removed. */
486 void
487 getaddrinfo (void)
488 {
489 abort ();
490 }