/*
 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
 *
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 *
 * The Initial Developer of the Original Code is
 * Anthony Minessale II <anthm@freeswitch.org>
 * Portions created by the Initial Developer are Copyright (C)
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Anthony Minessale II <anthm@freeswitch.org>
 * Michael Jerris <mike@jerris.com>
 * Paul D. Tinsley <pdt at jackhammer.org>
 * Marcel Barbulescu <marcelbarbulescu@gmail.com>
 * Joseph Sullivan <jossulli@amazon.com>
 * Seven Du <dujinfang@gmail.com>
 * Andrey Volk <andywolk@gmail.com>
 *
 * switch_core.c -- Main Core Library
 *
 */



#include <switch.h>
#include <switch_ssl.h>
#include <switch_stun.h>
#include <switch_nat.h>
#include "private/switch_apr_pvt.h"
#include "private/switch_core_pvt.h"
#include <switch_curl.h>
#include <switch_msrp.h>
#ifndef WIN32
#include <switch_private.h>
#ifdef HAVE_SETRLIMIT
#include <sys/resource.h>
#endif
#endif
#include <errno.h>
#include <sqlite3.h>
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
#ifdef SOLARIS_PRIVILEGES
#include <priv.h>
#endif

#ifdef __linux__
#include <sys/wait.h>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* Required for POSIX_SPAWN_USEVFORK */
#endif
#include <spawn.h>
#include <poll.h>
#endif

#ifdef WIN32
#define popen _popen
#define pclose _pclose
#endif

#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif

SWITCH_DECLARE_DATA switch_directories SWITCH_GLOBAL_dirs = { 0 };
SWITCH_DECLARE_DATA switch_filenames SWITCH_GLOBAL_filenames = { 0 };

/* The main runtime obj we keep this hidden for ourselves */
struct switch_runtime runtime = { 0 };
static void switch_load_core_config(const char *file);

static void send_heartbeat(void)
{
	switch_event_t *event;
	switch_core_time_duration_t duration;

	switch_core_measure_time(switch_core_uptime(), &duration);

	if (switch_event_create(&event, SWITCH_EVENT_HEARTBEAT) == SWITCH_STATUS_SUCCESS) {
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Event-Info", "System Ready");
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Up-Time",
								"%u year%s, "
								"%u day%s, "
								"%u hour%s, "
								"%u minute%s, "
								"%u second%s, "
								"%u millisecond%s, "
								"%u microsecond%s",
								duration.yr, duration.yr == 1 ? "" : "s",
								duration.day, duration.day == 1 ? "" : "s",
								duration.hr, duration.hr == 1 ? "" : "s",
								duration.min, duration.min == 1 ? "" : "s",
								duration.sec, duration.sec == 1 ? "" : "s",
								duration.ms, duration.ms == 1 ? "" : "s", duration.mms, duration.mms == 1 ? "" : "s");

		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "FreeSWITCH-Version", "%s", switch_version_full());
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Uptime-msec", "%"SWITCH_TIME_T_FMT, switch_core_uptime() / 1000);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Count", "%u", switch_core_session_count());
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Max-Sessions", "%u", switch_core_session_limit(0));
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Per-Sec", "%u", runtime.sps);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Per-Sec-Last", "%u", runtime.sps_last);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Per-Sec-Max", "%u", runtime.sps_peak);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Per-Sec-FiveMin", "%u", runtime.sps_peak_fivemin);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Since-Startup", "%" SWITCH_SIZE_T_FMT, switch_core_session_id() - 1);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Peak-Max", "%u", runtime.sessions_peak);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Peak-FiveMin", "%u", runtime.sessions_peak_fivemin);
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Idle-CPU", "%f", switch_core_idle_cpu());
		switch_event_fire(&event);
	}
}

static char main_ip4[256] = "";
static char main_ip6[256] = "";

static void check_ip(void)
{
	char guess_ip4[256] = "";
	char guess_ip6[256] = "";
	char old_ip4[256] = "";
	char old_ip6[256] = "";
	int ok4 = 1, ok6 = 1;
	int mask = 0;
	switch_status_t check6, check4;
	switch_event_t *event;
	char *hostname = switch_core_get_variable("hostname");

	gethostname(runtime.hostname, sizeof(runtime.hostname));

	if (zstr(hostname)) {
		switch_core_set_variable("hostname", runtime.hostname);
	} else if (strcmp(hostname, runtime.hostname)) {
		if (switch_event_create(&event, SWITCH_EVENT_TRAP) == SWITCH_STATUS_SUCCESS) {
			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "condition", "hostname-change");
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "old-hostname", hostname ? hostname : "nil");
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "new-hostname", runtime.hostname);
			switch_event_fire(&event);
		}

		switch_core_set_variable("hostname", runtime.hostname);
	}

	check4 = switch_find_local_ip(guess_ip4, sizeof(guess_ip4), &mask, AF_INET);
	check6 = switch_find_local_ip(guess_ip6, sizeof(guess_ip6), NULL, AF_INET6);

	if (check6 != SWITCH_STATUS_SUCCESS && (zstr(main_ip6) || !strcasecmp(main_ip6, "::1"))) {
		check6 = SWITCH_STATUS_SUCCESS;
	}

	if (check4 != SWITCH_STATUS_SUCCESS) {
		ok4 = 2;
	} else if (!*main_ip4) {
		switch_set_string(main_ip4, guess_ip4);
	} else {
		if (!(ok4 = !strcmp(main_ip4, guess_ip4))) {
			struct in_addr in;

			in.s_addr = mask;
			switch_set_string(old_ip4, main_ip4);
			switch_set_string(main_ip4, guess_ip4);
			switch_core_set_variable("local_ip_v4", guess_ip4);
			switch_core_set_variable("local_mask_v4", inet_ntoa(in));
		}
	}

	if (check6 != SWITCH_STATUS_SUCCESS) {
		ok6 = 2;
	} else if (!*main_ip6) {
		switch_set_string(main_ip6, guess_ip6);
	} else {
		if (!(ok6 = !strcmp(main_ip6, guess_ip6))) {
			switch_set_string(old_ip6, main_ip6);
			switch_set_string(main_ip6, guess_ip6);
			switch_core_set_variable("local_ip_v6", guess_ip6);
		}
	}

	if (!ok4 || !ok6) {
		if (switch_event_create(&event, SWITCH_EVENT_TRAP) == SWITCH_STATUS_SUCCESS) {
			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "condition", "network-address-change");
			if (!ok4) {
				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-address-previous-v4", old_ip4);
				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-address-change-v4", main_ip4);
			}
			if (!ok6) {
				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-address-previous-v6", old_ip6);
				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-address-change-v6", main_ip6);
			}
			switch_event_fire(&event);
		}
	}

	if (ok4 == 2 || ok6 == 2) {
		if (switch_event_create(&event, SWITCH_EVENT_TRAP) == SWITCH_STATUS_SUCCESS) {
			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "condition", "network-outage");

			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-status-v4", ok4 == 2 ? "disconnected" : "active");
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-address-v4", main_ip4);

			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-status-v6", ok6 == 2 ? "disconnected" : "active");
			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "network-address-v6", main_ip6);

			switch_event_fire(&event);
		}
	}

}

SWITCH_STANDARD_SCHED_FUNC(heartbeat_callback)
{
	send_heartbeat();

	/* reschedule this task */
	task->runtime = switch_epoch_time_now(NULL) + runtime.event_heartbeat_interval;
}


SWITCH_STANDARD_SCHED_FUNC(check_ip_callback)
{
	check_ip();

	/* reschedule this task */
	task->runtime = switch_epoch_time_now(NULL) + 60;
}


SWITCH_DECLARE(switch_status_t) switch_core_set_console(const char *console)
{
	if ((runtime.console = fopen(console, "a")) == 0) {
		fprintf(stderr, "Cannot open output file %s.\n", console);
		return SWITCH_STATUS_FALSE;
	}

	return SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(FILE *) switch_core_get_console(void)
{
	return runtime.console;
}

#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
SWITCH_DECLARE(void) switch_core_screen_size(int *x, int *y)
{

#ifdef WIN32
	CONSOLE_SCREEN_BUFFER_INFO csbi;
	int ret;

	if ((ret = GetConsoleScreenBufferInfo(GetStdHandle( STD_OUTPUT_HANDLE ), &csbi))) {
		if (x) *x = csbi.dwSize.X;
		if (y) *y = csbi.dwSize.Y;
	}

#elif defined(TIOCGWINSZ)
	struct winsize w;
	ioctl(0, TIOCGWINSZ, &w);

	if (x) *x = w.ws_col;
	if (y) *y = w.ws_row;
#else
	if (x) *x = 80;
	if (y) *y = 24;
#endif

}

SWITCH_DECLARE(FILE *) switch_core_data_channel(switch_text_channel_t channel)
{
	return runtime.console;
}


SWITCH_DECLARE(void) switch_core_remove_state_handler(const switch_state_handler_table_t *state_handler)
{
	int index, tmp_index = 0;
	const switch_state_handler_table_t *tmp[SWITCH_MAX_STATE_HANDLERS + 1] = { 0 };

	switch_mutex_lock(runtime.global_mutex);

	for (index = 0; index < runtime.state_handler_index; index++) {
		const switch_state_handler_table_t *cur = runtime.state_handlers[index];
		runtime.state_handlers[index] = NULL;
		if (cur == state_handler) {
			continue;
		}
		tmp[tmp_index++] = cur;
	}

	runtime.state_handler_index = 0;

	for (index = 0; index < tmp_index; index++) {
		runtime.state_handlers[runtime.state_handler_index++] = tmp[index];
	}
	switch_mutex_unlock(runtime.global_mutex);
}


SWITCH_DECLARE(int) switch_core_add_state_handler(const switch_state_handler_table_t *state_handler)
{
	int index;

	switch_mutex_lock(runtime.global_mutex);
	index = runtime.state_handler_index;

	if (index > (SWITCH_MAX_STATE_HANDLERS - 1)) {
		index = -1;
	} else {
		runtime.state_handlers[index] = state_handler;
		runtime.state_handler_index++;
	}

	switch_mutex_unlock(runtime.global_mutex);
	return index;
}

SWITCH_DECLARE(const switch_state_handler_table_t *) switch_core_get_state_handler(int index)
{

	if (index >= SWITCH_MAX_STATE_HANDLERS || index > runtime.state_handler_index) {
		return NULL;
	}

	return runtime.state_handlers[index];
}

SWITCH_DECLARE(void) switch_core_dump_variables(switch_stream_handle_t *stream)
{
	switch_event_header_t *hi;

	switch_mutex_lock(runtime.global_mutex);
	for (hi = runtime.global_vars->headers; hi; hi = hi->next) {
		stream->write_function(stream, "%s=%s\n", hi->name, hi->value);
	}
	switch_mutex_unlock(runtime.global_mutex);
}

SWITCH_DECLARE(const char *) switch_core_get_hostname(void)
{
	return runtime.hostname;
}

SWITCH_DECLARE(const char *) switch_core_get_switchname(void)
{
	if (!zstr(runtime.switchname)) return runtime.switchname;
	return runtime.hostname;
}

SWITCH_DECLARE(char *) switch_core_get_domain(switch_bool_t dup)
{
	char *domain;
	const char *var;

	switch_thread_rwlock_rdlock(runtime.global_var_rwlock);
	if (!(var = switch_core_get_variable("domain"))) {
		var = "freeswitch.local";
	}
	if (dup) {
		domain = strdup(var);
	} else {
		domain = (char *) var;
	}
	switch_thread_rwlock_unlock(runtime.global_var_rwlock);

	return domain;
}

SWITCH_DECLARE(switch_status_t) switch_core_get_variables(switch_event_t **event)
{
	switch_status_t status;
	switch_thread_rwlock_rdlock(runtime.global_var_rwlock);
	status = switch_event_dup(event, runtime.global_vars);
	switch_thread_rwlock_unlock(runtime.global_var_rwlock);
	return status;
}

SWITCH_DECLARE(char *) switch_core_get_variable(const char *varname)
{
	char *val;
	switch_thread_rwlock_rdlock(runtime.global_var_rwlock);
	val = (char *) switch_event_get_header(runtime.global_vars, varname);
	switch_thread_rwlock_unlock(runtime.global_var_rwlock);
	return val;
}

SWITCH_DECLARE(char *) switch_core_get_variable_dup(const char *varname)
{
	char *val = NULL, *v;

	if (varname) {
		switch_thread_rwlock_rdlock(runtime.global_var_rwlock);
		if ((v = (char *) switch_event_get_header(runtime.global_vars, varname))) {
			val = strdup(v);
		}
		switch_thread_rwlock_unlock(runtime.global_var_rwlock);
	}

	return val;
}

SWITCH_DECLARE(char *) switch_core_get_variable_pdup(const char *varname, switch_memory_pool_t *pool)
{
	char *val = NULL, *v;

	if (varname) {
		switch_thread_rwlock_rdlock(runtime.global_var_rwlock);
		if ((v = (char *) switch_event_get_header(runtime.global_vars, varname))) {
			val = switch_core_strdup(pool, v);
		}
		switch_thread_rwlock_unlock(runtime.global_var_rwlock);
	}

	return val;
}

static void switch_core_unset_variables(void)
{
	switch_thread_rwlock_wrlock(runtime.global_var_rwlock);
	switch_event_destroy(&runtime.global_vars);
	switch_event_create_plain(&runtime.global_vars, SWITCH_EVENT_CHANNEL_DATA);
	switch_thread_rwlock_unlock(runtime.global_var_rwlock);
}

SWITCH_DECLARE(void) switch_core_set_variable(const char *varname, const char *value)
{
	char *val;

	if (varname) {
		switch_thread_rwlock_wrlock(runtime.global_var_rwlock);
		val = (char *) switch_event_get_header(runtime.global_vars, varname);
		if (val) {
			switch_event_del_header(runtime.global_vars, varname);
		}
		if (value) {
			char *v = strdup(value);
			switch_string_var_check(v, SWITCH_TRUE);
			switch_event_add_header_string(runtime.global_vars, SWITCH_STACK_BOTTOM, varname, v);
			free(v);
		} else {
			switch_event_del_header(runtime.global_vars, varname);
		}
		switch_thread_rwlock_unlock(runtime.global_var_rwlock);
	}
}

SWITCH_DECLARE(switch_bool_t) switch_core_set_var_conditional(const char *varname, const char *value, const char *val2)
{
	char *val;

	if (varname) {
		switch_thread_rwlock_wrlock(runtime.global_var_rwlock);
		val = (char *) switch_event_get_header(runtime.global_vars, varname);

		if (val) {
			if (!val2 || strcmp(val, val2) != 0) {
				switch_thread_rwlock_unlock(runtime.global_var_rwlock);
				return SWITCH_FALSE;
			}
			switch_event_del_header(runtime.global_vars, varname);
		} else if (!zstr(val2)) {
			switch_thread_rwlock_unlock(runtime.global_var_rwlock);
			return SWITCH_FALSE;
		}

		if (value) {
			char *v = strdup(value);
			switch_string_var_check(v, SWITCH_TRUE);
			switch_event_add_header_string_nodup(runtime.global_vars, SWITCH_STACK_BOTTOM, varname, v);
		} else {
			switch_event_del_header(runtime.global_vars, varname);
		}
		switch_thread_rwlock_unlock(runtime.global_var_rwlock);
	}
	return SWITCH_TRUE;
}

SWITCH_DECLARE(char *) switch_core_get_uuid(void)
{
	return runtime.uuid_str;
}


static void *SWITCH_THREAD_FUNC switch_core_service_thread(switch_thread_t *thread, void *obj)
{
	switch_core_session_t *session = obj;
	switch_channel_t *channel;
	switch_frame_t *read_frame = NULL;

//  switch_assert(thread != NULL);
//  switch_assert(session != NULL);

	if (switch_core_session_read_lock(session) != SWITCH_STATUS_SUCCESS) {
		return NULL;
	}

	switch_mutex_lock(session->frame_read_mutex);

	channel = switch_core_session_get_channel(session);

	switch_channel_set_flag(channel, CF_SERVICE);
	while (switch_channel_test_flag(channel, CF_SERVICE)) {

		if (switch_channel_test_flag(channel, CF_SERVICE_AUDIO)) {
			switch (switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0)) {
			case SWITCH_STATUS_SUCCESS:
			case SWITCH_STATUS_TIMEOUT:
			case SWITCH_STATUS_BREAK:
				break;
			default:
				switch_channel_clear_flag(channel, CF_SERVICE);
				break;
			}
		}

		if (switch_channel_test_flag(channel, CF_SERVICE_VIDEO) && switch_channel_test_flag(channel, CF_VIDEO)) {
			switch (switch_core_session_read_video_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0)) {
			case SWITCH_STATUS_SUCCESS:
			case SWITCH_STATUS_TIMEOUT:
			case SWITCH_STATUS_BREAK:
				break;
			default:
				switch_channel_clear_flag(channel, CF_SERVICE);
				break;
			}
		}
	}

	switch_mutex_unlock(session->frame_read_mutex);

	switch_channel_clear_flag(channel, CF_SERVICE_AUDIO);
	switch_channel_clear_flag(channel, CF_SERVICE_VIDEO);

	switch_core_session_rwunlock(session);

	return NULL;
}

