#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <freeradius-devel/rad_assert.h>
#include <sys/file.h>
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#ifndef WEXITSTATUS
# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
#endif
#ifndef WIFEXITED
# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
#endif
const char *progname = NULL;
const char *radius_dir = NULL;
const char *radacct_dir = NULL;
const char *radlog_dir = NULL;
const char *radlib_dir = NULL;
int log_stripped_names;
int debug_flag = 0;
int check_config = FALSE;
const char *radiusd_version = "FreeRADIUS Version " RADIUSD_VERSION ", for host " HOSTINFO ", built on " __DATE__ " at " __TIME__;
pid_t radius_pid;
static int debug_memory = 0;
static void usage(int);
static void sig_fatal (int);
#ifdef SIGHUP
static void sig_hup (int);
#endif
int main(int argc, char *argv[])
{
int rcode;
int argval;
int spawn_flag = TRUE;
int dont_fork = FALSE;
int flag = 0;
#ifdef HAVE_SIGACTION
struct sigaction act;
#endif
#ifdef OSFC2
set_auth_parameters(argc,argv);
#endif
if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL)
progname = argv[0];
else
progname++;
#ifdef WIN32
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
fprintf(stderr, "%s: Unable to initialize socket library.\n");
return 1;
}
}
#endif
debug_flag = 0;
spawn_flag = TRUE;
radius_dir = strdup(RADIUS_DIR);
memset(&mainconfig, 0, sizeof(mainconfig));
mainconfig.myip.af = AF_UNSPEC;
mainconfig.port = -1;
mainconfig.name = "radiusd";
#ifdef HAVE_SIGACTION
memset(&act, 0, sizeof(act));
act.sa_flags = 0 ;
sigemptyset( &act.sa_mask ) ;
#endif
mainconfig.radlog_dest = RADLOG_NULL;
mainconfig.radlog_fd = -1;
mainconfig.log_file = NULL;
while ((argval = getopt(argc, argv, "Cd:fhi:l:mn:p:stvxX")) != EOF) {
switch(argval) {
case 'C':
check_config = TRUE;
spawn_flag = FALSE;
dont_fork = TRUE;
break;
case 'd':
if (radius_dir) free(radius_dir);
radius_dir = strdup(optarg);
break;
case 'f':
dont_fork = TRUE;
break;
case 'h':
usage(0);
break;
case 'l':
if (strcmp(optarg, "stdout") == 0) {
goto do_stdout;
}
mainconfig.log_file = strdup(optarg);
mainconfig.radlog_dest = RADLOG_FILES;
mainconfig.radlog_fd = open(mainconfig.log_file,
O_WRONLY | O_APPEND | O_CREAT, 0640);
if (mainconfig.radlog_fd < 0) {
fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, strerror(errno));
exit(1);
}
break;
case 'i':
if (ip_hton(optarg, AF_UNSPEC, &mainconfig.myip) < 0) {
fprintf(stderr, "radiusd: Invalid IP Address or hostname \"%s\"\n", optarg);
exit(1);
}
flag |= 1;
break;
case 'n':
mainconfig.name = optarg;
break;
case 'm':
debug_memory = 1;
break;
case 'p':
mainconfig.port = atoi(optarg);
if ((mainconfig.port <= 0) ||
(mainconfig.port >= 65536)) {
fprintf(stderr, "radiusd: Invalid port number %s\n", optarg);
exit(1);
}
flag |= 2;
break;
case 's':
spawn_flag = FALSE;
dont_fork = TRUE;
break;
case 't':
spawn_flag = FALSE;
break;
case 'v':
version();
break;
case 'X':
spawn_flag = FALSE;
dont_fork = TRUE;
debug_flag += 2;
mainconfig.log_auth = TRUE;
mainconfig.log_auth_badpass = TRUE;
mainconfig.log_auth_goodpass = TRUE;
do_stdout:
mainconfig.radlog_dest = RADLOG_STDOUT;
mainconfig.radlog_fd = STDOUT_FILENO;
fr_log_fp = stdout;
break;
case 'x':
debug_flag++;
break;
default:
usage(1);
break;
}
}
if (flag && (flag != 0x03)) {
fprintf(stderr, "radiusd: The options -i and -p cannot be used individually.\n");
exit(1);
}
if (debug_flag) {
radlog(L_INFO, "%s", radiusd_version);
radlog(L_INFO, "Copyright (C) 1999-2009 The FreeRADIUS server project and contributors.\n");
radlog(L_INFO, "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n");
radlog(L_INFO, "PARTICULAR PURPOSE.\n");
radlog(L_INFO, "You may redistribute copies of FreeRADIUS under the terms of the\n");
radlog(L_INFO, "GNU General Public License v2.\n");
fflush(NULL);
}
if (read_mainconfig(0) < 0) {
exit(1);
}
#ifndef __MINGW32__
if (dont_fork == FALSE) {
pid_t pid = fork();
if (pid < 0) {
radlog(L_ERR, "Couldn't fork: %s", strerror(errno));
exit(1);
}
if (pid > 0) {
exit(0);
}
#ifdef HAVE_SETSID
setsid();
#endif
}
#endif
radius_pid = getpid();
if (!debug_flag) {
int devnull;
devnull = open("/dev/null", O_RDWR);
if (devnull < 0) {
radlog(L_ERR|L_CONS, "Failed opening /dev/null: %s\n",
strerror(errno));
exit(1);
}
dup2(devnull, STDIN_FILENO);
if (mainconfig.radlog_dest == RADLOG_STDOUT) {
mainconfig.radlog_fd = dup(STDOUT_FILENO);
setlinebuf(stdout);
}
dup2(devnull, STDOUT_FILENO);
if (mainconfig.radlog_dest == RADLOG_STDERR) {
mainconfig.radlog_fd = dup(STDERR_FILENO);
setlinebuf(stdout);
}
dup2(devnull, STDERR_FILENO);
close(devnull);
} else {
setlinebuf(stdout);
}
radius_event_init(mainconfig.config, spawn_flag);
#ifdef SIGPIPE
signal(SIGPIPE, SIG_IGN);
#endif
#ifdef HAVE_SIGACTION
act.sa_handler = sig_hup;
sigaction(SIGHUP, &act, NULL);
act.sa_handler = sig_fatal;
sigaction(SIGTERM, &act, NULL);
#else
#ifdef SIGHUP
signal(SIGHUP, sig_hup);
#endif
signal(SIGTERM, sig_fatal);
#endif
if ((debug_memory == 1) || (debug_flag == 0)) {
#ifdef HAVE_SIGACTION
act.sa_handler = sig_fatal;
sigaction(SIGINT, &act, NULL);
sigaction(SIGQUIT, &act, NULL);
#else
signal(SIGINT, sig_fatal);
#ifdef SIGQUIT
signal(SIGQUIT, sig_fatal);
#endif
#endif
}
if (check_config) {
DEBUG("Configuration appears to be OK.");
exit(0);
}
radius_stats_init(0);
if (dont_fork == FALSE) {
FILE *fp;
fp = fopen(mainconfig.pid_file, "w");
if (fp != NULL) {
fprintf(fp, "%d\n", (int) radius_pid);
fclose(fp);
} else {
radlog(L_ERR|L_CONS, "Failed creating PID file %s: %s\n",
mainconfig.pid_file, strerror(errno));
exit(1);
}
}
while ((rcode = radius_event_process()) == 0x80) {
radius_stats_init(1);
hup_mainconfig();
}
radlog(L_INFO, "Exiting normally.");
signal(SIGTERM, SIG_IGN);
#ifndef __MINGW32__
if (spawn_flag) kill(-radius_pid, SIGTERM);
#endif
if (dont_fork == FALSE) {
unlink(mainconfig.pid_file);
}
radius_event_free();
free_mainconfig();
detach_modules();
xlat_free();
free(radius_dir);
#ifdef WIN32
WSACleanup();
#endif
return (rcode - 1);
}
static void NEVER_RETURNS usage(int status)
{
FILE *output = status?stderr:stdout;
fprintf(output,
"Usage: %s [-d db_dir] [-l log_dir] [-i address] [-n name] [-fsvXx]\n", progname);
fprintf(output, "Options:\n\n");
fprintf(output, " -C Check configuration and exit.\n");
fprintf(output, " -d raddb_dir Configuration files are in \"raddbdir/*\".\n");
fprintf(output, " -f Run as a foreground process, not a daemon.\n");
fprintf(output, " -h Print this help message.\n");
fprintf(output, " -i ipaddr Listen on ipaddr ONLY\n");
fprintf(output, " -n name Read raddb/name.conf instead of raddb/radiusd.conf\n");
fprintf(output, " -p port Listen on port ONLY\n");
fprintf(output, " -s Do not spawn child processes to handle requests.\n");
fprintf(output, " -v Print server version information.\n");
fprintf(output, " -X Turn on full debugging.\n");
fprintf(output, " -x Turn on additional debugging. (-xx gives more debugging).\n");
exit(status);
}
static void sig_fatal(int sig)
{
switch(sig) {
case SIGTERM:
radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
break;
case SIGINT:
#ifdef SIGQUIT
case SIGQUIT:
#endif
if (debug_memory) {
radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
break;
}
default:
_exit(sig);
}
}
#ifdef SIGHUP
static void sig_hup(int sig)
{
sig = sig;
reset_signal(SIGHUP, sig_hup);
radius_signal_self(RADIUS_SIGNAL_SELF_HUP);
}
#endif