[asterisk-commits] branch file/func_supertimeout - r7290
/team/file/func_supertimeout/funcs/
asterisk-commits at lists.digium.com
asterisk-commits at lists.digium.com
Fri Dec 2 16:18:46 CST 2005
Author: file
Date: Fri Dec 2 16:18:45 2005
New Revision: 7290
URL: http://svn.digium.com/view/asterisk?rev=7290&view=rev
Log:
Add func_supertimeout for extended timeout-like stuff.
Added:
team/file/func_supertimeout/funcs/func_supertimeout.c
Added: team/file/func_supertimeout/funcs/func_supertimeout.c
URL: http://svn.digium.com/view/asterisk/team/file/func_supertimeout/funcs/func_supertimeout.c?rev=7290&view=auto
==============================================================================
--- team/file/func_supertimeout/funcs/func_supertimeout.c (added)
+++ team/file/func_supertimeout/funcs/func_supertimeout.c Fri Dec 2 16:18:45 2005
@@ -1,0 +1,416 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2005, Joshua Colp
+ *
+ * Joshua Colp <jcolp at asterlink.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Super Timeout application
+ *
+ * This is an extended timeout application
+ * \ingroup applications
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+
+#include "asterisk.h"
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+
+static char *tdesc = "Super Timeout Function";
+static char *cdr_desc = "Timeout CDR Handler";
+static char *cdr_name = "cdr-timeout";
+
+/* Timeout types */
+#define TYPE_ANSWER 1
+#define TYPE_ABSOLUTE 2
+#define TYPE_STATE 3
+#define TYPE_SIGSEGV 4
+
+/* Structure to represent a channel to timeout */
+struct timeout {
+ char *channel_name; /* Channel name of owner - used for cdr-timeout */
+ struct ast_channel *channel; /* Channel to timeout */
+ time_t start; /* Start time of timeout */
+ unsigned int timeout; /* Actual timeout amount */
+ int dead; /* Is this timeout dead?!? */
+ int type; /* Type of timeout entry */
+ int active; /* Is the timeout active? */
+ int state; /* Last recorded state */
+ struct timeout *next; /* Next entry in linked list */
+};
+
+AST_MUTEX_DEFINE_STATIC(timeout_lock);
+static struct timeout *timeouts = NULL;
+
+static pthread_t timeout_thread = AST_PTHREADT_NULL;
+
+STANDARD_LOCAL_USER;
+
+LOCAL_USER_DECL;
+
+/* Uncomment to allow multiple timeouts of the same type per channel */
+//#define MULTI_TIMEOUT_TYPES
+/* Uncomment to support the SIGSEGV timeout type */
+//#define SUPPORT_SIGSEGV
+
+/* Signal channel owner death by name */
+static void signal_owner_death_by_name(char *name)
+{
+ struct timeout *timeout = NULL;
+
+ /* This assumes it is locked before calling */
+ timeout = timeouts;
+ while (timeout) {
+ /* Only signal death if they are us... and not already dead */
+ if (!strcasecmp(name, timeout->channel_name)) {
+ /* Get rid of owner channel and signal death... */
+ timeout->channel = NULL;
+ timeout->dead = 1;
+ timeout->type = -1;
+ }
+ timeout = timeout->next;
+ }
+
+}
+
+/* Signal channel owner death to other timeouts */
+static void signal_owner_death(struct ast_channel *chan)
+{
+ struct timeout *timeout = NULL;
+
+ /* This assumes it is locked before calling */
+ timeout = timeouts;
+ while (timeout) {
+ /* Only signal death if they are us... and not already dead */
+ if (timeout->channel == chan && timeout->dead != 1) {
+ /* Get rid of owner channel and signal death... */
+ timeout->channel = NULL;
+ timeout->dead = 1;
+ timeout->type = -1;
+ }
+ timeout = timeout->next;
+ }
+
+}
+
+/* Background timeout thread */
+static void *timeout_proc(void *arg)
+{
+ time_t now;
+ struct timeout *timeout = NULL, *previous_timeout = NULL, *dead_timeout = NULL;
+
+ for (;;) {
+ time(&now);
+ ast_mutex_lock(&timeout_lock);
+ timeout = timeouts;
+ while (timeout) {
+ /* Check the type so we can do different things */
+ switch (timeout->type) {
+ case TYPE_ANSWER:
+ if (timeout->active != 1) {
+ /* We aren't active yet... so see if we were answered so we can switch to active! */
+ ast_mutex_lock(&timeout->channel->lock);
+ if (timeout->channel->_state == AST_STATE_UP) {
+ /* Switch to active state and record time */
+ timeout->active = 1;
+ time(&timeout->start);
+ }
+ ast_mutex_unlock(&timeout->channel->lock);
+ } else if ((now-timeout->start) >= timeout->timeout) {
+ timeout->dead = 1;
+ signal_owner_death(timeout->channel);
+ }
+ break;
+ case TYPE_ABSOLUTE:
+ /* If we aren't active... meh - don't care REALLY */
+ if (timeout->active != 1) {
+ timeout->active = 1;
+ }
+ /* Do standard time check... if positive - switch to dead state */
+ if ((now-timeout->start) >= timeout->timeout) {
+ timeout->dead = 1;
+ signal_owner_death(timeout->channel);
+ }
+ break;
+ case TYPE_STATE:
+ ast_mutex_lock(&timeout->channel->lock);
+ if (timeout->state != timeout->channel->_state && ((now-timeout->start) < timeout->timeout)) {
+ /* The state changed within the timeout... so we're okay! */
+ timeout->dead = 1;
+ ast_mutex_unlock(&timeout->channel->lock);
+ timeout->channel = NULL;
+ } else if ((now-timeout->start) >= timeout->timeout) {
+ /* State did not change within the timeout... bleh - jump up a priority and set a variable */
+ ast_async_goto(timeout->channel, timeout->channel->context, timeout->channel->exten, timeout->channel->priority + 1);
+ pbx_builtin_setvar_helper(timeout->channel, "STATE_TIMEOUT", "1");
+ timeout->dead = 1;
+ ast_mutex_unlock(&timeout->channel->lock);
+ timeout->channel = NULL;
+ } else {
+ ast_mutex_unlock(&timeout->channel->lock);
+ }
+ break;
+ case TYPE_SIGSEGV:
+ if (timeout->active != 1) {
+ timeout->active = 1;
+ }
+ if ((now-timeout->start) >= timeout->timeout) {
+ /* Crash it! */
+ ((char *)0)[0] = 0;
+ }
+ break;
+ default:
+ /* If we got here we really don't care... */
+ break;
+ }
+ if (timeout->dead == 1) {
+ /* Hangup the channel if applicable */
+ if (timeout->channel != NULL) {
+ /* Queue actual hangup on the channel */
+ ast_mutex_lock(&timeout->channel->lock);
+ ast_softhangup(timeout->channel, AST_SOFTHANGUP_EXPLICIT);
+ ast_mutex_unlock(&timeout->channel->lock);
+ timeout->channel = NULL;
+ }
+ /* Now drop this timeout out of existence!!! */
+ if (timeouts == timeout) {
+ timeouts = timeout->next;
+ } else if (previous_timeout != NULL) {
+ previous_timeout->next = timeout->next;
+ }
+ /* Finally do cleanup of this dead entry */
+ if (timeout->channel_name)
+ free(timeout->channel_name);
+ dead_timeout = timeout;
+ timeout = timeout->next;
+ /* Finally free the memory used by it */
+ free(dead_timeout);
+ dead_timeout = NULL;
+ } else {
+ previous_timeout = timeout;
+ timeout = timeout->next;
+ }
+ }
+ ast_mutex_unlock(&timeout_lock);
+ /* 1 second accuray is good enough for me */
+ sleep(1);
+ }
+
+ return NULL;
+}
+
+/* CDR handler (essentially we get called whenever the call is hungup */
+static int timeout_cdr(struct ast_cdr *cdr)
+{
+
+ ast_mutex_lock(&timeout_lock);
+ signal_owner_death_by_name(cdr->channel);
+ ast_mutex_unlock(&timeout_lock);
+
+ return 0;
+}
+
+/* Conversion from string to numerical value for timeout types */
+static int type_to_int(char *timeout_type)
+{
+ if (!strcasecmp(timeout_type, "answer"))
+ return TYPE_ANSWER;
+ else if (!strcasecmp(timeout_type, "absolute"))
+ return TYPE_ABSOLUTE;
+ else if (!strcasecmp(timeout_type, "state"))
+ return TYPE_STATE;
+#ifdef SUPPORT_SIGSEGV
+ else if (!strcasecmp(timeout_type, "sigsegv"))
+ return TYPE_SIGSEGV;
+#endif
+ else
+ return -1;
+}
+
+/* Actual functions of glory */
+static char *timeout_get_time(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
+{
+ int timeout_type = -1;
+ struct timeout *timeout = NULL;
+
+ /* Make sure we have a valid type */
+ timeout_type = type_to_int(data);
+ if (timeout_type == -1)
+ return buf;
+
+ ast_mutex_lock(&timeout_lock);
+ timeout = timeouts;
+ while (timeout) {
+ if (timeout->channel == chan && timeout->type == timeout_type) {
+ snprintf(buf, len, "%u", timeout->timeout);
+ break;
+ }
+ timeout = timeout->next;
+ }
+ ast_mutex_unlock(&timeout_lock);
+
+ return buf;
+}
+
+static void timeout_make_time(struct ast_channel *chan, char *cmd, char *data, const char *value)
+{
+ int timeout_type = -1;
+ struct timeout *timeout = NULL;
+
+ /* Make sure we have a valid timeout type first */
+ timeout_type = type_to_int(data);
+ if (timeout_type == -1) /* Unsupported type */
+ return;
+
+#ifndef MULTI_TIMEOUT_TYPES
+ /* Check to see if a timeout is already in existence of this type */
+ ast_mutex_lock(&timeout_lock);
+ timeout = timeouts;
+ while (timeout) {
+ if (timeout->channel == chan && timeout->type == timeout_type) {
+ /* Update the timeout value */
+ timeout->timeout = atoi(value);
+ if (timeout->timeout <= 0) {
+ /* They actually want to remove this timeout */
+ timeout->channel = NULL;
+ timeout->type = -1;
+ timeout->dead = 1;
+ }
+ break;
+ }
+ timeout = timeout->next;
+ }
+ ast_mutex_unlock(&timeout_lock);
+ if (timeout != NULL) /* We successfully found and updated the entry */
+ return;
+#endif
+
+ /* Create a new timeout and put it into the list */
+ timeout = (struct timeout*)malloc(sizeof(struct timeout));
+ if (timeout == NULL)
+ return;
+
+ /* Clear the memory */
+ memset(timeout, 0, sizeof(struct timeout));
+
+ /* Assign standard information */
+ timeout->dead = 0;
+ timeout->channel_name = strdup(chan->name);
+ timeout->channel = chan;
+ timeout->type = timeout_type;
+ /* Depending on the type... start the time now, or later */
+ if (timeout->type == TYPE_ABSOLUTE || timeout->type == TYPE_SIGSEGV) {
+ /* Switch to active state and record start time */
+ timeout->active = 1;
+ time(&timeout->start);
+ } else if (timeout->type == TYPE_STATE) {
+ /* Switch to active state... record state... record start time */
+ timeout->active = 1;
+ timeout->state = chan->_state;
+ time(&timeout->start);
+ }
+ timeout->timeout = atoi(value);
+
+ /* Shuffle the timeout into the list of DOOM(tm) */
+ ast_mutex_lock(&timeout_lock);
+ timeout->next = timeouts;
+ timeouts = timeout;
+ ast_mutex_unlock(&timeout_lock);
+
+ return;
+}
+
+static struct ast_custom_function group_timeout_function = {
+ .name = "SUPER_TIMEOUT",
+ .synopsis = "Gets or sets applicable timeout for a timeout type.",
+ .syntax = "SUPER_TIMEOUT(type)",
+ .desc = "Gets or sets applicable timeout for a timeout type.\n",
+ .read = timeout_get_time,
+ .write = timeout_make_time,
+};
+
+static void kill_thread_proc(void)
+{
+ pthread_cancel(timeout_thread);
+ pthread_kill(timeout_thread, SIGURG);
+ pthread_join(timeout_thread, NULL);
+ timeout_thread = AST_PTHREADT_NULL;
+ return;
+}
+
+int unload_module(void)
+{
+
+ kill_thread_proc();
+ ast_cdr_unregister(cdr_name);
+ ast_custom_function_unregister(&group_timeout_function);
+
+ STANDARD_HANGUP_LOCALUSERS;
+
+ return 0;
+}
+
+int load_module(void)
+{
+ /* Spawn our timeout handling thread */
+ if (pthread_create(&timeout_thread, NULL, timeout_proc, NULL)) {
+ ast_log(LOG_ERROR, "Failed to spawn timeout handling thread!\n");
+ return -1;
+ }
+ /* Register CDR handler so we have some record of hangup */
+ if (ast_cdr_register(cdr_name, cdr_desc, timeout_cdr)) {
+ kill_thread_proc();
+ ast_log(LOG_ERROR, "Failed to register CDR Timeout handler\n");
+ return -1;
+ }
+ /* Register the dialplan function */
+ if (ast_custom_function_register(&group_timeout_function)) {
+ kill_thread_proc();
+ ast_cdr_unregister(cdr_name);
+ ast_log(LOG_ERROR, "Failed to register GROUP_TIMEOUT function\n");
+ return -1;
+ }
+ /* All is well in the world! */
+ return 0;
+}
+
+char *description(void)
+{
+ return tdesc;
+}
+
+int usecount(void)
+{
+ int res;
+ STANDARD_USECOUNT(res);
+ return res;
+}
+
+char *key()
+{
+ return ASTERISK_GPL_KEY;
+}
More information about the asterisk-commits
mailing list