Mon Mar 20 08:26:03 2006

Asterisk developer's documentation


Main Page | Modules | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

pbx_spool.c File Reference

Full-featured outgoing call spool support. More...

#include <sys/stat.h>
#include <errno.h>
#include <time.h>
#include <utime.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <stdio.h>
#include "asterisk.h"
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/callerid.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/options.h"
#include "asterisk/utils.h"

Go to the source code of this file.

Data Structures

struct  outgoing

Functions

int apply_outgoing (struct outgoing *o, char *fn, FILE *f)
void * attempt_thread (void *data)
char * description (void)
 Provides a description of the module.
void free_outgoing (struct outgoing *o)
void init_outgoing (struct outgoing *o)
char * key ()
 Returns the ASTERISK_GPL_KEY.
void launch_service (struct outgoing *o)
int load_module (void)
 Initialize the module.
void safe_append (struct outgoing *o, time_t now, char *s)
int scan_service (char *fn, time_t now, time_t atime)
void * scan_thread (void *unused)
int unload_module (void)
 Cleanup all module structures, sockets, etc.
int usecount (void)
 Provides a usecount.

Variables

char qdir [255]
char * tdesc = "Outgoing Spool Support"


Detailed Description

Full-featured outgoing call spool support.

Definition in file pbx_spool.c.


Function Documentation

int apply_outgoing struct outgoing o,
char *  fn,
FILE *  f
[static]
 

Definition at line 112 of file pbx_spool.c.

References outgoing::account, outgoing::app, ast_callerid_split(), ast_log(), ast_strlen_zero(), ast_variable_new(), outgoing::callingpid, outgoing::cid_name, outgoing::cid_num, outgoing::context, outgoing::data, outgoing::dest, outgoing::exten, outgoing::fn, LOG_NOTICE, LOG_WARNING, outgoing::maxretries, ast_variable::next, outgoing::priority, outgoing::retries, outgoing::retrytime, strsep(), outgoing::tech, var, outgoing::vars, and outgoing::waittime.

Referenced by scan_service().

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, "%d", &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, "%d", &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, "%d", &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, "%d", &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, "%d", &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 }

void* attempt_thread void *  data  )  [static]
 

Definition at line 255 of file pbx_spool.c.

References outgoing::account, outgoing::app, AST_FORMAT_SLINEAR, ast_log(), ast_pbx_outgoing_app(), ast_pbx_outgoing_exten(), ast_strlen_zero(), ast_verbose(), outgoing::cid_name, outgoing::cid_num, outgoing::context, outgoing::data, outgoing::dest, outgoing::exten, outgoing::fn, free_outgoing(), LOG_EVENT, LOG_NOTICE, outgoing::maxretries, option_verbose, outgoing::priority, outgoing::retries, safe_append(), outgoing::tech, outgoing::vars, VERBOSE_PREFIX_3, and outgoing::waittime.

Referenced by launch_service().

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 }

char* description void   ) 
 

Provides a description of the module.

Returns:
a short description of your module

Definition at line 432 of file pbx_spool.c.

00433 {
00434    return tdesc;
00435 }

void free_outgoing struct outgoing o  )  [static]
 

Definition at line 107 of file pbx_spool.c.

References free.

Referenced by attempt_thread(), launch_service(), and scan_service().

00108 {
00109    free(o);
00110 }

void init_outgoing struct outgoing o  )  [static]
 

Definition at line 99 of file pbx_spool.c.

References outgoing::priority, outgoing::retrytime, and outgoing::waittime.

Referenced by scan_service().

00100 {
00101    memset(o, 0, sizeof(struct outgoing));
00102    o->priority = 1;
00103    o->retrytime = 300;
00104    o->waittime = 45;
00105 }

char* key void   ) 
 

Returns the ASTERISK_GPL_KEY.

This returns the ASTERISK_GPL_KEY, signifiying that you agree to the terms of the GPL stated in the ASTERISK_GPL_KEY. Your module will not load if it does not return the EXACT message:

 char *key(void) {
         return ASTERISK_GPL_KEY;
 }

Returns:
ASTERISK_GPL_KEY

Definition at line 442 of file pbx_spool.c.

00443 {
00444    return ASTERISK_GPL_KEY;
00445 }

void launch_service struct outgoing o  )  [static]
 

Definition at line 287 of file pbx_spool.c.

References ast_log(), ast_pthread_create, attempt_thread(), free_outgoing(), and LOG_WARNING.

Referenced by scan_service().

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 }

int load_module void   ) 
 

Initialize the module.

Initialize the Agents module. This function is being called by Asterisk when loading the module. Among other thing it registers applications, cli commands and reads the cofiguration file.

Returns:
int Always 0.

Definition at line 413 of file pbx_spool.c.

References ast_config_AST_SPOOL_DIR, ast_log(), ast_pthread_create, LOG_WARNING, qdir, and scan_thread().

