]> git.ipfire.org Git - thirdparty/glibc.git/blob - nscd/nscd.c
2009-01-10 Roland McGrath <roland@redhat.com>
[thirdparty/glibc.git] / nscd / nscd.c
1 /* Copyright (c) 1998-2008, 2009 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 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; version 2 of the License, or
8 (at your option) any later version.
9
10 This program 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
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18
19 /* nscd - Name Service Cache Daemon. Caches passwd, group, and hosts. */
20
21 #include <argp.h>
22 #include <assert.h>
23 #include <dirent.h>
24 #include <errno.h>
25 #include <error.h>
26 #include <fcntl.h>
27 #include <libintl.h>
28 #include <locale.h>
29 #include <paths.h>
30 #include <pthread.h>
31 #include <signal.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <syslog.h>
37 #include <unistd.h>
38 #include <sys/mman.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <sys/uio.h>
42 #include <sys/un.h>
43
44 #include "dbg_log.h"
45 #include "nscd.h"
46 #include "selinux.h"
47 #include "../nss/nsswitch.h"
48 #include <device-nrs.h>
49
50 /* Get libc version number. */
51 #include <version.h>
52
53 #define PACKAGE _libc_intl_domainname
54
55 /* Structure used by main() thread to keep track of the number of
56 active threads. Used to limit how many threads it will create
57 and under a shutdown condition to wait till all in-progress
58 requests have finished before "turning off the lights". */
59
60 typedef struct
61 {
62 int num_active;
63 pthread_cond_t thread_exit_cv;
64 pthread_mutex_t mutex;
65 } thread_info_t;
66
67 thread_info_t thread_info;
68
69 int do_shutdown;
70 int disabled_passwd;
71 int disabled_group;
72 int go_background = 1;
73
74 static const char *conffile = _PATH_NSCDCONF;
75
76 time_t start_time;
77
78 uintptr_t pagesize_m1;
79
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;
86
87 static int check_pid (const char *file);
88 static int write_pid (const char *file);
89
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;
93
94 /* Definitions of arguments for argp functions. */
95 static const struct argp_option options[] =
96 {
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 { "statistics", 'g', NULL, 0, N_("Print current configuration statistics") },
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 }
109 };
110
111 /* Short description of program. */
112 static const char doc[] = N_("Name Service Cache Daemon.");
113
114 /* Prototype for option handler. */
115 static error_t parse_opt (int key, char *arg, struct argp_state *state);
116
117 /* Data structure to communicate with argp functions. */
118 static struct argp argp =
119 {
120 options, parse_opt, NULL, doc,
121 };
122
123 /* True if only statistics are requested. */
124 static bool get_stats;
125
126 int
127 main (int argc, char **argv)
128 {
129 int remaining;
130
131 /* Set locale via LC_ALL. */
132 setlocale (LC_ALL, "");
133 /* Set the text message domain. */
134 textdomain (PACKAGE);
135
136 /* Determine if the kernel has SELinux support. */
137 nscd_selinux_enabled (&selinux_enabled);
138
139 /* Parse and process arguments. */
140 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
141
142 if (remaining != argc)
143 {
144 error (0, 0, gettext ("wrong number of arguments"));
145 argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
146 exit (1);
147 }
148
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"));
155
156 /* Do we only get statistics? */
157 if (get_stats)
158 /* Does not return. */
159 receive_print_stats ();
160
161 /* Check if we are already running. */
162 if (check_pid (_PATH_NSCDPID))
163 error (EXIT_FAILURE, 0, _("already running"));
164
165 /* Remember when we started. */
166 start_time = time (NULL);
167
168 /* Determine page size. */
169 pagesize_m1 = getpagesize () - 1;
170
171 /* Behave like a daemon. */
172 if (go_background)
173 {
174 int i;
175
176 pid_t pid = fork ();
177 if (pid == -1)
178 error (EXIT_FAILURE, errno, _("cannot fork"));
179 if (pid != 0)
180 exit (0);
181
182 int nullfd = open (_PATH_DEVNULL, O_RDWR);
183 if (nullfd != -1)
184 {
185 struct stat64 st;
186
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
191 )
192 {
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);
197
198 if (nullfd > 2)
199 close (nullfd);
200 }
201 else
202 {
203 /* Ugh, somebody is trying to play a trick on us. */
204 close (nullfd);
205 nullfd = -1;
206 }
207 }
208 int min_close_fd = nullfd == -1 ? 0 : STDERR_FILENO + 1;
209
210 DIR *d = opendir ("/proc/self/fd");
211 if (d != NULL)
212 {
213 struct dirent64 *dirent;
214 int dfdn = dirfd (d);
215
216 while ((dirent = readdir64 (d)) != NULL)
217 {
218 char *endp;
219 long int fdn = strtol (dirent->d_name, &endp, 10);
220
221 if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd)
222 close ((int) fdn);
223 }
224
225 closedir (d);
226 }
227 else
228 for (i = min_close_fd; i < getdtablesize (); i++)
229 close (i);
230
231 pid = fork ();
232 if (pid == -1)
233 error (EXIT_FAILURE, errno, _("cannot fork"));
234 if (pid != 0)
235 exit (0);
236
237 setsid ();
238
239 if (chdir ("/") != 0)
240 error (EXIT_FAILURE, errno,
241 _("cannot change current working directory to \"/\""));
242
243 openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
244
245 if (write_pid (_PATH_NSCDPID) < 0)
246 dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
247
248 if (!init_logfile ())
249 dbg_log (_("Could not create log file"));
250
251 /* Ignore job control signals. */
252 signal (SIGTTOU, SIG_IGN);
253 signal (SIGTTIN, SIG_IGN);
254 signal (SIGTSTP, SIG_IGN);
255 }
256 else
257 /* In foreground mode we are not paranoid. */
258 paranoia = 0;
259
260 /* Start the SELinux AVC. */
261 if (selinux_enabled)
262 nscd_avc_init ();
263
264 signal (SIGINT, termination_handler);
265 signal (SIGQUIT, termination_handler);
266 signal (SIGTERM, termination_handler);
267 signal (SIGPIPE, SIG_IGN);
268
269 /* Cleanup files created by a previous 'bind'. */
270 unlink (_PATH_NSCDSOCKET);
271
272 /* Make sure we do not get recursive calls. */
273 __nss_disable_nscd ();
274
275 /* Init databases. */
276 nscd_init ();
277
278 /* Handle incoming requests */
279 start_threads ();
280
281 return 0;
282 }
283
284
285 /* Handle program arguments. */
286 static error_t
287 parse_opt (int key, char *arg, struct argp_state *state)
288 {
289 switch (key)
290 {
291 case 'd':
292 ++debug_level;
293 go_background = 0;
294 break;
295
296 case 'f':
297 conffile = arg;
298 break;
299
300 case 'K':
301 if (getuid () != 0)
302 error (4, 0, _("Only root is allowed to use this option!"));
303 {
304 int sock = nscd_open_socket ();
305
306 if (sock == -1)
307 exit (EXIT_FAILURE);
308
309 request_header req;
310 req.version = NSCD_VERSION;
311 req.type = SHUTDOWN;
312 req.key_len = 0;
313
314 ssize_t 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);
319 }
320
321 case 'g':
322 get_stats = true;
323 break;
324
325 case 'i':
326 if (getuid () != 0)
327 error (4, 0, _("Only root is allowed to use this option!"));
328 else
329 {
330 int sock = nscd_open_socket ();
331
332 if (sock == -1)
333 exit (EXIT_FAILURE);
334
335 dbtype cnt;
336 for (cnt = pwddb; cnt < lastdb; ++cnt)
337 if (strcmp (arg, dbnames[cnt]) == 0)
338 break;
339
340 if (cnt == lastdb)
341 {
342 argp_error (state, _("'%s' is not a known database"), arg);
343 return EINVAL;
344 }
345
346 size_t arg_len = strlen (arg) + 1;
347 struct
348 {
349 request_header req;
350 char arg[arg_len];
351 } reqdata;
352
353 reqdata.req.key_len = strlen (arg) + 1;
354 reqdata.req.version = NSCD_VERSION;
355 reqdata.req.type = INVALIDATE;
356 memcpy (reqdata.arg, arg, arg_len);
357
358 ssize_t nbytes = TEMP_FAILURE_RETRY (send (sock, &reqdata,
359 sizeof (request_header)
360 + arg_len,
361 MSG_NOSIGNAL));
362
363 if (nbytes != sizeof (request_header) + arg_len)
364 {
365 int err = errno;
366 close (sock);
367 error (EXIT_FAILURE, err, _("write incomplete"));
368 }
369
370 /* Wait for ack. Older nscd just closed the socket when
371 prune_cache finished, silently ignore that. */
372 int32_t resp = 0;
373 nbytes = TEMP_FAILURE_RETRY (read (sock, &resp, sizeof (resp)));
374 if (nbytes != 0 && nbytes != sizeof (resp))
375 {
376 int err = errno;
377 close (sock);
378 error (EXIT_FAILURE, err, _("cannot read invalidate ACK"));
379 }
380
381 close (sock);
382
383 if (resp != 0)
384 error (EXIT_FAILURE, resp, _("invalidation failed"));
385
386 exit (0);
387 }
388
389 case 't':
390 nthreads = atol (arg);
391 break;
392
393 case 'S':
394 error (0, 0, _("secure services not implemented anymore"));
395 break;
396
397 default:
398 return ARGP_ERR_UNKNOWN;
399 }
400
401 return 0;
402 }
403
404 /* Print the version information. */
405 static void
406 print_version (FILE *stream, struct argp_state *state)
407 {
408 fprintf (stream, "nscd (GNU %s) %s\n", PACKAGE, VERSION);
409 fprintf (stream, gettext ("\
410 Copyright (C) %s Free Software Foundation, Inc.\n\
411 This is free software; see the source for copying conditions. There is NO\n\
412 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
413 "), "2009");
414 fprintf (stream, gettext ("Written by %s.\n"),
415 "Thorsten Kukuk and Ulrich Drepper");
416 }
417
418
419 /* Create a socket connected to a name. */
420 int
421 nscd_open_socket (void)
422 {
423 struct sockaddr_un addr;
424 int sock;
425
426 sock = socket (PF_UNIX, SOCK_STREAM, 0);
427 if (sock < 0)
428 return -1;
429
430 addr.sun_family = AF_UNIX;
431 assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
432 strcpy (addr.sun_path, _PATH_NSCDSOCKET);
433 if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
434 {
435 close (sock);
436 return -1;
437 }
438
439 return sock;
440 }
441
442
443 /* Cleanup. */
444 void
445 termination_handler (int signum)
446 {
447 close_sockets ();
448
449 /* Clean up the file created by 'bind'. */
450 unlink (_PATH_NSCDSOCKET);
451
452 /* Clean up pid file. */
453 unlink (_PATH_NSCDPID);
454
455 // XXX Terminate threads.
456
457 /* Synchronize memory. */
458 for (int cnt = 0; cnt < lastdb; ++cnt)
459 {
460 if (!dbs[cnt].enabled)
461 continue;
462
463 /* Make sure nobody keeps using the database. */
464 dbs[cnt].head->timestamp = 0;
465
466 if (dbs[cnt].persistent)
467 // XXX async OK?
468 msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
469 }
470
471 /* Shutdown the SELinux AVC. */
472 if (selinux_enabled)
473 nscd_avc_destroy ();
474
475 _exit (EXIT_SUCCESS);
476 }
477
478 /* Returns 1 if the process in pid file FILE is running, 0 if not. */
479 static int
480 check_pid (const char *file)
481 {
482 FILE *fp;
483
484 fp = fopen (file, "r");
485 if (fp)
486 {
487 pid_t pid;
488 int n;
489
490 n = fscanf (fp, "%d", &pid);
491 fclose (fp);
492
493 /* If we cannot parse the file default to assuming nscd runs.
494 If the PID is alive, assume it is running. That all unless
495 the PID is the same as the current process' since tha latter
496 can mean we re-exec. */
497 if ((n != 1 || kill (pid, 0) == 0) && pid != getpid ())
498 return 1;
499 }
500
501 return 0;
502 }
503
504 /* Write the current process id to the file FILE.
505 Returns 0 if successful, -1 if not. */
506 static int
507 write_pid (const char *file)
508 {
509 FILE *fp;
510
511 fp = fopen (file, "w");
512 if (fp == NULL)
513 return -1;
514
515 fprintf (fp, "%d\n", getpid ());
516
517 int result = fflush (fp) || ferror (fp) ? -1 : 0;
518
519 fclose (fp);
520
521 return result;
522 }