/* Either add a timeout here or make damn sure the thread cannot get hung somehow (my preference) */
SWITCH_DECLARE(void) switch_core_thread_session_end(switch_core_session_t *session)
{
	switch_channel_t *channel;
	switch_assert(session);

	channel = switch_core_session_get_channel(session);
	switch_assert(channel);

	switch_channel_clear_flag(channel, CF_SERVICE);
	switch_channel_clear_flag(channel, CF_SERVICE_AUDIO);
	switch_channel_clear_flag(channel, CF_SERVICE_VIDEO);

	switch_core_session_kill_channel(session, SWITCH_SIG_BREAK);

}

SWITCH_DECLARE(void) switch_core_service_session_av(switch_core_session_t *session, switch_bool_t audio, switch_bool_t video)
{
	switch_channel_t *channel;
	switch_assert(session);

	channel = switch_core_session_get_channel(session);
	switch_assert(channel);

	if (audio) switch_channel_set_flag(channel, CF_SERVICE_AUDIO);
	if (video) switch_channel_set_flag(channel, CF_SERVICE_VIDEO);

	switch_core_session_launch_thread(session, (void *(*)(switch_thread_t *,void *))switch_core_service_thread, session);
}

/* This function abstracts the thread creation for modules by allowing you to pass a function ptr and
   a void object and trust that that the function will be run in a thread with arg  This lets
   you request and activate a thread without giving up any knowledge about what is in the thread
   neither the core nor the calling module know anything about each other.

   This thread is expected to never exit until the application exits so the func is responsible
   to make sure that is the case.

   The typical use for this is so switch_loadable_module.c can start up a thread for each module
   passing the table of module methods as a session obj into the core without actually allowing
   the core to have any clue and keeping switch_loadable_module.c from needing any thread code.

*/

SWITCH_DECLARE(switch_thread_t *) switch_core_launch_thread(switch_thread_start_t func, void *obj, switch_memory_pool_t *pool)
{
	switch_thread_t *thread = NULL;
	switch_threadattr_t *thd_attr = NULL;
	switch_core_thread_session_t *ts;
	int mypool;

	mypool = pool ? 0 : 1;

	if (!pool && switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not allocate memory pool\n");
		return NULL;
	}

	switch_threadattr_create(&thd_attr, pool);

	if ((ts = switch_core_alloc(pool, sizeof(*ts))) == 0) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Could not allocate memory\n");
	} else {
		if (mypool) {
			ts->pool = pool;
		}
		ts->objs[0] = obj;
		ts->objs[1] = thread;
		switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
		switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME);
		switch_thread_create(&thread, thd_attr, func, ts, pool);
	}

	return thread;
}

