Asterisk.org: Developer Documentation (28 Oct 2009)

Wed Oct 28 15:49:12 2009

## Asterisk developer's documentation

# 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"`

Include dependency graph for pbx_spool.c:

Go to the source code of this file.

## Data Structures

struct | outgoing

## Functions

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

## Variables

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

## Detailed Description

Full-featured outgoing call spool support.

Definition in file pbx_spool.c.

## Function Documentation

static 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, ast_variable::lineno, 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, "%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 }

static 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 434 of file pbx_spool.c.

    00435 {
    00436    return tdesc;
    00437 }

static 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 }

static 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 444 of file pbx_spool.c.

References ASTERISK_GPL_KEY.

    00445 {
    00446    return ASTERISK_GPL_KEY;
    00447 }

static 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(), LOG_WARNING, and t.

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    pthread_attr_destroy(&attr);
    00299 }

int load_module | ( | void |  | ) |

Initialize the module.

This function is called at module load time. Put all code in here that needs to set up your module's hardware, software, registrations, etc.

Returns:
  This function should return 0 on success and non-zero on failure. If the module is not loaded successfully, Asterisk will call its unload_module() function.

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.

TE STUFF END

Definition at line 414 of file pbx_spool.c.

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

    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 }

static 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, and outgoing::retrytime.

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

static int scan_service | ( | char * | fn,
|  | time_t | now,
|  | time_t | atime |
| ) |  |  | `[static]`

Definition at line 301 of file pbx_spool.c.

References apply_outgoing(), ast_log(), ast_mainpid, 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().

    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 }

static void* scan_thread | ( | void * | unused | ) | `[static]`

Definition at line 355 of file pbx_spool.c.

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

Referenced by load_module().

    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 }

int unload_module | ( | void |  | ) |

Cleanup all module structures, sockets, etc.

This is called at exit. Any registrations and memory allocations need to be unregistered and free'd here. Nothing else will do these for you (until exit).

Returns:
  Zero on success, or non-zero on error.

Standard module functions ...

Definition at line 409 of file pbx_spool.c.

    00410 {
    00411    return -1;
    00412 }

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 439 of file pbx_spool.c.

    00440 {
    00441    return 1;
    00442 }

## Variable Documentation

char qdir[255]`[static]`

Definition at line 57 of file pbx_spool.c.

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

Definition at line 56 of file pbx_spool.c.