Wed Oct 28 15:47:55 2009

Asterisk developer's documentation


pbx_spool.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Full-featured outgoing call spool support
00022  * 
00023  */
00024 
00025 #include <sys/stat.h>
00026 #include <errno.h>
00027 #include <time.h>
00028 #include <utime.h>
00029 #include <stdlib.h>
00030 #include <unistd.h>
00031 #include <dirent.h>
00032 #include <string.h>
00033 #include <string.h>
00034 #include <stdio.h>
00035 #include <unistd.h>
00036 
00037 #include "asterisk.h"
00038 
00039 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 211526 $")
00040 
00041 #include "asterisk/lock.h"
00042 #include "asterisk/file.h"
00043 #include "asterisk/logger.h"
00044 #include "asterisk/channel.h"
00045 #include "asterisk/callerid.h"
00046 #include "asterisk/pbx.h"
00047 #include "asterisk/module.h"
00048 #include "asterisk/options.h"
00049 #include "asterisk/utils.h"
00050 
00051 /*
00052  * pbx_spool is similar in spirit to qcall, but with substantially enhanced functionality...
00053  * The spool file contains a header 
00054  */
00055 
00056 static char *tdesc = "Outgoing Spool Support";
00057 static char qdir[255];
00058 
00059 struct outgoing {
00060    char fn[256];
00061    /* Current number of retries */
00062    int retries;
00063    /* Maximum number of retries permitted */
00064    int maxretries;
00065    /* How long to wait between retries (in seconds) */
00066    int retrytime;
00067    /* How long to wait for an answer */
00068    int waittime;
00069    /* PID which is currently calling */
00070    int callingpid;
00071    
00072    /* What to connect to outgoing */
00073    char tech[256];
00074    char dest[256];
00075    
00076    /* If application */
00077    char app[256];
00078    char data[256];
00079 
00080    /* If extension/context/priority */
00081    char exten[256];
00082    char context[256];
00083    int priority;
00084 
00085    /* CallerID Information */
00086    char cid_num[256];
00087    char cid_name[256];
00088 
00089    /* account code */
00090    char account[AST_MAX_ACCOUNT_CODE];
00091 
00092    /* Variables and Functions */
00093    struct ast_variable *vars;
00094    
00095    /* Maximum length of call */
00096    int maxlen;
00097 };
00098 
00099 static void init_outgoing(struct outgoing *o)
00100 {
00101    memset(o, 0, sizeof(struct outgoing));
00102    o->priority = 1;
00103    o->retrytime = 300;
00104    o->waittime = 45;
00105 }
00106 
00107 static void free_outgoing(struct outgoing *o)
00108 {
00109    free(o);
00110 }
00111 
00112 static int apply_outgoing(struct outgoing *o, char *fn, FILE *f)
00113 {
00114    char buf[256];
00115    char *c, *c2;
00116    int lineno = 0;
00117    struct ast_variable *var;
00118 
00119    while(fgets(buf, sizeof(buf), f)) {
00120       lineno++;
00121       /* Trim comments */
00122       c = buf;
00123       while ((c = strchr(c, '#'))) {
00124          if ((c == buf) || (*(c-1) == ' ') || (*(c-1) == '\t'))
00125             *c = '\0';
00126          else
00127             c++;
00128       }
00129 
00130       c = buf;
00131       while ((c = strchr(c, ';'))) {
00132          if ((c > buf) && (c[-1] == '\\')) {
00133             memmove(c - 1, c, strlen(c) + 1);
00134             c++;
00135          } else {
00136             *c = '\0';
00137             break;
00138          }
00139       }
00140 
00141       /* Trim trailing white space */
00142       while(!ast_strlen_zero(buf) && buf[strlen(buf) - 1] < 33)
00143          buf[strlen(buf) - 1] = '\0';
00144       if (!ast_strlen_zero(buf)) {
00145          c = strchr(buf, ':');
00146          if (c) {
00147             *c = '\0';
00148             c++;
00149             while ((*c) && (*c < 33))
00150                c++;
00151 #if 0
00152             printf("'%s' is '%s' at line %d\n", buf, c, lineno);
00153 #endif
00154             if (!strcasecmp(buf, "channel")) {
00155                strncpy(o->tech, c, sizeof(o->tech) - 1);
00156                if ((c2 = strchr(o->tech, '/'))) {
00157                   *c2 = '\0';
00158                   c2++;
00159                   strncpy(o->dest, c2, sizeof(o->dest) - 1);
00160                } else {
00161                   ast_log(LOG_NOTICE, "Channel should be in form Tech/Dest at line %d of %s\n", lineno, fn);
00162                   o->tech[0] = '\0';
00163                }
00164             } else if (!strcasecmp(buf, "callerid")) {
00165                ast_callerid_split(c, o->cid_name, sizeof(o->cid_name), o->cid_num, sizeof(o->cid_num));
00166             } else if (!strcasecmp(buf, "application")) {
00167                strncpy(o->app, c, sizeof(o->app) - 1);
00168             } else if (!strcasecmp(buf, "data")) {
00169                strncpy(o->data, c, sizeof(o->data) - 1);
00170             } else if (!strcasecmp(buf, "maxretries")) {
00171                if (sscanf(c, "%30d", &o->maxretries) != 1) {
00172                   ast_log(LOG_WARNING, "Invalid max retries at line %d of %s\n", lineno, fn);
00173                   o->maxretries = 0;
00174                }
00175             } else if (!strcasecmp(buf, "context")) {
00176                strncpy(o->context, c, sizeof(o->context) - 1);
00177             } else if (!strcasecmp(buf, "extension")) {
00178                strncpy(o->exten, c, sizeof(o->exten) - 1);
00179             } else if (!strcasecmp(buf, "priority")) {
00180                if ((sscanf(c, "%30d", &o->priority) != 1) || (o->priority < 1)) {
00181                   ast_log(LOG_WARNING, "Invalid priority at line %d of %s\n", lineno, fn);
00182                   o->priority = 1;
00183                }
00184             } else if (!strcasecmp(buf, "retrytime")) {
00185                if ((sscanf(c, "%30d", &o->retrytime) != 1) || (o->retrytime < 1)) {
00186                   ast_log(LOG_WARNING, "Invalid retrytime at line %d of %s\n", lineno, fn);
00187                   o->retrytime = 300;
00188                }
00189             } else if (!strcasecmp(buf, "waittime")) {
00190                if ((sscanf(c, "%30d", &o->waittime) != 1) || (o->waittime < 1)) {
00191                   ast_log(LOG_WARNING, "Invalid retrytime at line %d of %s\n", lineno, fn);
00192                   o->waittime = 45;
00193                }
00194             } else if (!strcasecmp(buf, "retry")) {
00195                o->retries++;
00196             } else if (!strcasecmp(buf, "startretry")) {
00197                if (sscanf(c, "%30d", &o->callingpid) != 1) {
00198                   ast_log(LOG_WARNING, "Unable to retrieve calling PID!\n");
00199                   o->callingpid = 0;
00200                }
00201             } else if (!strcasecmp(buf, "endretry") || !strcasecmp(buf, "abortretry")) {
00202                o->callingpid = 0;
00203                o->retries++;
00204             } else if (!strcasecmp(buf, "delayedretry")) {
00205             } else if (!strcasecmp(buf, "setvar") || !strcasecmp(buf, "set")) {
00206                c2 = c;
00207                strsep(&c2, "=");
00208                if (c2) {
00209                   var = ast_variable_new(c, c2);
00210                   if (var) {
00211                      var->next = o->vars;
00212                      o->vars = var;
00213                   }
00214                } else {
00215                   ast_log(LOG_WARNING, "Malformed Set: argument! Should be Set: Variable=value\n");
00216                }
00217             } else if (!strcasecmp(buf, "account")) {
00218                ast_copy_string(o->account, c, sizeof(o->account));
00219             } else {
00220                ast_log(LOG_WARNING, "Unknown keyword '%s' at line %d of %s\n", buf, lineno, fn);
00221             }
00222          } else
00223             ast_log(LOG_NOTICE, "Syntax error at line %d of %s\n", lineno, fn);
00224       }
00225    }
00226    strncpy(o->fn, fn, sizeof(o->fn) - 1);
00227    if (ast_strlen_zero(o->tech) || ast_strlen_zero(o->dest) || (ast_strlen_zero(o->app) && ast_strlen_zero(o->exten))) {
00228       ast_log(LOG_WARNING, "At least one of app or extension must be specified, along with tech and dest in file %s\n", fn);
00229       return -1;
00230    }
00231    return 0;
00232 }
00233 
00234 static void safe_append(struct outgoing *o, time_t now, char *s)
00235 {
00236    int fd;
00237    FILE *f;
00238    struct utimbuf tbuf;
00239    fd = open(o->fn, O_WRONLY|O_APPEND);
00240    if (fd > -1) {
00241       f = fdopen(fd, "a");
00242       if (f) {
00243          fprintf(f, "\n%s: %ld %d (%ld)\n", s, (long)ast_mainpid, o->retries, (long) now);
00244          fclose(f);
00245       } else
00246          close(fd);
00247       /* Update the file time */
00248       tbuf.actime = now;
00249       tbuf.modtime = now + o->retrytime;
00250       if (utime(o->fn, &tbuf))
00251          ast_log(LOG_WARNING, "Unable to set utime on %s: %s\n", o->fn, strerror(errno));
00252    }
00253 }
00254 
00255 static void *attempt_thread(void *data)
00256 {
00257    struct outgoing *o = data;
00258    int res, reason;
00259    if (!ast_strlen_zero(o->app)) {
00260       if (option_verbose > 2)
00261          ast_verbose(VERBOSE_PREFIX_3 "Attempting call on %s/%s for application %s(%s) (Retry %d)\n", o->tech, o->dest, o->app, o->data, o->retries);
00262       res = ast_pbx_outgoing_app(o->tech, AST_FORMAT_SLINEAR, o->dest, o->waittime * 1000, o->app, o->data, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL);
00263    } else {
00264       if (option_verbose > 2)
00265          ast_verbose(VERBOSE_PREFIX_3 "Attempting call on %s/%s for %s@%s:%d (Retry %d)\n", o->tech, o->dest, o->exten, o->context,o->priority, o->retries);
00266       res = ast_pbx_outgoing_exten(o->tech, AST_FORMAT_SLINEAR, o->dest, o->waittime * 1000, o->context, o->exten, o->priority, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL);
00267    }
00268    if (res) {
00269       ast_log(LOG_NOTICE, "Call failed to go through, reason %d\n", reason);
00270       if (o->retries >= o->maxretries + 1) {
00271          /* Max retries exceeded */
00272          ast_log(LOG_EVENT, "Queued call to %s/%s expired without completion after %d attempt%s\n", o->tech, o->dest, o->retries - 1, ((o->retries - 1) != 1) ? "s" : "");
00273          unlink(o->fn);
00274       } else {
00275          /* Notate that the call is still active */
00276          safe_append(o, time(NULL), "EndRetry");
00277       }
00278    } else {
00279       ast_log(LOG_NOTICE, "Call completed to %s/%s\n", o->tech, o->dest);
00280       ast_log(LOG_EVENT, "Queued call to %s/%s completed\n", o->tech, o->dest);
00281       unlink(o->fn);
00282    }
00283    free_outgoing(o);
00284    return NULL;
00285 }
00286 
00287 static void launch_service(struct outgoing *o)
00288 {
00289    pthread_t t;
00290    pthread_attr_t attr;
00291    int ret;
00292    pthread_attr_init(&attr);
00293    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00294    if ((ret = ast_pthread_create(&t,&attr,attempt_thread, o)) != 0) {
00295       ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
00296       free_outgoing(o);
00297    }
00298    pthread_attr_destroy(&attr);
00299 }
00300 
00301 static int scan_service(char *fn, time_t now, time_t atime)
00302 {
00303    struct outgoing *o;
00304    FILE *f;
00305    o = malloc(sizeof(struct outgoing));
00306    if (o) {
00307       init_outgoing(o);
00308       f = fopen(fn, "r+");
00309       if (f) {
00310          if (!apply_outgoing(o, fn, f)) {
00311 #if 0
00312             printf("Filename: %s, Retries: %d, max: %d\n", fn, o->retries, o->maxretries);
00313 #endif
00314             fclose(f);
00315             if (o->retries <= o->maxretries) {
00316                now += o->retrytime;
00317                if (o->callingpid && (o->callingpid == ast_mainpid)) {
00318                   safe_append(o, time(NULL), "DelayedRetry");
00319                   ast_log(LOG_DEBUG, "Delaying retry since we're currently running '%s'\n", o->fn);
00320                   free_outgoing(o);
00321                } else {
00322                   /* Increment retries */
00323                   o->retries++;
00324                   /* If someone else was calling, they're presumably gone now
00325                      so abort their retry and continue as we were... */
00326                   if (o->callingpid)
00327                      safe_append(o, time(NULL), "AbortRetry");
00328 
00329                   safe_append(o, now, "StartRetry");
00330                   launch_service(o);
00331                }
00332                return now;
00333             } else {
00334                ast_log(LOG_EVENT, "Queued call to %s/%s expired without completion after %d attempt%s\n", o->tech, o->dest, o->retries - 1, ((o->retries - 1) != 1) ? "s" : "");
00335                free_outgoing(o);
00336                unlink(fn);
00337                return 0;
00338             }
00339          } else {
00340             free_outgoing(o);
00341             ast_log(LOG_WARNING, "Invalid file contents in %s, deleting\n", fn);
00342             fclose(f);
00343             unlink(fn);
00344          }
00345       } else {
00346          free_outgoing(o);
00347          ast_log(LOG_WARNING, "Unable to open %s: %s, deleting\n", fn, strerror(errno));
00348          unlink(fn);
00349       }
00350    } else
00351       ast_log(LOG_WARNING, "Out of memory :(\n");
00352    return -1;
00353 }
00354 
00355 static void *scan_thread(void *unused)
00356 {
00357    struct stat st;
00358    DIR *dir;
00359    struct dirent *de;
00360    char fn[256];
00361    int res;
00362    time_t last = 0, next = 0, now;
00363    for(;;) {
00364       /* Wait a sec */
00365       sleep(1);
00366       time(&now);
00367       if (!stat(qdir, &st)) {
00368          if ((st.st_mtime != last) || (next && (now > next))) {
00369 #if 0
00370             printf("atime: %ld, mtime: %ld, ctime: %ld\n", st.st_atime, st.st_mtime, st.st_ctime);
00371             printf("Ooh, something changed / timeout\n");
00372 #endif            
00373             next = 0;
00374             last = st.st_mtime;
00375             dir = opendir(qdir);
00376             if (dir) {
00377                while((de = readdir(dir))) {
00378                   snprintf(fn, sizeof(fn), "%s/%s", qdir, de->d_name);
00379                   if (!stat(fn, &st)) {
00380                      if (S_ISREG(st.st_mode)) {
00381                         if (st.st_mtime <= now) {
00382                            res = scan_service(fn, now, st.st_atime);
00383                            if (res > 0) {
00384                               /* Update next service time */
00385                               if (!next || (res < next)) {
00386                                  next = res;
00387                               }
00388                            } else if (res)
00389                               ast_log(LOG_WARNING, "Failed to scan service '%s'\n", fn);
00390                         } else {
00391                            /* Update "next" update if necessary */
00392                            if (!next || (st.st_mtime < next))
00393                               next = st.st_mtime;
00394                         }
00395                      }
00396                   } else
00397                      ast_log(LOG_WARNING, "Unable to stat %s: %s\n", fn, strerror(errno));
00398                }
00399                closedir(dir);
00400             } else
00401                ast_log(LOG_WARNING, "Unable to open directory %s: %s\n", qdir, strerror(errno));
00402          }
00403       } else
00404          ast_log(LOG_WARNING, "Unable to stat %s\n", qdir);
00405    }
00406    return NULL;
00407 }
00408 
00409 int unload_module(void)
00410 {
00411    return -1;
00412 }
00413 
00414 int load_module(void)
00415 {
00416    pthread_t thread;
00417    pthread_attr_t attr;
00418    int ret;
00419    snprintf(qdir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing");
00420    if (mkdir(qdir, 0700) && (errno != EEXIST)) {
00421       ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool disabled\n", qdir);
00422       return 0;
00423    }
00424    pthread_attr_init(&attr);
00425    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00426    if ((ret = ast_pthread_create(&thread,&attr,scan_thread, NULL)) != 0) {
00427       ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
00428       return -1;
00429    }
00430    pthread_attr_destroy(&attr);
00431    return 0;
00432 }
00433 
00434 char *description(void)
00435 {
00436    return tdesc;
00437 }
00438 
00439 int usecount(void)
00440 {
00441    return 1;
00442 }
00443 
00444 char *key()
00445 {
00446    return ASTERISK_GPL_KEY;
00447 }

Generated on Wed Oct 28 15:47:55 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6