SWITCH_DECLARE(void) switch_core_set_globals(void)
{
#define BUFSIZE 1024
#ifdef WIN32
	char lpPathBuffer[BUFSIZE];
	DWORD dwBufSize = BUFSIZE;
	char base_dir[1024];
	char *lastbacklash;
	char *tmp;

	GetModuleFileName(NULL, base_dir, BUFSIZE);
	lastbacklash = strrchr(base_dir, '\\');
	base_dir[(lastbacklash - base_dir)] = '\0';
	/* set base_dir as cwd, to be able to use relative paths in scripting languages (e.g. mod_lua) when FS is running as a service or while debugging FS using visual studio */
	SetCurrentDirectory(base_dir);
	tmp = switch_string_replace(base_dir, "\\", "/");
	strcpy(base_dir, tmp);
	free(tmp);

#else
	char base_dir[1024] = SWITCH_PREFIX_DIR;
#endif

	/* Order of precedence for, eg, rundir:
	 *   -run
	 *   -base
	 *   --with-rundir
	 *   --prefix
	 */

	if (!SWITCH_GLOBAL_dirs.mod_dir && (SWITCH_GLOBAL_dirs.mod_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.mod_dir, BUFSIZE, "%s%smod", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_MOD_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.mod_dir, BUFSIZE, "%s", SWITCH_MOD_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.mod_dir, BUFSIZE, "%s%smod", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.lib_dir && (SWITCH_GLOBAL_dirs.lib_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.lib_dir, BUFSIZE, "%s%slib", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_LIB_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.lib_dir, BUFSIZE, "%s", SWITCH_LIB_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.lib_dir, BUFSIZE, "%s%slib", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.conf_dir && (SWITCH_GLOBAL_dirs.conf_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.conf_dir, BUFSIZE, "%s%sconf", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_CONF_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.conf_dir, BUFSIZE, "%s", SWITCH_CONF_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.conf_dir, BUFSIZE, "%s%sconf", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.log_dir && (SWITCH_GLOBAL_dirs.log_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.log_dir, BUFSIZE, "%s%slog", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_LOG_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.log_dir, BUFSIZE, "%s", SWITCH_LOG_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.log_dir, BUFSIZE, "%s%slog", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.run_dir && (SWITCH_GLOBAL_dirs.run_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.run_dir, BUFSIZE, "%s%srun", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_RUN_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.run_dir, BUFSIZE, "%s", SWITCH_RUN_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.run_dir, BUFSIZE, "%s%srun", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.recordings_dir && (SWITCH_GLOBAL_dirs.recordings_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.recordings_dir, BUFSIZE, "%s%srecordings", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_RECORDINGS_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.recordings_dir, BUFSIZE, "%s", SWITCH_RECORDINGS_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.recordings_dir, BUFSIZE, "%s%srecordings", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.sounds_dir && (SWITCH_GLOBAL_dirs.sounds_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.sounds_dir, BUFSIZE, "%s%ssounds", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_SOUNDS_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.sounds_dir, BUFSIZE, "%s", SWITCH_SOUNDS_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.sounds_dir, BUFSIZE, "%s%ssounds", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.storage_dir && (SWITCH_GLOBAL_dirs.storage_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.storage_dir, BUFSIZE, "%s%sstorage", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_STORAGE_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.storage_dir, BUFSIZE, "%s", SWITCH_STORAGE_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.storage_dir, BUFSIZE, "%s%sstorage", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.cache_dir && (SWITCH_GLOBAL_dirs.cache_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.cache_dir, BUFSIZE, "%s%scache", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_CACHE_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.cache_dir, BUFSIZE, "%s", SWITCH_CACHE_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.cache_dir, BUFSIZE, "%s%scache", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.db_dir && (SWITCH_GLOBAL_dirs.db_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.db_dir, BUFSIZE, "%s%sdb", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_DB_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.db_dir, BUFSIZE, "%s", SWITCH_DB_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.db_dir, BUFSIZE, "%s%sdb", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.script_dir && (SWITCH_GLOBAL_dirs.script_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.script_dir, BUFSIZE, "%s%sscripts", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_SCRIPT_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.script_dir, BUFSIZE, "%s", SWITCH_SCRIPT_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.script_dir, BUFSIZE, "%s%sscripts", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.htdocs_dir && (SWITCH_GLOBAL_dirs.htdocs_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.htdocs_dir, BUFSIZE, "%s%shtdocs", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_HTDOCS_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.htdocs_dir, BUFSIZE, "%s", SWITCH_HTDOCS_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.htdocs_dir, BUFSIZE, "%s%shtdocs", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.grammar_dir && (SWITCH_GLOBAL_dirs.grammar_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.grammar_dir, BUFSIZE, "%s%sgrammar", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_GRAMMAR_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.grammar_dir, BUFSIZE, "%s", SWITCH_GRAMMAR_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.grammar_dir, BUFSIZE, "%s%sgrammar", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.fonts_dir && (SWITCH_GLOBAL_dirs.fonts_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.fonts_dir, BUFSIZE, "%s%sfonts", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_FONTS_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.fonts_dir, BUFSIZE, "%s", SWITCH_FONTS_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.fonts_dir, BUFSIZE, "%s%sfonts", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.images_dir && (SWITCH_GLOBAL_dirs.images_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.images_dir, BUFSIZE, "%s%simages", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_IMAGES_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.images_dir, BUFSIZE, "%s", SWITCH_IMAGES_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.images_dir, BUFSIZE, "%s%simages", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.data_dir && (SWITCH_GLOBAL_dirs.data_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.data_dir, BUFSIZE, "%s", SWITCH_GLOBAL_dirs.base_dir);
		else
#ifdef SWITCH_DATA_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.data_dir, BUFSIZE, "%s", SWITCH_DATA_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.data_dir, BUFSIZE, "%s", base_dir);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.localstate_dir && (SWITCH_GLOBAL_dirs.localstate_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.localstate_dir, BUFSIZE, "%s", SWITCH_GLOBAL_dirs.base_dir);
		else
#ifdef SWITCH_LOCALSTATE_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.localstate_dir, BUFSIZE, "%s", SWITCH_LOCALSTATE_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.localstate_dir, BUFSIZE, "%s", base_dir);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.certs_dir && (SWITCH_GLOBAL_dirs.certs_dir = (char *) malloc(BUFSIZE))) {
		if (SWITCH_GLOBAL_dirs.base_dir)
			switch_snprintf(SWITCH_GLOBAL_dirs.certs_dir, BUFSIZE, "%s%scert", SWITCH_GLOBAL_dirs.base_dir, SWITCH_PATH_SEPARATOR);
		else
#ifdef SWITCH_CERTS_DIR
			switch_snprintf(SWITCH_GLOBAL_dirs.certs_dir, BUFSIZE, "%s", SWITCH_CERTS_DIR);
#else
			switch_snprintf(SWITCH_GLOBAL_dirs.certs_dir, BUFSIZE, "%s%scert", base_dir, SWITCH_PATH_SEPARATOR);
#endif
	}

	if (!SWITCH_GLOBAL_dirs.temp_dir && (SWITCH_GLOBAL_dirs.temp_dir = (char *) malloc(BUFSIZE))) {
#ifdef SWITCH_TEMP_DIR
		switch_snprintf(SWITCH_GLOBAL_dirs.temp_dir, BUFSIZE, "%s", SWITCH_TEMP_DIR);
#else
#ifdef WIN32
		GetTempPath(dwBufSize, lpPathBuffer);
		lpPathBuffer[strlen(lpPathBuffer)-1] = 0;
		tmp = switch_string_replace(lpPathBuffer, "\\", "/");
		strcpy(lpPathBuffer, tmp);
		free(tmp);
		switch_snprintf(SWITCH_GLOBAL_dirs.temp_dir, BUFSIZE, "%s", lpPathBuffer);
#else
		switch_snprintf(SWITCH_GLOBAL_dirs.temp_dir, BUFSIZE, "%s", "/tmp");
#endif
#endif
	}

	if (!SWITCH_GLOBAL_filenames.conf_name && (SWITCH_GLOBAL_filenames.conf_name = (char *) malloc(BUFSIZE))) {
		switch_snprintf(SWITCH_GLOBAL_filenames.conf_name, BUFSIZE, "%s", "freeswitch.xml");
	}

	/* Do this last because it being empty is part of the above logic */
	if (!SWITCH_GLOBAL_dirs.base_dir && (SWITCH_GLOBAL_dirs.base_dir = (char *) malloc(BUFSIZE))) {
		switch_snprintf(SWITCH_GLOBAL_dirs.base_dir, BUFSIZE, "%s", base_dir);
	}

	switch_assert(SWITCH_GLOBAL_dirs.base_dir);
	switch_assert(SWITCH_GLOBAL_dirs.mod_dir);
	switch_assert(SWITCH_GLOBAL_dirs.lib_dir);
	switch_assert(SWITCH_GLOBAL_dirs.conf_dir);
	switch_assert(SWITCH_GLOBAL_dirs.log_dir);
	switch_assert(SWITCH_GLOBAL_dirs.run_dir);
	switch_assert(SWITCH_GLOBAL_dirs.db_dir);
	switch_assert(SWITCH_GLOBAL_dirs.script_dir);
	switch_assert(SWITCH_GLOBAL_dirs.htdocs_dir);
	switch_assert(SWITCH_GLOBAL_dirs.grammar_dir);
	switch_assert(SWITCH_GLOBAL_dirs.fonts_dir);
	switch_assert(SWITCH_GLOBAL_dirs.images_dir);
	switch_assert(SWITCH_GLOBAL_dirs.recordings_dir);
	switch_assert(SWITCH_GLOBAL_dirs.sounds_dir);
	switch_assert(SWITCH_GLOBAL_dirs.certs_dir);
	switch_assert(SWITCH_GLOBAL_dirs.temp_dir);
	switch_assert(SWITCH_GLOBAL_dirs.data_dir);
	switch_assert(SWITCH_GLOBAL_dirs.localstate_dir);

	switch_assert(SWITCH_GLOBAL_filenames.conf_name);
}


SWITCH_DECLARE(int32_t) switch_core_set_process_privileges(void)
{
#ifdef SOLARIS_PRIVILEGES
	priv_set_t *basicset;

	/* make the process privilege-aware */
	setpflags(PRIV_AWARE, 1);

	/* reset the privileges to basic */
	basicset = priv_str_to_set("basic", ",", NULL);
	if (setppriv(PRIV_SET, PRIV_EFFECTIVE, basicset) != 0) {
		fprintf(stderr, "ERROR: Failed to acquire basic privileges (%s)\n", strerror(errno));
	}

	/* we need high-resolution clock, and this requires a non-basic privilege */
	if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_CLOCK_HIGHRES, NULL) < 0) {
		fprintf(stderr, "ERROR: Failed to acquire proc_clock_highres privilege (%s)\n", strerror(errno));
		return -1;
	}

	/* need this for setrlimit */
	if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_RESOURCE, NULL) < 0) {
		fprintf(stderr, "ERROR: Failed to acquire sys_resource privilege (%s)\n", strerror(errno));
		return -1;
	}

	/* we need to read directories belonging to other uid */
	if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_SEARCH, NULL) < 0) {
		fprintf(stderr, "ERROR: Failed to acquire file_dac_search privilege (%s)\n", strerror(errno));
		return -1;
	}
#endif
	return 0;
}

SWITCH_DECLARE(int32_t) set_low_priority(void)
{
#ifdef WIN32
	return SetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS);
#else
#if defined(USE_SCHED_SETSCHEDULER) && ! defined(SOLARIS_PRIVILEGES)
	/*
	 * Try to use a normal scheduler
	 */
	struct sched_param sched = { 0 };
	sched.sched_priority = 0;
	if (sched_setscheduler(0, SCHED_OTHER, &sched) < 0) {
		fprintf(stderr, "ERROR: Failed to set SCHED_OTHER scheduler (%s)\n", strerror(errno));
		return -1;
	}
#endif

#ifdef HAVE_SETPRIORITY
	/*
	 * setpriority() works on FreeBSD (6.2), nice() doesn't
	 */
	if (setpriority(PRIO_PROCESS, getpid(), 19) < 0) {
		fprintf(stderr, "ERROR: Could not set nice level\n");
		return -1;
	}
#else
	if (nice(19) != 19) {
		fprintf(stderr, "ERROR: Could not set nice level\n");
		return -1;
	}
#endif

	return 0;
#endif
}

SWITCH_DECLARE(int32_t) set_realtime_priority(void)
{
#ifdef WIN32
	return SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
#else
#ifdef USE_SCHED_SETSCHEDULER
	/*
	 * Try to use a round-robin scheduler
	 * with a fallback if that does not work
	 */
	struct sched_param sched = { 0 };
	sched.sched_priority = SWITCH_PRI_LOW;
#endif

#ifdef SOLARIS_PRIVILEGES
	/* request the privileges to elevate the priority */
	if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_PRIOCNTL, NULL) < 0) {
#ifdef PRIV_PROC_PRIOUP
		/* fallback to PRIV_PROC_PRIOUP on SmartOS */
		fprintf(stderr, "WARN: Failed to acquire proc_priocntl privilege (%s)\n", strerror(errno));
		if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_PRIOUP, NULL) < 0) {
			fprintf(stderr, "ERROR: Failed to acquire proc_prioup privilege (%s)\n", strerror(errno));
			return -1;
		}
#else
		fprintf(stderr, "ERROR: Failed to acquire proc_priocntl privilege (%s)\n", strerror(errno));
		return -1;
#endif
	}

	if (sched_setscheduler(0, SCHED_FIFO, &sched) < 0) {
		fprintf(stderr, "WARN: Failed to set SCHED_FIFO scheduler (%s)\n", strerror(errno));
	} else {
		return 0;
	}

	if (setpriority(PRIO_PROCESS, 0, -10) < 0) {
		fprintf(stderr, "ERROR: Could not set nice level\n");
		return -1;
	}

	return 0;
#else

#ifdef USE_SCHED_SETSCHEDULER
	if (sched_setscheduler(0, SCHED_FIFO, &sched) < 0) {
		fprintf(stderr, "ERROR: Failed to set SCHED_FIFO scheduler (%s)\n", strerror(errno));
		sched.sched_priority = 0;
		if (sched_setscheduler(0, SCHED_OTHER, &sched) < 0 ) {
			fprintf(stderr, "ERROR: Failed to set SCHED_OTHER scheduler (%s)\n", strerror(errno));
			return -1;
		}
	}
#endif

#ifdef HAVE_SETPRIORITY
	/*
	 * setpriority() works on FreeBSD (6.2), nice() doesn't
	 */
	if (setpriority(PRIO_PROCESS, getpid(), -10) < 0) {
		fprintf(stderr, "ERROR: Could not set nice level\n");
		return -1;
	}
#else
	if (nice(-10) != -10) {
		fprintf(stderr, "ERROR: Could not set nice level\n");
		return -1;
	}
#endif
#endif
	return 0;
#endif
}

SWITCH_DECLARE(uint32_t) switch_core_cpu_count(void)
{
	return runtime.cpu_count;
}

SWITCH_DECLARE(int32_t) set_normal_priority(void)
{
	return 0;
}

SWITCH_DECLARE(int32_t) set_auto_priority(void)
{
#ifndef WIN32
	runtime.cpu_count = sysconf (_SC_NPROCESSORS_ONLN);
#else
	SYSTEM_INFO sysinfo;
	GetSystemInfo( &sysinfo );
	runtime.cpu_count = sysinfo.dwNumberOfProcessors;
#endif

	if (!runtime.cpu_count) runtime.cpu_count = 1;

	return set_realtime_priority();


	// ERROR: code not reachable on Windows Visual Studio Express 2008 return 0;
}

SWITCH_DECLARE(int32_t) change_user_group(const char *user, const char *group)
{
#ifndef WIN32
	uid_t runas_uid = 0;
	gid_t runas_gid = 0;
	struct passwd *runas_pw = NULL;

	if (user) {
		/*
		 * Lookup user information in the system's db
		 */
		runas_pw = getpwnam(user);
		if (!runas_pw) {
			fprintf(stderr, "ERROR: Unknown user \"%s\"\n", user);
			return -1;
		}
		runas_uid = runas_pw->pw_uid;
	}

	if (group) {
		struct group *gr = NULL;

		/*
		 * Lookup group information in the system's db
		 */
		gr = getgrnam(group);
		if (!gr) {
			fprintf(stderr, "ERROR: Unknown group \"%s\"\n", group);
			return -1;
		}
		runas_gid = gr->gr_gid;
	}

	if (runas_uid && getuid() == runas_uid && (!runas_gid || runas_gid == getgid())) {
		/* already running as the right user and group, nothing to do! */
		return 0;
	}

	if (runas_uid) {
#ifdef SOLARIS_PRIVILEGES
		/* request the privilege to set the UID */
		if (priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_PROC_SETID, NULL) < 0) {
			fprintf(stderr, "ERROR: Failed to acquire proc_setid privilege (%s)\n", strerror(errno));
			return -1;
		}
#endif
#ifdef HAVE_SETGROUPS
		/*
		 * Drop all group memberships prior to changing anything
		 * or else we're going to inherit the parent's list of groups
		 * (which is not what we want...)
		 */
		if (setgroups(0, NULL) < 0) {
			fprintf(stderr, "ERROR: Failed to drop group access list\n");
			return -1;
		}
#endif
		if (runas_gid) {
			/*
			 * A group has been passed, switch to it
			 * (without loading the user's other groups)
			 */
			if (setgid(runas_gid) < 0) {
				fprintf(stderr, "ERROR: Failed to change gid!\n");
				return -1;
			}
		} else {
			/*
			 * No group has been passed, use the user's primary group in this case
			 */
			if (setgid(runas_pw->pw_gid) < 0) {
				fprintf(stderr, "ERROR: Failed to change gid!\n");
				return -1;
			}
#ifdef HAVE_INITGROUPS
			/*
			 * Set all the other groups the user is a member of
			 * (This can be really useful for fine-grained access control)
			 */
			if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
				fprintf(stderr, "ERROR: Failed to set group access list for user\n");
				return -1;
			}
#endif
		}

		/*
		 * Finally drop all privileges by switching to the new userid
		 */
		if (setuid(runas_uid) < 0) {
			fprintf(stderr, "ERROR: Failed to change uid!\n");
			return -1;
		}
#ifdef HAVE_SYS_PRCTL_H
		if (prctl(PR_SET_DUMPABLE, 1) < 0) {
			fprintf(stderr, "ERROR: Failed to enable core dumps!\n");
			return -1;
		}
#endif
	}
#endif
	return 0;
}

SWITCH_DECLARE(void) switch_core_runtime_loop(int bg)
{
#ifdef WIN32
	HANDLE shutdown_event;
	char path[256] = "";
#endif
	if (bg) {
#ifdef WIN32
		switch_snprintf(path, sizeof(path), "Global\\Freeswitch.%d", getpid());
		shutdown_event = CreateEvent(NULL, FALSE, FALSE, path);
		if (shutdown_event) {
			WaitForSingleObject(shutdown_event, INFINITE);
		}
#else
		while (runtime.running) {
			switch_yield(1000000);
		}
#endif
	} else {
		/* wait for console input */
		switch_console_loop();
	}
}

SWITCH_DECLARE(const char *) switch_core_mime_ext2type(const char *ext)
{
	if (!ext) {
		return NULL;
	}
	return (const char *) switch_core_hash_find(runtime.mime_types, ext);
}

SWITCH_DECLARE(const char *) switch_core_mime_type2ext(const char *mime)
{
	if (!mime) {
		return NULL;
	}
	return (const char *) switch_core_hash_find(runtime.mime_type_exts, mime);
}

SWITCH_DECLARE(switch_hash_index_t *) switch_core_mime_index(void)
{
	return switch_core_hash_first(runtime.mime_types);
}

SWITCH_DECLARE(switch_status_t) switch_core_mime_add_type(const char *type, const char *ext)
{
	char *ptype = NULL;
	char *ext_list = NULL;
	int argc = 0;
	char *argv[20] = { 0 };
	int x;
	switch_status_t status = SWITCH_STATUS_FALSE;

	switch_assert(type);
	switch_assert(ext);

	ptype = switch_core_permanent_strdup(type);
	ext_list = strdup(ext);

	switch_assert(ext_list);

	/* Map each file extension to this MIME type if not already mapped.  Map the MIME type to the first file extension in the list if not already mapped. */
	if ((argc = switch_separate_string(ext_list, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
		int is_mapped_type = switch_core_hash_find(runtime.mime_type_exts, ptype) != NULL;
		for (x = 0; x < argc; x++) {
			if (argv[x] && ptype) {
				if (!switch_core_hash_find(runtime.mime_types, ext)) {
					switch_core_hash_insert(runtime.mime_types, argv[x], ptype);
				}
				if (!is_mapped_type) {
					switch_core_hash_insert(runtime.mime_type_exts, ptype, switch_core_permanent_strdup(argv[x]));
					is_mapped_type = 1;
				}
			}
		}

		status = SWITCH_STATUS_SUCCESS;
	}

	free(ext_list);

	return status;
}

static void load_mime_types(void)
{
	char *cf = "mime.types";
	FILE *fd = NULL;
	char *line_buf = NULL;
	switch_size_t llen = 0;
	char *mime_path = NULL;

	mime_path = switch_mprintf("%s/%s", SWITCH_GLOBAL_dirs.conf_dir, cf);
	switch_assert(mime_path);

	fd = fopen(mime_path, "rb");

	if (fd == NULL) {
		goto end;
	}

	while ((switch_fp_read_dline(fd, &line_buf, &llen))) {
		char *p;
		char *type = line_buf;

		if (*line_buf == '#') {
			continue;
		}

		if ((p = strchr(line_buf, '\r')) || (p = strchr(line_buf, '\n'))) {
			*p = '\0';
		}

		if ((p = strchr(type, '\t')) || (p = strchr(type, ' '))) {
			*p++ = '\0';

			while (*p == ' ' || *p == '\t') {
				p++;
			}

			switch_core_mime_add_type(type, p);
		}

	}

	switch_safe_free(line_buf);
	fclose(fd);

  end:

	switch_safe_free(mime_path);

}

SWITCH_DECLARE(void) switch_core_setrlimits(void)
{
#ifdef HAVE_SETRLIMIT
	struct rlimit rlp;

	/*
	   Setting the stack size on FreeBSD results in an instant crash.

	   If anyone knows how to fix this,
	   feel free to submit a patch to https://github.com/signalwire/freeswitch
	 */

#ifndef __FreeBSD__
	memset(&rlp, 0, sizeof(rlp));
	rlp.rlim_cur = SWITCH_THREAD_STACKSIZE;
	rlp.rlim_max = SWITCH_SYSTEM_THREAD_STACKSIZE;
	setrlimit(RLIMIT_STACK, &rlp);
#endif

	memset(&rlp, 0, sizeof(rlp));
	rlp.rlim_cur = 999999;
	rlp.rlim_max = 999999;
	setrlimit(RLIMIT_NOFILE, &rlp);

	memset(&rlp, 0, sizeof(rlp));
	rlp.rlim_cur = RLIM_INFINITY;
	rlp.rlim_max = RLIM_INFINITY;

	setrlimit(RLIMIT_CPU, &rlp);
	setrlimit(RLIMIT_DATA, &rlp);
	setrlimit(RLIMIT_FSIZE, &rlp);
#ifdef RLIMIT_NPROC
	setrlimit(RLIMIT_NPROC, &rlp);
#endif
#ifdef RLIMIT_RTPRIO
	setrlimit(RLIMIT_RTPRIO, &rlp);
#endif

#if !defined(__OpenBSD__) && !defined(__NetBSD__)
	setrlimit(RLIMIT_AS, &rlp);
#endif
#endif
	return;
}

typedef struct {
	switch_memory_pool_t *pool;
	switch_hash_t *hash;
} switch_ip_list_t;

static switch_ip_list_t IP_LIST = { 0 };

SWITCH_DECLARE(switch_bool_t) switch_check_network_list_ip_port_token(const char *ip_str, int port, const char *list_name, const char **token)
{
	switch_network_list_t *list;
	ip_t  ip, mask, net;
	uint32_t bits;
	char *ipv6 = strchr(ip_str,':');
	switch_bool_t ok = SWITCH_FALSE;
	char *ipv4 = NULL;

	if (!list_name) {
		return SWITCH_FALSE;
	}

	if ((ipv4 = switch_network_ipv4_mapped_ipv6_addr(ip_str))) {
		ip_str = ipv4;
		ipv6 = NULL;
	}

	switch_mutex_lock(runtime.global_mutex);
	if (ipv6) {
		switch_inet_pton(AF_INET6, ip_str, &ip);
	} else {
		switch_inet_pton(AF_INET, ip_str, &ip);
		ip.v4 = htonl(ip.v4);
	}

	if ((list = switch_core_hash_find(IP_LIST.hash, list_name))) {
		if (ipv6) {
			ok = switch_network_list_validate_ip6_port_token(list, ip, port, token);
		} else {
			ok = switch_network_list_validate_ip_port_token(list, ip.v4, port, token);
		}
	} else if (strchr(list_name, '/')) {
		if (strchr(list_name, ',')) {
			char *list_name_dup = strdup(list_name);
			char *argv[100]; /* MAX ACL */
			int argc;

			switch_assert(list_name_dup);

			if ((argc = switch_separate_string(list_name_dup, ',', argv, (sizeof(argv) / sizeof(argv[0]))))) {
				int i;
				for (i = 0; i < argc; i++) {
					switch_parse_cidr(argv[i], &net, &mask, &bits);
					if (ipv6) {
						if ((ok = switch_testv6_subnet(ip, net, mask))){
							break;
						}
					} else {
						if ((ok = switch_test_subnet(ip.v4, net.v4, mask.v4))) {
							break;
						}
					}
				}
			}
			free(list_name_dup);
		} else {
			switch_parse_cidr(list_name, &net, &mask, &bits);

			if (ipv6) {
				ok = switch_testv6_subnet(ip, net, mask);
			} else {
				ok = switch_test_subnet(ip.v4, net.v4, mask.v4);
			}
		}
	}

	switch_safe_free(ipv4);
	switch_mutex_unlock(runtime.global_mutex);

	return ok;
}

SWITCH_DECLARE(switch_bool_t) switch_check_network_list_ip_token(const char *ip_str, const char *list_name, const char **token)
{
	return switch_check_network_list_ip_port_token(ip_str, 0, list_name, token);
}

SWITCH_DECLARE(void) switch_load_network_lists(switch_bool_t reload)
{
	switch_xml_t xml = NULL, x_lists = NULL, x_list = NULL, x_node = NULL, cfg = NULL;
	switch_network_list_t *rfc_list, *list;
	char guess_ip[16] = "";
	int mask = 0;
	char guess_mask[16] = "";
	char *tmp_name;
	struct in_addr in;

	switch_find_local_ip(guess_ip, sizeof(guess_ip), &mask, AF_INET);
	in.s_addr = mask;
	switch_set_string(guess_mask, inet_ntoa(in));

	switch_mutex_lock(runtime.global_mutex);

	if (IP_LIST.hash) {
		switch_core_hash_destroy(&IP_LIST.hash);
	}

	if (IP_LIST.pool) {
		switch_core_destroy_memory_pool(&IP_LIST.pool);
	}

	memset(&IP_LIST, 0, sizeof(IP_LIST));
	switch_core_new_memory_pool(&IP_LIST.pool);
	switch_core_hash_init(&IP_LIST.hash);


	tmp_name = "rfc6598.auto";
	switch_network_list_create(&rfc_list, tmp_name, SWITCH_FALSE, IP_LIST.pool);
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (deny)\n", tmp_name);
	switch_network_list_add_cidr(rfc_list, "100.64.0.0/10", SWITCH_TRUE);
	switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list);

	tmp_name = "rfc1918.auto";
	switch_network_list_create(&rfc_list, tmp_name, SWITCH_FALSE, IP_LIST.pool);
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (deny)\n", tmp_name);
	switch_network_list_add_cidr(rfc_list, "10.0.0.0/8", SWITCH_TRUE);
	switch_network_list_add_cidr(rfc_list, "172.16.0.0/12", SWITCH_TRUE);
	switch_network_list_add_cidr(rfc_list, "192.168.0.0/16", SWITCH_TRUE);
	switch_network_list_add_cidr(rfc_list, "fe80::/10", SWITCH_TRUE);
	switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list);

	tmp_name = "wan.auto";
	switch_network_list_create(&rfc_list, tmp_name, SWITCH_TRUE, IP_LIST.pool);
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (allow)\n", tmp_name);
	switch_network_list_add_cidr(rfc_list, "0.0.0.0/8", SWITCH_FALSE);
	switch_network_list_add_cidr(rfc_list, "10.0.0.0/8", SWITCH_FALSE);
	switch_network_list_add_cidr(rfc_list, "172.16.0.0/12", SWITCH_FALSE);
	switch_network_list_add_cidr(rfc_list, "192.168.0.0/16", SWITCH_FALSE);
	switch_network_list_add_cidr(rfc_list, "169.254.0.0/16", SWITCH_FALSE);
	switch_network_list_add_cidr(rfc_list, "100.64.0.0/10", SWITCH_FALSE);
	switch_network_list_add_cidr(rfc_list, "fe80::/10", SWITCH_FALSE);
	switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list);

	tmp_name = "wan_v6.auto";
	switch_network_list_create(&rfc_list, tmp_name, SWITCH_TRUE, IP_LIST.pool);
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (allow)\n", tmp_name);
	switch_network_list_add_cidr(rfc_list, "0.0.0.0/0", SWITCH_FALSE);
	switch_network_list_add_cidr(rfc_list, "fe80::/10", SWITCH_FALSE);
	switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list);


	tmp_name = "wan_v4.auto";
	switch_network_list_create(&rfc_list, tmp_name, SWITCH_TRUE, IP_LIST.pool);
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (allow)\n", tmp_name);
	switch_network_list_add_cidr(rfc_list, "0.0.0.0/8", SWITCH_FALSE);
	switch_network_list_add_cidr(rfc_list, "10.0.0.0/8", SWITCH_FALSE);
	switch_network_list_add_cidr(rfc_list, "172.16.0.0/12", SWITCH_FALSE);
	switch_network_list_add_cidr(rfc_list, "192.168.0.0/16", SWITCH_FALSE);
	switch_network_list_add_cidr(rfc_list, "169.254.0.0/16", SWITCH_FALSE);
	switch_network_list_add_cidr(rfc_list, "100.64.0.0/10", SWITCH_FALSE);
	switch_network_list_add_cidr(rfc_list, "::/0", SWITCH_FALSE);
	switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list);


	tmp_name = "any_v6.auto";
	switch_network_list_create(&rfc_list, tmp_name, SWITCH_TRUE, IP_LIST.pool);
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (allow)\n", tmp_name);
	switch_network_list_add_cidr(rfc_list, "0.0.0.0/0", SWITCH_FALSE);
	switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list);


	tmp_name = "any_v4.auto";
	switch_network_list_create(&rfc_list, tmp_name, SWITCH_TRUE, IP_LIST.pool);
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (allow)\n", tmp_name);
	switch_network_list_add_cidr(rfc_list, "::/0", SWITCH_FALSE);
	switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list);


	tmp_name = "nat.auto";
	switch_network_list_create(&rfc_list, tmp_name, SWITCH_FALSE, IP_LIST.pool);
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (deny)\n", tmp_name);
	if (switch_network_list_add_host_mask(rfc_list, guess_ip, guess_mask, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding %s/%s (deny) to list %s\n", guess_ip, guess_mask, tmp_name);
	}
	switch_network_list_add_cidr(rfc_list, "10.0.0.0/8", SWITCH_TRUE);
	switch_network_list_add_cidr(rfc_list, "172.16.0.0/12", SWITCH_TRUE);
	switch_network_list_add_cidr(rfc_list, "192.168.0.0/16", SWITCH_TRUE);
	switch_network_list_add_cidr(rfc_list, "100.64.0.0/10", SWITCH_TRUE);
	switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list);

	tmp_name = "loopback.auto";
	switch_network_list_create(&rfc_list, tmp_name, SWITCH_FALSE, IP_LIST.pool);
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (deny)\n", tmp_name);
	switch_network_list_add_cidr(rfc_list, "127.0.0.0/8", SWITCH_TRUE);
	switch_network_list_add_cidr(rfc_list, "::1/128", SWITCH_TRUE);
	switch_core_hash_insert(IP_LIST.hash, tmp_name, rfc_list);

	tmp_name = "localnet.auto";
	switch_network_list_create(&list, tmp_name, SWITCH_FALSE, IP_LIST.pool);
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (deny)\n", tmp_name);

	if (switch_network_list_add_host_mask(list, guess_ip, guess_mask, SWITCH_TRUE) == SWITCH_STATUS_SUCCESS) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding %s/%s (allow) to list %s\n", guess_ip, guess_mask, tmp_name);
	}
	switch_core_hash_insert(IP_LIST.hash, tmp_name, list);


	if ((xml = switch_xml_open_cfg("acl.conf", &cfg, NULL))) {
		if ((x_lists = switch_xml_child(cfg, "network-lists"))) {
			for (x_list = switch_xml_child(x_lists, "list"); x_list; x_list = x_list->next) {
				const char *name = switch_xml_attr(x_list, "name");
				const char *dft = switch_xml_attr(x_list, "default");
				switch_bool_t default_type = SWITCH_TRUE;

				if (zstr(name)) {
					continue;
				}

				if (dft) {
					default_type = switch_true(dft);
				}

				if (switch_network_list_create(&list, name, default_type, IP_LIST.pool) != SWITCH_STATUS_SUCCESS) {
					abort();
				}

				if (reload) {
					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created ip list %s default (%s)\n", name, default_type ? "allow" : "deny");
				} else {
					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Created ip list %s default (%s)\n", name, default_type ? "allow" : "deny");
				}


				for (x_node = switch_xml_child(x_list, "node"); x_node; x_node = x_node->next) {
					const char *cidr = NULL, *host = NULL, *mask = NULL, *domain = NULL, *port = NULL;
					switch_bool_t ok = default_type;
					const char *type = switch_xml_attr(x_node, "type");
					switch_network_port_range_t port_range;
					char *argv[MAX_NETWORK_PORTS] = { 0 };
					int argc = 0, i;

					if (type) {
						ok = switch_true(type);
					}

					cidr = switch_xml_attr(x_node, "cidr");
					host = switch_xml_attr(x_node, "host");
					mask = switch_xml_attr(x_node, "mask");
					domain = switch_xml_attr(x_node, "domain");

					memset(&port_range, 0, sizeof(switch_network_port_range_t));

					if( (port = switch_xml_attr(x_node, "port")) != NULL) {
						port_range.port = atoi(port);
					}

					if( (port = switch_xml_attr(x_node, "ports")) != NULL) {
						argc = switch_separate_string((char*)port, ',', argv, (sizeof(argv) / sizeof(argv[0])));
						for(i=0; i < argc; i++) {
							port_range.ports[i] = atoi(argv[i]);
						}
					}
					if( (port = switch_xml_attr(x_node, "port-min")) != NULL) {
						port_range.min_port = atoi(port);
					}
					if( (port = switch_xml_attr(x_node, "port-max")) != NULL) {
						port_range.max_port = atoi(port);
					}

					if (domain) {
						switch_event_t *my_params = NULL;
						switch_xml_t x_domain, xml_root;
						switch_xml_t gt, gts, ut, uts;

						switch_event_create(&my_params, SWITCH_EVENT_GENERAL);
						switch_assert(my_params);
						switch_event_add_header_string(my_params, SWITCH_STACK_BOTTOM, "domain", domain);
						switch_event_add_header_string(my_params, SWITCH_STACK_BOTTOM, "purpose", "network-list");

						if (switch_xml_locate_domain(domain, my_params, &xml_root, &x_domain) != SWITCH_STATUS_SUCCESS) {
							switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot locate domain %s\n", domain);
							switch_event_destroy(&my_params);
							continue;
						}

						switch_event_destroy(&my_params);

						if ((ut = switch_xml_child(x_domain, "users"))) {
							x_domain = ut;
						}

						for (ut = switch_xml_child(x_domain, "user"); ut; ut = ut->next) {
							const char *user_cidr = switch_xml_attr(ut, "cidr");
							const char *id = switch_xml_attr(ut, "id");

							if (id && user_cidr) {
								char *token = switch_mprintf("%s@%s", id, domain);
								switch_assert(token);
								switch_network_list_add_cidr_token(list, user_cidr, ok, token);
								free(token);
							}
						}

						for (gts = switch_xml_child(x_domain, "groups"); gts; gts = gts->next) {
							for (gt = switch_xml_child(gts, "group"); gt; gt = gt->next) {
								for (uts = switch_xml_child(gt, "users"); uts; uts = uts->next) {
									for (ut = switch_xml_child(uts, "user"); ut; ut = ut->next) {
										const char *user_cidr = switch_xml_attr(ut, "cidr");
										const char *id = switch_xml_attr(ut, "id");

										if (id && user_cidr) {
											char *token = switch_mprintf("%s@%s", id, domain);
											switch_assert(token);
											switch_network_list_add_cidr_port_token(list, user_cidr, ok, token, &port_range);
											free(token);
										}
									}
								}
							}
						}

						switch_xml_free(xml_root);
					} else if (cidr) {
						switch_network_list_add_cidr_port_token(list, cidr, ok, NULL, &port_range);
					} else if (host && mask) {
						switch_network_list_add_host_port_mask(list, host, mask, ok, &port_range);
					}
				}

				switch_core_hash_insert(IP_LIST.hash, name, list);
			}
		}

		switch_xml_free(xml);
	}

	switch_mutex_unlock(runtime.global_mutex);
}

