$(SRC)/libteletone_generate.c \
$(SRC)/ftdm_buffer.c \
$(SRC)/ftdm_threadmutex.c \
-$(SRC)/ftdm_dso.c
+$(SRC)/ftdm_dso.c \
+$(SRC)/ftdm_cpu_monitor.c
library_include_HEADERS = \
$(SRC)/include/fsk.h \
$(SRC)/include/ftdm_config.h \
$(SRC)/include/ftdm_threadmutex.h \
$(SRC)/include/ftdm_dso.h \
-$(SRC)/include/ftdm_types.h
+$(SRC)/include/ftdm_types.h \
+$(SRC)/include/ftdm_cpu_monitor.h
lib_LTLIBRARIES = libfreetdm.la
libfreetdm_la_CFLAGS = $(AM_CFLAGS) $(MY_CFLAGS)
module_pool = pool;
ftdm_global_set_logger(ftdm_logger);
+
+ ftdm_cpu_monitor_disable();
if (ftdm_global_init() != FTDM_SUCCESS) {
ftdm_log(FTDM_LOG_ERROR, "Error loading FreeTDM\n");
--- /dev/null
+/*
+ * Copyright (c) 2010, Sangoma Technologies
+ * Moises Silva <moy@sangoma.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Contributors:
+ * David Yat Sin <dyatsin@sangoma.com>
+ *
+ */
+
+#ifdef WIN32
+#define _WIN32_WINNT 0x0501 // To make GetSystemTimes visible in windows.h
+#include <windows.h>
+#else /* LINUX */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#endif
+
+#include "freetdm.h"
+#include "ftdm_cpu_monitor.h"
+struct ftdm_cpu_monitor_stats
+{
+ /* bool, just used to retrieve the values for the first time and not calculate the percentage of idle time */
+ int valid_last_times;
+
+ /* last calculated percentage of idle time */
+ double last_percentage_of_idle_time;
+
+#ifdef __linux__
+ /* all of these are the Linux jiffies last retrieved count */
+ unsigned long long last_user_time;
+ unsigned long long last_system_time;
+ unsigned long long last_idle_time;
+
+ unsigned long long last_nice_time;
+ unsigned long long last_irq_time;
+ unsigned long long last_soft_irq_time;
+ unsigned long long last_io_wait_time;
+ unsigned long long last_steal_time;
+
+ /* /proc/stat file descriptor used to retrieve the counters */
+ int procfd;
+ int initd;
+#elif defined (WIN32) || defined (WIN64)
+ __int64 i64LastUserTime;
+ __int64 i64LastKernelTime;
+ __int64 i64LastIdleTime;
+#else
+/* Unsupported */
+#endif
+};
+
+#ifdef __linux__
+static ftdm_status_t ftdm_cpu_read_stats(struct ftdm_cpu_monitor_stats *p,
+ unsigned long long *user,
+ unsigned long long *nice,
+ unsigned long long *system,
+ unsigned long long *idle,
+ unsigned long long *iowait,
+ unsigned long long *irq,
+ unsigned long long *softirq,
+ unsigned long long *steal)
+{
+// the output of proc should not change that often from one kernel to other
+// see fs/proc/proc_misc.c or fs/proc/stat.c in the Linux kernel for more details
+// also man 5 proc is useful
+#define CPU_ELEMENTS 8 // change this if you change the format string
+#define CPU_INFO_FORMAT "cpu %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu"
+ static const char procfile[] = "/proc/stat";
+ int rc = 0;
+ int myerrno = 0;
+ int elements = 0;
+ const char *cpustr = NULL;
+ char statbuff[1024];
+
+ if (!p->initd) {
+ p->procfd = open(procfile, O_RDONLY, 0);
+ if(p->procfd == -1) {
+ ftdm_log(FTDM_LOG_ERROR, "Failed to open CPU statistics file %s: %s\n", procfile, strerror(myerrno));
+ return FTDM_FAIL;
+ }
+ p->initd = 1;
+ } else {
+ lseek(p->procfd, 0L, SEEK_SET);
+ }
+
+ rc = read(p->procfd, statbuff, sizeof(statbuff) - 1);
+ if (rc <= 0) {
+ myerrno = errno;
+ ftdm_log(FTDM_LOG_ERROR, "Failed to read CPU statistics file %s: %s\n", procfile, strerror(myerrno));
+ return FTDM_FAIL;
+ }
+
+ cpustr = strstr(statbuff, "cpu ");
+ if (!cpustr) {
+ ftdm_log(FTDM_LOG_ERROR, "wrong format for Linux proc cpu statistics: missing cpu string\n");
+ return FTDM_FAIL;
+ }
+
+ elements = sscanf(cpustr, CPU_INFO_FORMAT, user, nice, system, idle, iowait, irq, softirq, steal);
+ if (elements != CPU_ELEMENTS) {
+ ftdm_log(FTDM_LOG_ERROR, "wrong format for Linux proc cpu statistics: expected %d elements, but just found %d\n", CPU_ELEMENTS, elements);
+ return FTDM_FAIL;
+ }
+ return FTDM_SUCCESS;
+}
+#endif
+
+#ifdef __linux__
+FT_DECLARE(ftdm_status_t) ftdm_cpu_get_system_idle_time (struct ftdm_cpu_monitor_stats *p, double *idle_percentage)
+{
+ unsigned long long user, nice, system, idle, iowait, irq, softirq, steal;
+ unsigned long long usertime, kerneltime, idletime, totaltime, halftime;
+
+ if (ftdm_cpu_read_stats(p, &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal)) {
+ ftdm_log(FTDM_LOG_ERROR, "Failed to retrieve Linux CPU statistics\n");
+ return FTDM_FAIL;
+ }
+
+ if (!p->valid_last_times) {
+ // we dont strictly need to save all of them but I feel code is more clear if we do
+ p->valid_last_times = 1;
+ p->last_user_time = user;
+ p->last_nice_time = nice;
+ p->last_system_time = system;
+ p->last_irq_time = irq;
+ p->last_soft_irq_time = softirq;
+ p->last_io_wait_time = iowait;
+ p->last_steal_time = steal;
+ p->last_idle_time = idle;
+ p->last_percentage_of_idle_time = 100.0;
+ *idle_percentage = p->last_percentage_of_idle_time;
+ return FTDM_SUCCESS;
+ }
+
+ usertime = (user - p->last_user_time) + (nice - p->last_nice_time);
+ kerneltime = (system - p->last_system_time) + (irq - p->last_irq_time) + (softirq - p->last_soft_irq_time);
+ kerneltime += (iowait - p->last_io_wait_time);
+ kerneltime += (steal - p->last_steal_time);
+ idletime = (idle - p->last_idle_time);
+
+ totaltime = usertime + kerneltime + idletime;
+
+ if (totaltime <= 0) {
+ // this may happen if not enough time has elapsed and the jiffies counters are the same than the last time we checked
+ // jiffies depend on timer interrupts which depend on the number of HZ compile time setting of the kernel
+ // typical configs set HZ to 100 (that means, 100 jiffies updates per second, that is one each 10ms)
+ // avoid an arithmetic exception and return the same values
+ *idle_percentage = p->last_percentage_of_idle_time;
+ return FTDM_SUCCESS;
+ }
+
+ halftime = totaltime / 2UL;
+
+ p->last_percentage_of_idle_time = ((100 * idletime + halftime) / totaltime);
+ *idle_percentage = p->last_percentage_of_idle_time;
+
+ p->last_user_time = user;
+ p->last_nice_time = nice;
+ p->last_system_time = system;
+ p->last_irq_time = irq;
+ p->last_soft_irq_time = softirq;
+ p->last_io_wait_time = iowait;
+ p->last_steal_time = steal;
+ p->last_idle_time = idle;
+
+ return FTDM_SUCCESS;
+}
+
+#elif defined (WIN32) || defined (WIN64)
+FT_DECLARE(ftdm_status_t) ftdm_cpu_get_system_idle_time(struct ftdm_cpu_monitor_stats *p, double *idle_percentage)
+{
+ FILETIME idleTime;
+ FILETIME kernelTime;
+ FILETIME userTime;
+
+ if (!::GetSystemTimes(&idleTime, &kernelTime, &userTime)) {
+ return false;
+ }
+
+ __int64 i64UserTime = (__int64)userTime.dwLowDateTime | ((__int64)userTime.dwHighDateTime << 32);
+
+ __int64 i64KernelTime = (__int64)kernelTime.dwLowDateTime | ((__int64)kernelTime.dwHighDateTime << 32);
+
+ __int64 i64IdleTime = (__int64)idleTime.dwLowDateTime | ((__int64)idleTime.dwHighDateTime << 32);
+
+ if (p->valid_last_times) {
+ __int64 i64User = i64UserTime - p->i64LastUserTime;
+ __int64 i64Kernel = i64KernelTime - p->i64LastKernelTime;
+ __int64 i64Idle = i64IdleTime - p->i64LastIdleTime;
+ __int64 i64System = i64User + i64Kernel;
+ *idle_percentage = 100.0 * i64Idle / i64System;
+ } else {
+ *idle_percentage = 100.0;
+ p->valid_last_times = 1;
+ }
+
+ /* Remember current value for the next call */
+ p->i64LastUserTime = i64UserTime;
+ p->i64LastKernelTime = i64KernelTime;
+ p->i64LastIdleTime = i64IdleTime;
+
+ /* Success */
+ return FTDM_SUCCESS;
+}
+#else
+/* Unsupported */
+FT_DECLARE(ftdm_status_t) ftdm_cpu_get_system_idle_time(struct ftdm_cpu_monitor_stats *p, double *idle_percentage)
+{
+ return FTDM_FAIL;
+}
+#endif
+
+FT_DECLARE(struct ftdm_cpu_monitor_stats*) ftdm_new_cpu_monitor(void)
+{
+ return calloc(1, sizeof(struct ftdm_cpu_monitor_stats));
+}
+
+FT_DECLARE(void) ftdm_delete_cpu_monitor(struct ftdm_cpu_monitor_stats *p)
+{
+#ifdef __linux__
+ close(p->procfd);
+#endif
+ free(p);
+}
+
+
+/* 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:
+ */
#ifdef FTDM_PIKA_SUPPORT
#include "ftdm_pika.h"
#endif
+#include "ftdm_cpu_monitor.h"
#define SPAN_PENDING_CHANS_QUEUE_SIZE 1000
#endif
}
+typedef struct {
+ uint8_t running;
+ uint8_t alarm;
+ uint32_t interval;
+ uint8_t alarm_action_flags;
+ uint8_t set_alarm_threshold;
+ uint8_t reset_alarm_threshold;
+ ftdm_interrupt_t *interrupt;
+} cpu_monitor_t;
+
static struct {
ftdm_hash_t *interface_hash;
ftdm_hash_t *module_hash;
uint32_t running;
ftdm_span_t *spans;
ftdm_group_t *groups;
+ cpu_monitor_t cpu_monitor;
} globals;
+static uint8_t ftdm_cpu_monitor_disabled = 0;
+
+enum ftdm_enum_cpu_alarm_action_flags
+{
+ FTDM_CPU_ALARM_ACTION_WARN = (1 << 0),
+ FTDM_CPU_ALARM_ACTION_REJECT = (1 << 1)
+};
/* enum lookup funcs */
FTDM_ENUM_NAMES(TONEMAP_NAMES, TONEMAP_STRINGS)
snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", "Channel is alarmed\n");
return FTDM_FAIL;
}
+
+ if (globals.cpu_monitor.alarm &&
+ globals.cpu_monitor.alarm_action_flags & FTDM_CPU_ALARM_ACTION_REJECT) {
+ snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", "CPU usage alarm is on - refusing to open channel\n");
+ ftdm_log(FTDM_LOG_WARNING, "CPU usage alarm is on - refusing to open channel\n");
+ ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_SWITCH_CONGESTION;
+ return FTDM_FAIL;
+ }
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY) || (status = ftdm_mutex_trylock(ftdmchan->mutex)) != FTDM_SUCCESS) {
snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "Channel is not ready or is in use %d %d", ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY), status);
} else {
ftdm_log(FTDM_LOG_ERROR, "unknown span variable '%s'\n", var);
}
+ } else if (!strncasecmp(cfg.category, "general", 7)) {
+ if (!strncasecmp(var, "cpu_monitoring_interval", sizeof("cpu_monitoring_interval")-1)) {
+ if (atoi(val) > 0) {
+ globals.cpu_monitor.interval = atoi(val);
+ } else {
+ ftdm_log(FTDM_LOG_ERROR, "Invalid cpu monitoring interval %s\n", val);
+ }
+ } else if (!strncasecmp(var, "cpu_set_alarm_threshold", sizeof("cpu_set_alarm_threshold")-1)) {
+ if (atoi(val) > 0 && atoi(val) < 100) {
+ globals.cpu_monitor.set_alarm_threshold = atoi(val);
+ } else {
+ ftdm_log(FTDM_LOG_ERROR, "Invalid cpu alarm set threshold %s\n", val);
+ }
+ } else if (!strncasecmp(var, "cpu_reset_alarm_threshold", sizeof("cpu_reset_alarm_threshold")-1)) {
+ if (atoi(val) > 0 && atoi(val) < 100) {
+ globals.cpu_monitor.reset_alarm_threshold = atoi(val);
+ if (globals.cpu_monitor.reset_alarm_threshold > globals.cpu_monitor.set_alarm_threshold) {
+ globals.cpu_monitor.reset_alarm_threshold = globals.cpu_monitor.set_alarm_threshold - 10;
+ ftdm_log(FTDM_LOG_ERROR, "Cpu alarm reset threshold must be lower than set threshold"
+ ", setting threshold to %d\n", globals.cpu_monitor.reset_alarm_threshold);
+ }
+ } else {
+ ftdm_log(FTDM_LOG_ERROR, "Invalid cpu alarm reset threshold %s\n", val);
+ }
+ } else if (!strncasecmp(var, "cpu_alarm_action", sizeof("cpu_alarm_action")-1)) {
+ char* p = val;
+ do {
+ if (!strncasecmp(p, "reject", sizeof("reject")-1)) {
+ globals.cpu_monitor.alarm_action_flags |= FTDM_CPU_ALARM_ACTION_REJECT;
+ } else if (!strncasecmp(p, "warn", sizeof("warn")-1)) {
+ globals.cpu_monitor.alarm_action_flags |= FTDM_CPU_ALARM_ACTION_WARN;
+ }
+ p = strchr(p, ',');
+ if (p) {
+ while(*p++) if (*p != 0x20) break;
+ }
+ } while (p);
+ }
} else {
ftdm_log(FTDM_LOG_ERROR, "unknown param [%s] '%s' / '%s'\n", cfg.category, var, val);
}
return status;
}
+static void *ftdm_cpu_monitor_run(ftdm_thread_t *me, void *obj)
+{
+ cpu_monitor_t *monitor = (cpu_monitor_t *)obj;
+ struct ftdm_cpu_monitor_stats *cpu_stats = ftdm_new_cpu_monitor();
+ if (!cpu_stats) {
+ return NULL;
+ }
+ monitor->running = 1;
+
+ while(ftdm_running()) {
+ double time;
+ if (ftdm_cpu_get_system_idle_time(cpu_stats, &time)) {
+ break;
+ }
+
+ if (monitor->alarm) {
+ if ((int)time >= (100 - monitor->set_alarm_threshold)) {
+ ftdm_log(FTDM_LOG_DEBUG, "CPU alarm OFF (idle:%d)\n", (int) time);
+ monitor->alarm = 0;
+ }
+ if (monitor->alarm_action_flags & FTDM_CPU_ALARM_ACTION_WARN) {
+ ftdm_log(FTDM_LOG_WARNING, "CPU alarm is ON (cpu usage:%d)\n", (int) (100-time));
+ }
+ } else {
+ if ((int)time <= (100-monitor->reset_alarm_threshold)) {
+ ftdm_log(FTDM_LOG_DEBUG, "CPU alarm ON (idle:%d)\n", (int) time);
+ monitor->alarm = 1;
+ }
+ }
+ ftdm_interrupt_wait(monitor->interrupt, monitor->interval);
+ }
+
+ ftdm_delete_cpu_monitor(cpu_stats);
+ monitor->running = 0;
+ return NULL;
+}
+
+static ftdm_status_t ftdm_cpu_monitor_start(cpu_monitor_t* monitor)
+{
+ if (ftdm_interrupt_create(&monitor->interrupt, FTDM_INVALID_SOCKET) != FTDM_SUCCESS) {
+ ftdm_log(FTDM_LOG_CRIT, "Failed to create CPU monitor interrupt\n");
+ return FTDM_FAIL;
+ }
+
+ if (ftdm_thread_create_detached(ftdm_cpu_monitor_run, monitor) != FTDM_SUCCESS) {
+ ftdm_log(FTDM_LOG_CRIT, "Failed to create cpu monitor thread!!\n");
+ return FTDM_FAIL;
+ }
+ return FTDM_SUCCESS;
+}
+
+static void ftdm_cpu_monitor_stop(cpu_monitor_t* monitor)
+{
+ ftdm_interrupt_signal(monitor->interrupt);
+ while(monitor->running) {
+ ftdm_sleep(10);
+ }
+}
+
+FT_DECLARE(void) ftdm_cpu_monitor_disable(void)
+{
+ ftdm_cpu_monitor_disabled = 1;
+}
+
+
FT_DECLARE(ftdm_status_t) ftdm_global_init(void)
{
memset(&globals, 0, sizeof(globals));
FT_DECLARE(ftdm_status_t) ftdm_global_configuration(void)
{
- int modcount = ftdm_load_modules();
+ int modcount = 0;
+
+ if (!globals.running) {
+ return FTDM_FAIL;
+ }
+
+ modcount = ftdm_load_modules();
+
ftdm_log(FTDM_LOG_NOTICE, "Modules configured: %d \n", modcount);
+ globals.cpu_monitor.interval = 1000;
+ globals.cpu_monitor.alarm_action_flags = FTDM_CPU_ALARM_ACTION_WARN | FTDM_CPU_ALARM_ACTION_REJECT;
+ globals.cpu_monitor.set_alarm_threshold = 80;
+ globals.cpu_monitor.reset_alarm_threshold = 70;
+
if (load_config() != FTDM_SUCCESS) {
globals.running = 0;
ftdm_log(FTDM_LOG_ERROR, "FreeTDM global configuration failed!\n");
return FTDM_FAIL;
}
+
+ if (!ftdm_cpu_monitor_disabled) {
+ if (ftdm_cpu_monitor_start(&globals.cpu_monitor) != FTDM_SUCCESS) {
+ return FTDM_FAIL;
+ }
+ }
+
+
return FTDM_SUCCESS;
}
globals.running = 0;
+ ftdm_cpu_monitor_stop(&globals.cpu_monitor);
+
globals.span_index = 0;
ftdm_span_close_all();
ftdm_mutex_unlock(globals.mutex);
ftdm_mutex_destroy(&globals.mutex);
ftdm_mutex_destroy(&globals.span_mutex);
+ ftdm_interrupt_destroy(&globals.cpu_monitor.interrupt);
memset(&globals, 0, sizeof(globals));
return FTDM_SUCCESS;
*/
static void handle_call_start(ftdm_span_t *span, sangomabc_connection_t *mcon, sangomabc_event_t *event)
{
- ftdm_channel_t *ftdmchan;
-
+ ftdm_channel_t *ftdmchan = NULL;
+ int hangup_cause = FTDM_CAUSE_CALL_REJECTED;
if (!(ftdmchan = find_ftdmchan(span, (sangomabc_short_event_t*)event, 0))) {
if ((ftdmchan = find_ftdmchan(span, (sangomabc_short_event_t*)event, 1))) {
int r;
}
ftdm_set_sflag(ftdmchan, SFLAG_SENT_FINAL_MSG);
- ftdmchan=NULL;
+ ftdmchan = NULL;
}
ftdm_log(FTDM_LOG_CRIT, "START CANT FIND CHAN %d:%d\n", event->span+1,event->chan+1);
goto error;
return;
error:
+ hangup_cause = ftdmchan ? ftdmchan->caller_data.hangup_cause : FTDM_CAUSE_REQUESTED_CHAN_UNAVAIL;
sangomabc_exec_command(mcon,
event->span,
event->chan,
0,
SIGBOOST_EVENT_CALL_START_NACK,
- 0, 0);
+ hangup_cause, 0);
}
FT_DECLARE(char *) ftdm_api_execute(const char *type, const char *cmd);
FT_DECLARE(int) ftdm_vasprintf(char **ret, const char *fmt, va_list ap);
FT_DECLARE(ftdm_status_t) ftdm_channel_set_caller_data(ftdm_channel_t *ftdmchan, ftdm_caller_data_t *caller_data);
+FT_DECLARE(void) ftdm_cpu_monitor_disable(void);
FIO_CODEC_FUNCTION(fio_slin2ulaw);
FIO_CODEC_FUNCTION(fio_ulaw2slin);
--- /dev/null
+/*
+ * Copyright (c) 2010, Sangoma Technologies
+ * Moises Silva <moy@sangoma.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * Contributors:
+ * David Yat Sin <dyatsin@sangoma.com>
+ *
+ */
+
+/*! \brief opaque cpu stats structure */
+struct ftdm_cpu_monitor_stats;
+
+/*!
+ * \brief create a new cpu monitor
+ * \return profile timer structure previously created with new_profile_timer, NULL on error
+ */
+FT_DECLARE(struct ftdm_cpu_monitor_stats*) ftdm_new_cpu_monitor(void);
+
+/*!
+ * \brief Deletes cpu_monitor
+ */
+FT_DECLARE(void) ftdm_delete_cpu_monitor(struct ftdm_cpu_monitor_stats *p);
+
+/*!
+ * \brief provides the percentage of idle system time
+ * \param p cpu_stats structure previously created with ftdm_new_cpu_monitor
+ * \param pointer to store the percentage of idle time
+ * \return -1 on error 0 for success
+ */
+FT_DECLARE(ftdm_status_t) ftdm_cpu_get_system_idle_time (struct ftdm_cpu_monitor_stats *p, double *idle_percentage);
+
+
+
+
+
+/* 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:
+ */