]> git.ipfire.org Git - thirdparty/glibc.git/blame - nscd/nscd.c
Fix Wundef warning for SEPARATE_KEY
[thirdparty/glibc.git] / nscd / nscd.c
CommitLineData
d4697bc9 1/* Copyright (c) 1998-2014 Free Software Foundation, Inc.
d67281a7 2 This file is part of the GNU C Library.
a1c542bf 3 Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
d67281a7 4
43bc8ac6 5 This program is free software; you can redistribute it and/or modify
2e2efe65
RM
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.
d67281a7 9
43bc8ac6 10 This program is distributed in the hope that it will be useful,
d67281a7 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
43bc8ac6
UD
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
d67281a7 14
43bc8ac6 15 You should have received a copy of the GNU General Public License
59ba27a6 16 along with this program; if not, see <http://www.gnu.org/licenses/>. */
d67281a7 17
67479a70 18/* nscd - Name Service Cache Daemon. Caches passwd, group, and hosts. */
d67281a7 19
4d06461a 20#include <argp.h>
67479a70 21#include <assert.h>
d4397629 22#include <dirent.h>
d67281a7 23#include <errno.h>
4d06461a 24#include <error.h>
d4397629 25#include <fcntl.h>
d67281a7
UD
26#include <libintl.h>
27#include <locale.h>
d4397629 28#include <paths.h>
d67281a7 29#include <pthread.h>
d67281a7 30#include <signal.h>
a12ce44f 31#include <stdbool.h>
d67281a7
UD
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <syslog.h>
4c223a7c 36#include <unistd.h>
a95a08b4 37#include <sys/mman.h>
d67281a7 38#include <sys/socket.h>
d4397629 39#include <sys/stat.h>
ead07d01 40#include <sys/uio.h>
d67281a7 41#include <sys/un.h>
532a6035
SP
42#include <sys/wait.h>
43#include <stdarg.h>
d67281a7
UD
44
45#include "dbg_log.h"
46#include "nscd.h"
74a30a58 47#include "selinux.h"
ef4d5b32 48#include "../nss/nsswitch.h"
d4397629 49#include <device-nrs.h>
319b9ad4
UD
50#ifdef HAVE_INOTIFY
51# include <sys/inotify.h>
52#endif
9bc6103d 53#include <kernel-features.h>
d67281a7
UD
54
55/* Get libc version number. */
56#include <version.h>
57
58#define PACKAGE _libc_intl_domainname
59
a1c542bf
UD
60int do_shutdown;
61int disabled_passwd;
62int disabled_group;
bb90b80b 63
edfe0dbe 64typedef enum
bb90b80b 65{
edfe0dbe
AJ
66 /* Running in background as daemon. */
67 RUN_DAEMONIZE,
bb90b80b
AJ
68 /* Running in foreground but otherwise behave like a daemon,
69 i.e., detach from terminal and use syslog. This allows
70 better integration with services like systemd. */
71 RUN_FOREGROUND,
bb90b80b
AJ
72 /* Run in foreground in debug mode. */
73 RUN_DEBUG
edfe0dbe
AJ
74} run_modes;
75
76static run_modes run_mode = RUN_DAEMONIZE;
a1c542bf 77
67479a70 78static const char *conffile = _PATH_NSCDCONF;
d67281a7 79
c86e6aec
UD
80time_t start_time;
81
3418007e
UD
82uintptr_t pagesize_m1;
83
4401d759
UD
84int paranoia;
85time_t restart_time;
86time_t restart_interval = RESTART_INTERVAL;
87const char *oldcwd;
88uid_t old_uid;
89gid_t old_gid;
90
d67281a7
UD
91static int check_pid (const char *file);
92static int write_pid (const char *file);
532a6035 93static int monitor_child (int fd);
d67281a7 94
4d06461a
UD
95/* Name and version of program. */
96static void print_version (FILE *stream, struct argp_state *state);
97void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
98
cbbcaf23
UD
99/* Function to print some extra text in the help message. */
100static char *more_help (int key, const char *text, void *input);
101
4d06461a
UD
102/* Definitions of arguments for argp functions. */
103static const struct argp_option options[] =
104{
105 { "config-file", 'f', N_("NAME"), 0,
106 N_("Read configuration data from NAME") },
107 { "debug", 'd', NULL, 0,
108 N_("Do not fork and display messages on the current tty") },
bb90b80b 109 { "foreground", 'F', NULL, 0,
edfe0dbe 110 N_("Do not fork, but otherwise behave like a daemon") },
67479a70 111 { "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") },
4d06461a 112 { "shutdown", 'K', NULL, 0, N_("Shut the server down") },
1d20f7f8 113 { "statistics", 'g', NULL, 0, N_("Print current configuration statistics") },
756409c4
UD
114 { "invalidate", 'i', N_("TABLE"), 0,
115 N_("Invalidate the specified cache") },
3c12b91a
UD
116 { "secure", 'S', N_("TABLE,yes"), OPTION_HIDDEN,
117 N_("Use separate cache for each user")},
4d06461a
UD
118 { NULL, 0, NULL, 0, NULL }
119};
120
121/* Short description of program. */
67a96999 122static const char doc[] = N_("Name Service Cache Daemon.");
4d06461a
UD
123
124/* Prototype for option handler. */
d8cf93f4 125static error_t parse_opt (int key, char *arg, struct argp_state *state);
4d06461a
UD
126
127/* Data structure to communicate with argp functions. */
128static struct argp argp =
129{
cbbcaf23 130 options, parse_opt, NULL, doc, NULL, more_help
4d06461a
UD
131};
132
a12ce44f
UD
133/* True if only statistics are requested. */
134static bool get_stats;
532a6035 135static int parent_fd = -1;
a12ce44f 136
d67281a7
UD
137int
138main (int argc, char **argv)
139{
4d06461a 140 int remaining;
d67281a7
UD
141
142 /* Set locale via LC_ALL. */
143 setlocale (LC_ALL, "");
144 /* Set the text message domain. */
145 textdomain (PACKAGE);
146
74a30a58
UD
147 /* Determine if the kernel has SELinux support. */
148 nscd_selinux_enabled (&selinux_enabled);
149
4d06461a
UD
150 /* Parse and process arguments. */
151 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
152
14e9dd67 153 if (remaining != argc)
d67281a7 154 {
4d06461a
UD
155 error (0, 0, gettext ("wrong number of arguments"));
156 argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
64d64de6 157 exit (1);
d67281a7
UD
158 }
159
a12ce44f
UD
160 /* Read the configuration file. */
161 if (nscd_parse_file (conffile, dbs) != 0)
4fb5ab74
UD
162 /* We couldn't read the configuration file. We don't start the
163 server. */
164 error (EXIT_FAILURE, 0,
165 _("failure while reading configuration file; this is fatal"));
a12ce44f
UD
166
167 /* Do we only get statistics? */
168 if (get_stats)
169 /* Does not return. */
170 receive_print_stats ();
171
d67281a7
UD
172 /* Check if we are already running. */
173 if (check_pid (_PATH_NSCDPID))
67479a70 174 error (EXIT_FAILURE, 0, _("already running"));
d67281a7 175
c86e6aec
UD
176 /* Remember when we started. */
177 start_time = time (NULL);
178
3418007e
UD
179 /* Determine page size. */
180 pagesize_m1 = getpagesize () - 1;
181
91d8d69e 182 if (run_mode == RUN_DAEMONIZE || run_mode == RUN_FOREGROUND)
d67281a7 183 {
6c202c68 184 int i;
bb90b80b 185 pid_t pid;
6c202c68 186
bb90b80b
AJ
187 /* Behave like a daemon. */
188 if (run_mode == RUN_DAEMONIZE)
189 {
532a6035
SP
190 int fd[2];
191
192 if (pipe (fd) != 0)
193 error (EXIT_FAILURE, errno,
194 _("cannot create a pipe to talk to the child"));
195
bb90b80b
AJ
196 pid = fork ();
197 if (pid == -1)
198 error (EXIT_FAILURE, errno, _("cannot fork"));
199 if (pid != 0)
532a6035
SP
200 {
201 /* The parent only reads from the child. */
202 close (fd[1]);
203 exit (monitor_child (fd[0]));
204 }
205 else
206 {
207 /* The child only writes to the parent. */
208 close (fd[0]);
209 parent_fd = fd[1];
210 }
bb90b80b 211 }
6c202c68 212
d4397629
UD
213 int nullfd = open (_PATH_DEVNULL, O_RDWR);
214 if (nullfd != -1)
215 {
216 struct stat64 st;
217
218 if (fstat64 (nullfd, &st) == 0 && S_ISCHR (st.st_mode) != 0
219#if defined DEV_NULL_MAJOR && defined DEV_NULL_MINOR
220 && st.st_rdev == makedev (DEV_NULL_MAJOR, DEV_NULL_MINOR)
221#endif
222 )
223 {
224 /* It is the /dev/null special device alright. */
225 (void) dup2 (nullfd, STDIN_FILENO);
226 (void) dup2 (nullfd, STDOUT_FILENO);
227 (void) dup2 (nullfd, STDERR_FILENO);
228
229 if (nullfd > 2)
230 close (nullfd);
231 }
232 else
233 {
234 /* Ugh, somebody is trying to play a trick on us. */
235 close (nullfd);
236 nullfd = -1;
237 }
238 }
239 int min_close_fd = nullfd == -1 ? 0 : STDERR_FILENO + 1;
240
241 DIR *d = opendir ("/proc/self/fd");
242 if (d != NULL)
243 {
244 struct dirent64 *dirent;
245 int dfdn = dirfd (d);
246
247 while ((dirent = readdir64 (d)) != NULL)
248 {
249 char *endp;
4c5dd2a2 250 long int fdn = strtol (dirent->d_name, &endp, 10);
d4397629 251
532a6035
SP
252 if (*endp == '\0' && fdn != dfdn && fdn >= min_close_fd
253 && fdn != parent_fd)
d4397629
UD
254 close ((int) fdn);
255 }
256
257 closedir (d);
258 }
259 else
260 for (i = min_close_fd; i < getdtablesize (); i++)
532a6035
SP
261 if (i != parent_fd)
262 close (i);
6c202c68 263
adcf0e4a
UD
264 setsid ();
265
be686f18 266 if (chdir ("/") != 0)
532a6035
SP
267 do_exit (EXIT_FAILURE, errno,
268 _("cannot change current working directory to \"/\""));
6c202c68 269
d67281a7
UD
270 openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
271
d67281a7 272 if (write_pid (_PATH_NSCDPID) < 0)
561470e0 273 dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
d67281a7 274
f4047366
UD
275 if (!init_logfile ())
276 dbg_log (_("Could not create log file"));
277
67479a70 278 /* Ignore job control signals. */
d67281a7
UD
279 signal (SIGTTOU, SIG_IGN);
280 signal (SIGTTIN, SIG_IGN);
281 signal (SIGTSTP, SIG_IGN);
282 }
4401d759 283 else
bb90b80b 284 /* In debug mode we are not paranoid. */
4401d759 285 paranoia = 0;
6c202c68
UD
286
287 signal (SIGINT, termination_handler);
288 signal (SIGQUIT, termination_handler);
289 signal (SIGTERM, termination_handler);
290 signal (SIGPIPE, SIG_IGN);
291
230c3e1e 292 /* Cleanup files created by a previous 'bind'. */
d67281a7
UD
293 unlink (_PATH_NSCDSOCKET);
294
319b9ad4
UD
295#ifdef HAVE_INOTIFY
296 /* Use inotify to recognize changed files. */
297 inotify_fd = inotify_init1 (IN_NONBLOCK);
298# ifndef __ASSUME_IN_NONBLOCK
299 if (inotify_fd == -1 && errno == ENOSYS)
300 {
301 inotify_fd = inotify_init ();
302 if (inotify_fd != -1)
303 fcntl (inotify_fd, F_SETFL, O_RDONLY | O_NONBLOCK);
304 }
305# endif
306#endif
307
3cc3ef96 308#ifdef USE_NSCD
ef4d5b32 309 /* Make sure we do not get recursive calls. */
319b9ad4 310 __nss_disable_nscd (register_traced_file);
3cc3ef96 311#endif
ef4d5b32 312
67479a70 313 /* Init databases. */
a12ce44f 314 nscd_init ();
d67281a7 315
a9ae54a1
AS
316 /* Start the SELinux AVC. */
317 if (selinux_enabled)
318 nscd_avc_init ();
319
d67281a7 320 /* Handle incoming requests */
67479a70 321 start_threads ();
d67281a7
UD
322
323 return 0;
324}
325
4d06461a
UD
326
327/* Handle program arguments. */
328static error_t
329parse_opt (int key, char *arg, struct argp_state *state)
330{
331 switch (key)
332 {
333 case 'd':
67479a70 334 ++debug_level;
bb90b80b
AJ
335 run_mode = RUN_DEBUG;
336 break;
337
338 case 'F':
339 run_mode = RUN_FOREGROUND;
4d06461a 340 break;
67479a70 341
4d06461a
UD
342 case 'f':
343 conffile = arg;
344 break;
67479a70 345
4d06461a
UD
346 case 'K':
347 if (getuid () != 0)
64d64de6 348 error (4, 0, _("Only root is allowed to use this option!"));
4d06461a 349 {
67479a70 350 int sock = nscd_open_socket ();
4d06461a
UD
351
352 if (sock == -1)
353 exit (EXIT_FAILURE);
354
8c6d1043 355 request_header req;
4d06461a
UD
356 req.version = NSCD_VERSION;
357 req.type = SHUTDOWN;
358 req.key_len = 0;
8c6d1043
UD
359
360 ssize_t nbytes = TEMP_FAILURE_RETRY (send (sock, &req,
361 sizeof (request_header),
362 MSG_NOSIGNAL));
4d06461a 363 close (sock);
67479a70 364 exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
4d06461a 365 }
67479a70 366
4d06461a 367 case 'g':
a12ce44f
UD
368 get_stats = true;
369 break;
67479a70 370
756409c4
UD
371 case 'i':
372 if (getuid () != 0)
64d64de6 373 error (4, 0, _("Only root is allowed to use this option!"));
756409c4
UD
374 else
375 {
376 int sock = nscd_open_socket ();
756409c4
UD
377
378 if (sock == -1)
379 exit (EXIT_FAILURE);
380
b21fa963
UD
381 dbtype cnt;
382 for (cnt = pwddb; cnt < lastdb; ++cnt)
383 if (strcmp (arg, dbnames[cnt]) == 0)
384 break;
385
386 if (cnt == lastdb)
251fe50c
RM
387 {
388 argp_error (state, _("'%s' is not a known database"), arg);
389 return EINVAL;
390 }
756409c4 391
8c6d1043
UD
392 size_t arg_len = strlen (arg) + 1;
393 struct
394 {
395 request_header req;
396 char arg[arg_len];
397 } reqdata;
756409c4 398
8c6d1043
UD
399 reqdata.req.key_len = strlen (arg) + 1;
400 reqdata.req.version = NSCD_VERSION;
401 reqdata.req.type = INVALIDATE;
402 memcpy (reqdata.arg, arg, arg_len);
ead07d01 403
8c6d1043
UD
404 ssize_t nbytes = TEMP_FAILURE_RETRY (send (sock, &reqdata,
405 sizeof (request_header)
406 + arg_len,
407 MSG_NOSIGNAL));
902c4291 408
8c6d1043 409 if (nbytes != sizeof (request_header) + arg_len)
902c4291
UD
410 {
411 int err = errno;
412 close (sock);
413 error (EXIT_FAILURE, err, _("write incomplete"));
414 }
415
416 /* Wait for ack. Older nscd just closed the socket when
417 prune_cache finished, silently ignore that. */
418 int32_t resp = 0;
419 nbytes = TEMP_FAILURE_RETRY (read (sock, &resp, sizeof (resp)));
420 if (nbytes != 0 && nbytes != sizeof (resp))
421 {
422 int err = errno;
423 close (sock);
424 error (EXIT_FAILURE, err, _("cannot read invalidate ACK"));
425 }
756409c4
UD
426
427 close (sock);
428
902c4291
UD
429 if (resp != 0)
430 error (EXIT_FAILURE, resp, _("invalidation failed"));
431
432 exit (0);
756409c4
UD
433 }
434
67479a70
UD
435 case 't':
436 nthreads = atol (arg);
437 break;
438
a1c542bf 439 case 'S':
3c12b91a 440 error (0, 0, _("secure services not implemented anymore"));
a1c542bf
UD
441 break;
442
4d06461a
UD
443 default:
444 return ARGP_ERR_UNKNOWN;
445 }
67479a70 446
4d06461a
UD
447 return 0;
448}
449
cbbcaf23
UD
450/* Print bug-reporting information in the help message. */
451static char *
452more_help (int key, const char *text, void *input)
453{
0c813d1f
SK
454 char *tables, *tp = NULL;
455
cbbcaf23
UD
456 switch (key)
457 {
458 case ARGP_KEY_HELP_EXTRA:
0c813d1f
SK
459 {
460 dbtype cnt;
461
462 tables = xmalloc (sizeof (dbnames) + 1);
463 for (cnt = 0; cnt < lastdb; cnt++)
464 {
465 strcat (tables, dbnames[cnt]);
466 strcat (tables, " ");
467 }
468 }
469
cbbcaf23 470 /* We print some extra information. */
8b748aed 471 if (asprintf (&tp, gettext ("\
0c813d1f
SK
472Supported tables:\n\
473%s\n\
474\n\
cbbcaf23 475For bug reporting instructions, please see:\n\
0c813d1f
SK
476%s.\n\
477"), tables, REPORT_BUGS_TO) < 0)
478 tp = NULL;
479 free (tables);
8b748aed 480 return tp;
0c813d1f 481
cbbcaf23
UD
482 default:
483 break;
484 }
0c813d1f 485
cbbcaf23
UD
486 return (char *) text;
487}
488
4d06461a
UD
489/* Print the version information. */
490static void
491print_version (FILE *stream, struct argp_state *state)
492{
8b748aed 493 fprintf (stream, "nscd %s%s\n", PKGVERSION, VERSION);
4d06461a
UD
494 fprintf (stream, gettext ("\
495Copyright (C) %s Free Software Foundation, Inc.\n\
496This is free software; see the source for copying conditions. There is NO\n\
497warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
88726d48 498"), "2014");
67479a70
UD
499 fprintf (stream, gettext ("Written by %s.\n"),
500 "Thorsten Kukuk and Ulrich Drepper");
4d06461a
UD
501}
502
503
67479a70 504/* Create a socket connected to a name. */
d67281a7 505int
67479a70 506nscd_open_socket (void)
d67281a7
UD
507{
508 struct sockaddr_un addr;
509 int sock;
510
511 sock = socket (PF_UNIX, SOCK_STREAM, 0);
512 if (sock < 0)
513 return -1;
514
515 addr.sun_family = AF_UNIX;
67479a70 516 assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
d67281a7
UD
517 strcpy (addr.sun_path, _PATH_NSCDSOCKET);
518 if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
519 {
520 close (sock);
521 return -1;
522 }
523
524 return sock;
525}
526
4401d759 527
d67281a7 528/* Cleanup. */
67479a70 529void
d67281a7
UD
530termination_handler (int signum)
531{
532 close_sockets ();
533
230c3e1e 534 /* Clean up the file created by 'bind'. */
d67281a7
UD
535 unlink (_PATH_NSCDSOCKET);
536
537 /* Clean up pid file. */
538 unlink (_PATH_NSCDPID);
539
a95a08b4
UD
540 // XXX Terminate threads.
541
542 /* Synchronize memory. */
543 for (int cnt = 0; cnt < lastdb; ++cnt)
74fef3bb 544 {
feb1eb0b 545 if (!dbs[cnt].enabled || dbs[cnt].head == NULL)
6aa10807
UD
546 continue;
547
74fef3bb
UD
548 /* Make sure nobody keeps using the database. */
549 dbs[cnt].head->timestamp = 0;
550
551 if (dbs[cnt].persistent)
552 // XXX async OK?
553 msync (dbs[cnt].head, dbs[cnt].memsize, MS_ASYNC);
554 }
a95a08b4 555
230c3e1e 556 _exit (EXIT_SUCCESS);
d67281a7
UD
557}
558
d67281a7
UD
559/* Returns 1 if the process in pid file FILE is running, 0 if not. */
560static int
561check_pid (const char *file)
562{
563 FILE *fp;
564
565 fp = fopen (file, "r");
566 if (fp)
567 {
568 pid_t pid;
67479a70 569 int n;
d67281a7 570
67479a70 571 n = fscanf (fp, "%d", &pid);
d67281a7
UD
572 fclose (fp);
573
4401d759
UD
574 /* If we cannot parse the file default to assuming nscd runs.
575 If the PID is alive, assume it is running. That all unless
576 the PID is the same as the current process' since tha latter
577 can mean we re-exec. */
578 if ((n != 1 || kill (pid, 0) == 0) && pid != getpid ())
561470e0 579 return 1;
d67281a7
UD
580 }
581
582 return 0;
583}
584
585/* Write the current process id to the file FILE.
586 Returns 0 if successful, -1 if not. */
587static int
588write_pid (const char *file)
589{
590 FILE *fp;
591
592 fp = fopen (file, "w");
593 if (fp == NULL)
594 return -1;
595
596 fprintf (fp, "%d\n", getpid ());
0292b0dd
UD
597
598 int result = fflush (fp) || ferror (fp) ? -1 : 0;
d67281a7
UD
599
600 fclose (fp);
601
0292b0dd 602 return result;
d67281a7 603}
532a6035
SP
604
605static int
606monitor_child (int fd)
607{
608 int child_ret = 0;
609 int ret = read (fd, &child_ret, sizeof (child_ret));
610
611 /* The child terminated with an error, either via exit or some other abnormal
612 method, like a segfault. */
613 if (ret <= 0 || child_ret != 0)
614 {
615 int err = wait (&child_ret);
616
617 if (err < 0)
618 {
619 fprintf (stderr, _("wait failed"));
620 return 1;
621 }
622
623 fprintf (stderr, _("child exited with status %d"),
624 WEXITSTATUS (child_ret));
625 if (WIFSIGNALED (child_ret))
626 fprintf (stderr, _(", terminated by signal %d.\n"),
627 WTERMSIG (child_ret));
628 else
629 fprintf (stderr, ".\n");
630 }
631
632 /* We have the child status, so exit with that code. */
633 close (fd);
634
635 return child_ret;
636}
637
638void
639do_exit (int child_ret, int errnum, const char *format, ...)
640{
641 if (parent_fd != -1)
642 {
643 int ret = write (parent_fd, &child_ret, sizeof (child_ret));
644 assert (ret == sizeof (child_ret));
645 close (parent_fd);
646 }
647
648 if (format != NULL)
649 {
650 /* Emulate error() since we don't have a va_list variant for it. */
651 va_list argp;
652
653 fflush (stdout);
654
655 fprintf (stderr, "%s: ", program_invocation_name);
656
657 va_start (argp, format);
658 vfprintf (stderr, format, argp);
659 va_end (argp);
660
661 fprintf (stderr, ": %s\n", strerror (errnum));
662 fflush (stderr);
663 }
664
665 /* Finally, exit. */
666 exit (child_ret);
667}
668
669void
670notify_parent (int child_ret)
671{
672 if (parent_fd == -1)
673 return;
674
675 int ret = write (parent_fd, &child_ret, sizeof (child_ret));
676 assert (ret == sizeof (child_ret));
677 close (parent_fd);
678 parent_fd = -1;
679}