SWITCH_DECLARE(uint32_t) switch_core_max_dtmf_duration(uint32_t duration)
{
	if (duration) {
		if (duration > SWITCH_MAX_DTMF_DURATION) {
			duration = SWITCH_MAX_DTMF_DURATION;
		}
		if (duration < SWITCH_MIN_DTMF_DURATION) {
			duration = SWITCH_MIN_DTMF_DURATION;
		}
		runtime.max_dtmf_duration = duration;
		if (duration < runtime.min_dtmf_duration) {
			runtime.min_dtmf_duration = duration;
		}
	}
	return runtime.max_dtmf_duration;
}

SWITCH_DECLARE(uint32_t) switch_core_default_dtmf_duration(uint32_t duration)
{
	if (duration) {
		if (duration < SWITCH_MIN_DTMF_DURATION) {
			duration = SWITCH_MIN_DTMF_DURATION;
		}
		if (duration > SWITCH_MAX_DTMF_DURATION) {
			duration = SWITCH_MAX_DTMF_DURATION;
		}
		runtime.default_dtmf_duration = duration;

		if (duration < runtime.min_dtmf_duration) {
			runtime.min_dtmf_duration = duration;
		}

		if (duration > runtime.max_dtmf_duration) {
			runtime.max_dtmf_duration = duration;
		}

	}
	return runtime.default_dtmf_duration;
}

SWITCH_DECLARE(uint32_t) switch_core_min_dtmf_duration(uint32_t duration)
{
	if (duration) {
		if (duration < SWITCH_MIN_DTMF_DURATION) {
			duration = SWITCH_MIN_DTMF_DURATION;
		}
		if (duration > SWITCH_MAX_DTMF_DURATION) {
			duration = SWITCH_MAX_DTMF_DURATION;
		}

		runtime.min_dtmf_duration = duration;

		if (duration > runtime.max_dtmf_duration) {
			runtime.max_dtmf_duration = duration;
		}
	}
	return runtime.min_dtmf_duration;
}

SWITCH_DECLARE(switch_status_t) switch_core_thread_set_cpu_affinity(int cpu)
{
	switch_status_t status = SWITCH_STATUS_FALSE;

	if (cpu > -1) {

#ifdef HAVE_CPU_SET_MACROS
		cpu_set_t set;

		CPU_ZERO(&set);
		CPU_SET(cpu, &set);

		if (!sched_setaffinity(0, sizeof(set), &set)) {
			status = SWITCH_STATUS_SUCCESS;
		}

#else
#if WIN32
		if (SetThreadAffinityMask(GetCurrentThread(), (DWORD_PTR) cpu)) {
			status = SWITCH_STATUS_SUCCESS;
		}
#endif
#endif
	}

	return status;
}


SWITCH_DECLARE(int) switch_core_test_flag(int flag)
{
	return switch_test_flag((&runtime), flag);
}


