Beispiel #1
0
void *mod_conn_create(TALLOC_CTX *ctx, void *instance)
{
	int rcode;
	rlm_sql_t *inst = instance;
	rlm_sql_handle_t *handle;

	/*
	 *	Connections cannot be alloced from the inst or
	 *	pool contexts due to threading issues.
	 */
	handle = talloc_zero(ctx, rlm_sql_handle_t);
	if (!handle) return NULL;

	handle->log_ctx = talloc_pool(handle, 2048);
	if (!handle->log_ctx) {
		talloc_free(handle);
		return NULL;
	}

	/*
	 *	Handle requires a pointer to the SQL inst so the
	 *	destructor has access to the module configuration.
	 */
	handle->inst = inst;

	/*
	 *	When something frees this handle the destructor set by
	 *	the driver will be called first, closing any open sockets.
	 *	Then we call our destructor to trigger an modules.sql.close
	 *	event, then all the memory is freed.
	 */
	talloc_set_destructor(handle, _mod_conn_free);

	rcode = (inst->module->sql_socket_init)(handle, inst->config);
	if (rcode != 0) {
	fail:
		exec_trigger(NULL, inst->cs, "modules.sql.fail", true);

		/*
		 *	Destroy any half opened connections.
		 */
		talloc_free(handle);
		return NULL;
	}

	if (inst->config->connect_query) {
		if (rlm_sql_select_query(inst, NULL, &handle, inst->config->connect_query) != RLM_SQL_OK) goto fail;
		(inst->module->sql_finish_select_query)(handle, inst->config);
	}

	exec_trigger(NULL, inst->cs, "modules.sql.open", false);
	return handle;
}
Beispiel #2
0
int h_stop(struct cmd_desc *cmdd, struct atom *atoms)
{
	if (atoms == (struct atom *)VERBOSE_HELP) {
		printf("%s - Stop Sampling\n", cmdd->name);
		return 1;
	}
	return exec_trigger(SIS33_TRIGGER_STOP);
}
static void *mod_conn_create(void *instance)
{
	int rcode;
	rlm_sql_t *inst = instance;
	rlm_sql_handle_t *handle;

	handle = talloc_zero(instance, rlm_sql_handle_t);

	/*
	 *	Handle requires a pointer to the SQL inst so the
	 *	destructor has access to the module configuration.
	 */
	handle->inst = inst;
	handle->init = false;

	/*
	 *	When something frees this handle the destructor set by
	 *	the driver will be called first, closing any open sockets.
	 *	Then we call our destructor to trigger an modules.sql.close
	 *	event, then all the memory is freed.
	 */
	talloc_set_destructor(handle, _sql_conn_destructor);

	rcode = (inst->module->sql_socket_init)(handle, inst->config);
	if (rcode != 0) {
	fail:
		exec_trigger(NULL, inst->cs, "modules.sql.fail", true);

		/*
		 *	Destroy any half opened connections.
		 */
		talloc_free(handle);
		return NULL;
	}

	if (inst->config->open_query && *inst->config->open_query) {
		if (rlm_sql_select_query(&handle, inst, inst->config->open_query)) {
			goto fail;
		}
	}

	exec_trigger(NULL, inst->cs, "modules.sql.open", false);
	handle->init = true;
	return handle;
}
Beispiel #4
0
static int _mod_conn_free(rlm_sql_handle_t *conn)
{
	rlm_sql_t *inst = conn->inst;

	rad_assert(inst);

	exec_trigger(NULL, inst->cs, "modules.sql.close", false);

	return 0;
}
Beispiel #5
0
static void *sql_conn_create(void *ctx)
{
	int rcode;
	SQL_INST *inst = ctx;
	SQLSOCK *sqlsocket;

	sqlsocket = rad_malloc(sizeof(*sqlsocket));
	memset(sqlsocket, 0, sizeof(*sqlsocket));

	rcode = (inst->module->sql_init_socket)(sqlsocket, inst->config);
	if (rcode == 0) {
	  exec_trigger(NULL, inst->cs, "modules.sql.open", FALSE);
		return sqlsocket;
	}

	exec_trigger(NULL, inst->cs, "modules.sql.fail", TRUE);

	free(sqlsocket);
	return NULL;
}
Beispiel #6
0
static int sql_conn_delete(void *ctx, void *connection)
{
	SQL_INST *inst = ctx;
	SQLSOCK *sqlsocket = connection;

	exec_trigger(NULL, inst->cs, "modules.sql.close", FALSE);

	if (sqlsocket->conn) {
		(inst->module->sql_close)(sqlsocket, inst->config);
	}
	if (inst->module->sql_destroy_socket) {
		(inst->module->sql_destroy_socket)(sqlsocket, inst->config);
	}
	free(sqlsocket);

	return 0;
}
Beispiel #7
0
/**
 * Main entry point for processing arguments and starting the split process
 */
