Add invalidate cache option.
[kopensolaris-gnu/glibc.git] / nscd / nscd.c
1 /* Copyright (c) 1998, 1999 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 Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    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    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 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 <errno.h>
25 #include <error.h>
26 #include <libintl.h>
27 #include <locale.h>
28 #include <pthread.h>
29 #include <pwd.h>
30 #include <signal.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <syslog.h>
35 #include <unistd.h>
36 #include <sys/socket.h>
37 #include <sys/un.h>
38
39 #include "dbg_log.h"
40 #include "nscd.h"
41
42 /* Get libc version number.  */
43 #include <version.h>
44
45 #define PACKAGE _libc_intl_domainname
46
47 /* Structure used by main() thread to keep track of the number of
48    active threads.  Used to limit how many threads it will create
49    and under a shutdown condition to wait till all in-progress
50    requests have finished before "turning off the lights".  */
51
52 typedef struct
53 {
54   int             num_active;
55   pthread_cond_t  thread_exit_cv;
56   pthread_mutex_t mutex;
57 } thread_info_t;
58
59 thread_info_t thread_info;
60
61 int do_shutdown;
62 int disabled_passwd;
63 int disabled_group;
64 int go_background = 1;
65
66 int secure[lastdb];
67 int secure_in_use;
68 static const char *conffile = _PATH_NSCDCONF;
69
70 static int check_pid (const char *file);
71 static int write_pid (const char *file);
72
73 /* Name and version of program.  */
74 static void print_version (FILE *stream, struct argp_state *state);
75 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
76
77 /* Definitions of arguments for argp functions.  */
78 static const struct argp_option options[] =
79 {
80   { "config-file", 'f', N_("NAME"), 0,
81     N_("Read configuration data from NAME") },
82   { "debug", 'd', NULL, 0,
83     N_("Do not fork and display messages on the current tty") },
84   { "nthreads", 't', N_("NUMBER"), 0, N_("Start NUMBER threads") },
85   { "shutdown", 'K', NULL, 0, N_("Shut the server down") },
86   { "statistic", 'g', NULL, 0, N_("Print current configuration statistic") },
87   { "invalidate", 'i', N_("TABLE"), 0,
88     N_("Invalidate the specified cache") },
89   { "secure", 'S', N_("TABLE,yes"), 0, N_("Use separate cache for each user")},
90   { NULL, 0, NULL, 0, NULL }
91 };
92
93 /* Short description of program.  */
94 static const char doc[] = N_("Name Service Cache Daemon.");
95
96 /* Prototype for option handler.  */
97 static error_t parse_opt __P ((int key, char *arg, struct argp_state *state));
98
99 /* Data structure to communicate with argp functions.  */
100 static struct argp argp =
101 {
102   options, parse_opt, NULL, doc,
103 };
104
105 int
106 main (int argc, char **argv)
107 {
108   int remaining;
109
110   /* Set locale via LC_ALL.  */
111   setlocale (LC_ALL, "");
112   /* Set the text message domain.  */
113   textdomain (PACKAGE);
114
115   /* Parse and process arguments.  */
116   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
117
118   if (remaining != argc)
119     {
120       error (0, 0, gettext ("wrong number of arguments"));
121       argp_help (&argp, stdout, ARGP_HELP_SEE, program_invocation_short_name);
122       exit (EXIT_FAILURE);
123     }
124
125   /* Check if we are already running. */
126   if (check_pid (_PATH_NSCDPID))
127     error (EXIT_FAILURE, 0, _("already running"));
128
129   /* Behave like a daemon.  */
130   if (go_background)
131     {
132       int i;
133
134       if (fork ())
135         exit (0);
136
137       for (i = 0; i < getdtablesize (); i++)
138         close (i);
139
140       if (fork ())
141         exit (0);
142
143       chdir ("/");
144
145       openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
146
147       if (write_pid (_PATH_NSCDPID) < 0)
148         dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
149
150       /* Ignore job control signals.  */
151       signal (SIGTTOU, SIG_IGN);
152       signal (SIGTTIN, SIG_IGN);
153       signal (SIGTSTP, SIG_IGN);
154     }
155
156   signal (SIGINT, termination_handler);
157   signal (SIGQUIT, termination_handler);
158   signal (SIGTERM, termination_handler);
159   signal (SIGPIPE, SIG_IGN);
160
161   /* Cleanup files created by a previous `bind'.  */
162   unlink (_PATH_NSCDSOCKET);
163
164   /* Init databases.  */
165   nscd_init (conffile);
166
167   /* Handle incoming requests */
168   start_threads ();
169
170   return 0;
171 }
172
173
174 /* Handle program arguments.  */
175 static error_t
176 parse_opt (int key, char *arg, struct argp_state *state)
177 {
178   switch (key)
179     {
180     case 'd':
181       ++debug_level;
182       go_background = 0;
183       break;
184
185     case 'f':
186       conffile = arg;
187       break;
188
189     case 'K':
190       if (getuid () != 0)
191         error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!"));
192       {
193         int sock = nscd_open_socket ();
194         request_header req;
195         ssize_t nbytes;
196
197         if (sock == -1)
198           exit (EXIT_FAILURE);
199
200         req.version = NSCD_VERSION;
201         req.type = SHUTDOWN;
202         req.key_len = 0;
203         nbytes = TEMP_FAILURE_RETRY (write (sock, &req,
204                                             sizeof (request_header)));
205         close (sock);
206         exit (nbytes != sizeof (request_header) ? EXIT_FAILURE : EXIT_SUCCESS);
207       }
208
209     case 'g':
210       if (getuid () != 0)
211         error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!"));
212       receive_print_stats ();
213       /* Does not return.  */
214
215     case 'i':
216       if (getuid () != 0)
217         error (EXIT_FAILURE, 0, _("Only root is allowed to use this option!"));
218       else
219         {
220           int sock = nscd_open_socket ();
221           request_header req;
222           ssize_t nbytes;
223
224           if (sock == -1)
225             exit (EXIT_FAILURE);
226
227           if (strcmp (arg, "passwd") == 0)
228             req.key_len = sizeof "passwd";
229           else if (strcmp (arg, "group") == 0)
230             req.key_len = sizeof "group";
231           else if (strcmp (arg, "hosts") == 0)
232             req.key_len = sizeof "hosts";
233           else
234             return ARGP_ERR_UNKNOWN;
235
236           req.version = NSCD_VERSION;
237           req.type = INVALIDATE;
238           nbytes = TEMP_FAILURE_RETRY (write (sock, &req,
239                                               sizeof (request_header)));
240           if (nbytes != sizeof (request_header))
241             {
242               close (sock);
243               exit (EXIT_FAILURE);
244             }
245
246           nbytes = TEMP_FAILURE_RETRY (write (sock, (void *)arg, req.key_len));
247
248           close (sock);
249
250           exit (nbytes != req.key_len ? EXIT_FAILURE : EXIT_SUCCESS);
251         }
252
253     case 't':
254       nthreads = atol (arg);
255       break;
256
257     case 'S':
258       if (strcmp (arg, "passwd,yes") == 0)
259         secure_in_use = secure[pwddb] = 1;
260       else if (strcmp (arg, "group,yes") == 0)
261         secure_in_use = secure[grpdb] = 1;
262       else if (strcmp (arg, "hosts,yes") == 0)
263         secure_in_use = secure[hstdb] = 1;
264       break;
265
266     default:
267       return ARGP_ERR_UNKNOWN;
268     }
269
270   return 0;
271 }
272
273 /* Print the version information.  */
274 static void
275 print_version (FILE *stream, struct argp_state *state)
276 {
277   fprintf (stream, "nscd (GNU %s) %s\n", PACKAGE, VERSION);
278   fprintf (stream, gettext ("\
279 Copyright (C) %s Free Software Foundation, Inc.\n\
280 This is free software; see the source for copying conditions.  There is NO\n\
281 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
282 "), "1999");
283   fprintf (stream, gettext ("Written by %s.\n"),
284            "Thorsten Kukuk and Ulrich Drepper");
285 }
286
287
288 /* Create a socket connected to a name.  */
289 int
290 nscd_open_socket (void)
291 {
292   struct sockaddr_un addr;
293   int sock;
294
295   sock = socket (PF_UNIX, SOCK_STREAM, 0);
296   if (sock < 0)
297     return -1;
298
299   addr.sun_family = AF_UNIX;
300   assert (sizeof (addr.sun_path) >= sizeof (_PATH_NSCDSOCKET));
301   strcpy (addr.sun_path, _PATH_NSCDSOCKET);
302   if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
303     {
304       close (sock);
305       return -1;
306     }
307
308   return sock;
309 }
310
311 /* Cleanup.  */
312 void
313 termination_handler (int signum)
314 {
315   close_sockets ();
316
317   /* Clean up the file created by `bind'.  */
318   unlink (_PATH_NSCDSOCKET);
319
320   /* Clean up pid file.  */
321   unlink (_PATH_NSCDPID);
322
323   exit (EXIT_SUCCESS);
324 }
325
326 /* Returns 1 if the process in pid file FILE is running, 0 if not.  */
327 static int
328 check_pid (const char *file)
329 {
330   FILE *fp;
331
332   fp = fopen (file, "r");
333   if (fp)
334     {
335       pid_t pid;
336       int n;
337
338       n = fscanf (fp, "%d", &pid);
339       fclose (fp);
340
341       if (n != 1 || kill (pid, 0) == 0)
342         return 1;
343     }
344
345   return 0;
346 }
347
348 /* Write the current process id to the file FILE.
349    Returns 0 if successful, -1 if not.  */
350 static int
351 write_pid (const char *file)
352 {
353   FILE *fp;
354
355   fp = fopen (file, "w");
356   if (fp == NULL)
357     return -1;
358
359   fprintf (fp, "%d\n", getpid ());
360   if (fflush (fp) || ferror (fp))
361     return -1;
362
363   fclose (fp);
364
365   return 0;
366 }