SWITCH_DECLARE(switch_status_t) switch_core_init(switch_core_flag_t flags, switch_bool_t console, const char **err)
{
	switch_uuid_t uuid;
	char guess_ip[256];
	int mask = 0;
	struct in_addr in;


	if (runtime.runlevel > 0) {
		/* one per customer */
		return SWITCH_STATUS_SUCCESS;
	}

	memset(&runtime, 0, sizeof(runtime));
	gethostname(runtime.hostname, sizeof(runtime.hostname));

	runtime.shutdown_cause = SWITCH_CAUSE_SYSTEM_SHUTDOWN;
	runtime.max_db_handles = 50;
	runtime.db_handle_timeout = 5000000;
	runtime.event_heartbeat_interval = 20;

	runtime.runlevel++;
	runtime.dummy_cng_frame.data = runtime.dummy_data;
	runtime.dummy_cng_frame.datalen = sizeof(runtime.dummy_data);
	runtime.dummy_cng_frame.buflen = sizeof(runtime.dummy_data);
	runtime.dbname = "core";
	switch_set_flag((&runtime.dummy_cng_frame), SFF_CNG);
	switch_set_flag((&runtime), SCF_AUTO_SCHEMAS);
	switch_set_flag((&runtime), SCF_CLEAR_SQL);
	switch_set_flag((&runtime), SCF_API_EXPANSION);
	switch_set_flag((&runtime), SCF_SESSION_THREAD_POOL);
#ifdef WIN32
	switch_set_flag((&runtime), SCF_THREADED_SYSTEM_EXEC);
#endif
	switch_set_flag((&runtime), SCF_NO_NEW_SESSIONS);
	if (flags & SCF_LOG_DISABLE) {
		runtime.hard_log_level = SWITCH_LOG_DISABLE;
		flags &= ~SCF_LOG_DISABLE;
	} else {
		runtime.hard_log_level = SWITCH_LOG_DEBUG;
	}
	runtime.mailer_app = "sendmail";
	runtime.mailer_app_args = "-t";
	runtime.max_dtmf_duration = SWITCH_MAX_DTMF_DURATION;
	runtime.default_dtmf_duration = SWITCH_DEFAULT_DTMF_DURATION;
	runtime.min_dtmf_duration = SWITCH_MIN_DTMF_DURATION;
	runtime.odbc_dbtype = DBTYPE_DEFAULT;
	runtime.dbname = NULL;
#ifndef WIN32
	runtime.cpu_count = sysconf (_SC_NPROCESSORS_ONLN);
#else
	{
		SYSTEM_INFO sysinfo;
		GetSystemInfo( &sysinfo );
		runtime.cpu_count = sysinfo.dwNumberOfProcessors;
	}
#endif

	if (!runtime.cpu_count) runtime.cpu_count = 1;

	if (sqlite3_initialize() != SQLITE_OK) {
		*err = "FATAL ERROR! Could not initialize SQLite\n";
		return SWITCH_STATUS_MEMERR;
	}

	/* INIT APR and Create the pool context */
	if (fspr_initialize() != SWITCH_STATUS_SUCCESS) {
		*err = "FATAL ERROR! Could not initialize APR\n";
		return SWITCH_STATUS_MEMERR;
	}

	if (!(runtime.memory_pool = switch_core_memory_init())) {
		*err = "FATAL ERROR! Could not allocate memory pool\n";
		return SWITCH_STATUS_MEMERR;
	}
	switch_assert(runtime.memory_pool != NULL);

	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.base_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.mod_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.conf_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.log_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.run_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.db_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.script_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.htdocs_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.grammar_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.fonts_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.images_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.recordings_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.sounds_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.temp_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);
	switch_dir_make_recursive(SWITCH_GLOBAL_dirs.certs_dir, SWITCH_DEFAULT_DIR_PERMS, runtime.memory_pool);

	switch_mutex_init(&runtime.uuid_mutex, SWITCH_MUTEX_NESTED, runtime.memory_pool);

	switch_mutex_init(&runtime.throttle_mutex, SWITCH_MUTEX_NESTED, runtime.memory_pool);

	switch_mutex_init(&runtime.session_hash_mutex, SWITCH_MUTEX_NESTED, runtime.memory_pool);
	switch_mutex_init(&runtime.global_mutex, SWITCH_MUTEX_NESTED, runtime.memory_pool);

	switch_thread_rwlock_create(&runtime.global_var_rwlock, runtime.memory_pool);
	switch_core_set_globals();
	switch_core_session_init(runtime.memory_pool);
	switch_event_create_plain(&runtime.global_vars, SWITCH_EVENT_CHANNEL_DATA);
	switch_core_hash_init_case(&runtime.mime_types, SWITCH_FALSE);
	switch_core_hash_init_case(&runtime.mime_type_exts, SWITCH_FALSE);
	switch_core_hash_init_case(&runtime.ptimes, SWITCH_FALSE);
	load_mime_types();
	runtime.flags |= flags;
	runtime.sps_total = 30;

	*err = NULL;

	if (console) {
		runtime.console = stdout;
	}

	SSL_library_init();
	switch_ssl_init_ssl_locks();
	OpenSSL_add_all_algorithms();
	switch_curl_init();

	switch_core_set_variable("hostname", runtime.hostname);
	switch_find_local_ip(guess_ip, sizeof(guess_ip), &mask, AF_INET);
	switch_core_set_variable("local_ip_v4", guess_ip);
	in.s_addr = mask;
	switch_core_set_variable("local_mask_v4", inet_ntoa(in));


	switch_find_local_ip(guess_ip, sizeof(guess_ip), NULL, AF_INET6);
	switch_core_set_variable("local_ip_v6", guess_ip);
	switch_core_set_variable("base_dir", SWITCH_GLOBAL_dirs.base_dir);
	switch_core_set_variable("recordings_dir", SWITCH_GLOBAL_dirs.recordings_dir);
	switch_core_set_variable("sound_prefix", SWITCH_GLOBAL_dirs.sounds_dir);
	switch_core_set_variable("sounds_dir", SWITCH_GLOBAL_dirs.sounds_dir);
	switch_core_set_variable("conf_dir", SWITCH_GLOBAL_dirs.conf_dir);
	switch_core_set_variable("log_dir", SWITCH_GLOBAL_dirs.log_dir);
	switch_core_set_variable("run_dir", SWITCH_GLOBAL_dirs.run_dir);
	switch_core_set_variable("db_dir", SWITCH_GLOBAL_dirs.db_dir);
	switch_core_set_variable("mod_dir", SWITCH_GLOBAL_dirs.mod_dir);
	switch_core_set_variable("htdocs_dir", SWITCH_GLOBAL_dirs.htdocs_dir);
	switch_core_set_variable("script_dir", SWITCH_GLOBAL_dirs.script_dir);
	switch_core_set_variable("temp_dir", SWITCH_GLOBAL_dirs.temp_dir);
	switch_core_set_variable("grammar_dir", SWITCH_GLOBAL_dirs.grammar_dir);
	switch_core_set_variable("fonts_dir", SWITCH_GLOBAL_dirs.fonts_dir);
	switch_core_set_variable("images_dir", SWITCH_GLOBAL_dirs.images_dir);
	switch_core_set_variable("certs_dir", SWITCH_GLOBAL_dirs.certs_dir);
	switch_core_set_variable("storage_dir", SWITCH_GLOBAL_dirs.storage_dir);
	switch_core_set_variable("cache_dir", SWITCH_GLOBAL_dirs.cache_dir);
	switch_core_set_variable("data_dir", SWITCH_GLOBAL_dirs.data_dir);
	switch_core_set_variable("localstate_dir", SWITCH_GLOBAL_dirs.localstate_dir);
	switch_console_init(runtime.memory_pool);
	switch_event_init(runtime.memory_pool);
	switch_channel_global_init(runtime.memory_pool);

	if (switch_xml_init(runtime.memory_pool, err) != SWITCH_STATUS_SUCCESS) {
		/* allow missing configuration if MINIMAL */
		if (!(flags & SCF_MINIMAL)) {
			fspr_terminate();
			return SWITCH_STATUS_MEMERR;
		}
	}

	if (switch_test_flag((&runtime), SCF_USE_AUTO_NAT)) {
		switch_nat_init(runtime.memory_pool, switch_test_flag((&runtime), SCF_USE_NAT_MAPPING));
	}

	switch_log_init(runtime.memory_pool, runtime.colorize_console);

	runtime.tipping_point = 0;
	runtime.timer_affinity = -1;
	runtime.microseconds_per_tick = 20000;

	if (flags & SCF_MINIMAL) return SWITCH_STATUS_SUCCESS;

	switch_load_core_config("switch.conf");

	switch_core_state_machine_init(runtime.memory_pool);

	switch_core_media_init();
	switch_scheduler_task_thread_start();

	switch_nat_late_init();

	switch_rtp_init(runtime.memory_pool);

	runtime.running = 1;
	runtime.initiated = switch_mono_micro_time_now();

	switch_scheduler_add_task(switch_epoch_time_now(NULL), heartbeat_callback, "heartbeat", "core", 0, NULL, SSHF_NONE | SSHF_NO_DEL);

	switch_scheduler_add_task(switch_epoch_time_now(NULL), check_ip_callback, "check_ip", "core", 0, NULL, SSHF_NONE | SSHF_NO_DEL | SSHF_OWN_THREAD);

	switch_uuid_get(&uuid);
	switch_uuid_format(runtime.uuid_str, &uuid);
	switch_core_set_variable("core_uuid", runtime.uuid_str);


	return SWITCH_STATUS_SUCCESS;
}


#ifdef TRAP_BUS
static void handle_SIGBUS(int sig)
{
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Sig BUS!\n");
	return;
}
#endif

static void handle_SIGHUP(int sig)
{
	if (sig) {
		switch_event_t *event;

		if (switch_event_create(&event, SWITCH_EVENT_TRAP) == SWITCH_STATUS_SUCCESS) {
			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Trapped-Signal", "HUP");
			switch_event_fire(&event);
		}
	}
	return;
}


SWITCH_DECLARE(uint32_t) switch_default_ptime(const char *name, uint32_t number)
{
	uint32_t *p;

	if ((p = switch_core_hash_find(runtime.ptimes, name))) {
		return *p;
	}

	return 20;
}

SWITCH_DECLARE(uint32_t) switch_default_rate(const char *name, uint32_t number)
{

	if (!strcasecmp(name, "opus")) {
		return 48000;
	} else if (!strncasecmp(name, "h26", 3)) { // h26x
		return 90000;
	} else if (!strncasecmp(name, "vp", 2)) {  // vp8, vp9
		return 90000;
	}

	return 8000;
}

static uint32_t d_30 = 30;

static void switch_load_core_config(const char *file)
{
	switch_xml_t xml = NULL, cfg = NULL;

	switch_core_hash_insert(runtime.ptimes, "ilbc", &d_30);
	switch_core_hash_insert(runtime.ptimes, "isac", &d_30);
	switch_core_hash_insert(runtime.ptimes, "G723", &d_30);


	if ((xml = switch_xml_open_cfg(file, &cfg, NULL))) {
		switch_xml_t settings, param;

		if ((settings = switch_xml_child(cfg, "default-ptimes"))) {
			for (param = switch_xml_child(settings, "codec"); param; param = param->next) {
				const char *var = switch_xml_attr_soft(param, "name");
				const char *val = switch_xml_attr_soft(param, "ptime");

				if (!zstr(var) && !zstr(val)) {
					uint32_t *p;
					uint32_t v = switch_atoul(val);

					if (!strcasecmp(var, "G723") || !strcasecmp(var, "iLBC")) {
						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error adding %s, defaults cannot be changed\n", var);
						continue;
					}

					if (v == 0) {
						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error adding %s, invalid ptime\n", var);
						continue;
					}

					p = switch_core_alloc(runtime.memory_pool, sizeof(*p));
					*p = v;
					switch_core_hash_insert(runtime.ptimes, var, p);
				}

			}
		}

		if ((settings = switch_xml_child(cfg, "settings"))) {
			for (param = switch_xml_child(settings, "param"); param; param = param->next) {
				const char *var = switch_xml_attr_soft(param, "name");
				const char *val = switch_xml_attr_soft(param, "value");

				if (!strcasecmp(var, "loglevel")) {
					int level;
					if (*val > 47 && *val < 58) {
						level = atoi(val);
					} else {
						level = switch_log_str2level(val);
					}

					if (level != SWITCH_LOG_INVALID) {
						switch_core_session_ctl(SCSC_LOGLEVEL, &level);
					}
#ifdef HAVE_SETRLIMIT
				} else if (!strcasecmp(var, "dump-cores") && switch_true(val)) {
					struct rlimit rlp;
					memset(&rlp, 0, sizeof(rlp));
					rlp.rlim_cur = RLIM_INFINITY;
					rlp.rlim_max = RLIM_INFINITY;
					setrlimit(RLIMIT_CORE, &rlp);
#endif
				} else if (!strcasecmp(var, "debug-level")) {
					int tmp = atoi(val);
					if (tmp > -1 && tmp < 11) {
						switch_core_session_ctl(SCSC_DEBUG_LEVEL, &tmp);
					}
				} else if (!strcasecmp(var, "max-db-handles")) {
					long tmp = atol(val);

					if (tmp > 4 && tmp < 5001) {
						runtime.max_db_handles = (uint32_t) tmp;
					} else {
						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "max-db-handles must be between 5 and 5000\n");
					}
				} else if (!strcasecmp(var, "odbc-skip-autocommit-flip")) {
					if (switch_true(val)) {
						switch_odbc_skip_autocommit_flip();
					}
				} else if (!strcasecmp(var, "db-handle-timeout")) {
					long tmp = atol(val);

					if (tmp > 0 && tmp < 5001) {
						runtime.db_handle_timeout = (uint32_t) tmp * 1000000;
					} else {
						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "db-handle-timeout must be between 1 and 5000\n");
					}

				} else if (!strcasecmp(var, "event-heartbeat-interval")) {
					long tmp = atol(val);

					if (tmp > 0) {
						runtime.event_heartbeat_interval = (uint32_t) tmp;
					} else {
						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "heartbeat-interval must be a greater than 0\n");
					}

				} else if (!strcasecmp(var, "multiple-registrations")) {
					runtime.multiple_registrations = switch_true(val);
				} else if (!strcasecmp(var, "auto-create-schemas")) {
					if (switch_true(val)) {
						switch_set_flag((&runtime), SCF_AUTO_SCHEMAS);
					} else {
						switch_clear_flag((&runtime), SCF_AUTO_SCHEMAS);
					}
				} else if (!strcasecmp(var, "session-thread-pool")) {
					if (switch_true(val)) {
						switch_set_flag((&runtime), SCF_SESSION_THREAD_POOL);
					} else {
						switch_clear_flag((&runtime), SCF_SESSION_THREAD_POOL);
					}
				} else if (!strcasecmp(var, "auto-clear-sql")) {
					if (switch_true(val)) {
						switch_set_flag((&runtime), SCF_CLEAR_SQL);
					} else {
						switch_clear_flag((&runtime), SCF_CLEAR_SQL);
					}
				} else if (!strcasecmp(var, "api-expansion")) {
					if (switch_true(val)) {
						switch_set_flag((&runtime), SCF_API_EXPANSION);
					} else {
						switch_clear_flag((&runtime), SCF_API_EXPANSION);
					}
				} else if (!strcasecmp(var, "enable-early-hangup") && switch_true(val)) {
					switch_set_flag((&runtime), SCF_EARLY_HANGUP);
				} else if (!strcasecmp(var, "colorize-console") && switch_true(val)) {
					runtime.colorize_console = SWITCH_TRUE;
				} else if (!strcasecmp(var, "core-db-pre-trans-execute") && !zstr(val)) {
					runtime.core_db_pre_trans_execute = switch_core_strdup(runtime.memory_pool, val);
				} else if (!strcasecmp(var, "core-db-post-trans-execute") && !zstr(val)) {
					runtime.core_db_post_trans_execute = switch_core_strdup(runtime.memory_pool, val);
				} else if (!strcasecmp(var, "core-db-inner-pre-trans-execute") && !zstr(val)) {
					runtime.core_db_inner_pre_trans_execute = switch_core_strdup(runtime.memory_pool, val);
				} else if (!strcasecmp(var, "core-db-inner-post-trans-execute") && !zstr(val)) {
					runtime.core_db_inner_post_trans_execute = switch_core_strdup(runtime.memory_pool, val);
				} else if (!strcasecmp(var, "dialplan-timestamps")) {
					if (switch_true(val)) {
						switch_set_flag((&runtime), SCF_DIALPLAN_TIMESTAMPS);
					} else {
						switch_clear_flag((&runtime), SCF_DIALPLAN_TIMESTAMPS);
					}
				} else if (!strcasecmp(var, "mailer-app") && !zstr(val)) {
					runtime.mailer_app = switch_core_strdup(runtime.memory_pool, val);
				} else if (!strcasecmp(var, "mailer-app-args") && val) {
					runtime.mailer_app_args = switch_core_strdup(runtime.memory_pool, val);
				} else if (!strcasecmp(var, "sessions-per-second") && !zstr(val)) {
					switch_core_sessions_per_second(atoi(val));
				} else if (!strcasecmp(var, "max-dtmf-duration") && !zstr(val)) {
					int tmp = atoi(val);
					if (tmp > 0) {
						switch_core_max_dtmf_duration((uint32_t) tmp);
					}
				} else if (!strcasecmp(var, "min-dtmf-duration") && !zstr(val)) {
					int tmp = atoi(val);
					if (tmp > 0) {
						switch_core_min_dtmf_duration((uint32_t) tmp);
					}
				} else if (!strcasecmp(var, "default-dtmf-duration") && !zstr(val)) {
					int tmp = atoi(val);
					if (tmp > 0) {
						switch_core_default_dtmf_duration((uint32_t) tmp);
					}
				} else if (!strcasecmp(var, "enable-use-system-time")) {
					switch_time_set_use_system_time(switch_true(val));
				} else if (!strcasecmp(var, "enable-monotonic-timing")) {
					switch_time_set_monotonic(switch_true(val));
				} else if (!strcasecmp(var, "enable-softtimer-timerfd")) {
					int ival = 0;
					if (val) {
						if (switch_true(val)) {
							ival = 2;
						} else {
							if (strcasecmp(val, "broadcast")) {
								ival = 1;
							} else if (strcasecmp(val, "fd-per-timer")) {
								ival = 2;
							}
						}
					}
					switch_time_set_timerfd(ival);
				} else if (!strcasecmp(var, "enable-clock-nanosleep")) {
					switch_time_set_nanosleep(switch_true(val));
				} else if (!strcasecmp(var, "enable-cond-yield")) {
					switch_time_set_cond_yield(switch_true(val));
				} else if (!strcasecmp(var, "enable-timer-matrix")) {
					switch_time_set_matrix(switch_true(val));
				} else if (!strcasecmp(var, "max-sessions") && !zstr(val)) {
					switch_core_session_limit(atoi(val));
				} else if (!strcasecmp(var, "verbose-channel-events") && !zstr(val)) {
					int v = switch_true(val);
					if (v) {
						switch_set_flag((&runtime), SCF_VERBOSE_EVENTS);
					} else {
						switch_clear_flag((&runtime), SCF_VERBOSE_EVENTS);
					}
				} else if (!strcasecmp(var, "threaded-system-exec") && !zstr(val)) {
#ifdef WIN32
					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "threaded-system-exec is not implemented on this platform\n");
#else
					int v = switch_true(val);
					if (v) {
						switch_set_flag((&runtime), SCF_THREADED_SYSTEM_EXEC);
					} else {
						switch_clear_flag((&runtime), SCF_THREADED_SYSTEM_EXEC);
					}
#endif
				} else if (!strcasecmp(var, "spawn-instead-of-system") && !zstr(val)) {
#ifdef WIN32
					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "spawn-instead-of-system is not implemented on this platform\n");