int main(int argc, char **argv) {
	// Create our context object, null it out
	struct csv_context ctx;
    memset(&ctx, 0, sizeof(struct csv_context));

    // Initialize buffers
    context_init(&ctx);

    // Attempt to parse our arguments
    parse_args(&ctx, argc, argv);

    // Allocate memory for thread storage
    ctx.io_threads = malloc(ctx.thread_count * sizeof *ctx.io_threads);

    // OOM sanity check
    if(!ctx.io_threads) {
        fprintf(stderr, "Error:  Couldn't allocate thread storage.\n");
        exit(EXIT_FAILURE);
    }

    // Initialize our IO threads
    spool_threads(&ctx);

    // Process our input
    process_csv(&ctx);

    // Signal that we're done inside our queue
    fq_fin(&ctx.io_queue);

    // Join our threads
    join_threads(&ctx);

    // One last trigger showing we're done
    exec_trigger(ctx.trigger_cmd, "", 0);

    // Free memory from our context
    context_free(&ctx);

    // Success
    return 0;
}
Beispiel #8
0
/**
 * Our IO worker thread, where we wait on our IO queue (files to be written)
 * and write them as we get them.  Once the queue is flagged done, we'll finish
 */
void *io_worker(void *arg) {
    // Grab our queue
    fqueue *queue = (fqueue*)arg;

    struct q_flush_item *item;
    void *itm_ptr;
    char out_file[1024];

    // Block until we have work, or we're done
    while(!fq_get(queue, &itm_ptr)) {
        // Assign the item for us
        item = itm_ptr;

        // Write either uncompressed or compressed data
        if(item->gzip) {
            // Append gz extension and write the file
            snprintf(out_file, sizeof(out_file),"%s.gz", item->out_file);
            write_gz_file(out_file, item->str, item->len, item->gzip);
        } else {
        	// We're writing to the filename passed
        	strncpy(out_file, item->out_file, sizeof(out_file));
        	write_file(out_file, item->str, item->len);
        }

        // Execute our trigger if one is set
        if(item->trigger_cmd) {
            exec_trigger(item->trigger_cmd, out_file, item->row_count);
        }

        // Now free our memory as this was a copy
        free(item->str);
        free(item);
    }

    return NULL;
}
Beispiel #9
0
/*
 *	Spawn a new thread, and place it in the thread pool.
 *
 *	The thread is started initially in the blocked state, waiting
 *	for the semaphore.
 */
static THREAD_HANDLE *spawn_thread(time_t now, int do_trigger)
{
	int rcode;
	THREAD_HANDLE *handle;

	/*
	 *	Ensure that we don't spawn too many threads.
	 */
	if (thread_pool.total_threads >= thread_pool.max_threads) {
		DEBUG2("Thread spawn failed.  Maximum number of threads (%d) already running.", thread_pool.max_threads);
		exec_trigger(NULL, NULL, "server.thread.max_threads", TRUE);
		return NULL;
	}

	/*
	 *	Allocate a new thread handle.
	 */
	handle = (THREAD_HANDLE *) rad_malloc(sizeof(THREAD_HANDLE));
	memset(handle, 0, sizeof(THREAD_HANDLE));
	handle->prev = NULL;
	handle->next = NULL;
	handle->thread_num = thread_pool.max_thread_num++;
	handle->request_count = 0;
	handle->status = THREAD_RUNNING;
	handle->timestamp = time(NULL);

	/*
	 *	Create the thread joinable, so that it can be cleaned up
	 *	using pthread_join().
	 *
	 *	Note that the function returns non-zero on error, NOT
	 *	-1.  The return code is the error, and errno isn't set.
	 */
	rcode = pthread_create(&handle->pthread_id, 0,
			request_handler_thread, handle);
	if (rcode != 0) {
		radlog(L_ERR, "Thread create failed: %s",
		       strerror(rcode));
		return NULL;
	}

	/*
	 *	One more thread to go into the list.
	 */
	thread_pool.total_threads++;
	DEBUG2("Thread spawned new child %d. Total threads in pool: %d",
			handle->thread_num, thread_pool.total_threads);
	if (do_trigger) exec_trigger(NULL, NULL, "server.thread.start", TRUE);

	/*
	 *	Add the thread handle to the tail of the thread pool list.
	 */
	if (thread_pool.tail) {
		thread_pool.tail->next = handle;
		handle->prev = thread_pool.tail;
		thread_pool.tail = handle;
	} else {
		rad_assert(thread_pool.head == NULL);
		thread_pool.head = thread_pool.tail = handle;
	}

	/*
	 *	Update the time we last spawned a thread.
	 */
	thread_pool.time_last_spawned = now;

	/*
	 *	And return the new handle to the caller.
	 */
	return handle;
}
Beispiel #10
0
/*
 *	The main thread handler for requests.
 *
 *	Wait on the semaphore until we have it, and process the request.
 */
