11948b65e26386369f9285ef3b9ac9a3dd4b23b8
[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         int rcode;
93         unsigned char buffer[4096];
94         int argval;
95         pid_t pid;
96         int spawn_flag = TRUE;
97         int dont_fork = FALSE;
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], '/')) == 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          *      Now that we've set everything up, we can install the signal
388          *      handlers.  Before this, if we get any signal, we don't know
389          *      what to do, so we might as well do the default, and die.
390          */
391         signal(SIGPIPE, SIG_IGN);
392 #ifdef HAVE_SIGACTION
393         act.sa_handler = sig_hup;
394         sigaction(SIGHUP, &act, NULL);
395         act.sa_handler = sig_fatal;
396         sigaction(SIGTERM, &act, NULL);
397 #else
398         signal(SIGHUP, sig_hup);
399         signal(SIGTERM, sig_fatal);
400 #endif
401         /*
402          *      If we're debugging, then a CTRL-C will cause the
403          *      server to die immediately.  Use SIGTERM to shut down
404          *      the server cleanly in that case.
405          */
406         if ((debug_memory == 1) || (debug_flag == 0)) {
407 #ifdef HAVE_SIGACTION
408                 act.sa_handler = sig_fatal;
409                 sigaction(SIGINT, &act, NULL);
410                 sigaction(SIGQUIT, &act, NULL);
411 #else
412                 signal(SIGINT, sig_fatal);
413                 signal(SIGQUIT, sig_fatal);
414 #endif
415         }
416
417         /*
418          *      Loop while doing stuff.
419          */
420         while ((rcode = radius_event_process()) == 0x80) {
421                 /*
422                  *      HUP handler.
423                  */
424         }
425         
426         DEBUG("Exiting...");
427         
428         /*
429          *      Ignore the TERM signal: we're
430          *      about to die.
431          */
432         signal(SIGTERM, SIG_IGN);
433         
434         /*
435          *      Send a TERM signal to all
436          *      associated processes
437          *      (including us, which gets
438          *      ignored.)
439          */
440         kill(-radius_pid, SIGTERM);
441         
442         /*
443          *      We're exiting, so we can delete the PID
444          *      file.  (If it doesn't exist, we can ignore
445          *      the error returned by unlink)
446          */
447         if (dont_fork == FALSE) {
448                 unlink(mainconfig.pid_file);
449         }
450                 
451         radius_event_free();
452         
453         /*
454          *      Free the configuration items.
455          */
456         free_mainconfig();
457         
458         /*
459          *      Detach any modules.
460          */
461         detach_modules();
462         
463         free(radius_dir);
464                 
465         return (rcode - 1);
466 }
467
468
469 /*
470  *  Display the syntax for starting this program.
471  */
472 static void NEVER_RETURNS usage(int status)
473 {
474         FILE *output = status?stderr:stdout;
475
476         fprintf(output,
477                         "Usage: %s [-a acct_dir] [-d db_dir] [-l log_dir] [-i address] [-AcfnsSvXxyz]\n", progname);
478         fprintf(output, "Options:\n\n");
479         fprintf(output, "  -a acct_dir     use accounting directory 'acct_dir'.\n");
480         fprintf(output, "  -A              Log auth detail.\n");
481         fprintf(output, "  -d raddb_dir    Configuration files are in \"raddbdir/*\".\n");
482         fprintf(output, "  -f              Run as a foreground process, not a daemon.\n");
483         fprintf(output, "  -h              Print this help message.\n");
484         fprintf(output, "  -i ipaddr       Listen on ipaddr ONLY\n");
485         fprintf(output, "  -l log_dir      Log file is \"log_dir/radius.log\" (not used in debug mode)\n");
486         fprintf(output, "  -p port         Listen on port ONLY\n");
487         fprintf(output, "  -s              Do not spawn child processes to handle requests.\n");
488         fprintf(output, "  -S              Log stripped names.\n");
489         fprintf(output, "  -v              Print server version information.\n");
490         fprintf(output, "  -X              Turn on full debugging.\n");
491         fprintf(output, "  -x              Turn on additional debugging. (-xx gives more debugging).\n");
492         fprintf(output, "  -y              Log authentication failures, with password.\n");
493         fprintf(output, "  -z              Log authentication successes, with password.\n");
494         exit(status);
495 }
496
497
498 /*
499  *      We got a fatal signal.
500  */
501 static void sig_fatal(int sig)
502 {
503         switch(sig) {
504                 case SIGSEGV:
505                         /*
506                          *      We can't really do anything
507                          *      intelligent here so just die
508                          */
509                         _exit(1);
510
511                 case SIGTERM:
512                         radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
513                         break;
514
515                 case SIGINT:
516                 case SIGQUIT:
517                         if (debug_memory) {
518                                 radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
519                                 break;
520                         }
521                         /* FALL-THROUGH */
522
523                 default:
524                         radius_signal_self(RADIUS_SIGNAL_SELF_EXIT);
525                         break;
526         }
527 }
528
529
530 /*
531  *  We got the hangup signal.
532  *  Re-read the configuration files.
533  */
534 static void sig_hup(int sig)
535 {
536         sig = sig; /* -Wunused */
537
538         reset_signal(SIGHUP, sig_hup);
539
540         write(STDOUT_FILENO, "STUFF\n", 6);
541
542         radius_signal_self(RADIUS_SIGNAL_SELF_HUP);
543 }