#else
					int v = switch_true(val);
					if (v) {
						switch_core_set_variable("spawn_instead_of_system", "true");
					} else {
						switch_core_set_variable("spawn_instead_of_system", "false");
					}
#endif
				} else if (!strcasecmp(var, "exclude-error-log-from-xml-cdr") && !zstr(val)) {
					int v = switch_true(val);
					if (v) {
						switch_core_set_variable("exclude_error_log_from_xml_cdr", "true");
					} else {
						switch_core_set_variable("exclude_error_log_from_xml_cdr", "false");
					}
				} else if (!strcasecmp(var, "min-idle-cpu") && !zstr(val)) {
					switch_core_min_idle_cpu(atof(val));
				} else if (!strcasecmp(var, "tipping-point") && !zstr(val)) {
					runtime.tipping_point = atoi(val);
				} else if (!strcasecmp(var, "cpu-idle-smoothing-depth") && !zstr(val)) {
					runtime.cpu_idle_smoothing_depth = atoi(val);
				} else if (!strcasecmp(var, "events-use-dispatch") && !zstr(val)) {
					runtime.events_use_dispatch = switch_true(val);
				} else if (!strcasecmp(var, "initial-event-threads") && !zstr(val)) {
					int tmp;

					if (!runtime.events_use_dispatch) {
						runtime.events_use_dispatch = 1;
						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
										  "Implicitly setting events-use-dispatch based on usage of this initial-event-threads parameter.\n");
					}

					tmp = atoi(val);

					if (tmp > runtime.cpu_count / 2) {
						tmp = runtime.cpu_count / 2;
						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "This value cannot be higher than %d so setting it to that value\n",
										  runtime.cpu_count / 2);
					}

					if (tmp < 1) {
						tmp = 1;
						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "This value cannot be lower than 1 so setting it to that level\n");
					}

					switch_event_launch_dispatch_threads(tmp);

				} else if (!strcasecmp(var, "1ms-timer") && switch_true(val)) {
					runtime.microseconds_per_tick = 1000;
				} else if (!strcasecmp(var, "timer-affinity") && !zstr(val)) {
					if (!strcasecmp(val, "disabled")) {
						runtime.timer_affinity = -1;
					} else {
						runtime.timer_affinity = atoi(val);
					}
				} else if (!strcasecmp(var, "ice-resolve-candidate")) {
					switch_core_media_set_resolveice(switch_true(val));
				} else if (!strcasecmp(var, "rtp-start-port") && !zstr(val)) {
					switch_rtp_set_start_port((switch_port_t) atoi(val));
				} else if (!strcasecmp(var, "rtp-end-port") && !zstr(val)) {
					switch_rtp_set_end_port((switch_port_t) atoi(val));
				} else if (!strcasecmp(var, "rtp-port-usage-robustness") && switch_true(val)) {
					runtime.port_alloc_flags |= SPF_ROBUST_UDP;
				} else if (!strcasecmp(var, "core-db-name") && !zstr(val)) {
					runtime.dbname = switch_core_strdup(runtime.memory_pool, val);
				} else if (!strcasecmp(var, "core-db-dsn") && !zstr(val)) {
					runtime.odbc_dsn = switch_core_strdup(runtime.memory_pool, val);
				} else if (!strcasecmp(var, "core-non-sqlite-db-required") && !zstr(val)) {
					switch_set_flag((&runtime), SCF_CORE_NON_SQLITE_DB_REQ);
				} else if (!strcasecmp(var, "core-dbtype") && !zstr(val)) {
					if (!strcasecmp(val, "MSSQL")) {
						runtime.odbc_dbtype = DBTYPE_MSSQL;
					} else {
						runtime.odbc_dbtype = DBTYPE_DEFAULT;
					}
				} else if (!strcasecmp(var, "switchname") && !zstr(val)) {
					runtime.switchname = switch_core_strdup(runtime.memory_pool, val);
					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Set switchname to %s\n", runtime.switchname);
				} else if (!strcasecmp(var, "rtp-retain-crypto-keys")) {
					if (switch_true(val)) {
						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
										  "rtp-retain-crypto-keys enabled. Could be used to decrypt secure media.\n");
					}
					switch_core_set_variable("rtp_retain_crypto_keys", val);
				} else if (!strcasecmp(var, "caller-profile-soft-variables-uses-prefix") && !zstr(val)) {
					int v = switch_true(val);
					if (v) {
						switch_set_flag((&runtime), SCF_CPF_SOFT_PREFIX);
					} else {
						switch_clear_flag((&runtime), SCF_CPF_SOFT_PREFIX);
					}
				} else if (!strcasecmp(var, "caller-profile-soft-lookup-values") && !zstr(val)) {
					int v = switch_true(val);
					if (v) {
						switch_set_flag((&runtime), SCF_CPF_SOFT_LOOKUP);
					} else {
						switch_clear_flag((&runtime), SCF_CPF_SOFT_LOOKUP);
					}
				} else if (!strcasecmp(var, "event-channel-key-separator") && !zstr(val)) {
					runtime.event_channel_key_separator = switch_core_strdup(runtime.memory_pool, val);
				} else if (!strcasecmp(var, "event-channel-enable-hierarchy-deliver") && !zstr(val)) {
					int v = switch_true(val);
					if (v) {
						switch_set_flag((&runtime), SCF_EVENT_CHANNEL_ENABLE_HIERARCHY_DELIVERY);
					} else {
						switch_clear_flag((&runtime), SCF_EVENT_CHANNEL_ENABLE_HIERARCHY_DELIVERY);
					}
				} else if (!strcasecmp(var, "event-channel-hierarchy-deliver-once") && !zstr(val)) {
					int v = switch_true(val);
					if (v) {
						switch_set_flag((&runtime), SCF_EVENT_CHANNEL_HIERARCHY_DELIVERY_ONCE);
					} else {
						switch_clear_flag((&runtime), SCF_EVENT_CHANNEL_HIERARCHY_DELIVERY_ONCE);
					}
				} else if (!strcasecmp(var, "event-channel-log-undeliverable-json") && !zstr(val)) {
					int v = switch_true(val);
					if (v) {
						switch_set_flag((&runtime), SCF_EVENT_CHANNEL_LOG_UNDELIVERABLE_JSON);
					} else {
						switch_clear_flag((&runtime), SCF_EVENT_CHANNEL_LOG_UNDELIVERABLE_JSON);
					}
				} else if (!strcasecmp(var, "max-audio-channels") && !zstr(val)) {
					switch_core_max_audio_channels(atoi(val));
				}
			}
		}

		if (runtime.event_channel_key_separator == NULL) {
			runtime.event_channel_key_separator = switch_core_strdup(runtime.memory_pool, ".");
		}

		if ((settings = switch_xml_child(cfg, "variables"))) {
			for (param = switch_xml_child(settings, "variable"); param; param = param->next) {
				const char *var = switch_xml_attr_soft(param, "name");
				const char *val = switch_xml_attr_soft(param, "value");
				if (var && val) {
					switch_core_set_variable(var, val);
				}
			}
		}

		switch_xml_free(xml);
	}


}

SWITCH_DECLARE(const char *) switch_core_banner(void)
{

	return ("\n"
			".=============================================================.\n"
			"|   _____              ______        _____ _____ ____ _   _   |\n"
			"|  |  ___| __ ___  ___/ ___\\ \\      / /_ _|_   _/ ___| | | |  |\n"
			"|  | |_ | '__/ _ \\/ _ \\___ \\\\ \\ /\\ / / | |  | || |   | |_| |  |\n"
			"|  |  _|| | |  __/  __/___) |\\ V  V /  | |  | || |___|  _  |  |\n"
			"|  |_|  |_|  \\___|\\___|____/  \\_/\\_/  |___| |_| \\____|_| |_|  |\n"
			"|                                                             |\n"
			".=============================================================."
			"\n"

			"|   Anthony Minessale II, Michael Jerris, Brian West, Others  |\n"
			"|   FreeSWITCH (http://www.freeswitch.org)                    |\n"
			"|   Paypal Donations Appreciated: paypal@freeswitch.org       |\n"
			"|   Brought to you by ClueCon http://www.cluecon.com/         |\n"
			".=============================================================.\n"
			"\n");
}

switch_status_t switch_core_sqldb_init(const char **err)
{
	if (switch_core_check_core_db_dsn() != SWITCH_STATUS_SUCCESS) {
		*err = "NO SUITABLE DATABASE INTERFACE IS AVAILABLE TO SERVE 'core-db-dsn'!\n";
		return SWITCH_STATUS_GENERR;
	}

	if (switch_core_sqldb_start(runtime.memory_pool, switch_test_flag((&runtime), SCF_USE_SQL) ? SWITCH_TRUE : SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) {
		*err = "Error activating database";
		return SWITCH_STATUS_GENERR;
	}

	return SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(switch_status_t) switch_core_init_and_modload(switch_core_flag_t flags, switch_bool_t console, const char **err)
{
	switch_event_t *event;
	char *cmd;
	int x = 0;
	const char *use = NULL;
#include "cc.h"


	if (switch_core_init(flags, console, err) != SWITCH_STATUS_SUCCESS) {
		return SWITCH_STATUS_GENERR;
	}

	if (runtime.runlevel > 1) {
		/* one per customer */
		return SWITCH_STATUS_SUCCESS;
	}

	runtime.runlevel++;
	runtime.events_use_dispatch = 1;

	switch_core_set_signal_handlers();
	switch_load_network_lists(SWITCH_FALSE);

	switch_msrp_init();

	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Bringing up environment.\n");
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Loading Modules.\n");
	if (switch_loadable_module_init(SWITCH_TRUE) != SWITCH_STATUS_SUCCESS) {
		*err = "Cannot load modules";
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Error: %s\n", *err);
		return SWITCH_STATUS_GENERR;
	}

	switch_load_network_lists(SWITCH_FALSE);

	switch_load_core_config("post_load_switch.conf");

	switch_core_set_signal_handlers();

	if (switch_event_create(&event, SWITCH_EVENT_STARTUP) == SWITCH_STATUS_SUCCESS) {
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Event-Info", "System Ready");
		switch_event_fire(&event);
	}

	switch_core_screen_size(&x, NULL);

	use = (x > 100) ? cc : cc_s;

#ifdef WIN32
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "%s%s\n\n", switch_core_banner(), use);
#else
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "%s%s%s%s%s%s\n\n",
					  SWITCH_SEQ_DEFAULT_COLOR,
					  SWITCH_SEQ_FYELLOW, SWITCH_SEQ_BBLUE,
					  switch_core_banner(),
					  use, SWITCH_SEQ_DEFAULT_COLOR);

#endif


	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
					  "\nFreeSWITCH Version %s (%s)\n\nFreeSWITCH Started\nMax Sessions [%u]\nSession Rate [%d]\nSQL [%s]\n",
					  switch_version_full(), switch_version_revision_human(),
					  switch_core_session_limit(0),
					  switch_core_sessions_per_second(0), switch_test_flag((&runtime), SCF_USE_SQL) ? "Enabled" : "Disabled");


	if (x < 160) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "\n[This app Best viewed at 160x60 or more..]\n");
	}

	switch_clear_flag((&runtime), SCF_NO_NEW_SESSIONS);

	if ((cmd = switch_core_get_variable_dup("api_on_startup"))) {
		switch_stream_handle_t stream = { 0 };
		SWITCH_STANDARD_STREAM(stream);
		switch_console_execute(cmd, 0, &stream);
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Startup command [%s] executed. Output:\n%s\n", cmd, (char *)stream.data);
		free(stream.data);
		free(cmd);
	}

#ifdef HAVE_SYSTEMD
	sd_notifyf(0, "READY=1\n"
		"MAINPID=%lu\n", (unsigned long) getpid());
#endif

	return SWITCH_STATUS_SUCCESS;

}

SWITCH_DECLARE(void) switch_core_measure_time(switch_time_t total_ms, switch_core_time_duration_t *duration)
{
	switch_time_t temp = total_ms / 1000;
	memset(duration, 0, sizeof(*duration));
	duration->mms = (uint32_t) (total_ms % 1000);
	duration->ms = (uint32_t) (temp % 1000);
	temp = temp / 1000;
	duration->sec = (uint32_t) (temp % 60);
	temp = temp / 60;
	duration->min = (uint32_t) (temp % 60);
	temp = temp / 60;
	duration->hr = (uint32_t) (temp % 24);
	temp = temp / 24;
	duration->day = (uint32_t) (temp % 365);
	duration->yr = (uint32_t) (temp / 365);
}

SWITCH_DECLARE(switch_time_t) switch_core_uptime(void)
{
	return switch_mono_micro_time_now() - runtime.initiated;
}


#ifdef _MSC_VER
static void win_shutdown(void)
{

	HANDLE shutdown_event;
	char path[512];
	/* for windows we need the event to signal for shutting down a background FreeSWITCH */
	snprintf(path, sizeof(path), "Global\\Freeswitch.%d", getpid());

	/* open the event so we can signal it */
	shutdown_event = OpenEvent(EVENT_MODIFY_STATE, FALSE, path);

	if (shutdown_event) {
		/* signal the event to shutdown */
		SetEvent(shutdown_event);
		/* cleanup */
		CloseHandle(shutdown_event);
	}
}
#endif