static void *request_handler_thread(void *arg)
{
	THREAD_HANDLE	  *self = (THREAD_HANDLE *) arg;

	/*
	 *	Loop forever, until told to exit.
	 */
	do {
		/*
		 *	Wait to be signalled.
		 */
		DEBUG2("Thread %d waiting to be assigned a request",
		       self->thread_num);
	re_wait:
		if (sem_wait(&thread_pool.semaphore) != 0) {
			/*
			 *	Interrupted system call.  Go back to
			 *	waiting, but DON'T print out any more
			 *	text.
			 */
			if (errno == EINTR) {
				DEBUG2("Re-wait %d", self->thread_num);
				goto re_wait;
			}
			radlog(L_ERR, "Thread %d failed waiting for semaphore: %s: Exiting\n",
			       self->thread_num, strerror(errno));
			break;
		}

		DEBUG2("Thread %d got semaphore", self->thread_num);

#ifdef HAVE_OPENSSL_ERR_H
 		/*
		 *	Clear the error queue for the current thread.
		 */
		ERR_clear_error ();
#endif

		/*
		 *	The server is exiting.  Don't dequeue any
		 *	requests.
		 */
		if (thread_pool.stop_flag) break;

		/*
		 *	Try to grab a request from the queue.
		 *
		 *	It may be empty, in which case we fail
		 *	gracefully.
		 */
		if (!request_dequeue(&self->request)) continue;

		self->request->child_pid = self->pthread_id;
		self->request_count++;

		DEBUG2("Thread %d handling request %d, (%d handled so far)",
		       self->thread_num, self->request->number,
		       self->request_count);

		if ((self->request->packet->code == PW_ACCOUNTING_REQUEST) &&
		    thread_pool.auto_limit_acct) {
			VALUE_PAIR *vp;
			REQUEST *request = self->request;

			vp = radius_paircreate(request, &request->config_items,
					       181, VENDORPEC_FREERADIUS);
			if (vp) vp->vp_integer = thread_pool.pps_in.pps;

			vp = radius_paircreate(request, &request->config_items,
					       182, VENDORPEC_FREERADIUS);
			if (vp) vp->vp_integer = thread_pool.pps_in.pps;
			
			vp = radius_paircreate(request, &request->config_items,
					       183, VENDORPEC_FREERADIUS);
			if (vp) {
				vp->vp_integer = thread_pool.max_queue_size - thread_pool.num_queued;
				vp->vp_integer *= 100;
				vp->vp_integer /= thread_pool.max_queue_size;
			}
		}

		self->request->process(self->request, FR_ACTION_RUN);
		self->request = NULL;

		/*
		 *	Update the active threads.
		 */
		pthread_mutex_lock(&thread_pool.queue_mutex);
		rad_assert(thread_pool.active_threads > 0);
		thread_pool.active_threads--;
		pthread_mutex_unlock(&thread_pool.queue_mutex);
	} while (self->status != THREAD_CANCELLED);

	DEBUG2("Thread %d exiting...", self->thread_num);

#ifdef HAVE_OPENSSL_ERR_H
	/*
	 *	If we linked with OpenSSL, the application
	 *	must remove the thread's error queue before
	 *	exiting to prevent memory leaks.
	 */
	ERR_remove_state(0);
#endif

	/*
	 *  Do this as the LAST thing before exiting.
	 */
	self->request = NULL;
	self->status = THREAD_EXITED;
	exec_trigger(NULL, NULL, "server.thread.stop", TRUE);

	return NULL;
}
Beispiel #11
0
/*
 *	The main guy.
 */
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", progname);
			return 1;
		}
	}
#endif

	debug_flag = 0;
	spawn_flag = TRUE;
	radius_dir = strdup(RADIUS_DIR);

	/*
	 *	Ensure that the configuration is initialized.
	 */
	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

	/*
	 *	Don't put output anywhere until we get told a little
	 *	more.
	 */
	mainconfig.radlog_dest = RADLOG_NULL;
	mainconfig.radlog_fd = -1;
	mainconfig.log_file = NULL;

	/*  Process the options.  */
	while ((argval = getopt(argc, argv, "Cd:fhi:l:mMn: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);
				}
				fr_log_fp = fdopen(mainconfig.radlog_fd, "a");
				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':
				mainconfig.debug_memory = 1;
				break;

			case 'M':
				memory_report = 1;
				mainconfig.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':	/* Single process mode */
				spawn_flag = FALSE;
				dont_fork = TRUE;
				break;

			case 't':	/* no child threads */
				spawn_flag = FALSE;
				break;

			case 'v':
				/* Don't print timestamps */
				debug_flag += 2;
				fr_log_fp = stdout;
				mainconfig.radlog_dest = RADLOG_STDOUT;
				mainconfig.radlog_fd = STDOUT_FILENO;
				
				version();
				exit(0);
			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:
				fr_log_fp = stdout;
				mainconfig.radlog_dest = RADLOG_STDOUT;
				mainconfig.radlog_fd = STDOUT_FILENO;
				break;

			case 'x':
				debug_flag++;
				break;

			default:
				usage(1);
				break;
		}
	}

	if (memory_report) {
		talloc_enable_null_tracking();
		talloc_set_log_fn(log_talloc);
	}

	/*
	 *	Mismatch between build time OpenSSL and linked SSL,
	 *	better to die here than segfault later.
	 */
#ifdef HAVE_OPENSSL_CRYPTO_H
	if (ssl_check_version() < 0) {
		exit(1);
	}
