freeswitch plugin: It compiles, if it also works, I'm going to eat my shoe
authorLeon de Rooij <leon@scarlet-internet.nl>
Thu, 23 Apr 2009 15:34:25 +0000 (17:34 +0200)
committerFlorian Forster <sifnfors@informatik.stud.uni-erlangen.de>
Thu, 16 Jul 2009 08:54:14 +0000 (10:54 +0200)
src/freeswitch.c

index dc7eb9e..be8a905 100644 (file)
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
-
-/*
 #include "utils_match.h"
-*/
 #include <esl.h>
 
-#define FREESWITCH_DEF_HOST "127.0.0.1"
-#define FREESWITCH_DEF_PORT "8021"
-#define FREESWITCH_DEF_PASSWORD "ClueCon"
+#define FS_DEF_HOST "127.0.0.1"
+#define FS_DEF_PORT "8021"
+#define FS_DEF_PASS "ClueCon"
 
-static const char *config_keys[] = 
+/*
+ *     <Plugin freeswitch>
+ *             Host "127.0.0.1"
+ *             Port "8021"
+ *             Pass "ClueCon"
+ *             <Command "sofia status profile res-public">
+ *                     Instance "profile-sofia-res-public"
+ *                     <Match>
+ *                             Regex "\\<CALLS-IN\s+(\d+)\\>"
+ *                             DSType "CounterInc"
+ *                             Type "counter"
+ *                             Instance "calls-in"
+ *                     </Match>
+ *             </Command>
+ *     </Plugin>
+ */
+
+/*
+ * Data types
+ */
+struct fs_match_s;
+typedef struct fs_match_s fs_match_t;
+struct fs_match_s
 {
-       "Host",
-       "Port",
-       "Password"
+       char *regex;
+       int dstype;
+       char *type;
+       char *instance;
+       cu_match_t *match;
+       fs_match_t *next;
 };