SWITCH_DECLARE(void) switch_core_set_signal_handlers(void)
{
	/* set signal handlers */
	signal(SIGINT, SIG_IGN);
#ifdef SIGPIPE
	signal(SIGPIPE, SIG_IGN);
#endif
#ifdef SIGALRM
	signal(SIGALRM, SIG_IGN);
#endif
#ifdef SIGQUIT
	signal(SIGQUIT, SIG_IGN);
#endif
#ifdef SIGPOLL
	signal(SIGPOLL, SIG_IGN);
#endif
#ifdef SIGIO
	signal(SIGIO, SIG_IGN);
#endif
#ifdef TRAP_BUS
	signal(SIGBUS, handle_SIGBUS);
#endif
#ifdef SIGUSR1
	signal(SIGUSR1, handle_SIGHUP);
#endif
	signal(SIGHUP, handle_SIGHUP);
}

SWITCH_DECLARE(uint32_t) switch_core_debug_level(void)
{
	return runtime.debug_level;
}

SWITCH_DECLARE(int32_t) switch_core_sps(void)
{
	return runtime.sps;
}

SWITCH_DECLARE(int32_t) switch_core_sps_last(void)
{
	return runtime.sps_last;
}

SWITCH_DECLARE(int32_t) switch_core_sps_peak(void)
{
	return runtime.sps_peak;
}

SWITCH_DECLARE(int32_t) switch_core_sps_peak_fivemin(void)
{
	return runtime.sps_peak_fivemin;
}

SWITCH_DECLARE(int32_t) switch_core_sessions_peak(void)
{
	return runtime.sessions_peak;
}

SWITCH_DECLARE(int32_t) switch_core_sessions_peak_fivemin(void)
{
	return runtime.sessions_peak_fivemin;
}

SWITCH_DECLARE(uint32_t) switch_core_max_audio_channels(uint32_t limit)
{
	if (limit) {
		runtime.max_audio_channels = limit;
	}

	return runtime.max_audio_channels;
}

SWITCH_DECLARE(int32_t) switch_core_session_ctl(switch_session_ctl_t cmd, void *val)
{
	int *intval = (int *) val;
	int oldintval = 0, newintval = 0;

	if (intval) {
		oldintval = *intval;
	}

	if (switch_test_flag((&runtime), SCF_SHUTTING_DOWN)) {
		return -1;
	}

	switch (cmd) {
	case SCSC_RECOVER:
		{
			char *arg = (char *) val;
			char *tech = NULL, *prof = NULL;
			int r, flush = 0;

			if (!zstr(arg)) {
				tech = strdup(arg);
				switch_assert(tech);

				if ((prof = strchr(tech, ':'))) {
					*prof++ = '\0';
				}

				if (!strcasecmp(tech, "flush")) {
					flush++;

					if (prof) {
						char *tech = prof;
						if ((prof = strchr(tech, ':'))) {
							*prof++ = '\0';
						}
					}
				}

			}

			if (flush) {
				switch_core_recovery_flush(tech, prof);
				r = -1;
			} else {
				r = switch_core_recovery_recover(tech, prof);
			}

			switch_safe_free(tech);
			return r;

		}
		break;
	case SCSC_DEBUG_SQL:
		{
			if (switch_test_flag((&runtime), SCF_DEBUG_SQL)) {
				switch_clear_flag((&runtime), SCF_DEBUG_SQL);
				newintval = 0;
			} else {
				switch_set_flag((&runtime), SCF_DEBUG_SQL);
				newintval = 1;
			}
		}
		break;
	case SCSC_VERBOSE_EVENTS:
		if (intval) {
			if (oldintval > -1) {
				if (oldintval) {
					switch_set_flag((&runtime), SCF_VERBOSE_EVENTS);
				} else {
					switch_clear_flag((&runtime), SCF_VERBOSE_EVENTS);
				}
			}
			newintval = switch_test_flag((&runtime), SCF_VERBOSE_EVENTS);
		}
		break;
	case SCSC_API_EXPANSION:
		if (intval) {
			if (oldintval > -1) {
				if (oldintval) {
					switch_set_flag((&runtime), SCF_API_EXPANSION);
				} else {
					switch_clear_flag((&runtime), SCF_API_EXPANSION);
				}
			}
			newintval = switch_test_flag((&runtime), SCF_API_EXPANSION);
		}
		break;
	case SCSC_THREADED_SYSTEM_EXEC:
		if (intval) {
			if (oldintval > -1) {
				if (oldintval) {
					switch_set_flag((&runtime), SCF_THREADED_SYSTEM_EXEC);
				} else {
					switch_clear_flag((&runtime), SCF_THREADED_SYSTEM_EXEC);
				}
			}
			newintval = switch_test_flag((&runtime), SCF_THREADED_SYSTEM_EXEC);
		}
		break;
	case SCSC_CALIBRATE_CLOCK:
		switch_time_calibrate_clock();
		break;
	case SCSC_FLUSH_DB_HANDLES:
		switch_cache_db_flush_handles();
		break;
	case SCSC_SEND_SIGHUP:
		handle_SIGHUP(1);
		break;
	case SCSC_SYNC_CLOCK:
		switch_time_sync();
		newintval = 0;
		break;
	case SCSC_SYNC_CLOCK_WHEN_IDLE:
		newintval = switch_core_session_sync_clock();
		break;
	case SCSC_SQL:
		if (oldintval) {
			switch_core_sqldb_resume();
		} else {
			switch_core_sqldb_pause();
		}
		break;
	case SCSC_PAUSE_ALL:
		if (oldintval) {
			switch_set_flag((&runtime), SCF_NO_NEW_SESSIONS);
		} else {
			switch_clear_flag((&runtime), SCF_NO_NEW_SESSIONS);
		}
		break;
	case SCSC_PAUSE_INBOUND:
		if (oldintval) {
			switch_set_flag((&runtime), SCF_NO_NEW_INBOUND_SESSIONS);
		} else {
			switch_clear_flag((&runtime), SCF_NO_NEW_INBOUND_SESSIONS);
		}
		break;
	case SCSC_PAUSE_OUTBOUND:
		if (oldintval) {
			switch_set_flag((&runtime), SCF_NO_NEW_OUTBOUND_SESSIONS);
		} else {
			switch_clear_flag((&runtime), SCF_NO_NEW_OUTBOUND_SESSIONS);
		}
		break;
	case SCSC_HUPALL:
		switch_core_session_hupall(SWITCH_CAUSE_MANAGER_REQUEST);
		break;
	case SCSC_CANCEL_SHUTDOWN:
		switch_clear_flag((&runtime), SCF_SHUTDOWN_REQUESTED);
		break;
	case SCSC_SAVE_HISTORY:
		switch_console_save_history();
		break;
	case SCSC_CRASH:
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Declinatio Mortuus Obfirmo!\n");
		switch_console_save_history();
		abort();
		break;
	case SCSC_SHUTDOWN_NOW:
		switch_console_save_history();
		exit(0);
		break;
	case SCSC_REINCARNATE_NOW:
		switch_console_save_history();
		exit(SWITCH_STATUS_RESTART);
		break;
	case SCSC_SHUTDOWN_ELEGANT:
	case SCSC_SHUTDOWN_ASAP:
		{
			int x = 19;
			uint32_t count;
			switch_event_t *shutdown_requested_event = NULL;
			if (switch_event_create(&shutdown_requested_event, SWITCH_EVENT_SHUTDOWN_REQUESTED) == SWITCH_STATUS_SUCCESS) {
				switch_event_add_header(shutdown_requested_event, SWITCH_STACK_BOTTOM, "Event-Info", "%s", cmd == SCSC_SHUTDOWN_ASAP ? "ASAP" : "elegant");
				switch_event_fire(&shutdown_requested_event);
			}
			switch_set_flag((&runtime), SCF_SHUTDOWN_REQUESTED);
			if (cmd == SCSC_SHUTDOWN_ASAP) {
				switch_set_flag((&runtime), SCF_NO_NEW_SESSIONS);
			}

			while (runtime.running && switch_test_flag((&runtime), SCF_SHUTDOWN_REQUESTED) && (count = switch_core_session_count())) {
				switch_yield(500000);
				if (++x == 20) {
					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
									  "Shutdown in progress, %u session(s) remain.\nShutting down %s\n",
									  count, cmd == SCSC_SHUTDOWN_ASAP ? "ASAP" : "once there are no active calls.");
					x = 0;
				}
			}

			if (switch_test_flag((&runtime), SCF_SHUTDOWN_REQUESTED)) {
				switch_set_flag((&runtime), SCF_NO_NEW_SESSIONS);
#ifdef _MSC_VER
				win_shutdown();
#endif

				if (oldintval) {
					switch_set_flag((&runtime), SCF_RESTART);
					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Restarting\n");
				} else {
					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Shutting down\n");
#ifdef HAVE_SYSTEMD
					sd_notifyf(0, "STOPPING=1\n");
#endif
#ifdef _MSC_VER
					fclose(stdin);
#endif
				}
				runtime.running = 0;
			} else {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Shutdown Cancelled\n");
				switch_clear_flag((&runtime), SCF_NO_NEW_SESSIONS);
			}
		}
		break;
	case SCSC_PAUSE_CHECK:
		newintval = !!(switch_test_flag((&runtime), SCF_NO_NEW_SESSIONS) == SCF_NO_NEW_SESSIONS);
		break;
	case SCSC_PAUSE_INBOUND_CHECK:
		newintval = !!switch_test_flag((&runtime), SCF_NO_NEW_INBOUND_SESSIONS);
		break;
	case SCSC_PAUSE_OUTBOUND_CHECK:
		newintval = !!switch_test_flag((&runtime), SCF_NO_NEW_OUTBOUND_SESSIONS);
		break;
	case SCSC_READY_CHECK:
		newintval = switch_core_ready();
		break;
	case SCSC_SHUTDOWN_CHECK:
		newintval = !!switch_test_flag((&runtime), SCF_SHUTDOWN_REQUESTED);
		break;
	case SCSC_SHUTDOWN:

#ifdef _MSC_VER
		win_shutdown();
#endif

		if (oldintval) {
			switch_set_flag((&runtime), SCF_RESTART);
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Restarting\n");
		} else {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Shutting down\n");
#ifdef _MSC_VER
			fclose(stdin);
#endif
		}
		runtime.running = 0;
		break;
	case SCSC_CHECK_RUNNING:
		newintval = runtime.running;
		break;
	case SCSC_LOGLEVEL:
		if (oldintval >= SWITCH_LOG_DISABLE) {
			runtime.hard_log_level = oldintval;
		}

		if (runtime.hard_log_level > SWITCH_LOG_DEBUG) {
			runtime.hard_log_level = SWITCH_LOG_DEBUG;
		}
		newintval = runtime.hard_log_level;
		break;
	case SCSC_DEBUG_LEVEL:
		if (oldintval > -1) {
			if (oldintval > 10)
				oldintval = 10;
			runtime.debug_level = oldintval;
		}
		newintval = runtime.debug_level;
		break;
	case SCSC_MIN_IDLE_CPU:
		{
			double *dval = (double *) val;
			if (dval) {
				*dval = switch_core_min_idle_cpu(*dval);
			}
			intval = NULL;
		}
		break;
	case SCSC_MAX_SESSIONS:
		newintval = switch_core_session_limit(oldintval);
		break;
	case SCSC_LAST_SPS:
		newintval = runtime.sps_last;
		break;
	case SCSC_SPS_PEAK:
		if (oldintval == -1) {
			runtime.sps_peak = 0;
		}
		newintval = runtime.sps_peak;
		break;
	case SCSC_SPS_PEAK_FIVEMIN:
		newintval = runtime.sps_peak_fivemin;
		break;
	case SCSC_SESSIONS_PEAK:
		newintval = runtime.sessions_peak;
		break;
	case SCSC_SESSIONS_PEAK_FIVEMIN:
		newintval = runtime.sessions_peak_fivemin;
		break;
	case SCSC_MAX_DTMF_DURATION:
		newintval = switch_core_max_dtmf_duration(oldintval);
		break;
	case SCSC_MIN_DTMF_DURATION:
		newintval = switch_core_min_dtmf_duration(oldintval);
		break;
	case SCSC_DEFAULT_DTMF_DURATION:
		newintval = switch_core_default_dtmf_duration(oldintval);
		break;
	case SCSC_SPS:
		switch_mutex_lock(runtime.throttle_mutex);
		if (oldintval > 0) {
			runtime.sps_total = oldintval;
		}
		newintval = runtime.sps_total;
		switch_mutex_unlock(runtime.throttle_mutex);
		break;

	case SCSC_RECLAIM:
		switch_core_memory_reclaim_all();
		newintval = 0;
		break;
	case SCSC_MDNS_RESOLVE:
		switch_core_media_set_resolveice(!!oldintval);
		break;
	case SCSC_SHUTDOWN_CAUSE:
		runtime.shutdown_cause = oldintval;
		break;
	}

	if (intval) {
		*intval = newintval;
	}


	return 0;
}

SWITCH_DECLARE(switch_core_flag_t) switch_core_flags(void)
{
	return runtime.flags;
}

SWITCH_DECLARE(switch_bool_t) switch_core_running(void)
{
	return runtime.running ? SWITCH_TRUE : SWITCH_FALSE;
}

SWITCH_DECLARE(switch_bool_t) switch_core_ready(void)
{
	return (switch_test_flag((&runtime), SCF_SHUTTING_DOWN) || switch_test_flag((&runtime), SCF_NO_NEW_SESSIONS) == SCF_NO_NEW_SESSIONS) ? SWITCH_FALSE : SWITCH_TRUE;
}

SWITCH_DECLARE(switch_bool_t) switch_core_ready_inbound(void)
{
	return (switch_test_flag((&runtime), SCF_SHUTTING_DOWN) || switch_test_flag((&runtime), SCF_NO_NEW_INBOUND_SESSIONS)) ? SWITCH_FALSE : SWITCH_TRUE;
}

SWITCH_DECLARE(switch_bool_t) switch_core_ready_outbound(void)
{
	return (switch_test_flag((&runtime), SCF_SHUTTING_DOWN) || switch_test_flag((&runtime), SCF_NO_NEW_OUTBOUND_SESSIONS)) ? SWITCH_FALSE : SWITCH_TRUE;
}

void switch_core_sqldb_destroy()
{
	if (switch_test_flag((&runtime), SCF_USE_SQL)) {
		switch_core_sqldb_stop();
	}
}