#endif

	if (flag && (flag != 0x03)) {
		fprintf(stderr, "radiusd: The options -i and -p cannot be used individually.\n");
		exit(1);
	}

	if (debug_flag)
		version();
		

	/*  Read the configuration files, BEFORE doing anything else.  */
	if (read_mainconfig(0) < 0) {
		exit(1);
	}

#ifndef __MINGW32__
	/*
	 *  Disconnect from session
	 */
	if (dont_fork == FALSE) {
		pid_t pid = fork();

		if (pid < 0) {
			radlog(L_ERR, "Couldn't fork: %s", strerror(errno));
			exit(1);
		}

		/*
		 *  The parent exits, so the child can run in the background.
		 */
		if (pid > 0) {
			exit(0);
		}
#ifdef HAVE_SETSID
		setsid();
#endif
	}
#endif

	/*
	 *  Ensure that we're using the CORRECT pid after forking,
	 *  NOT the one we started with.
	 */
	radius_pid = getpid();

	/*
	 *	If we're running as a daemon, close the default file
	 *	descriptors, AFTER forking.
	 */
	if (!debug_flag) {
		int devnull;

		devnull = open("/dev/null", O_RDWR);
		if (devnull < 0) {
			radlog(L_ERR, "Failed opening /dev/null: %s\n",
			       strerror(errno));
			exit(1);
		}
		dup2(devnull, STDIN_FILENO);
		if (mainconfig.radlog_dest == RADLOG_STDOUT) {
			setlinebuf(stdout);
			mainconfig.radlog_fd = STDOUT_FILENO;
		} else {
			dup2(devnull, STDOUT_FILENO);
		}
		if (mainconfig.radlog_dest == RADLOG_STDERR) {
			setlinebuf(stderr);
			mainconfig.radlog_fd = STDERR_FILENO;
		} else {
			dup2(devnull, STDERR_FILENO);
		}
		close(devnull);

	} else {
		setlinebuf(stdout); /* unbuffered output */
	}
	
	/*
	 *	Now we have logging check that the OpenSSL 
	 */

	/*
	 *	Initialize the event pool, including threads.
	 */
	radius_event_init(mainconfig.config, spawn_flag);

	/*
	 *	Now that we've set everything up, we can install the signal
	 *	handlers.  Before this, if we get any signal, we don't know
	 *	what to do, so we might as well do the default, and die.
	 */
#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 we're debugging, then a CTRL-C will cause the
	 *	server to die immediately.  Use SIGTERM to shut down
	 *	the server cleanly in that case.
	 */
	if ((mainconfig.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
	}

	/*
	 *	Everything seems to have loaded OK, exit gracefully.
	 */
	if (check_config) {
		DEBUG("Configuration appears to be OK.");
		exit(0);
	}

#ifdef WITH_STATS
	radius_stats_init(0);
#endif

	/*
	 *  Only write the PID file if we're running as a daemon.
	 *
	 *  And write it AFTER we've forked, so that we write the
	 *  correct PID.
	 */
	if (dont_fork == FALSE) {
		FILE *fp;

		fp = fopen(mainconfig.pid_file, "w");
		if (fp != NULL) {
			/*
			 *	FIXME: What about following symlinks,
			 *	and having it over-write a normal file?
			 */
			fprintf(fp, "%d\n", (int) radius_pid);
			fclose(fp);
		} else {
			radlog(L_ERR, "Failed creating PID file %s: %s\n",
			       mainconfig.pid_file, strerror(errno));
			exit(1);
		}
	}

	exec_trigger(NULL, NULL, "server.start", FALSE);

	/*
	 *	Process requests until HUP or exit.
	 */
	while ((rcode = radius_event_process()) == 0x80) {
#ifdef WITH_STATS
		radius_stats_init(1);
#endif
		hup_mainconfig();
	}

	if (rcode < 0) {
		radlog(L_ERR, "Exiting due to internal error: %s",
		       fr_strerror());
		rcode = 2;
	} else {
		radlog(L_INFO, "Exiting normally.");
	}

	exec_trigger(NULL, NULL, "server.stop", FALSE);

	/*
	 *	Ignore the TERM signal: we're
	 *	about to die.
	 */
	signal(SIGTERM, SIG_IGN);
	
	/*
	 *	Send a TERM signal to all
	 *	associated processes
	 *	(including us, which gets
	 *	ignored.)
	 */
#ifndef __MINGW32__
	if (spawn_flag) kill(-radius_pid, SIGTERM);
#endif
	
	/*
	 *	We're exiting, so we can delete the PID
	 *	file.  (If it doesn't exist, we can ignore
	 *	the error returned by unlink)
	 */
	if (dont_fork == FALSE) {
		unlink(mainconfig.pid_file);
	}
		
	radius_event_free();
	
	/*
	 *	Detach any modules.
	 */
	detach_modules();
	
	xlat_free();		/* modules may have xlat's */

	/*
	 *	Free the configuration items.
	 */
	free_mainconfig();
	
	free(radius_dir);
		
#ifdef WIN32
	WSACleanup();
#endif

	if (memory_report) {
		log_talloc_report(NULL);
	}

	return (rcode - 1);
}
Beispiel #12
0
/*
 *	The main guy.
 */