-static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
-typedef struct profilename
+struct fs_command_s;
+typedef struct fs_command_s fs_command_t;
+struct fs_command_s
+{
+       char *line;             // "sofia status profile res-public"
+       char *instance;         // "profile-sofia-res-public"
+       char *buffer;           // <output from esl command as a char*>
+       size_t buffer_size;     // sizeof(*buffer)
+       size_t buffer_fill;     // 0 or 1
+       fs_match_t *matches;
+       fs_command_t *next;
+};
+
+static fs_command_t *fs_commands_g = NULL;
+
+static char *fs_host = NULL;
+static char *fs_port = NULL;
+static char *fs_pass = NULL;
+
+static esl_handle_t esl_handle = {{0}};
+// static int thread_running = 0; // for when subscribing to esl events
+
+/*
+ * Private functions
+ */
+
+static int fs_config_add_string (const char *name, char **dest, oconfig_item_t *ci)
+{
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+       {
+               WARNING ("freeswitch plugin: '%s' needs exactly one string argument.", name);
+               return (-1);
+       }
+
+       sfree (*dest);
+       *dest = strdup (ci->values[0].value.string);
+       if (*dest == NULL)
+               return (-1);
+
+       return (0);
+} /* int fs_config_add_string */
+
+static void fs_match_free (fs_match_t *fm)
+{
+       if (fm == NULL)
+               return;
+
+       sfree (fm->regex);
+       sfree (fm->type);
+       sfree (fm->instance);
+       match_destroy (fm->match);
+       fs_match_free (fm->next);
+       sfree (fm);
+} /* void fs_match_free */
+
+static int fs_config_add_match_dstype (int *dstype_ret, oconfig_item_t *ci)
+{
+       int dstype;
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+       {
+               WARNING ("freeswitch plugin: 'DSType' needs exactly one string argument.");
+               return (-1);
+       }
+
+       if (strncasecmp ("Gauge", ci->values[0].value.string, strlen ("Gauge")) == 0)
+       {
+               dstype = UTILS_MATCH_DS_TYPE_GAUGE;
+               if (strcasecmp ("GaugeAverage", ci->values[0].value.string) == 0)
+                       dstype |= UTILS_MATCH_CF_GAUGE_AVERAGE;
+               else if (strcasecmp ("GaugeMin", ci->values[0].value.string) == 0)
+                       dstype |= UTILS_MATCH_CF_GAUGE_MIN;
+               else if (strcasecmp ("GaugeMax", ci->values[0].value.string) == 0)
+                       dstype |= UTILS_MATCH_CF_GAUGE_MAX;
+               else if (strcasecmp ("GaugeLast", ci->values[0].value.string) == 0)
+                       dstype |= UTILS_MATCH_CF_GAUGE_LAST;
+               else
+                       dstype = 0;
+       }
+       else if (strncasecmp ("Counter", ci->values[0].value.string, strlen ("Counter")) == 0)
+       {
+               dstype = UTILS_MATCH_DS_TYPE_COUNTER;
+               if (strcasecmp ("CounterSet", ci->values[0].value.string) == 0)
+                       dstype |= UTILS_MATCH_CF_COUNTER_SET;
+               else if (strcasecmp ("CounterAdd", ci->values[0].value.string) == 0)
+                       dstype |= UTILS_MATCH_CF_COUNTER_ADD;
+               else if (strcasecmp ("CounterInc", ci->values[0].value.string) == 0)
+                       dstype |= UTILS_MATCH_CF_COUNTER_INC;
+               else
+                       dstype = 0;
+       }
+       else
+       {
+               dstype = 0;
+       }
+
+       if (dstype == 0)
+       {
+               WARNING ("freeswitch plugin: `%s' is not a valid argument to `DSType'.",
+               ci->values[0].value.string);
+               return (-1);
+       }
+
+       *dstype_ret = dstype;
+       return (0);
+} /* int fs_config_add_match_dstype */
+
+static int fs_config_add_match (fs_command_t *fs_command, oconfig_item_t *ci)
+{
+       fs_match_t *fs_match;
+       int status;
+       int i;
+
+       if (ci->values_num != 0)
+       {
+               WARNING ("freeswitch plugin: Ignoring arguments for the 'Match' block.");
+       }
+
+       fs_match = (fs_match_t *) malloc (sizeof (*fs_match));
+       if (fs_match == NULL)
+       {
+               ERROR ("freeswitch plugin: malloc failed.");
+               return (-1);
+       }
+       memset (fs_match, 0, sizeof (*fs_match));
+
+       status = 0;
+       for (i = 0; i < ci->children_num; i++)
+       {
+               oconfig_item_t *child = ci->children + i;
+
+               if (strcasecmp ("Regex", child->key) == 0)
+                       status = fs_config_add_string ("Regex", &fs_match->regex, child);
+               else if (strcasecmp ("DSType", child->key) == 0)
+                       status = fs_config_add_match_dstype (&fs_match->dstype, child);
+               else if (strcasecmp ("Type", child->key) == 0)
+                       status = fs_config_add_string ("Type", &fs_match->type, child);
+               else if (strcasecmp ("Instance", child->key) == 0)
+                       status = fs_config_add_string ("Instance", &fs_match->instance, child);
+               else
+               {
+                       WARNING ("freeswitch plugin: Option `%s' not allowed here.", child->key);
+                       status = -1;
+               }
+
+               if (status != 0)
+                       break;
+       } /* for (i = 0; i < ci->children_num; i++) */
+
+       while (status == 0)
+       {
+               if (fs_match->regex == NULL)
+               {
+                       WARNING ("freeswitch plugin: `Regex' missing in `Match' block.");
+                       status = -1;
+               }
+
+               if (fs_match->type == NULL)
+               {
+                       WARNING ("freeswitch plugin: `Type' missing in `Match' block.");
+                       status = -1;
+               }
+
+               if (fs_match->dstype == 0)
+               {
+                       WARNING ("freeswitch plugin: `DSType' missing in `Match' block.");
+                       status = -1;
+               }
+
+               break;
+       } /* while (status == 0) */
+
+       if (status != 0)
+       return (status);
+
+       fs_match->match = match_create_simple (fs_match->regex, fs_match->dstype);
+       if (fs_match->match == NULL)
+       {
+               ERROR ("freeswitch plugin: tail_match_add_match_simple failed.");
+               fs_match_free (fs_match);
+               return (-1);
+       }
+       else
+       {
+               fs_match_t *prev;
+
+               prev = fs_command->matches;
+               while ((prev != NULL) && (prev->next != NULL))
+                       prev = prev->next;
+
+               if (prev == NULL)
+                       fs_command->matches = fs_match;
+               else
+                       prev->next = fs_match;
+       }
+
+       return (0);
+} /* int fs_config_add_match */
+
+static int fs_config_add_command (oconfig_item_t *ci)
+{
+       fs_command_t *command;
+       int status;
+       int i;
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+       {
+               WARNING ("freeswitch plugin: 'Command' blocks need exactly one string argument.");
+               return (-1);
+       }
+
+       command = (fs_command_t *) malloc (sizeof (*command));
+       if (command == NULL)
+       {
+               ERROR ("freeswitch plugin: malloc failed.");
+               return (-1);
+       }
+       memset (command, 0, sizeof (*command));
+       command->line = NULL;
+
+       command->instance = strdup (ci->values[0].value.string);
+       if (command->instance == NULL)
+       {
+               ERROR ("freeswitch plugin: strdup failed.");
+               sfree (command);
+               return (-1);
+       }
+
+       /* Process all children */
+       status = 0;
+       for (i = 0; i < ci->children_num; i++)
+       {
+               oconfig_item_t *child = ci->children + i;
+
+               if (strcasecmp ("Match", child->key) == 0)
+                       fs_config_add_match (command, child);
+               else
+               {
+                       WARNING ("freeswitch plugin: Option '%s' not allowed here.", child->key);
+                       status = -1;
+               }
+
+               if (status != 0)
+                       break;
+       }
+
+       /* Add the new command to the linked list */
+       if (fs_commands_g == NULL)
+               fs_commands_g = command;
+       else
+       {
+               fs_command_t *prev;
+
+               prev = fs_commands_g;
+               while ((prev != NULL) && (prev->next != NULL))
+                       prev = prev->next;
+               prev->next = command;
+       }
+
+       return (0);
+} /* int fs_config_add_command */
+
+static int fs_complex_config (oconfig_item_t *ci)
 {
-       char *name;
-       struct profilename *next;
-} profilename_t;
+       int success;
+       int errors;
+       int status;
+       int i;
+
+       success = 0;
+       errors = 0;
+
+       for (i = 0; i < ci->children_num; i++)
+       {
+               oconfig_item_t *child = ci->children + i;
 
-static esl_handle_t handle = {{0}};
-// static int thread_running = 0;
+               if (strcasecmp ("Host", child->key) == 0)
+               {
+                       if (fs_host != NULL) free (fs_host);
+                       fs_host = strdup(child->values[0].value.string);
+               }
+               else if (strcasecmp ("Port", child->key) == 0)
+               {
+                       if (fs_port != NULL) free (fs_port);
+                       fs_port = strdup(child->values[0].value.string);
+               }
+               else if (strcasecmp ("Pass", child->key) == 0)
+               {
+                       if (fs_pass != NULL) free (fs_pass);
+                       fs_pass = strdup(child->values[0].value.string);
+               }
+               else if (strcasecmp ("Command", child->key) == 0)
+               {
+                       status = fs_config_add_command(child);
+                       if (status == 0)
+                               success++;
+                       else
+                               errors++;
+               }
+               else
+               {
+                       WARNING ("freeswitch plugin: Option '%s' not allowed here.", child->key);
+                       errors++;
+               }
+       }
+
+       if ((success == 0) && (errors > 0))
+       {
+               ERROR ("freeswitch plugin: All statements failed.");
+               return (-1);
+       }
 