SWITCH_DECLARE(switch_status_t) switch_core_destroy(void)
{
	switch_event_t *event;

	if (switch_event_create(&event, SWITCH_EVENT_SHUTDOWN) == SWITCH_STATUS_SUCCESS) {
		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Event-Info", "System Shutting Down");
		switch_event_fire(&event);
	}

	switch_set_flag((&runtime), SCF_NO_NEW_SESSIONS);
	switch_set_flag((&runtime), SCF_SHUTTING_DOWN);

	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "End existing sessions\n");
	switch_core_session_hupall(runtime.shutdown_cause);
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Clean up modules.\n");

	switch_loadable_module_shutdown();

	switch_curl_destroy();

	switch_ssl_destroy_ssl_locks();
	EVP_cleanup();

	switch_scheduler_task_thread_stop();

	switch_rtp_shutdown();
	switch_msrp_destroy();

	if (switch_test_flag((&runtime), SCF_USE_AUTO_NAT)) {
		switch_nat_shutdown();
	}
	switch_xml_destroy();
	switch_console_shutdown();
	switch_channel_global_uninit();

	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Closing Event Engine.\n");
	switch_event_shutdown();

	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Finalizing Shutdown.\n");
	switch_log_shutdown();

	switch_core_session_uninit();
	switch_core_unset_variables();
	switch_core_memory_stop();

	if (runtime.console && runtime.console != stdout && runtime.console != stderr) {
		fclose(runtime.console);
		runtime.console = NULL;
	}

	switch_safe_free(SWITCH_GLOBAL_dirs.base_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.mod_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.conf_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.log_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.db_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.script_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.htdocs_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.grammar_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.fonts_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.images_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.storage_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.cache_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.recordings_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.sounds_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.run_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.temp_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.data_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.localstate_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.certs_dir);
	switch_safe_free(SWITCH_GLOBAL_dirs.lib_dir);

	switch_safe_free(SWITCH_GLOBAL_filenames.conf_name);

	switch_event_destroy(&runtime.global_vars);
	switch_core_hash_destroy(&runtime.ptimes);
	switch_core_hash_destroy(&runtime.mime_types);
	switch_core_hash_destroy(&runtime.mime_type_exts);

	if (IP_LIST.hash) {
		switch_core_hash_destroy(&IP_LIST.hash);
	}

	if (IP_LIST.pool) {
		switch_core_destroy_memory_pool(&IP_LIST.pool);
	}

	switch_core_media_deinit();

	if (runtime.memory_pool) {
		fspr_pool_destroy(runtime.memory_pool);
		fspr_terminate();
	}

	sqlite3_shutdown();

	return switch_test_flag((&runtime), SCF_RESTART) ? SWITCH_STATUS_RESTART : SWITCH_STATUS_SUCCESS;
}

SWITCH_DECLARE(switch_status_t) switch_core_management_exec(char *relative_oid, switch_management_action_t action, char *data, switch_size_t datalen)
{
	const switch_management_interface_t *ptr;
	switch_status_t status = SWITCH_STATUS_FALSE;

	if ((ptr = switch_loadable_module_get_management_interface(relative_oid))) {
		status = ptr->management_function(relative_oid, action, data, datalen);
	}

	return status;
}

SWITCH_DECLARE(void) switch_core_memory_reclaim_all(void)
{
	switch_core_memory_reclaim_logger();
	switch_core_memory_reclaim_events();
	switch_core_memory_reclaim();
}


struct system_thread_handle {
	const char *cmd;
	switch_thread_cond_t *cond;
	switch_mutex_t *mutex;
	switch_memory_pool_t *pool;
	int ret;
	int *fds;
};

static void *SWITCH_THREAD_FUNC system_thread(switch_thread_t *thread, void *obj)
{
	struct system_thread_handle *sth = (struct system_thread_handle *) obj;

#if defined(HAVE_SETRLIMIT) && !defined(__FreeBSD__)
	struct rlimit rlim;
	struct rlimit rlim_save;

	memset(&rlim, 0, sizeof(rlim));
	getrlimit(RLIMIT_STACK, &rlim);

	memset(&rlim_save, 0, sizeof(rlim_save));
	getrlimit(RLIMIT_STACK, &rlim_save);

	rlim.rlim_cur = rlim.rlim_max;
	if (setrlimit(RLIMIT_STACK, &rlim) < 0) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Setting stack size failed! (%s)\n", strerror(errno));
	}
#endif

	if (sth->fds) {
		dup2(sth->fds[1], STDOUT_FILENO);
	}

	sth->ret = system(sth->cmd);

#if defined(HAVE_SETRLIMIT) && !defined(__FreeBSD__)
	if (setrlimit(RLIMIT_STACK, &rlim_save) < 0) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Setting stack size failed! (%s)\n", strerror(errno));
	}
#endif

	switch_mutex_lock(sth->mutex);
	switch_thread_cond_signal(sth->cond);
	switch_mutex_unlock(sth->mutex);

	switch_core_destroy_memory_pool(&sth->pool);

	return NULL;
}


static int switch_system_thread(const char *cmd, switch_bool_t wait)
{
	switch_thread_t *thread;
	switch_threadattr_t *thd_attr;
	int ret = 0;
	struct system_thread_handle *sth;
	switch_memory_pool_t *pool;

	if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Pool Failure\n");
		return 1;
	}

	if (!(sth = switch_core_alloc(pool, sizeof(struct system_thread_handle)))) {
		switch_core_destroy_memory_pool(&pool);
		return 1;
	}

	sth->pool = pool;
	sth->cmd = switch_core_strdup(pool, cmd);

	switch_thread_cond_create(&sth->cond, sth->pool);
	switch_mutex_init(&sth->mutex, SWITCH_MUTEX_NESTED, sth->pool);
	switch_mutex_lock(sth->mutex);

	switch_threadattr_create(&thd_attr, sth->pool);
	switch_threadattr_stacksize_set(thd_attr, SWITCH_SYSTEM_THREAD_STACKSIZE);
	switch_threadattr_detach_set(thd_attr, 1);
	switch_thread_create(&thread, thd_attr, system_thread, sth, sth->pool);

	if (wait) {
		switch_thread_cond_wait(sth->cond, sth->mutex);
		ret = sth->ret;
	}
	switch_mutex_unlock(sth->mutex);

	return ret;
}

SWITCH_DECLARE(int) switch_max_file_desc(void)
{
	int max = 0;

#ifndef WIN32
#if defined(HAVE_GETDTABLESIZE)
	max = getdtablesize();
#else
	max = sysconf(_SC_OPEN_MAX);
#endif
#endif

	return max;

}

SWITCH_DECLARE(void) switch_close_extra_files(int *keep, int keep_ttl)
{
	int open_max = switch_max_file_desc();
	int i, j;

	for (i = 3; i < open_max; i++) {
		if (keep) {
			for (j = 0; j < keep_ttl; j++) {
				if (i == keep[j]) {
					goto skip;
				}
			}
		}

		close(i);

	skip:

		continue;

	}
}


#ifdef WIN32
static int switch_system_fork(const char *cmd, switch_bool_t wait)
{
	return switch_system_thread(cmd, wait);
}

SWITCH_DECLARE(pid_t) switch_fork(void)
{
	return -1;
}


#else

SWITCH_DECLARE(pid_t) switch_fork(void)
{
	int i = fork();

	if (!i) {
		set_low_priority();
	}

	return i;
}



static int switch_system_fork(const char *cmd, switch_bool_t wait)
{
	int pid;
	char *dcmd = strdup(cmd);
#if defined(HAVE_SETRLIMIT) && !defined(__FreeBSD__)
	struct rlimit rlim;
	struct rlimit rlim_save;
#endif

	switch_core_set_signal_handlers();

	pid = switch_fork();

	if (pid) {
		if (wait) {
			waitpid(pid, NULL, 0);
		}
		free(dcmd);
	} else {
		switch_close_extra_files(NULL, 0);

#if defined(HAVE_SETRLIMIT) && !defined(__FreeBSD__)
		memset(&rlim, 0, sizeof(rlim));
		getrlimit(RLIMIT_STACK, &rlim);

		memset(&rlim_save, 0, sizeof(rlim_save));
		getrlimit(RLIMIT_STACK, &rlim_save);

		rlim.rlim_cur = rlim.rlim_max;
		if (setrlimit(RLIMIT_STACK, &rlim) < 0) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Setting stack size failed! (%s)\n", strerror(errno));
		}
#endif

		if (system(dcmd) == -1) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to execute because of a command error : %s\n", dcmd);
		}
		free(dcmd);
		exit(0);
	}

	return 0;
}
#endif



SWITCH_DECLARE(int) switch_system(const char *cmd, switch_bool_t wait)
{
	int retval = 0;
#ifdef __linux__
	switch_bool_t spawn_instead_of_system = switch_true(switch_core_get_variable("spawn_instead_of_system"));
#else
	switch_bool_t spawn_instead_of_system = SWITCH_FALSE;
#endif
	
	if (spawn_instead_of_system) {
		retval = switch_stream_spawn(cmd, SWITCH_TRUE, wait, NULL);
	} else if (switch_test_flag((&runtime), SCF_THREADED_SYSTEM_EXEC)) {
		retval = switch_system_thread(cmd, wait);
	} else {
		retval = switch_system_fork(cmd, wait);
	}
	return retval;
}



SWITCH_DECLARE(int) switch_stream_system_fork(const char *cmd, switch_stream_handle_t *stream)
{
	return switch_stream_system(cmd, stream);
}

#ifdef __linux__
extern char **environ;
#endif

SWITCH_DECLARE(int) switch_stream_spawn(const char *cmd, switch_bool_t shell, switch_bool_t wait, switch_stream_handle_t *stream)
{
#ifndef __linux__
	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "posix_spawn is unsupported on current platform\n");
	return 1;
#else
	int status = 0;
	char buffer[1024];
	pid_t pid;
	char *pdata = NULL, *argv[64];
	posix_spawn_file_actions_t action;
	posix_spawnattr_t *attr;
	int cout_pipe[2];
	int cerr_pipe[2];
	struct pollfd pfds[2] = { {0} };

	if (zstr(cmd)) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to execute switch_spawn_stream because of empty command\n");
		return 1;
	}

	if (shell) {
		argv[0] = switch_core_get_variable("spawn_system_shell");
		argv[1] = "-c";
		argv[2] = (char *)cmd;
		argv[3] = NULL;
		if (zstr(argv[0])) {
			argv[0] = "/bin/sh";
		}
	} else {
		if (!(pdata = strdup(cmd))) {
			return 1;
		}
		if (!switch_separate_string(pdata, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) {
			free(pdata);
			return 1;
		}
	}

	if (!(attr = malloc(sizeof(posix_spawnattr_t)))) {
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to execute switch_spawn_stream because of a memory error: %s\n", cmd);
		switch_safe_free(pdata);
		return 1;
	}

	if (stream) {
		if (pipe(cout_pipe)) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to execute switch_spawn_stream because of a pipe error: %s\n", cmd);
			free(attr);
			switch_safe_free(pdata);
			return 1;
		}

		if (pipe(cerr_pipe)) {
			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to execute switch_spawn_stream because of a pipe error: %s\n", cmd);
			close(cout_pipe[0]);
			close(cout_pipe[1]);
			free(attr);
			switch_safe_free(pdata);
			return 1;
		}
	}

	memset(attr, 0, sizeof(posix_spawnattr_t));
	posix_spawnattr_init(attr);
	posix_spawnattr_setflags(attr, POSIX_SPAWN_USEVFORK);

	posix_spawn_file_actions_init(&action);

	if (stream) {
		posix_spawn_file_actions_addclose(&action, cout_pipe[0]);
		posix_spawn_file_actions_addclose(&action, cerr_pipe[0]);
		posix_spawn_file_actions_adddup2(&action, cout_pipe[1], 1);
		posix_spawn_file_actions_adddup2(&action, cerr_pipe[1], 2);

		posix_spawn_file_actions_addclose(&action, cout_pipe[1]);
		posix_spawn_file_actions_addclose(&action, cerr_pipe[1]);
	}

	if (posix_spawnp(&pid, argv[0], &action, attr, argv, environ) != 0) {
		status = 1;
		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to execute posix_spawnp: %s\n", cmd);
		if (stream) {
			close(cout_pipe[0]), close(cerr_pipe[0]);
			close(cout_pipe[1]), close(cerr_pipe[1]);
		}
	} else {
		if (stream) {
			close(cout_pipe[1]), close(cerr_pipe[1]); /* close child-side of pipes */

			pfds[0] = (struct pollfd) {
				.fd = cout_pipe[0],
					.events = POLLIN,
					.revents = 0
			};

			pfds[1] = (struct pollfd) {
				.fd = cerr_pipe[0],
					.events = POLLIN,
					.revents = 0
			};

			while (poll(pfds, 2, /*timeout*/-1) > 0) {
				if (pfds[0].revents & POLLIN) {
					int bytes_read = read(cout_pipe[0], buffer, sizeof(buffer));
					stream->raw_write_function(stream, (unsigned char *)buffer, bytes_read);
				} else if (pfds[1].revents & POLLIN) {
					int bytes_read = read(cerr_pipe[0], buffer, sizeof(buffer));
					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "STDERR of cmd (%s): %.*s\n", cmd, bytes_read, buffer);
				} else {
					break; /* nothing left to read */
				}
			}

			close(cout_pipe[0]), close(cerr_pipe[0]);
		}

		if (wait) {
			if (waitpid(pid, &status, 0) != pid) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "waitpid failed: %s\n", cmd);
			} else if (status != 0) {
				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Exit status (%d): %s\n", status, cmd);
			}
		}
	}

	posix_spawnattr_destroy(attr);
	free(attr);
	posix_spawn_file_actions_destroy(&action);
	switch_safe_free(pdata);

	return status;
#endif
}

SWITCH_DECLARE(int) switch_spawn(const char *cmd, switch_bool_t wait)
{
	return switch_stream_spawn(cmd, SWITCH_FALSE, wait, NULL);
}

SWITCH_DECLARE(switch_status_t) switch_core_get_stacksizes(switch_size_t *cur, switch_size_t *max)
{
#ifdef HAVE_SETRLIMIT
	struct rlimit rlp;

	memset(&rlp, 0, sizeof(rlp));
	getrlimit(RLIMIT_STACK, &rlp);

	*cur = rlp.rlim_cur;
	*max = rlp.rlim_max;

	return SWITCH_STATUS_SUCCESS;

#else

	return SWITCH_STATUS_FALSE;

#endif



}


SWITCH_DECLARE(int) switch_stream_system(const char *cmd, switch_stream_handle_t *stream)
{
#ifdef __linux__
	switch_bool_t spawn_instead_of_system = switch_true(switch_core_get_variable("spawn_instead_of_system"));
#else
	switch_bool_t spawn_instead_of_system = SWITCH_FALSE;
#endif

	if (spawn_instead_of_system){
		return switch_stream_spawn(cmd, SWITCH_TRUE, SWITCH_TRUE, stream);
	} else {
		char buffer[128];
		size_t bytes;
		FILE* pipe = popen(cmd, "r");
		if (!pipe) return 1;

		while (!feof(pipe)) {
			while ((bytes = fread(buffer, 1, 128, pipe)) > 0) {
				if (stream != NULL) {
					stream->raw_write_function(stream, (unsigned char *)buffer, bytes);
				}
			}
		}

		if (ferror(pipe)) {
			pclose(pipe);
			return 1;
		}

		pclose(pipe);
		return 0;
	}
}

SWITCH_DECLARE(uint16_t) switch_core_get_rtp_port_range_start_port()
{
	uint16_t start_port = 0;

	/* By default pass rtp port range start value as zero in order to get actual
	 * RTP port range start value as configured */
	start_port = (uint16_t)switch_rtp_set_start_port((switch_port_t)start_port);

	return start_port;
}

SWITCH_DECLARE(uint16_t) switch_core_get_rtp_port_range_end_port()
{
	uint16_t end_port = 0;

	/* By default pass rtp port range end value as zero in order to get actual
	 * RTP port range end value as configured */
	end_port = (uint16_t)switch_rtp_set_end_port((switch_port_t)end_port);

	return end_port;
}

SWITCH_DECLARE(const char *) switch_core_get_event_channel_key_separator(void)
{
	return runtime.event_channel_key_separator;
}

/* For Emacs:
 * Local Variables:
 * mode:c
 * indent-tabs-mode:t
 * tab-width:4
 * c-basic-offset:4
 * End:
 * For VIM:
 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
 */