int main(int argc, char *argv[])
{
	int rcode = EXIT_SUCCESS;
	int status;
	int argval;
	bool spawn_flag = true;
	bool write_pid = false;
	bool display_version = false;
	int flag = 0;
	int from_child[2] = {-1, -1};
	fr_state_t *state = NULL;

	/*
	 *  We probably don't want to free the talloc autofree context
	 *  directly, so we'll allocate a new context beneath it, and
	 *  free that before any leak reports.
	 */
	TALLOC_CTX *autofree = talloc_init("main");

#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", progname);
			exit(EXIT_FAILURE);
		}
	}
#endif

	rad_debug_lvl = 0;
	set_radius_dir(autofree, RADIUS_DIR);

	/*
	 *	Ensure that the configuration is initialized.
	 */
	memset(&main_config, 0, sizeof(main_config));
	main_config.myip.af = AF_UNSPEC;
	main_config.port = 0;
	main_config.name = "radiusd";
	main_config.daemonize = true;

	/*
	 *	Don't put output anywhere until we get told a little
	 *	more.
	 */
	default_log.dst = L_DST_NULL;
	default_log.fd = -1;
	main_config.log_file = NULL;

	/*  Process the options.  */
	while ((argval = getopt(argc, argv, "Cd:D:fhi:l:mMn:p:PstvxX")) != EOF) {

		switch (argval) {
			case 'C':
				check_config = true;
				spawn_flag = false;
				main_config.daemonize = false;
				break;

			case 'd':
				set_radius_dir(autofree, optarg);
				break;

			case 'D':
				main_config.dictionary_dir = talloc_typed_strdup(autofree, optarg);
				break;

			case 'f':
				main_config.daemonize = false;
				break;

			case 'h':
				usage(0);
				break;

			case 'l':
				if (strcmp(optarg, "stdout") == 0) {
					goto do_stdout;
				}
				main_config.log_file = strdup(optarg);
				default_log.dst = L_DST_FILES;
				default_log.fd = open(main_config.log_file,
							    O_WRONLY | O_APPEND | O_CREAT, 0640);
				if (default_log.fd < 0) {
					fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", main_config.log_file, fr_syserror(errno));
					exit(EXIT_FAILURE);
				}
				fr_log_fp = fdopen(default_log.fd, "a");
				break;

			case 'i':
				if (ip_hton(&main_config.myip, AF_UNSPEC, optarg, false) < 0) {
					fprintf(stderr, "radiusd: Invalid IP Address or hostname \"%s\"\n", optarg);
					exit(EXIT_FAILURE);
				}
				flag |= 1;
				break;

			case 'n':
				main_config.name = optarg;
				break;

			case 'm':
				main_config.debug_memory = true;
				break;

			case 'M':
				main_config.memory_report = true;
				main_config.debug_memory = true;
				break;

			case 'p':
			{
				unsigned long port;

				port = strtoul(optarg, 0, 10);
				if ((port == 0) || (port > UINT16_MAX)) {
					fprintf(stderr, "radiusd: Invalid port number \"%s\"\n", optarg);
					exit(EXIT_FAILURE);
				}

				main_config.port = (uint16_t) port;
				flag |= 2;
			}
				break;

			case 'P':
				/* Force the PID to be written, even in -f mode */
				write_pid = true;
				break;

			case 's':	/* Single process mode */
				spawn_flag = false;
				main_config.daemonize = false;
				break;

			case 't':	/* no child threads */
				spawn_flag = false;
				break;

			case 'v':
				display_version = true;
				break;

			case 'X':
				spawn_flag = false;
				main_config.daemonize = false;
				rad_debug_lvl += 2;
				main_config.log_auth = true;
				main_config.log_auth_badpass = true;
				main_config.log_auth_goodpass = true;
		do_stdout:
				fr_log_fp = stdout;
				default_log.dst = L_DST_STDOUT;
				default_log.fd = STDOUT_FILENO;
				break;

			case 'x':
				rad_debug_lvl++;
				break;

			default:
				usage(1);
				break;
		}
	}

	/*
	 *  Mismatch between the binary and the libraries it depends on.
	 */
	if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
		fr_perror("radiusd");
		exit(EXIT_FAILURE);
	}

	if (rad_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) exit(EXIT_FAILURE);

	/*
	 *  Mismatch between build time OpenSSL and linked SSL, better to die
	 *  here than segfault later.
	 */
#ifdef HAVE_OPENSSL_CRYPTO_H
	if (ssl_check_consistency() < 0) exit(EXIT_FAILURE);
