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