Fri Sep 25 19:28:14 2009

Asterisk developer's documentation


res_esel.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Extension State Export Logic (E.S.E.L) (Sorry, i couldnt resist...)
00005  *
00006  * Resource to export extension states to other Asterisk servers
00007  *
00008  * Copyright (C) 2006, Junghanns.NET GmbH
00009  *
00010  * Klaus-Peter Junghanns <kpj@junghanns.net>
00011  *
00012  * This program is free software, distributed under the terms of
00013  * the GNU General Public License
00014  */
00015 
00016 #include "asterisk.h"
00017 
00018 #include <stdlib.h>
00019 #include <errno.h>
00020 #include <unistd.h>
00021 #include <string.h>
00022 #include <stdlib.h>
00023 #include <stdio.h>
00024 #include <sys/time.h>
00025 #include <sys/signal.h>
00026 #include <netinet/in.h>
00027 #include <sys/types.h>
00028 #include <sys/socket.h>
00029 
00030 #include "asterisk/file.h"
00031 #include "asterisk/logger.h"
00032 #include "asterisk/channel.h"
00033 #include "asterisk/pbx.h"
00034 #include "asterisk/options.h"
00035 #include "asterisk/module.h"
00036 #include "asterisk/translate.h"
00037 #include "asterisk/say.h"
00038 #include "asterisk/features.h"
00039 #include "asterisk/musiconhold.h"
00040 #include "asterisk/config.h"
00041 #include "asterisk/cli.h"
00042 #include "asterisk/manager.h"
00043 #include "asterisk/utils.h"
00044 #include "asterisk/lock.h"
00045 #include "asterisk/adsi.h"
00046 
00047 
00048 AST_MUTEX_DEFINE_STATIC(listlock);
00049 
00050 typedef struct esel_extension_state {
00051     char context[AST_MAX_EXTENSION];
00052     char exten[AST_MAX_EXTENSION];
00053     int state;
00054     char cid_num[AST_MAX_EXTENSION];
00055     char cid_name[AST_MAX_EXTENSION];
00056     char devstate[AST_MAX_EXTENSION];
00057     struct esel_extension_state *next;
00058     struct esel_extension_state *prev;
00059 } esel_extension_state;
00060 
00061 typedef struct esel_export {
00062     char context[AST_MAX_EXTENSION];
00063     char exten[AST_MAX_EXTENSION];
00064     char devstate[AST_MAX_EXTENSION];
00065     struct esel_export *next;
00066 } esel_export;
00067 
00068 typedef struct esel_queue {
00069    struct esel_extension_state *head;
00070    struct esel_extension_state *tail;
00071    int count;
00072    ast_cond_t cond;
00073    ast_mutex_t lock;
00074 }  esel_queue;
00075 
00076 typedef struct esel_pvt {
00077     char name[80];
00078     char username[80];
00079     char secret[80];
00080     char host[80];
00081     int port;
00082     struct sockaddr_in raddr;
00083     int sockfd;
00084     int connected;
00085     pthread_t esel_thread;
00086 
00087     /* list of extensions to export */
00088     struct esel_export *extensions;
00089 
00090     /* queue */
00091     struct esel_queue queue;
00092     
00093     struct esel_pvt *next;
00094 } esel_pvt;
00095 
00096 static struct esel_pvt *donkeys = NULL;
00097 
00098 static int esel_queue_extension_state(struct esel_queue *queue, char *context, char *exten, int state, void *data, char *cid_num, char *cid_name) {
00099    struct esel_extension_state *exstate = NULL;
00100 
00101    exstate = malloc(sizeof(struct esel_extension_state));
00102    if (!exstate) {
00103        ast_log(LOG_ERROR, "Unable to malloc!\n");
00104        return 1;
00105    }
00106    memset(exstate,0,sizeof(struct esel_extension_state));
00107    exstate->next = NULL;
00108    exstate->prev = NULL;
00109 
00110    ast_mutex_lock(&queue->lock);
00111    if (queue->count > 100) {
00112        ast_mutex_unlock(&queue->lock);
00113        free(exstate);
00114        if (option_verbose > 5)
00115       ast_log(LOG_WARNING, "E.S.E.L Queue too long.\n");
00116        return -1;
00117    }
00118    ast_copy_string(exstate->exten, exten, sizeof(exstate->exten));
00119    ast_copy_string(exstate->context, context, sizeof(exstate->context));
00120    ast_copy_string(exstate->cid_num, cid_num, sizeof(exstate->cid_num));
00121    ast_copy_string(exstate->cid_name, cid_name, sizeof(exstate->cid_name));
00122    exstate->state = state;
00123    if (!queue->head) {
00124       /* Empty queue */
00125       queue->head = exstate;
00126       queue->tail = exstate;
00127    } else {
00128       /* Double link */
00129       queue->tail->next = exstate;
00130       exstate->prev = queue->tail;
00131       queue->tail = exstate;
00132    }
00133    queue->count++;
00134    ast_cond_signal(&queue->cond);
00135    ast_mutex_unlock(&queue->lock);
00136    return 0;
00137 }
00138 
00139 static int esel_is_exported(struct esel_export *extensions, struct esel_extension_state *exstate) {
00140     struct esel_export *export = NULL;
00141     export = extensions;
00142     while (export) {
00143    if ((!strcasecmp(export->exten, exstate->exten)) && (!strcasecmp(export->context, exstate->context))) {
00144        /* copy mapping */
00145        ast_copy_string(exstate->devstate, export->devstate, sizeof(exstate->devstate));
00146        return 1;
00147    }
00148    export = export->next;
00149     }
00150     return 0;
00151 }
00152 
00153 static int esel_state2devstate(int state) {
00154     switch(state) {
00155    case 1:
00156        return 2;
00157    case 8:
00158        return 6;
00159    default:
00160        return state;
00161     }
00162 }
00163 
00164 static void esel_export_to_remote(struct esel_extension_state *exstate, struct esel_pvt *esel) {
00165     char msg[1024];
00166     int sent = 0;
00167     memset(msg, 0x0, sizeof(msg));
00168     snprintf(msg, sizeof(msg) - 1, "Action: Devstate\r\nDevstate: %s\r\nValue: %d\r\nCallerID: %s\r\nCallerIDName: %s\r\n\r\n", exstate->devstate, esel_state2devstate(exstate->state), exstate->cid_num, exstate->cid_name);
00169     sent = send(esel->sockfd, msg, strlen(msg), 0);
00170     if (sent == -1) {
00171    esel->connected = 0;
00172     }
00173 //    ast_log(LOG_NOTICE, "%s", msg);
00174 }
00175 
00176 static void *do_esel_thread(void *data) {
00177     struct esel_pvt *esel = (struct esel_pvt *)data;
00178     struct esel_queue *queue = &esel->queue;
00179     struct esel_extension_state *exstate = NULL;
00180     char msg[1024];
00181     char buf[1024];
00182     int numbytes = 0;
00183     int sent = 0;
00184     int res = 0;
00185     for (;;) {
00186    if (esel->connected) {
00187        ast_mutex_lock(&queue->lock);
00188        if (queue->count == 0) 
00189       ast_cond_wait(&queue->cond, &queue->lock);
00190        exstate = queue->head;
00191        if (exstate) {
00192       if (exstate->next) {
00193           queue->head = exstate->next;
00194       } else {
00195           queue->head = NULL;
00196           queue->tail = NULL;
00197       }
00198       queue->count--;
00199        } else {
00200       ast_log(LOG_ERROR, "I SHOULD NEVER HAPPEN! EXPECT SOME MAJOR KABOOM! DUCK AND COVER!\n");
00201        }
00202        ast_mutex_unlock(&queue->lock);
00203        
00204        if (exstate) {
00205            if (esel_is_exported(esel->extensions, exstate)) {
00206           esel_export_to_remote(exstate, esel);
00207       }
00208       free(exstate);
00209       exstate = NULL;
00210        }     
00211    } else {
00212        if (esel->sockfd > 0)
00213       close(esel->sockfd);
00214        if ((esel->sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
00215       ast_log(LOG_ERROR, "unable to request socket!\n");
00216       return NULL;
00217        }
00218        /* try to connect */
00219        res = connect(esel->sockfd, (struct sockaddr *)&esel->raddr, sizeof(struct sockaddr));
00220        if (res) {
00221       ast_log(LOG_NOTICE, "error connecting to %s:%d\n", esel->host, esel->port);
00222        } else {
00223       while (strncasecmp(buf, "Asterisk Call Manager:", 21)) {
00224           if ((numbytes=recv(esel->sockfd, buf, sizeof(buf), 0)) == -1) {
00225          esel->connected = 0;
00226          continue;
00227           }
00228           buf[numbytes] = '\0';
00229       //    ast_log(LOG_NOTICE, "read: %s", buf);
00230       }
00231       /* log into remote manager */
00232       memset(msg, 0x0, sizeof(msg));
00233       snprintf(msg, sizeof(msg) - 1, "Action: Login\r\nUsername: %s\r\nSecret: %s\r\n\r\n", esel->username, esel->secret);
00234       sent = send(esel->sockfd, msg, strlen(msg), 0);
00235    
00236       while (strncasecmp(buf, "Response:", 9)) {
00237           if ((numbytes=recv(esel->sockfd, buf, sizeof(buf), 0)) == -1) {
00238          continue;
00239           }
00240           buf[numbytes] = '\0';
00241       //    ast_log(LOG_NOTICE, "read: %s", buf);
00242       }
00243    
00244       if (!strncasecmp(buf, "Response: Success", 17)) {
00245           esel->connected = 1;
00246       } else {
00247           ast_log(LOG_ERROR, "error login into remote asterisk %s\n", esel->name);
00248       }
00249        }
00250        /* time heals everything... */
00251        sleep(10);
00252    }
00253     }
00254     return NULL;
00255 }
00256 
00257 static int esel_state_cb(char *context, char *exten, int state, void *data, char *cid_num, char *cid_name) {
00258    struct esel_pvt *esel;
00259 
00260    esel = donkeys;
00261         ast_mutex_lock(&listlock);
00262    while (esel) {
00263        esel_queue_extension_state(&esel->queue, context, exten, state, data, cid_num, cid_name);
00264        esel = esel->next;
00265    }
00266         ast_mutex_unlock(&listlock);
00267    return 0;
00268 }
00269 
00270 
00271 static int load_module(void)
00272 {
00273    int res = 0;
00274    const char *cat, *host, *port, *username, *secret, *name;
00275    struct ast_config *cfg;
00276    struct ast_variable *var;
00277    struct esel_pvt *esel = NULL;
00278    struct esel_export *export = NULL;
00279    struct hostent *he;
00280    struct ast_hostent h;
00281 
00282    cfg = ast_config_load("esel.conf");
00283    if (cfg) {
00284        cat = ast_category_browse(cfg, NULL);
00285        while(cat) {
00286       name = cat;
00287       host = ast_variable_retrieve(cfg, cat, "host");
00288       username = ast_variable_retrieve(cfg, cat, "username");
00289       secret = ast_variable_retrieve(cfg, cat, "secret");
00290       port = ast_variable_retrieve(cfg, cat, "port");
00291 
00292       if (name && host && username && secret && port) {
00293           esel = malloc(sizeof(struct esel_pvt));
00294           if (!esel) {
00295          ast_log(LOG_ERROR, "unable to malloc!\n");
00296          return -1;
00297           }
00298           memset(esel, 0x0, sizeof(struct esel_pvt));
00299           ast_copy_string(esel->name, name, sizeof(esel->name));
00300           ast_copy_string(esel->host, host, sizeof(esel->host));
00301           ast_copy_string(esel->username, username, sizeof(esel->username));
00302           ast_copy_string(esel->secret, secret, sizeof(esel->secret));
00303           
00304           esel->port = atoi(port);
00305           if ((he=ast_gethostbyname(host, &h)) == NULL) {
00306          ast_log(LOG_ERROR, "unknown host!\n");
00307          return -1;
00308           }
00309 
00310           esel->raddr.sin_family = AF_INET;
00311           esel->raddr.sin_port = htons(esel->port);
00312           esel->raddr.sin_addr = *((struct in_addr *)he->h_addr);
00313           bzero(&(esel->raddr.sin_zero), 8);  
00314           
00315           esel->connected = 0;
00316 
00317           ast_mutex_init(&esel->queue.lock);
00318           ast_cond_init(&esel->queue.cond, NULL);
00319 
00320 
00321           /* read exports */
00322           var = ast_variable_browse(cfg, cat);
00323           while (var) {
00324          if (!strcasecmp(var->name, "export")) {
00325              char *extenp = NULL, *contextp = NULL, *devstatep = NULL;
00326              extenp = var->value;
00327              devstatep = strchr(var->value, ',') + 1;
00328              contextp = strchr(var->value, '@') + 1;
00329              if (devstatep && contextp) {
00330             export = malloc(sizeof(struct esel_export));
00331             if (!export) {
00332                 ast_log(LOG_ERROR, "unable to malloc!\n");
00333                 return -1;
00334             }
00335             memset(export, 0x0, sizeof(struct esel_export));
00336                ast_copy_string(export->exten, extenp, contextp - extenp);
00337                ast_copy_string(export->context, contextp, devstatep - contextp);
00338                ast_copy_string(export->devstate, devstatep, sizeof(export->devstate));
00339             if (option_verbose > 2)
00340                 ast_verbose(VERBOSE_PREFIX_3 "exporting %s @ %s as %s to %s\n", export->exten, export->context , export->devstate , esel->name);          
00341             export->next = esel->extensions;
00342             esel->extensions = export;
00343             export = NULL;
00344              }
00345          }
00346          var = var->next;
00347           }
00348 
00349 
00350 
00351           esel->next = donkeys;
00352           donkeys = esel;
00353 
00354           ast_pthread_create(&esel->esel_thread, NULL, do_esel_thread, esel);
00355 
00356       }
00357       cat = ast_category_browse(cfg, cat);
00358        }
00359           ast_config_destroy(cfg);
00360    }
00361    ast_extension_state_add(NULL, NULL, esel_state_cb, NULL);
00362    return res;
00363 }
00364 
00365 
00366 static int unload_module(void)
00367 {
00368    struct esel_pvt *esel, *eseln;
00369    ast_module_user_hangup_all();
00370    esel = donkeys;
00371         ast_mutex_lock(&listlock);
00372    while (esel) {
00373        pthread_cancel(esel->esel_thread);
00374        pthread_join(esel->esel_thread, NULL);
00375        ast_mutex_destroy(&esel->queue.lock);
00376        close(esel->sockfd);
00377        eseln = esel->next;
00378        free(esel);
00379        esel = eseln;
00380    }
00381         ast_mutex_unlock(&listlock);
00382    return 0;
00383 }
00384 
00385 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Extension State Export Logic (E.S.E.L.) Resource",
00386       .load = load_module,
00387       .unload = unload_module,
00388 );

Generated on Fri Sep 25 19:28:14 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.5