#endif

	if (flag && (flag != 0x03)) {
		fprintf(stderr, "radiusd: The options -i and -p cannot be used individually.\n");
		exit(EXIT_FAILURE);
	}

	/*
	 *  According to the talloc peeps, no two threads may modify any part of
	 *  a ctx tree with a common root without synchronisation.
	 *
	 *  So we can't run with a null context and threads.
	 */
	if (main_config.memory_report) {
		if (spawn_flag) {
			fprintf(stderr, "radiusd: The server cannot produce memory reports (-M) in threaded mode\n");
			exit(EXIT_FAILURE);
		}
		talloc_enable_null_tracking();
	} else {
		talloc_disable_null_tracking();
	}

	/*
	 *  Better here, so it doesn't matter whether we get passed -xv or -vx.
	 */
	if (display_version) {
		/* Don't print timestamps */
		rad_debug_lvl += 2;
		fr_log_fp = stdout;
		default_log.dst = L_DST_STDOUT;
		default_log.fd = STDOUT_FILENO;

		INFO("%s: %s", progname, radiusd_version);
		version_print();
		exit(EXIT_SUCCESS);
	}

	if (rad_debug_lvl) version_print();

	/*
	 *  Under linux CAP_SYS_PTRACE is usually only available before setuid/setguid,
	 *  so we need to check whether we can attach before calling those functions
	 *  (in main_config_init()).
	 */
	fr_store_debug_state();

	/*
	 *  Initialising OpenSSL once, here, is safer than having individual modules do it.
	 */
#ifdef HAVE_OPENSSL_CRYPTO_H
	tls_global_init();
#endif

	/*
	 *  Read the configuration files, BEFORE doing anything else.
	 */
	if (main_config_init() < 0) exit(EXIT_FAILURE);

	/*
	 *  This is very useful in figuring out why the panic_action didn't fire.
	 */
	INFO("%s", fr_debug_state_to_msg(fr_debug_state));

	/*
	 *  Check for vulnerabilities in the version of libssl were linked against.
	 */
#if defined(HAVE_OPENSSL_CRYPTO_H) && defined(ENABLE_OPENSSL_VERSION_CHECK)
	if (tls_global_version_check(main_config.allow_vulnerable_openssl) < 0) exit(EXIT_FAILURE);
#endif

	fr_talloc_fault_setup();

	/*
	 *  Set the panic action (if required)
	 */
	{
		char const *panic_action = NULL;

		panic_action = getenv("PANIC_ACTION");
		if (!panic_action) panic_action = main_config.panic_action;

		if (panic_action && (fr_fault_setup(panic_action, argv[0]) < 0)) {
			fr_perror("radiusd");
			exit(EXIT_FAILURE);
		}
	}

#ifndef __MINGW32__
	/*
	 *  Disconnect from session
	 */
	if (main_config.daemonize) {
		pid_t pid;
		int devnull;

		/*
		 *  Really weird things happen if we leave stdin open and call things like
		 *  system() later.
		 */
		devnull = open("/dev/null", O_RDWR);
		if (devnull < 0) {
			ERROR("Failed opening /dev/null: %s", fr_syserror(errno));
			exit(EXIT_FAILURE);
		}
		dup2(devnull, STDIN_FILENO);

		close(devnull);

		if (pipe(from_child) != 0) {
			ERROR("Couldn't open pipe for child status: %s", fr_syserror(errno));
			exit(EXIT_FAILURE);
		}

		pid = fork();
		if (pid < 0) {
			ERROR("Couldn't fork: %s", fr_syserror(errno));
			exit(EXIT_FAILURE);
		}

		/*
		 *  The parent exits, so the child can run in the background.
		 *
		 *  As the child can still encounter an error during initialisation
		 *  we do a blocking read on a pipe between it and the parent.
		 *
		 *  Just before entering the event loop the child will send a success
		 *  or failure message to the parent, via the pipe.
		 */
		if (pid > 0) {
			uint8_t ret = 0;
			int stat_loc;

			/* So the pipe is correctly widowed if the child exits */
			close(from_child[1]);

			/*
			 *  The child writes a 0x01 byte on success, and closes
			 *  the pipe on error.
			 */
			if ((read(from_child[0], &ret, 1) < 0)) {
				ret = 0;
			}

			/* For cleanliness... */
			close(from_child[0]);

			/* Don't turn children into zombies */
			if (!ret) {
				waitpid(pid, &stat_loc, WNOHANG);
				exit(EXIT_FAILURE);
			}

			exit(EXIT_SUCCESS);
		}

		/* so the pipe is correctly widowed if the parent exits?! */
		close(from_child[0]);
#  ifdef HAVE_SETSID
		setsid();
#  endif
	}
#endif

	/*
	 *  Ensure that we're using the CORRECT pid after forking, NOT the one
	 *  we started with.
	 */
	radius_pid = getpid();

	/*
	 *  Initialize any event loops just enough so module instantiations can
	 *  add fd/event to them, but do not start them yet.
	 *
	 *  This has to be done post-fork in case we're using kqueue, where the
	 *  queue isn't inherited by the child process.
	 */
	if (!radius_event_init(autofree)) exit(EXIT_FAILURE);

	/*
	 *   Load the modules
	 */
	if (modules_init(main_config.config) < 0) exit(EXIT_FAILURE);

	/*
	 *  Redirect stderr/stdout as appropriate.
	 */
	if (radlog_init(&default_log, main_config.daemonize) < 0) {
		ERROR("%s", fr_strerror());
		exit(EXIT_FAILURE);
	}

	event_loop_started = true;

	/*
	 *  Start the event loop(s) and threads.
	 */
	radius_event_start(main_config.config, spawn_flag);

	/*
	 *  Now that we've set everything up, we can install the signal
	 *  handlers.  Before this, if we get any signal, we don't know
	 *  what to do, so we might as well do the default, and die.
	 */
