Clean up threads, and add thread pool lock/unlock functions,
[freeradius.git] / src / main / radiusd.c
1 /*
2  * radiusd.c    Main loop of the radius server.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000-2004,2006  The FreeRADIUS server project
21  * Copyright 1999,2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  * Copyright 2000  Alan Curry <pacman-radius@cqc.com>
24  * Copyright 2000  Jeff Carneal <jeff@apex.net>
25  * Copyright 2000  Chad Miller <cmiller@surfsouth.com>
26  */
27
28 #include <freeradius-devel/ident.h>
29 RCSID("$Id$")
30
31 #include <freeradius-devel/radiusd.h>
32 #include <freeradius-devel/radius_snmp.h>
33 #include <freeradius-devel/rad_assert.h>
34
35 #include <sys/file.h>
36
37 #include <fcntl.h>
38 #include <ctype.h>
39
40 #include <signal.h>
41
42 #ifdef HAVE_GETOPT_H
43 #       include <getopt.h>
44 #endif
45
46 #ifdef HAVE_SYS_WAIT_H
47 #       include <sys/wait.h>
48 #endif
49 #ifndef WEXITSTATUS
50 #       define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
51 #endif
52 #ifndef WIFEXITED
53 #       define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
54 #endif
55
56 #ifndef HAVE_PTHREAD_H
57 #define thread_pool_lock(_x)
58 #define thread_pool_unlock(_x)
59 #endif
60
61 /*
62  *  Global variables.
63  */
64 const char *progname = NULL;
65 const char *radius_dir = NULL;
66 const char *radacct_dir = NULL;
67 const char *radlog_dir = NULL;
68 const char *radlib_dir = NULL;
69 int log_stripped_names;
70 int debug_flag = 0;
71 int log_auth_detail = FALSE;
72
73 const char *radiusd_version = "FreeRADIUS Version " RADIUSD_VERSION ", for host " HOSTINFO ", built on " __DATE__ " at " __TIME__;
74
75 time_t time_now;
76 pid_t radius_pid;
77
78 static int debug_memory = 0;
79
80 /*
81  *  Configuration items.
82  */
83
84 /*
85  *      Static functions.
86  */
87 static void usage(int);
88
89 static void sig_fatal (int);
90 static void sig_hup (int);
91
92 /*
93  *      The main guy.
94  */
95 int main(int argc, char *argv[])
96 {
97         int rcode;
98         unsigned char buffer[4096];
99         int argval;
100         pid_t pid;
101         int spawn_flag = TRUE;
102         int dont_fork = FALSE;
103
104 #ifdef HAVE_SIGACTION
105         struct sigaction act;
106 #endif
107
108 #ifdef OSFC2
109         set_auth_parameters(argc,argv);
110 #endif
111
112         if ((progname = strrchr(argv[0], '/')) == NULL)
113                 progname = argv[0];
114         else
115                 progname++;
116
117         debug_flag = 0;
118         spawn_flag = TRUE;
119         radius_dir = strdup(RADIUS_DIR);
120
121         /*
122          *      Ensure that the configuration is initialized.
123          */
124         memset(&mainconfig, 0, sizeof(mainconfig));
125         mainconfig.myip.af = AF_UNSPEC;
126         mainconfig.port = -1;
127         mainconfig.radiusd_conf = strdup("radiusd.conf");
128
129 #ifdef HAVE_SIGACTION
130         memset(&act, 0, sizeof(act));
131         act.sa_flags = 0 ;
132         sigemptyset( &act.sa_mask ) ;
133 #endif
134
135         /*
136          *      Don't put output anywhere until we get told a little
137          *      more.
138          */
139         mainconfig.radlog_fd = -1;
140         mainconfig.log_file = NULL;
141
142         /*  Process the options.  */
143         while ((argval = getopt(argc, argv, "Aa:bcd:fg:hi:l:mn:p:sSvxXyz")) != EOF) {
144
145                 switch(argval) {
146
147                         case 'A':
148                                 log_auth_detail = TRUE;
149                                 break;
150
151                         case 'a':
152                                 if (radacct_dir) free(radacct_dir);
153                                 radacct_dir = strdup(optarg);
154                                 break;
155
156                         case 'c':
157                                 /* ignore for backwards compatibility with Cistron */
158                                 break;
159
160                         case 'd':
161                                 if (radius_dir) free(radius_dir);
162                                 radius_dir = strdup(optarg);
163                                 break;
164
165                         case 'f':
166                                 dont_fork = TRUE;
167                                 break;
168
169                         case 'h':
170                                 usage(0);
171                                 break;
172
173                         case 'i':
174                                 if (ip_hton(optarg, AF_UNSPEC, &mainconfig.myip) < 0) {
175                                         fprintf(stderr, "radiusd: Invalid IP Address or hostname \"%s\"\n", optarg);
176                                         exit(1);
177                                 }
178                                 break;
179
180                         case 'l':
181                                 if ((strcmp(optarg, "stdout") == 0) ||
182                                     (strcmp(optarg, "stderr") == 0) ||
183                                     (strcmp(optarg, "syslog") == 0)) {
184                                         fprintf(stderr, "radiusd: -l %s is unsupported.  Use log_destination in radiusd.conf\n", optarg);
185                                         exit(1);
186                                 }
187                                 if (radlog_dir) free(radlog_dir);
188                                 radlog_dir = strdup(optarg);
189                                 break;
190
191                         case 'g':
192                                 fprintf(stderr, "radiusd: -g is unsupported.  Use log_destination in radiusd.conf.\n");
193                                 exit(1);
194                                 break;
195
196                         case 'm':
197                                 debug_memory = 1;
198                                 break;
199
200                         case 'n':
201                                 if ((strchr(optarg, '/') != NULL) ||
202                                     (strchr(optarg, '.') != NULL) ||
203                                     (strlen(optarg) > 45)) usage(1);
204
205                                 snprintf(buffer, sizeof(buffer), "%s.conf",
206                                          optarg);
207                                 if (mainconfig.radiusd_conf)
208                                         free(mainconfig.radiusd_conf);
209                                 mainconfig.radiusd_conf = strdup(buffer);
210                                 break;
211
212                         case 'S':
213                                 log_stripped_names++;
214                                 break;
215
216                         case 'p':
217                                 mainconfig.port = atoi(optarg);
218                                 if ((mainconfig.port <= 0) ||
219                                     (mainconfig.port >= 65536)) {
220                                         fprintf(stderr, "radiusd: Invalid port number %s\n", optarg);
221                                         exit(1);
222                                 }
223                                 break;
224
225                         case 's':       /* Single process mode */
226                                 spawn_flag = FALSE;
227                                 dont_fork = TRUE;
228                                 break;
229
230                         case 'v':
231                                 version();
232                                 break;
233
234                                 /*
235                                  *  BIG debugging mode for users who are
236                                  *  TOO LAZY to type '-sfxxyz -l stdout' themselves.
237                                  */
238                         case 'X':
239                                 spawn_flag = FALSE;
240                                 dont_fork = TRUE;
241                                 debug_flag += 2;
242                                 mainconfig.log_auth = TRUE;
243                                 mainconfig.log_auth_badpass = TRUE;
244                                 mainconfig.log_auth_goodpass = TRUE;
245                                 mainconfig.radlog_dest = RADLOG_STDOUT;
246                                 mainconfig.radlog_fd = STDOUT_FILENO;
247                                 break;
248
249                         case 'x':
250                                 debug_flag++;
251                                 break;
252
253                         case 'y':
254                                 mainconfig.log_auth = TRUE;
255                                 mainconfig.log_auth_badpass = TRUE;
256                                 break;
257
258                         case 'z':
259                                 mainconfig.log_auth_badpass = TRUE;
260                                 mainconfig.log_auth_goodpass = TRUE;
261                                 break;
262
263                         default:
264                                 usage(1);
265                                 break;
266                 }
267         }
268
269         if (debug_flag) {
270                 radlog(L_INFO, "%s", radiusd_version);
271                 radlog(L_INFO, "Copyright (C) 2000-2007 The FreeRADIUS server project.\n");
272                 radlog(L_INFO, "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n");
273                 radlog(L_INFO, "PARTICULAR PURPOSE.\n");
274                 radlog(L_INFO, "You may redistribute copies of FreeRADIUS under the terms of the\n");
275                 radlog(L_INFO, "GNU General Public License.\n");
276                 fflush(NULL);
277         }
278
279         /*  Read the configuration files, BEFORE doing anything else.  */
280         if (read_mainconfig(0) < 0) {
281                 exit(1);
282         }
283
284         /*
285          *  Disconnect from session
286          */
287         if (debug_flag == 0 && dont_fork == FALSE) {
288                 pid = fork();
289                 if(pid < 0) {
290                         radlog(L_ERR|L_CONS, "Couldn't fork");
291                         exit(1);
292                 }
293
294                 /*
295                  *  The parent exits, so the child can run in the background.
296                  */
297                 if (pid > 0) {
298                         exit(0);
299                 }
300 #ifdef HAVE_SETSID
301                 setsid();
302 #endif
303         }
304
305         /*
306          *      If we're NOT debugging, trap fatal signals, so we can
307          *      easily clean up after ourselves.
308          *
309          *      If we ARE debugging, don't trap them, so we can
310          *      dump core.
311          */
312         if ((mainconfig.allow_core_dumps == FALSE) && (debug_flag == 0)) {
313 #ifdef SIGSEGV
314 #ifdef HAVE_SIGACTION
315                 act.sa_handler = sig_fatal;
316                 sigaction(SIGSEGV, &act, NULL);
317 #else
318                 signal(SIGSEGV, sig_fatal);
319 #endif
320 #endif
321         }
322
323         /*
324          *  Ensure that we're using the CORRECT pid after forking,
325          *  NOT the one we started with.
326          */
327         radius_pid = getpid();
328
329         /*
330          *  Only write the PID file if we're running as a daemon.
331          *
332          *  And write it AFTER we've forked, so that we write the
333          *  correct PID.
334          */
335         if (dont_fork == FALSE) {
336                 FILE *fp;
337
338                 fp = fopen(mainconfig.pid_file, "w");
339                 if (fp != NULL) {
340                         /*
341                          *      FIXME: What about following symlinks,
342                          *      and having it over-write a normal file?
343                          */
344                         fprintf(fp, "%d\n", (int) radius_pid);
345                         fclose(fp);
346                 } else {
347                         radlog(L_ERR|L_CONS, "Failed creating PID file %s: %s\n",
348                                mainconfig.pid_file, strerror(errno));
349                         exit(1);
350                 }
351         }
352
353         /*
354          *      If we're running as a daemon, close the default file
355          *      descriptors, AFTER forking.
356          */
357         if (!debug_flag) {
358                 int devnull;
359
360                 devnull = open("/dev/null", O_RDWR);
361                 if (devnull < 0) {
362                         radlog(L_ERR|L_CONS, "Failed opening /dev/null: %s\n",
363                                strerror(errno));
364                         exit(1);
365                 }
366                 dup2(devnull, STDIN_FILENO);
367                 if (mainconfig.radlog_dest == RADLOG_STDOUT) {
368                         mainconfig.radlog_fd = dup(STDOUT_FILENO);
369                 }
370                 dup2(devnull, STDOUT_FILENO);
371                 if (mainconfig.radlog_dest == RADLOG_STDERR) {
372                         mainconfig.radlog_fd = dup(STDERR_FILENO);
373                 }
374                 dup2(devnull, STDERR_FILENO);
375                 close(devnull);
376         }
377
378         /*
379          *      It's called the thread pool, but it does a little
380          *      more than that.
381          */
382         radius_event_init(mainconfig.config, spawn_flag);
383
384         /*
385          *  Use linebuffered or unbuffered stdout if
386          *  the debug flag is on.
387          */
388         if (debug_flag == TRUE)
389                 setlinebuf(stdout);
390
391         /*
392          *      Now that we've set everything up, we can install the signal
393          *      handlers.  Before this, if we get any signal, we don't know
394          *      what to do, so we might as well do the default, and die.
395          */
396         signal(SIGPIPE, SIG_IGN);
397 #ifdef HAVE_SIGACTION
398         act.sa_handler = sig_hup;
399         sigaction(SIGHUP, &act, NULL);
400         act.sa_handler = sig_fatal;
401         sigaction(SIGTERM, &act, NULL);
402 #else
403         signal(SIGHUP, sig_hup);
404         signal(SIGTERM, sig_fatal);
405 #endif
406         /*
407          *      If we're debugging, then a CTRL-C will cause the
408          *      server to die immediately.  Use SIGTERM to shut down
409          *      the server cleanly in that case.
410          */
411         if ((debug_memory == 1) || (debug_flag == 0)) {
412 #ifdef HAVE_SIGACTION
413                 act.sa_handler = sig_fatal;
414                 sigaction(SIGINT, &act, NULL);
415                 sigaction(SIGQUIT, &act, NULL);
416 #else
417                 signal(SIGINT, sig_fatal);
418                 signal(SIGQUIT, sig_fatal);
419 #endif
420         }
421
422         /*
423          *      Process requests until HUP or exit.
424          */
425         while ((rcode = radius_event_process()) == 0x80) {
426                 thread_pool_lock();
427                 /*
428                  *      Reload anything that can safely be reloaded.
429                  */
430                 DEBUG("HUP support not available.");
431
432                 thread_pool_unlock();
433         }
434         
435         DEBUG("Exiting...");
436         
437         /*
438          *      Ignore the TERM signal: we're
439          *      about to die.
440          */
441         signal(SIGTERM, SIG_IGN);
442         
443         /*
444          *      Send a TERM signal to all
445          *      associated processes
446          *      (including us, which gets
447          *      ignored.)
448          */
449         kill(-radius_pid, SIGTERM);
450         
451         /*
452          *      We're exiting, so we can delete the PID
453          *      file.  (If it doesn't exist, we can ignore
454          *      the error returned by unlink)
455          */
456         if (dont_fork == FALSE) {
457                 unlink(mainconfig.pid_file);
458         }
459                 
460         radius_event_free();
461         
462         /*
463          *      Free the configuration items.
464          */
465         free_mainconfig();
466         
467         /*
468          *      Detach any modules.
469          */
470         detach_modules();
471         
472         free(radius_dir);
473                 
474         return (rcode - 1);
475 }
476
477
478 /*
479  *  Display the syntax for starting this program.
480  */
481 static void NEVER_RETURNS usage(int status)
482 {
483         FILE *output = status?stderr:stdout;
484
485         fprintf(output,
486                         "Usage: %s [-a acct_dir] [-d db_dir] [-l log_dir] [-i address] [-AcfnsSvXxyz]\n", progname);
487         fprintf(output, "Options:\n\n");
488         fprintf(output, "  -a acct_dir     use accounting directory 'acct_dir'.\n");
489         fprintf(output, "  -A              Log auth detail.\n");
490         fprintf(output, "  -d raddb_dir    Configuration files are in \"raddbdir/*\".\n");
491         fprintf(output, "  -f              Run as a foreground process, not a daemon.\n");
492         fprintf(output, "  -h              Print this help message.\n");
493         fprintf(output, "  -i ipaddr       Listen on ipaddr ONLY\n");
494         fprintf(output, "  -l log_dir      Log file is \"log_dir/radius.log\" (not used in debug mode)\n");
495         fprintf(output, "  -p port         Listen on port ONLY\n");
496         fprintf(output, "  -s              Do not spawn child processes to handle requests.\n");
497         fprintf(output, "  -S              Log stripped names.\n");
498         fprintf(output, "  -v              Print server version information.\n");
499         fprintf(output, "  -X              Turn on full debugging.\n");
500         fprintf(output, "  -x              Turn on additional debugging. (-xx gives more debugging).\n");
501         fprintf(output, "  -y              Log authentication failures, with password.\n");
502         fprintf(output, "  -z              Log authentication successes, with password.\n");
503         exit(status);
504 }
505
506
507 /*
508  *      We got a fatal signal.
509  */
510 static void sig_fatal(int sig)
511 {
512         switch(sig) {
513                 case SIGSEGV:
514                         /*
515                          *      We can't really do anything
516                          *      intelligent here so just die
517                          */
518                         _exit(1);
519
520                 case SIGTERM:
521                         radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
522                         break;
523
524                 case SIGINT:
525                 case SIGQUIT:
526                         if (debug_memory) {
527                                 radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
528                                 break;
529                         }
530                         /* FALL-THROUGH */
531
532                 default:
533                         radius_signal_self(RADIUS_SIGNAL_SELF_EXIT);
534                         break;
535         }
536 }
537
538
539 /*
540  *  We got the hangup signal.
541  *  Re-read the configuration files.
542  */
543 static void sig_hup(int sig)
544 {
545         sig = sig; /* -Wunused */
546
547         reset_signal(SIGHUP, sig_hup);
548
549         radius_signal_self(RADIUS_SIGNAL_SELF_HUP);
550 }