Define USE_NSCD.
[kopensolaris-gnu/glibc.git] / nscd / nscd.c
1 /* Copyright (c) 1998 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.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 and group.  */
21
22 #include <errno.h>
23 #include <getopt.h>
24 #include <libintl.h>
25 #include <locale.h>
26 #include <pthread.h>
27 #include <pwd.h>
28 #include <signal.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <syslog.h>
33 #include <sys/socket.h>
34 #include <sys/un.h>
35
36 #include "dbg_log.h"
37 #include "nscd.h"
38
39 /* Get libc version number.  */
40 #include <version.h>
41
42 #define PACKAGE _libc_intl_domainname
43
44 /* Structure used by main() thread to keep track of the number of
45    active threads.  Used to limit how many threads it will create
46    and under a shutdown condition to wait till all in-progress
47    requests have finished before "turning off the lights".  */
48
49 typedef struct
50 {
51   int             num_active;
52   pthread_cond_t  thread_exit_cv;
53   pthread_mutex_t mutex;
54 } thread_info_t;
55
56 thread_info_t thread_info;
57
58 int do_shutdown = 0;
59 int disabled_passwd = 0;
60 int disabled_group = 0;
61
62 static void termination_handler (int signum);
63 static int check_pid (const char *file);
64 static int write_pid (const char *file);
65 static void usage (int status) __attribute__ ((noreturn));
66 static void handle_requests (void);
67
68 int
69 main (int argc, char **argv)
70 {
71   int go_background = 1;
72   const char *conffile = _PATH_NSCDCONF;
73
74   /* Set locale via LC_ALL.  */
75   setlocale (LC_ALL, "");
76   /* Set the text message domain.  */
77   textdomain (PACKAGE);
78
79   while (1)
80     {
81       int c;
82       int option_index = 0;
83       static struct option long_options[] = {
84         { "debug", no_argument, NULL, 'd' },
85         { "help", no_argument, NULL, 'h' },
86         { "version", no_argument, NULL, 'V' },
87         { "shutdown", no_argument, NULL, 'K' },
88         {NULL, 0, NULL, '\0'}
89       };
90
91       c = getopt_long (argc, argv, "df:ghKV", long_options, &option_index);
92       if (c == (-1))
93         break;
94       switch (c)
95         {
96         case 'd':
97           debug_flag = 1;
98           go_background = 0;
99           break;
100         case 'f':
101           conffile = optarg;
102           break;
103         case 'h':
104           usage (EXIT_SUCCESS);
105           break;
106         case 'K':
107           if (getuid () != 0)
108             {
109               printf (_("Only root is allowed to use this option!\n\n"));
110               usage (EXIT_FAILURE);
111             }
112           {
113             int sock = __nscd_open_socket ();
114             request_header req;
115             ssize_t nbytes;
116
117             if (sock == -1)
118               exit (EXIT_FAILURE);
119
120             req.version = NSCD_VERSION;
121             req.type = SHUTDOWN;
122             req.key_len = 0;
123             nbytes = write (sock, &req, sizeof (request_header));
124             close (sock);
125             if (nbytes != req.key_len)
126               exit (EXIT_FAILURE);
127             else
128               exit (EXIT_SUCCESS);
129           }
130         case 'g':
131           print_stat ();
132           exit (EXIT_SUCCESS);
133         case 'V':
134           printf ("nscd (GNU %s) %s\n", PACKAGE, VERSION);
135           printf (_("\
136 Copyright (C) %s Free Software Foundation, Inc.\n\
137 This is free software; see the source for copying conditions.  There is NO\n\
138 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
139 "), "1998");
140           printf (_("Written by %s.\n"), "Thorsten Kukuk");
141           exit (EXIT_SUCCESS);
142         default:
143           usage (EXIT_FAILURE);
144         }
145     }
146
147   signal (SIGINT, termination_handler);
148   signal (SIGQUIT, termination_handler);
149   signal (SIGTERM, termination_handler);
150
151   /* Check if we are already running. */
152   if (check_pid (_PATH_NSCDPID))
153     {
154       fputs (_("already running"), stderr);
155       exit (EXIT_FAILURE);
156     }
157
158   /* Behave like a daemon.  */
159   if (go_background)
160     {
161       openlog ("nscd", LOG_CONS | LOG_ODELAY, LOG_DAEMON);
162
163       if (daemon (0, 0) < 0)
164         {
165           fprintf (stderr, _("connot auto-background: %s\n"),
166                    strerror (errno));
167           exit (EXIT_FAILURE);
168         }
169       if (write_pid (_PATH_NSCDPID) < 0)
170         dbg_log ("%s: %s", _PATH_NSCDPID, strerror (errno));
171
172       /* Ignore job control signals */
173       signal (SIGTTOU, SIG_IGN);
174       signal (SIGTTIN, SIG_IGN);
175       signal (SIGTSTP, SIG_IGN);
176     }
177   /* Cleanup files created by a previous `bind' */
178   unlink (_PATH_NSCDSOCKET);
179
180   nscd_parse_file (conffile);
181
182   /* Create first sockets */
183   init_sockets ();
184   /* Init databases */
185   cache_pwdinit ();
186   cache_grpinit ();
187   /* Handle incoming requests */
188   handle_requests ();
189
190   return 0;
191 }
192
193 /* Create a socket connected to a name. */
194 int
195 __nscd_open_socket (void)
196 {
197   struct sockaddr_un addr;
198   int sock;
199
200   sock = socket (PF_UNIX, SOCK_STREAM, 0);
201   if (sock < 0)
202     return -1;
203
204   addr.sun_family = AF_UNIX;
205   strcpy (addr.sun_path, _PATH_NSCDSOCKET);
206   if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)) < 0)
207     {
208       close (sock);
209       return -1;
210     }
211
212   return sock;
213 }
214
215 /* Cleanup.  */
216 static void
217 termination_handler (int signum)
218 {
219   close_sockets ();
220
221   /* Clean up the files created by `bind'.  */
222   unlink (_PATH_NSCDSOCKET);
223
224   /* Clean up pid file.  */
225   unlink (_PATH_NSCDPID);
226
227   exit (EXIT_SUCCESS);
228 }
229
230 /* Display usage information and exit.  */
231 static void
232 usage (int status)
233 {
234   if (status != EXIT_SUCCESS)
235     fprintf (stderr, _("Try `%s --help' for more information.\n"),
236              program_invocation_name);
237   else
238     {
239       printf (_("\
240 Usage: %s [OPTION]...\n\
241   -d, --debug           do not fork and display messages on the current tty\n\
242   -h, --help            display this help and exit\n\
243   -V, --version         output version information and exit\n\
244   -f configuration-file read configuration data from the specified file.\n\
245   -K, --shutdown        shut the server down.\n\
246   -g                    Prints configuration and statistics to stdout.\n"),
247               program_invocation_name);
248       fputs (_("\
249 Report bugs using the `glibcbug' script to <bugs@gnu.org>.\n"),
250              stdout);
251     }
252   exit (status);
253 }
254
255 /* Returns 1 if the process in pid file FILE is running, 0 if not.  */
256 static int
257 check_pid (const char *file)
258 {
259   FILE *fp;
260
261   fp = fopen (file, "r");
262   if (fp)
263     {
264       pid_t pid;
265
266       fscanf (fp, "%d", &pid);
267       fclose (fp);
268
269       if (kill (pid, 0) == 0)
270         return 1;
271     }
272
273   return 0;
274 }
275
276 /* Write the current process id to the file FILE.
277    Returns 0 if successful, -1 if not.  */
278 static int
279 write_pid (const char *file)
280 {
281   FILE *fp;
282
283   fp = fopen (file, "w");
284   if (fp == NULL)
285     return -1;
286
287   fprintf (fp, "%d\n", getpid ());
288   if (ferror (fp))
289     return -1;
290
291   fclose (fp);
292
293   return 0;
294 }
295
296 /* Type of the lookup function for netname2user.  */
297 typedef int (*pwbyname_function) (const char *name, struct passwd *pw,
298                                    char *buffer, size_t buflen);
299
300 /* Hanlde incoming requests.  */
301 static
302 void handle_requests (void)
303 {
304   request_header req;
305   int conn; /* Handle on which connection (client) the request came from.  */
306   int done = 0;
307   char *key;
308
309   while (!done)
310     {
311       key = NULL;
312       get_request (&conn, &req, &key);
313       if (debug_flag)
314         dbg_log (_("handle_requests: request received (Version = %d)"),
315                  req.version);
316       switch (req.type)
317         {
318         case GETPWBYNAME:
319           {
320             param_t *param = malloc (sizeof (param_t));
321             pthread_t thread;
322
323             if (debug_flag)
324               dbg_log ("\tGETPWBYNAME (%s)", key);
325             param->key = key;
326             param->conn = conn;
327             if (disabled_passwd)
328               pthread_create (&thread, NULL, cache_pw_disabled, (void *)param);
329             else
330               pthread_create (&thread, NULL, cache_getpwnam, (void *)param);
331             pthread_detach (thread);
332           }
333           break;
334         case GETPWBYUID:
335           {
336             param_t *param = malloc (sizeof (param_t));
337             pthread_t thread;
338
339             if (debug_flag)
340               dbg_log ("\tGETPWBYUID (%s)", key);
341             param->key = key;
342             param->conn = conn;
343             if (disabled_passwd)
344               pthread_create (&thread, NULL, cache_pw_disabled, (void *)param);
345             else
346               pthread_create (&thread, NULL, cache_getpwuid, (void *)param);
347             pthread_detach (thread);
348           }
349           break;
350         case GETGRBYNAME:
351           {
352             param_t *param = malloc (sizeof (param_t));
353             pthread_t thread;
354
355             if (debug_flag)
356               dbg_log ("\tGETGRBYNAME (%s)", key);
357             param->key = key;
358             param->conn = conn;
359             if (disabled_group)
360               pthread_create (&thread, NULL, cache_gr_disabled, (void *)param);
361             else
362               pthread_create (&thread, NULL, cache_getgrnam, (void *)param);
363             pthread_detach (thread);
364           }
365           break;
366         case GETGRBYGID:
367           {
368             param_t *param = malloc (sizeof (param_t));
369             pthread_t thread;
370
371             if (debug_flag)
372               dbg_log ("\tGETGRBYGID (%s)", key);
373             param->key = key;
374             param->conn = conn;
375             if (disabled_group)
376               pthread_create (&thread, NULL, cache_gr_disabled, (void *)param);
377             else
378               pthread_create (&thread, NULL, cache_getgrgid, (void *)param);
379             pthread_detach (thread);
380           }
381           break;
382         case GETHOSTBYNAME:
383           /* Not yetimplemented.  */
384           close_socket (conn);
385           break;
386         case GETHOSTBYADDR:
387           /* Not yet implemented. */
388           close_socket (conn);
389           break;
390         case SHUTDOWN:
391           do_shutdown = 1;
392           close_socket (0);
393           close_socket (conn);
394           /* Clean up the files created by `bind'.  */
395           unlink (_PATH_NSCDSOCKET);
396           /* Clean up pid file.  */
397           unlink (_PATH_NSCDPID);
398           done = 1;
399           break;
400         case GETSTAT:
401           {
402             stat_response_header resp;
403
404             if (debug_flag)
405               dbg_log ("\tGETSTAT");
406
407             get_pw_stat (&resp);
408             get_gr_stat (&resp);
409             resp.debug_level = debug_flag;
410             resp.pw_enabled = !disabled_passwd;
411             resp.gr_enabled = !disabled_group;
412
413             stat_send (conn, &resp);
414
415             close_socket (conn);
416           }
417           break;
418         default:
419           dbg_log (_("Unknown request (%d)"), req.type);
420           break;
421         }
422     }
423 }