00414 {
00415    pthread_t thread;
00416    pthread_attr_t attr;
00417    int ret;
00418    snprintf(qdir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing");
00419    if (mkdir(qdir, 0700) && (errno != EEXIST)) {
00420       ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool disabled\n", qdir);
00421       return 0;
00422    }
00423    pthread_attr_init(&attr);
00424    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00425    if ((ret = ast_pthread_create(&thread,&attr,scan_thread, NULL)) != 0) {
00426       ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
00427       return -1;
00428    }
00429    return 0;
00430 }

void safe_append struct outgoing o,
time_t  now,
char *  s
[static]
 

Definition at line 234 of file pbx_spool.c.

References ast_log(), ast_mainpid, outgoing::fn, LOG_WARNING, outgoing::retries, outgoing::retrytime, and s.

Referenced by attempt_thread(), and scan_service().

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, "%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 }

int scan_service char *  fn,
time_t  now,
time_t  atime
[static]
 

Definition at line 300 of file pbx_spool.c.

References apply_outgoing(), ast_log(), outgoing::callingpid, outgoing::dest, outgoing::fn, free_outgoing(), init_outgoing(), launch_service(), LOG_DEBUG, LOG_EVENT, LOG_WARNING, malloc, outgoing::maxretries, outgoing::retries, outgoing::retrytime, safe_append(), and outgoing::tech.

Referenced by scan_thread().

00301 {
00302    struct outgoing *o;
00303    FILE *f;
00304    o = malloc(sizeof(struct outgoing));
00305    if (o) {
00306       init_outgoing(o);
00307       f = fopen(fn, "r+");
00308       if (f) {
00309          if (!apply_outgoing(o, fn, f)) {
00310 #if 0
00311             printf("Filename: %s, Retries: %d, max: %d\n", fn, o->retries, o->maxretries);
00312 #endif
00313             fclose(f);
00314             if (o->retries <= o->maxretries) {
00315                now += o->retrytime;
00316                if (o->callingpid && (o->callingpid == ast_mainpid)) {
00317                   safe_append(o, time(NULL), "DelayedRetry");
00318                   free_outgoing(o);
00319                   ast_log(LOG_DEBUG, "Delaying retry since we're currently running '%s'\n", o->fn);
00320                } else {
00321                   /* Increment retries */
00322                   o->retries++;
00323                   /* If someone else was calling, they're presumably gone now
00324                      so abort their retry and continue as we were... */
00325                   if (o->callingpid)
00326                      safe_append(o, time(NULL), "AbortRetry");
00327 
00328                   safe_append(o, now, "StartRetry");
00329                   launch_service(o);
00330                }
00331                return now;
00332             } else {
00333                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" : "");
00334                free_outgoing(o);
00335                unlink(fn);
00336                return 0;
00337             }
00338          } else {
00339             free_outgoing(o);
00340             ast_log(LOG_WARNING, "Invalid file contents in %s, deleting\n", fn);
00341             fclose(f);
00342             unlink(fn);
00343          }
00344       } else {
00345          free_outgoing(o);
00346          ast_log(LOG_WARNING, "Unable to open %s: %s, deleting\n", fn, strerror(errno));
00347          unlink(fn);
00348       }
00349    } else
00350       ast_log(LOG_WARNING, "Out of memory :(\n");
00351    return -1;
00352 }

void* scan_thread void *  unused  )  [static]
 

Definition at line 354 of file pbx_spool.c.

References ast_log(), LOG_WARNING, qdir, and scan_service().

Referenced by load_module().

00355 {
00356    struct stat st;
00357    DIR *dir;
00358    struct dirent *de;
00359    char fn[256];
00360    int res;
00361    time_t last = 0, next = 0, now;
00362    for(;;) {
00363       /* Wait a sec */
00364       sleep(1);
00365       time(&now);
00366       if (!stat(qdir, &st)) {
00367          if ((st.st_mtime != last) || (next && (now > next))) {
00368 #if 0
00369             printf("atime: %ld, mtime: %ld, ctime: %ld\n", st.st_atime, st.st_mtime, st.st_ctime);
00370             printf("Ooh, something changed / timeout\n");
00371 #endif            
00372             next = 0;
00373             last = st.st_mtime;
00374             dir = opendir(qdir);
00375             if (dir) {
00376                while((de = readdir(dir))) {
00377                   snprintf(fn, sizeof(fn), "%s/%s", qdir, de->d_name);
00378                   if (!stat(fn, &st)) {
00379                      if (S_ISREG(st.st_mode)) {
00380                         if (st.st_mtime <= now) {
00381                            res = scan_service(fn, now, st.st_atime);
00382                            if (res > 0) {
00383                               /* Update next service time */
00384                               if (!next || (res < next)) {
00385                                  next = res;
00386                               }
00387                            } else if (res)
00388                               ast_log(LOG_WARNING, "Failed to scan service '%s'\n", fn);
00389                         } else {
00390                            /* Update "next" update if necessary */
00391                            if (!next || (st.st_mtime < next))
00392                               next = st.st_mtime;
00393                         }
00394                      }
00395                   } else
00396                      ast_log(LOG_WARNING, "Unable to stat %s: %s\n", fn, strerror(errno));
00397                }
00398                closedir(dir);
00399             } else
00400                ast_log(LOG_WARNING, "Unable to open directory %s: %s\n", qdir, strerror(errno));
00401          }
00402       } else
00403          ast_log(LOG_WARNING, "Unable to stat %s\n", qdir);
00404    }
00405    return NULL;
00406 }

int unload_module void   ) 
 

Cleanup all module structures, sockets, etc.

Standard module functions ...

Definition at line 408 of file pbx_spool.c.

00409 {
00410    return -1;
00411 }

int usecount void   ) 
 

Provides a usecount.

This function will be called by various parts of asterisk. Basically, all it has to do is to return a usecount when called. You will need to maintain your usecount within the module somewhere. The usecount should be how many channels provided by this module are in use.

Returns:
The module's usecount.

Definition at line 437 of file pbx_spool.c.

00438 {
00439    return 1;
00440 }


Variable Documentation

char qdir[255] [static]
 

Definition at line 57 of file pbx_spool.c.

Referenced by load_module(), and scan_thread().

char* tdesc = "Outgoing Spool Support" [static]
 

Definition at line 56 of file pbx_spool.c.


Generated on Mon Mar 20 08:26:03 2006 for Asterisk - the Open Source PBX by  doxygen 1.3.9.1