#ifdef SIGPIPE
	signal(SIGPIPE, SIG_IGN);
#endif

	if ((fr_set_signal(SIGHUP, sig_hup) < 0) ||
	    (fr_set_signal(SIGTERM, sig_fatal) < 0)) {
		ERROR("%s", fr_strerror());
		exit(EXIT_FAILURE);
	}

	/*
	 *  If we're debugging, then a CTRL-C will cause the server to die
	 *  immediately.  Use SIGTERM to shut down the server cleanly in
	 *  that case.
	 */
	if (main_config.debug_memory || (rad_debug_lvl == 0)) {
		if ((fr_set_signal(SIGINT, sig_fatal) < 0)
#ifdef SIGQUIT
		|| (fr_set_signal(SIGQUIT, sig_fatal) < 0)
#endif
		) {
			ERROR("%s", fr_strerror());
			exit(EXIT_FAILURE);
		}
	}

	/*
	 *  Everything seems to have loaded OK, exit gracefully.
	 */
	if (check_config) {
		DEBUG("Configuration appears to be OK");

		/* for -C -m|-M */
		if (main_config.debug_memory) goto cleanup;

		exit(EXIT_SUCCESS);
	}

#ifdef WITH_STATS
	radius_stats_init(0);
#endif

	/*
	 *  Write the PID always if we're running as a daemon.
	 */
	if (main_config.daemonize) write_pid = true;

	/*
	 *  Write the PID after we've forked, so that we write the correct one.
	 */
	if (write_pid) {
		FILE *fp;

		fp = fopen(main_config.pid_file, "w");
		if (fp != NULL) {
			/*
			 *  @fixme What about following symlinks,
			 *  and having it over-write a normal file?
			 */
			fprintf(fp, "%d\n", (int) radius_pid);
			fclose(fp);
		} else {
			ERROR("Failed creating PID file %s: %s", main_config.pid_file, fr_syserror(errno));
			exit(EXIT_FAILURE);
		}
	}

	exec_trigger(NULL, NULL, "server.start", false);

	/*
	 *  Inform the parent (who should still be waiting) that the rest of
	 *  initialisation went OK, and that it should exit with a 0 status.
	 *  If we don't get this far, then we just close the pipe on exit, and the
	 *  parent gets a read failure.
	 */
	if (main_config.daemonize) {
		if (write(from_child[1], "\001", 1) < 0) {
			WARN("Failed informing parent of successful start: %s",
			     fr_syserror(errno));
		}
		close(from_child[1]);
	}

	/*
	 *  Clear the libfreeradius error buffer.
	 */
	fr_strerror();

	/*
	 *  Initialise the state rbtree (used to link multiple rounds of challenges).
	 */
	state = fr_state_init(NULL, 0);

	/*
	 *  Process requests until HUP or exit.
	 */
	while ((status = radius_event_process()) == 0x80) {
#ifdef WITH_STATS
		radius_stats_init(1);
#endif
		main_config_hup();
	}
	if (status < 0) {
		ERROR("Exiting due to internal error: %s", fr_strerror());
		rcode = EXIT_FAILURE;
	} else {
		INFO("Exiting normally");
	}

	/*
	 *  Ignore the TERM signal: we're about to die.
	 */
	signal(SIGTERM, SIG_IGN);

	/*
	 *   Fire signal and stop triggers after ignoring SIGTERM, so handlers are
	 *   not killed with the rest of the process group, below.
	 */
	if (status == 2) exec_trigger(NULL, NULL, "server.signal.term", true);
	exec_trigger(NULL, NULL, "server.stop", false);

	/*
	 *  Send a TERM signal to all associated processes
	 *  (including us, which gets ignored.)
	 */
#ifndef __MINGW32__
	if (spawn_flag) kill(-radius_pid, SIGTERM);
#endif

	/*
	 *  We're exiting, so we can delete the PID file.
	 *  (If it doesn't exist, we can ignore the error returned by unlink)
	 */
	if (main_config.daemonize) unlink(main_config.pid_file);

	radius_event_free();

cleanup:
	/*
	 *  Detach any modules.
	 */
	modules_free();

	xlat_free();		/* modules may have xlat's */

	fr_state_delete(state);

	/*
	 *  Free the configuration items.
	 */
	main_config_free();

#ifdef WIN32
	WSACleanup();
#endif

#ifdef HAVE_OPENSSL_CRYPTO_H
	tls_global_cleanup();
#endif
	/*
	 *  So we don't see autofreed memory in the talloc report
	 */
	talloc_free(autofree);

	if (main_config.memory_report) {
		INFO("Allocated memory at time of report:");
		fr_log_talloc_report(NULL);
	}

	return rcode;
}
/*
 *	Spawn a new thread, and place it in the thread pool.
 *
 *	The thread is started initially in the blocked state, waiting
 *	for the semaphore.
 */
