2 * collectd - src/freeswitch.c
3 * Copyright (C) 2005-2007 Florian octo Forster
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; only version 2 of the License is applicable.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 * Florian octo Forster <octo at verplant.org>
20 * Leon de Rooij <leon@scarlet-internet.nl>
26 #include "utils_match.h"
29 #define FS_DEF_HOST "127.0.0.1"
30 #define FS_DEF_PORT "8021"
31 #define FS_DEF_PASS "ClueCon"
38 * <Command "api sofia status profile res-public">
39 * Instance "profile-sofia-res-public"
41 * Regex "\\<CALLS-IN\s+(\d+)\\>"
54 typedef struct fs_match_s fs_match_t;
66 typedef struct fs_command_s fs_command_t;
69 char *line; // "api sofia status profile res-public"
70 char *instance; // "profile-sofia-res-public"
71 char *buffer; // <output from esl command as a char*>
72 size_t buffer_size; // sizeof(*buffer)
73 size_t buffer_fill; // 0 or 1
78 static fs_command_t *fs_commands_g = NULL;
80 static char *fs_host = NULL;
81 static char *fs_port = NULL;
82 static char *fs_pass = NULL;
84 static esl_handle_t esl_handle = {{0}};
85 // static int thread_running = 0; // for when subscribing to esl events
91 static int fs_config_add_string (const char *name, char **dest, oconfig_item_t *ci)
93 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
95 WARNING ("freeswitch plugin: '%s' needs exactly one string argument.", name);
100 *dest = strdup (ci->values[0].value.string);
105 } /* int fs_config_add_string */
107 static void fs_match_free (fs_match_t *fm)
114 sfree (fm->instance);
115 match_destroy (fm->match);
116 fs_match_free (fm->next);
118 } /* void fs_match_free */
120 static void fs_command_free (fs_command_t *fc)
126 sfree (fc->instance);
128 fs_match_free (fc->matches);
129 fs_command_free (fc->next);
131 } /* void fs_command_free */
133 static int fs_config_add_match_dstype (int *dstype_ret, oconfig_item_t *ci)
135 DEBUG ("freeswitch plugin: in fs_config_add_match_dstype");
139 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
141 WARNING ("freeswitch plugin: 'DSType' needs exactly one string argument.");
145 if (strncasecmp ("Gauge", ci->values[0].value.string, strlen ("Gauge")) == 0)
147 dstype = UTILS_MATCH_DS_TYPE_GAUGE;
148 if (strcasecmp ("GaugeAverage", ci->values[0].value.string) == 0)
149 dstype |= UTILS_MATCH_CF_GAUGE_AVERAGE;
150 else if (strcasecmp ("GaugeMin", ci->values[0].value.string) == 0)
151 dstype |= UTILS_MATCH_CF_GAUGE_MIN;
152 else if (strcasecmp ("GaugeMax", ci->values[0].value.string) == 0)
153 dstype |= UTILS_MATCH_CF_GAUGE_MAX;
154 else if (strcasecmp ("GaugeLast", ci->values[0].value.string) == 0)
155 dstype |= UTILS_MATCH_CF_GAUGE_LAST;
159 else if (strncasecmp ("Counter", ci->values[0].value.string, strlen ("Counter")) == 0)
161 dstype = UTILS_MATCH_DS_TYPE_COUNTER;
162 if (strcasecmp ("CounterSet", ci->values[0].value.string) == 0)
163 dstype |= UTILS_MATCH_CF_COUNTER_SET;
164 else if (strcasecmp ("CounterAdd", ci->values[0].value.string) == 0)
165 dstype |= UTILS_MATCH_CF_COUNTER_ADD;
166 else if (strcasecmp ("CounterInc", ci->values[0].value.string) == 0)
167 dstype |= UTILS_MATCH_CF_COUNTER_INC;
178 WARNING ("freeswitch plugin: `%s' is not a valid argument to `DSType'.",
179 ci->values[0].value.string);
183 *dstype_ret = dstype;
185 } /* int fs_config_add_match_dstype */
187 static int fs_config_add_match (fs_command_t *fs_command, oconfig_item_t *ci)
189 DEBUG ("freeswitch plugin: in fs_config_add_match");
191 fs_match_t *fs_match;
195 if (ci->values_num != 0)
197 WARNING ("freeswitch plugin: Ignoring arguments for the 'Match' block.");
200 fs_match = (fs_match_t *) malloc (sizeof (*fs_match));
201 if (fs_match == NULL)
203 ERROR ("freeswitch plugin: malloc failed.");
206 memset (fs_match, 0, sizeof (*fs_match));
209 for (i = 0; i < ci->children_num; i++)
211 oconfig_item_t *child = ci->children + i;
213 if (strcasecmp ("Regex", child->key) == 0)
214 status = fs_config_add_string ("Regex", &fs_match->regex, child);
215 else if (strcasecmp ("DSType", child->key) == 0)
216 status = fs_config_add_match_dstype (&fs_match->dstype, child);
217 else if (strcasecmp ("Type", child->key) == 0)
218 status = fs_config_add_string ("Type", &fs_match->type, child);
219 else if (strcasecmp ("Instance", child->key) == 0)
220 status = fs_config_add_string ("Instance", &fs_match->instance, child);
223 WARNING ("freeswitch plugin: Option `%s' not allowed here.", child->key);
229 } /* for (i = 0; i < ci->children_num; i++) */
233 if (fs_match->regex == NULL)
235 WARNING ("freeswitch plugin: `Regex' missing in `Match' block.");
239 if (fs_match->type == NULL)
241 WARNING ("freeswitch plugin: `Type' missing in `Match' block.");
245 if (fs_match->dstype == 0)
247 WARNING ("freeswitch plugin: `DSType' missing in `Match' block.");
252 } /* while (status == 0) */
257 fs_match->match = match_create_simple (fs_match->regex, fs_match->dstype);
258 if (fs_match->match == NULL)
260 ERROR ("freeswitch plugin: tail_match_add_match_simple failed.");
261 fs_match_free (fs_match);
268 prev = fs_command->matches;
269 while ((prev != NULL) && (prev->next != NULL))
273 fs_command->matches = fs_match;
275 prev->next = fs_match;
279 } /* int fs_config_add_match */
281 static int fs_config_add_command (oconfig_item_t *ci)
283 DEBUG ("freeswitch plugin: in fs_config_add_command");
285 fs_command_t *command;
289 if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
291 WARNING ("freeswitch plugin: 'Command' blocks need exactly one string argument.");
295 command = (fs_command_t *) malloc (sizeof (*command));
298 ERROR ("freeswitch plugin: malloc failed.");
301 memset (command, 0, sizeof (*command));
303 command->line = NULL;
304 command->line = strdup (ci->values[0].value.string);
306 if (command->line == NULL)
308 ERROR ("freeswitch plugin: strdup failed.");
313 /* Process all children */
315 for (i = 0; i < ci->children_num; i++)
317 oconfig_item_t *child = ci->children + i;
319 if (strcasecmp ("Instance", child->key) == 0)
320 status = fs_config_add_string ("Instance", &command->instance, child);
321 else if (strcasecmp ("Match", child->key) == 0)
322 fs_config_add_match (command, child);
325 WARNING ("freeswitch plugin: Option '%s' not allowed here.", child->key);
335 fs_command_free (command);
339 /* Add the new command to the linked list */
340 if (fs_commands_g == NULL)
341 fs_commands_g = command;
346 prev = fs_commands_g;
347 while ((prev != NULL) && (prev->next != NULL))
349 prev->next = command;
353 } /* int fs_config_add_command */
355 static int fs_complex_config (oconfig_item_t *ci)
362 DEBUG ("freeswitch plugin: reading config");
367 for (i = 0; i < ci->children_num; i++)
369 oconfig_item_t *child = ci->children + i;
371 if (strcasecmp ("Host", child->key) == 0)
373 if (fs_host != NULL) free (fs_host);
374 fs_host = strdup(child->values[0].value.string);
376 else if (strcasecmp ("Port", child->key) == 0)
378 if (fs_port != NULL) free (fs_port);
379 fs_port = strdup(child->values[0].value.string);
381 else if (strcasecmp ("Pass", child->key) == 0)
383 if (fs_pass != NULL) free (fs_pass);
384 fs_pass = strdup(child->values[0].value.string);
386 else if (strcasecmp ("Command", child->key) == 0)
388 status = fs_config_add_command(child);
396 WARNING ("freeswitch plugin: Option '%s' not allowed here.", child->key);
401 if ((success == 0) && (errors > 0))
403 ERROR ("freeswitch plugin: All statements failed.");
408 } /* int fs_complex_config */
410 static void fs_submit (const fs_command_t *fc,
411 const fs_match_t *fm, const cu_match_value_t *mv)
413 DEBUG ("freeswitch plugin: in fs_submit");
415 DEBUG ("fc->instance");
416 DEBUG (fc->instance);
425 value_list_t vl = VALUE_LIST_INIT;
427 values[0] = mv->value;
431 vl.time = time (NULL);
433 strncpy (vl.host, hostname_g, sizeof (vl.host));
434 strncpy (vl.plugin, "freeswitch", sizeof (vl.plugin));
435 strncpy (vl.plugin_instance, fc->instance, sizeof (vl.plugin_instance));
436 strncpy (vl.type, fm->type, sizeof (vl.type));
437 strncpy (vl.type_instance, fm->instance, sizeof (vl.type_instance));
439 plugin_dispatch_values (&vl);
440 } /* void fs_submit */
442 static int fs_read_command (fs_command_t *fc)
444 DEBUG ("freeswitch plugin: in fs_read_command");
449 /* can't the following be done nicer ? */
451 line = (char *) malloc (strlen(fc->line)+2);
452 snprintf(line, strlen(fc->line)+2, "%s\n\n", fc->line);
453 esl_send_recv(&esl_handle, line);
457 if (esl_handle.last_sr_event && esl_handle.last_sr_event->body)
459 DEBUG ("freeswitch plugin: output from esl (truncated):\n%s\n\n", esl_handle.last_sr_event->body);
461 fc->buffer = strdup(esl_handle.last_sr_event->body);
462 fc->buffer_size = strlen(fc->buffer);
466 for (fm = fc->matches; fm != NULL; fm = fm->next)
468 cu_match_value_t *mv;
470 status = match_apply (fm->match, fc->buffer);
473 WARNING ("freeswitch plugin: match_apply failed.");
477 mv = match_get_user_data (fm->match);
480 WARNING ("freeswitch plugin: match_get_user_data returned NULL.");
484 fs_submit (fc, fm, mv);
485 } /* for (fm = fc->matches; fm != NULL; fm = fm->next) */
488 } /* int fs_read_command */
490 static int fs_read (void)
494 DEBUG ("freeswitch plugin: read poll");
496 for (fc = fs_commands_g; fc != NULL; fc = fc->next)
497 fs_read_command (fc);
503 static void *msg_thread_run(esl_thread_t *me, void *obj)
505 esl_handle_t *esl_handle = (esl_handle_t *) obj;
508 // Maybe do some more in this loop later, like receive subscribed events,
509 // and create statistics of them
510 // see fs_cli.c function static void *msg_thread_run(), around line 198
511 while (thread_running && esl_handle->connected)
513 esl_status_t status = esl_recv_event_timed(esl_handle, 10, 1, NULL);
514 if (status == ESL_FAIL)
516 //DEBUG ("Disconnected [%s]\n", ESL_LOG_WARNING); // todo fixit
517 DEBUG ("Disconnected [%s]\n", "ESL_LOG_WARNING");
525 } */ /* void *msg_thread_run */
527 static int fs_init (void)
529 /* Set some default configuration variables */
530 if (fs_host == NULL) fs_host = FS_DEF_HOST;
531 if (fs_port == NULL) fs_port = FS_DEF_PORT;
532 if (fs_pass == NULL) fs_pass = FS_DEF_PASS;
534 /* Connect to FreeSWITCH over ESL */
535 DEBUG ("freeswitch plugin: making ESL connection to %s %s %s\n", fs_host, fs_port, fs_pass);
536 esl_connect(&esl_handle, fs_host, atoi(fs_port), fs_pass);
538 /* Start a seperate thread for incoming events here */
539 //esl_thread_create_detached(msg_thread_run, &esl_handle);
544 static int fs_shutdown (void)
546 DEBUG ("freeswitch plugin: disconnecting");
547 esl_disconnect(&esl_handle);
548 fs_command_free (fs_commands_g);
549 fs_commands_g = NULL;
551 } /* int fs_shutdown */
553 void module_register (void)
555 plugin_register_complex_config ("freeswitch", fs_complex_config);
556 plugin_register_init ("freeswitch", fs_init);
557 plugin_register_read ("freeswitch", fs_read);
558 plugin_register_shutdown ("freeswitch", fs_shutdown);
559 } /* void module_register */