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