static THREAD_HANDLE *spawn_thread(time_t now, int do_trigger)
{
	int rcode;
	THREAD_HANDLE *handle;
	pthread_attr_t attr;

	/*
	 *	Ensure that we don't spawn too many threads.
	 */
	if (thread_pool.total_threads >= thread_pool.max_threads) {
		DEBUG2("Thread spawn failed.  Maximum number of threads (%d) already running.", thread_pool.max_threads);
		exec_trigger(NULL, NULL, "server.thread.max_threads");
		return NULL;
	}

	/*
	 *	Allocate a new thread handle.
	 */
	handle = (THREAD_HANDLE *) rad_malloc(sizeof(THREAD_HANDLE));
	memset(handle, 0, sizeof(THREAD_HANDLE));
	handle->prev = NULL;
	handle->next = NULL;
	handle->thread_num = thread_pool.max_thread_num++;
	handle->request_count = 0;
	handle->status = THREAD_RUNNING;
	handle->timestamp = time(NULL);

	/*
	 *	Initialize the thread's attributes to detached.
	 *
	 *	We could call pthread_detach() later, but if the thread
	 *	exits between the create & detach calls, it will need to
	 *	be joined, which will never happen.
	 */
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

	/*
	 *	Create the thread detached, so that it cleans up it's
	 *	own memory when it exits.
	 *
	 *	Note that the function returns non-zero on error, NOT
	 *	-1.  The return code is the error, and errno isn't set.
	 */
	rcode = pthread_create(&handle->pthread_id, &attr,
			request_handler_thread, handle);
	if (rcode != 0) {
		radlog(L_ERR, "Thread create failed: %s",
		       strerror(rcode));
		return NULL;
	}
	pthread_attr_destroy(&attr);

	/*
	 *	One more thread to go into the list.
	 */
	thread_pool.total_threads++;
	DEBUG2("Thread spawned new child %d. Total threads in pool: %d",
			handle->thread_num, thread_pool.total_threads);
	if (do_trigger) exec_trigger(NULL, NULL, "server.thread.start");

	/*
	 *	Add the thread handle to the tail of the thread pool list.
	 */
	if (thread_pool.tail) {
		thread_pool.tail->next = handle;
		handle->prev = thread_pool.tail;
		thread_pool.tail = handle;
	} else {
		rad_assert(thread_pool.head == NULL);
		thread_pool.head = thread_pool.tail = handle;
	}

	/*
	 *	Update the time we last spawned a thread.
	 */
	thread_pool.time_last_spawned = now;

	/*
	 *	And return the new handle to the caller.
	 */
	return handle;
}
/*
 *	The main thread handler for requests.
 *
 *	Wait on the semaphore until we have it, and process the request.
 */
static void *request_handler_thread(void *arg)
{
	THREAD_HANDLE	  *self = (THREAD_HANDLE *) arg;

	/*
	 *	Loop forever, until told to exit.
	 */
	do {
		/*
		 *	Wait to be signalled.
		 */
		DEBUG2("Thread %d waiting to be assigned a request",
		       self->thread_num);
	re_wait:
		if (sem_wait(&thread_pool.semaphore) != 0) {
			/*
			 *	Interrupted system call.  Go back to
			 *	waiting, but DON'T print out any more
			 *	text.
			 */
			if (errno == EINTR) {
				DEBUG2("Re-wait %d", self->thread_num);
				goto re_wait;
			}
			radlog(L_ERR, "Thread %d failed waiting for semaphore: %s: Exiting\n",
			       self->thread_num, strerror(errno));
			break;
		}

		DEBUG2("Thread %d got semaphore", self->thread_num);

#ifdef HAVE_OPENSSL_ERR_H
 		/*
		 *	Clear the error queue for the current thread.
		 */
		ERR_clear_error ();
#endif

		/*
		 *	Try to grab a request from the queue.
		 *
		 *	It may be empty, in which case we fail
		 *	gracefully.
		 */
		if (!request_dequeue(&self->request)) continue;

		self->request->child_pid = self->pthread_id;
		self->request_count++;

		DEBUG2("Thread %d handling request %d, (%d handled so far)",
		       self->thread_num, self->request->number,
		       self->request_count);

		self->request->process(self->request, FR_ACTION_RUN);

		/*
		 *	Update the active threads.
		 */
		pthread_mutex_lock(&thread_pool.queue_mutex);
		rad_assert(thread_pool.active_threads > 0);
		thread_pool.active_threads--;
		pthread_mutex_unlock(&thread_pool.queue_mutex);
	} while (self->status != THREAD_CANCELLED);

	DEBUG2("Thread %d exiting...", self->thread_num);

#ifdef HAVE_OPENSSL_ERR_H
	/*
	 *	If we linked with OpenSSL, the application
	 *	must remove the thread's error queue before
	 *	exiting to prevent memory leaks.
	 */
	ERR_remove_state(0);
#endif

	/*
	 *  Do this as the LAST thing before exiting.
	 */
	self->request = NULL;
	self->status = THREAD_EXITED;
	exec_trigger(NULL, NULL, "server.thread.stop");

	return NULL;
}