[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