-static char *freeswitch_host = NULL;
-static char *freeswitch_port = NULL;
-static char *freeswitch_password = NULL;
+       return (0);
+} /* int fs_complex_config */
 
-static void freeswitch_submit (const char *profile, const char *type, gauge_t inbound, gauge_t outbound)
+static void fs_submit (const fs_command_t *fc,
+       const fs_match_t *fm, const cu_match_value_t *fmv)
 {
-       value_t values[2];
+       value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].gauge = inbound;
-       values[1].gauge = outbound;
+       values[0] = fmv->value;
 
        vl.values = values;
-       vl.values_len = 2;
-       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-       sstrncpy (vl.plugin, "freeswitch", sizeof (vl.plugin));
-       sstrncpy (vl.type, type, sizeof (vl.type));
-        sstrncpy (vl.type_instance, profile, sizeof (vl.type_instance));
+       vl.values_len = 1;
+       vl.time = time (NULL);
+
+       strncpy (vl.host, hostname_g, sizeof (vl.host));
+       strncpy (vl.plugin, "freeswitch", sizeof (vl.plugin));
+       strncpy (vl.plugin_instance, fc->instance, sizeof (vl.plugin_instance));
+       strncpy (vl.type, fm->type, sizeof (vl.type));
+       strncpy (vl.type_instance, fm->instance, sizeof (vl.type_instance));
 
        plugin_dispatch_values (&vl);
