Allow building WITHOUT_STATS
[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/modules.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 /*
57  *  Global variables.
58  */
59 const char *progname = NULL;
60 const char *radius_dir = NULL;
61 const char *radacct_dir = NULL;
62 const char *radlog_dir = NULL;
63 const char *radlib_dir = NULL;
64 int log_stripped_names;
65 int debug_flag = 0;
66 int check_config = FALSE;
67
68 const char *radiusd_version = "FreeRADIUS Version " RADIUSD_VERSION ", for host " HOSTINFO ", built on " __DATE__ " at " __TIME__;
69
70 pid_t radius_pid;
71
72 static int debug_memory = 0;
73
74 /*
75  *  Configuration items.
76  */
77
78 /*
79  *      Static functions.
80  */
81 static void usage(int);
82
83 static void sig_fatal (int);
84 #ifdef SIGHUP
85 static void sig_hup (int);
86 #endif
87
88 /*
89  *      The main guy.
90  */
91 int main(int argc, char *argv[])
92 {
93         int rcode;
94         int argval;
95         int spawn_flag = TRUE;
96         int dont_fork = FALSE;
97         int flag = 0;
98
99 #ifdef HAVE_SIGACTION
100         struct sigaction act;
101 #endif
102
103 #ifdef OSFC2
104         set_auth_parameters(argc,argv);
105 #endif
106
107         if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL)
108                 progname = argv[0];
109         else
110                 progname++;
111
112 #ifdef WIN32
113         {
114                 WSADATA wsaData;
115                 if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
116                         fprintf(stderr, "%s: Unable to initialize socket library.\n");
117                         return 1;
118                 }
119         }
120 #endif
121
122         debug_flag = 0;
123         spawn_flag = TRUE;
124         radius_dir = strdup(RADIUS_DIR);
125
126         /*
127          *      Ensure that the configuration is initialized.
128          */
129         memset(&mainconfig, 0, sizeof(mainconfig));
130         mainconfig.myip.af = AF_UNSPEC;
131         mainconfig.port = -1;
132         mainconfig.name = "radiusd";
133
134 #ifdef HAVE_SIGACTION
135         memset(&act, 0, sizeof(act));
136         act.sa_flags = 0 ;
137         sigemptyset( &act.sa_mask ) ;
138 #endif
139
140         /*
141          *      Don't put output anywhere until we get told a little
142          *      more.
143          */
144         mainconfig.radlog_dest = RADLOG_NULL;
145         mainconfig.radlog_fd = -1;
146         mainconfig.log_file = NULL;
147
148         /*  Process the options.  */
149         while ((argval = getopt(argc, argv, "Cd:fhi:l:mn:p:stvxX")) != EOF) {
150
151                 switch(argval) {
152                         case 'C':
153                                 check_config = TRUE;
154                                 spawn_flag = FALSE;
155                                 dont_fork = TRUE;
156                                 break;
157
158                         case 'd':
159                                 if (radius_dir) free(radius_dir);
160                                 radius_dir = strdup(optarg);
161                                 break;
162
163                         case 'f':
164                                 dont_fork = TRUE;
165                                 break;
166
167                         case 'h':
168                                 usage(0);
169                                 break;
170
171                         case 'l':
172                                 if (strcmp(optarg, "stdout") == 0) {
173                                         goto do_stdout;
174                                 }
175                                 mainconfig.log_file = strdup(optarg);
176                                 mainconfig.radlog_dest = RADLOG_FILES;
177                                 mainconfig.radlog_fd = open(mainconfig.log_file,
178                                                             O_WRONLY | O_APPEND | O_CREAT, 0640);
179                                 if (mainconfig.radlog_fd < 0) {
180                                         fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, strerror(errno));
181                                         exit(1);
182                                 }
183                                 break;            
184
185                         case 'i':
186                                 if (ip_hton(optarg, AF_UNSPEC, &mainconfig.myip) < 0) {
187                                         fprintf(stderr, "radiusd: Invalid IP Address or hostname \"%s\"\n", optarg);
188                                         exit(1);
189                                 }
190                                 flag |= 1;
191                                 break;
192
193                         case 'n':
194                                 mainconfig.name = optarg;
195                                 break;
196
197                         case 'm':
198                                 debug_memory = 1;
199                                 break;
200
201                         case 'p':
202                                 mainconfig.port = atoi(optarg);
203                                 if ((mainconfig.port <= 0) ||
204                                     (mainconfig.port >= 65536)) {
205                                         fprintf(stderr, "radiusd: Invalid port number %s\n", optarg);
206                                         exit(1);
207                                 }
208                                 flag |= 2;
209                                 break;
210
211                         case 's':       /* Single process mode */
212                                 spawn_flag = FALSE;
213                                 dont_fork = TRUE;
214                                 break;
215
216                         case 't':       /* no child threads */
217                                 spawn_flag = FALSE;
218                                 break;
219
220                         case 'v':
221                                 version();
222                                 break;
223
224                         case 'X':
225                                 spawn_flag = FALSE;
226                                 dont_fork = TRUE;
227                                 debug_flag += 2;
228                                 mainconfig.log_auth = TRUE;
229                                 mainconfig.log_auth_badpass = TRUE;
230                                 mainconfig.log_auth_goodpass = TRUE;
231                 do_stdout:
232                                 mainconfig.radlog_dest = RADLOG_STDOUT;
233                                 mainconfig.radlog_fd = STDOUT_FILENO;
234                                 fr_log_fp = stdout;
235                                 break;
236
237                         case 'x':
238                                 debug_flag++;
239                                 break;
240
241                         default:
242                                 usage(1);
243                                 break;
244                 }
245         }
246
247         if (flag && (flag != 0x03)) {
248                 fprintf(stderr, "radiusd: The options -i and -p cannot be used individually.\n");
249                 exit(1);
250         }
251
252         if (debug_flag) {
253                 radlog(L_INFO, "%s", radiusd_version);
254                 radlog(L_INFO, "Copyright (C) 1999-2009 The FreeRADIUS server project and contributors.\n");
255                 radlog(L_INFO, "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n");
256                 radlog(L_INFO, "PARTICULAR PURPOSE.\n");
257                 radlog(L_INFO, "You may redistribute copies of FreeRADIUS under the terms of the\n");
258                 radlog(L_INFO, "GNU General Public License v2.\n");
259                 fflush(NULL);
260         }
261
262         /*  Read the configuration files, BEFORE doing anything else.  */
263         if (read_mainconfig(0) < 0) {
264                 exit(1);
265         }
266
267 #ifndef __MINGW32__
268         /*
269          *  Disconnect from session
270          */
271         if (dont_fork == FALSE) {
272                 pid_t pid = fork();
273
274                 if (pid < 0) {
275                         radlog(L_ERR, "Couldn't fork: %s", strerror(errno));
276                         exit(1);
277                 }
278
279                 /*
280                  *  The parent exits, so the child can run in the background.
281                  */
282                 if (pid > 0) {
283                         exit(0);
284                 }
285 #ifdef HAVE_SETSID
286                 setsid();
287 #endif
288         }
289 #endif
290
291         /*
292          *  Ensure that we're using the CORRECT pid after forking,
293          *  NOT the one we started with.
294          */
295         radius_pid = getpid();
296
297         /*
298          *      If we're running as a daemon, close the default file
299          *      descriptors, AFTER forking.
300          */
301         if (!debug_flag) {
302                 int devnull;
303
304                 devnull = open("/dev/null", O_RDWR);
305                 if (devnull < 0) {
306                         radlog(L_ERR|L_CONS, "Failed opening /dev/null: %s\n",
307                                strerror(errno));
308                         exit(1);
309                 }
310                 dup2(devnull, STDIN_FILENO);
311                 if (mainconfig.radlog_dest == RADLOG_STDOUT) {
312                         mainconfig.radlog_fd = dup(STDOUT_FILENO);
313                         setlinebuf(stdout);
314                 }
315                 dup2(devnull, STDOUT_FILENO);
316                 if (mainconfig.radlog_dest == RADLOG_STDERR) {
317                         mainconfig.radlog_fd = dup(STDERR_FILENO);
318                         setlinebuf(stdout);
319                 }
320                 dup2(devnull, STDERR_FILENO);
321                 close(devnull);
322
323         } else {
324                 setlinebuf(stdout); /* unbuffered output */
325         }
326
327         /*
328          *      Initialize the event pool, including threads.
329          */
330         radius_event_init(mainconfig.config, spawn_flag);
331
332         /*
333          *      Now that we've set everything up, we can install the signal
334          *      handlers.  Before this, if we get any signal, we don't know
335          *      what to do, so we might as well do the default, and die.
336          */
337 #ifdef SIGPIPE
338         signal(SIGPIPE, SIG_IGN);
339 #endif
340 #ifdef HAVE_SIGACTION
341         act.sa_handler = sig_hup;
342         sigaction(SIGHUP, &act, NULL);
343         act.sa_handler = sig_fatal;
344         sigaction(SIGTERM, &act, NULL);
345 #else
346 #ifdef SIGHUP
347         signal(SIGHUP, sig_hup);
348 #endif
349         signal(SIGTERM, sig_fatal);
350 #endif
351         /*
352          *      If we're debugging, then a CTRL-C will cause the
353          *      server to die immediately.  Use SIGTERM to shut down
354          *      the server cleanly in that case.
355          */
356         if ((debug_memory == 1) || (debug_flag == 0)) {
357 #ifdef HAVE_SIGACTION
358                 act.sa_handler = sig_fatal;
359                 sigaction(SIGINT, &act, NULL);
360                 sigaction(SIGQUIT, &act, NULL);
361 #else
362                 signal(SIGINT, sig_fatal);
363 #ifdef SIGQUIT
364                 signal(SIGQUIT, sig_fatal);
365 #endif
366 #endif
367         }
368
369         /*
370          *      Everything seems to have loaded OK, exit gracefully.
371          */
372         if (check_config) {
373                 DEBUG("Configuration appears to be OK.");
374                 exit(0);
375         }
376
377 #ifdef WITH_STATS
378         radius_stats_init(0);
379 #endif
380
381         /*
382          *  Only write the PID file if we're running as a daemon.
383          *
384          *  And write it AFTER we've forked, so that we write the
385          *  correct PID.
386          */
387         if (dont_fork == FALSE) {
388                 FILE *fp;
389
390                 fp = fopen(mainconfig.pid_file, "w");
391                 if (fp != NULL) {
392                         /*
393                          *      FIXME: What about following symlinks,
394                          *      and having it over-write a normal file?
395                          */
396                         fprintf(fp, "%d\n", (int) radius_pid);
397                         fclose(fp);
398                 } else {
399                         radlog(L_ERR|L_CONS, "Failed creating PID file %s: %s\n",
400                                mainconfig.pid_file, strerror(errno));
401                         exit(1);
402                 }
403         }
404
405         /*
406          *      Process requests until HUP or exit.
407          */
408         while ((rcode = radius_event_process()) == 0x80) {
409                 radius_stats_init(1);
410                 hup_mainconfig();
411         }
412         
413         radlog(L_INFO, "Exiting normally.");
414         
415         /*
416          *      Ignore the TERM signal: we're
417          *      about to die.
418          */
419         signal(SIGTERM, SIG_IGN);
420         
421         /*
422          *      Send a TERM signal to all
423          *      associated processes
424          *      (including us, which gets
425          *      ignored.)
426          */
427 #ifndef __MINGW32__
428         if (spawn_flag) kill(-radius_pid, SIGTERM);
429 #endif
430         
431         /*
432          *      We're exiting, so we can delete the PID
433          *      file.  (If it doesn't exist, we can ignore
434          *      the error returned by unlink)
435          */
436         if (dont_fork == FALSE) {
437                 unlink(mainconfig.pid_file);
438         }
439                 
440         radius_event_free();
441         
442         /*
443          *      Free the configuration items.
444          */
445         free_mainconfig();
446         
447         /*
448          *      Detach any modules.
449          */
450         detach_modules();
451         
452         xlat_free();            /* modules may have xlat's */
453
454         free(radius_dir);
455                 
456 #ifdef WIN32
457         WSACleanup();
458 #endif
459
460         return (rcode - 1);
461 }
462
463
464 /*
465  *  Display the syntax for starting this program.
466  */
467 static void NEVER_RETURNS usage(int status)
468 {
469         FILE *output = status?stderr:stdout;
470
471         fprintf(output,
472                         "Usage: %s [-d db_dir] [-l log_dir] [-i address] [-n name] [-fsvXx]\n", progname);
473         fprintf(output, "Options:\n\n");
474         fprintf(output, "  -C              Check configuration and exit.\n");
475         fprintf(output, "  -d raddb_dir    Configuration files are in \"raddbdir/*\".\n");
476         fprintf(output, "  -f              Run as a foreground process, not a daemon.\n");
477         fprintf(output, "  -h              Print this help message.\n");
478         fprintf(output, "  -i ipaddr       Listen on ipaddr ONLY\n");
479         fprintf(output, "  -n name         Read raddb/name.conf instead of raddb/radiusd.conf\n");
480         fprintf(output, "  -p port         Listen on port ONLY\n");
481         fprintf(output, "  -s              Do not spawn child processes to handle requests.\n");
482         fprintf(output, "  -v              Print server version information.\n");
483         fprintf(output, "  -X              Turn on full debugging.\n");
484         fprintf(output, "  -x              Turn on additional debugging. (-xx gives more debugging).\n");
485         exit(status);
486 }
487
488
489 /*
490  *      We got a fatal signal.
491  */
492 static void sig_fatal(int sig)
493 {
494         switch(sig) {
495                 case SIGTERM:
496                         radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
497                         break;
498
499                 case SIGINT:
500 #ifdef SIGQUIT
501                 case SIGQUIT:
502 #endif
503                         if (debug_memory) {
504                                 radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
505                                 break;
506                         }
507                         /* FALL-THROUGH */
508
509                 default:
510                         _exit(sig);
511         }
512 }
513
514 #ifdef SIGHUP
515 /*
516  *  We got the hangup signal.
517  *  Re-read the configuration files.
518  */
519 static void sig_hup(int sig)
520 {
521         sig = sig; /* -Wunused */
522
523         reset_signal(SIGHUP, sig_hup);
524
525         radius_signal_self(RADIUS_SIGNAL_SELF_HUP);
526 }
527 #endif