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