-} /* void freeswitch_submit */
+} /* void fs_submit */
 
-static int freeswitch_read (void)
+static int fs_read_command (fs_command_t *fc)
 {
-/*
-       const char *host;
-       const char *port;
-       const char *password;
-*/
+       fs_match_t *fm;
+       int status;
 
+       fc->buffer_fill = 0;
+       esl_send_recv(&esl_handle, fc->line); /////////////////// NOTICE NO \n\n IS GIVEN AFTER fc->line !!!!!!!!!!!!!!!!!! THIS WON'T WORK!!!!!!!!!!
 
-       esl_send_recv(&handle, "api show channels\n\n");
-       if (handle.last_sr_event && handle.last_sr_event->body) {
-               DEBUG ("OUTPUT FROM FREESWITCH:\n%s\n\n", handle.last_sr_event->body);
+       if (esl_handle.last_sr_event && esl_handle.last_sr_event->body)
+       {
+               DEBUG ("OUTPUT FROM FS:\n%s\n\n", esl_handle.last_sr_event->body);
+               sfree(fc->buffer);
+               fc->buffer = strdup(esl_handle.last_sr_event->body);
+               fc->buffer_fill = 1; // ??
        }
 
-       freeswitch_submit ("res-public", "fs_channels", 3, 5);
+       for (fm = fc->matches; fm != NULL; fm = fm->next)
+       {
+               cu_match_value_t *mv;
+
+               status = match_apply (fm->match, fc->buffer);
+               if (status != 0)
+               {
+                       WARNING ("freeswitch plugin: match_apply failed.");
+                       continue;
+               }
+
+               mv = match_get_user_data (fm->match);
+               if (mv == NULL)
+               {
+                       WARNING ("freeswitch plugin: match_get_user_data returned NULL.");
+                       continue;
+               }
+
+               fs_submit (fc, fm, mv);
+       } /* for (fm = fc->matches; fm != NULL; fm = fm->next) */
 
        return (0);
-} /* int freeswitch_read */
+} /* int fs_read_command */
 
