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