-static int freeswitch_config (const char *key, const char *value)
+static int fs_read (void)
 {
-        if (strcasecmp ("Host", key) == 0)
-        {
-               if (freeswitch_host != NULL)
-                       free (freeswitch_host);
-                freeswitch_host = strdup (value);
-        }
-        else if (strcasecmp ("Port", key) == 0)
-       {
-               if (freeswitch_port != NULL)
-                       free (freeswitch_port);
-               freeswitch_port = strdup (value);
-       }
-        else if (strcasecmp ("Password", key) == 0)
-        {
-               if (freeswitch_password != NULL)
-                       free (freeswitch_password);
-                freeswitch_password = strdup (value);
-        }
-        else
-        {
-                return (-1);
-        }
-        return (0);
-} /* int freeswitch_config */
+       fs_command_t *fc;
+
+       for (fc = fs_commands_g; fc != NULL; fc = fc->next)
+               fs_read_command (fc);
+
+       return (0);
+} /* int fs_read */
 
 /*
 static void *msg_thread_run(esl_thread_t *me, void *obj)
 {
-       esl_handle_t *handle = (esl_handle_t *) obj;
+       esl_handle_t *esl_handle = (esl_handle_t *) obj;
        thread_running = 1;
 
        // Maybe do some more in this loop later, like receive subscribed events,
        // and create statistics of them
        // see fs_cli.c function static void *msg_thread_run(), around line 198
-       while (thread_running && handle->connected)
+       while (thread_running && esl_handle->connected)
        {
-               esl_status_t status = esl_recv_event_timed(handle, 10, 1, NULL);
+               esl_status_t status = esl_recv_event_timed(esl_handle, 10, 1, NULL);
                if (status == ESL_FAIL)
                {
                        //DEBUG ("Disconnected [%s]\n", ESL_LOG_WARNING); // todo fixit
@@ -143,32 +473,33 @@ static void *msg_thread_run(esl_thread_t *me, void *obj)
        return (NULL);
 } */ /* void *msg_thread_run */
 
-static int freeswitch_init (void)
+static int fs_init (void)
 {
        /* Set some default configuration variables */
-       if (freeswitch_host == NULL)
-               freeswitch_host = FREESWITCH_DEF_HOST;
-
-       if (freeswitch_port == NULL)
-               freeswitch_port = "8021";
-
-       if (freeswitch_password == NULL)
-               freeswitch_password = FREESWITCH_DEF_PASSWORD;
+       if (fs_host == NULL) fs_host = FS_DEF_HOST;
+       if (fs_port == NULL) fs_port = FS_DEF_PORT;
+       if (fs_pass == NULL) fs_pass = FS_DEF_PASS;
 
        /* Connect to FreeSWITCH over ESL */
-       DEBUG ("Making ESL connection to %s %s %s\n", freeswitch_host, freeswitch_port, freeswitch_password);
-       esl_connect(&handle, freeswitch_host, atoi(freeswitch_port), freeswitch_password);
+       DEBUG ("Making ESL connection to %s %s %s\n", fs_host, fs_port, fs_pass);
+       esl_connect(&esl_handle, fs_host, atoi(fs_port), fs_pass);
 
-       /* Start a seperate thread for incoming events */
-       //esl_thread_create_detached(msg_thread_run, &handle);
+       /* Start a seperate thread for incoming events here */
+       //esl_thread_create_detached(msg_thread_run, &esl_handle);
 
        return(0);
-} /* int freeswitch_init */
+} /* int fs_init */
+
+static int fs_shutdown (void)
+{
+       esl_disconnect(&esl_handle);
+       return (0);
+} /* int fs_shutdown */
 
 void module_register (void)
 {
-        plugin_register_config ("freeswitch", freeswitch_config,
-                       config_keys, config_keys_num);
-       plugin_register_init ("freeswitch", freeswitch_init);
-       plugin_register_read ("freeswitch", freeswitch_read);
+       plugin_register_complex_config ("freeswitch", fs_complex_config);
+       plugin_register_init ("freeswitch", fs_init);
+       plugin_register_read ("freeswitch", fs_read);
+       plugin_register_shutdown ("freeswitch", fs_shutdown);
 } /* void module_register */