diff options
-rw-r--r-- | doc/callfiles.txt | 139 | ||||
-rw-r--r-- | pbx/pbx_spool.c | 74 | ||||
-rw-r--r-- | sample.call | 9 |
3 files changed, 209 insertions, 13 deletions
diff --git a/doc/callfiles.txt b/doc/callfiles.txt new file mode 100644 index 000000000..19dd97b37 --- /dev/null +++ b/doc/callfiles.txt @@ -0,0 +1,139 @@ +Asterisk call files +=================== + +Asterisk has the ability to initiate a call from outside of the normal +methods such as the dialplan, manager interface, or spooling interface. + +Using the call file method, you must give Asterisk the following information: + +* How to perform the call, similar to the Dial() application +* What to do when the call is answered + +With call files you submit this information simply by creating a file with +the required syntax and placing it in the outgoing spooling directory, located +by default in /var/spool/asterisk/outgoing/ (configurable in asterisk.conf). + +The pbx_spool module aggressively examines the directory contents every second, +creating a new call for every call file it finds. Do NOT write or create +the call file directly in the outgoing directory, but always create the file +in another directory of the same filesystem and then move the file to the +/var/spool/asterisk/outgoing directory, or Asterisk may read just a partial +file. + + +The call file syntax +==================== + +The call file consists of <Key>: <value> pairs; one per line. + +Comments are indicated by a '#' character that begins a line, or follows a space +or tab character. To be consistant with the configuration files in Asterisk, +comments can also be indicated by a semicolon. However, the multiline comments +(;-- --;) used in Asterisk configuration files are not supported. Semicolons can +be escaped by a backslash. + + +The following keys-value pairs are used to specify how setup a call: + +Channel: <channel> the channel to use for the new call, in the form + technology/resource as in the Dial application. This + value is required. + +Callerid: <callerid> the caller id to use. + +WaitTime: <number> how many seconds to wait for an answer before the call + fails (ring cycle). Default 45 seconds. + +Maxretries: <number> number of retries before failing, not including the + initial attempt. Default = 0 e.g. don't retry if fails. + +RetryTime: <number> how many seconds to wait before retry. The default is + 300 (5 minutes). + +Account: <account> the account code for the call. This value will be + assigned to CDR(accountcode) + + + +When the call answers there are two choices: +* Execute a single application, or +* Execute the dialplan at the specified context/extension/priority. + + +To execute an application: +-------------------------- + +Application: <appname> the application to execute + +Data: <args> the application arguments + + +To start executing applications in the dialplan: +------------------------------------------------ + +Context: <context> the context in the dialplan + +Extension: <exten> the extension in the specified context + +Priority: <priority> the priority of the specified extension + (numeric or label) + + + +Setvar: <var=value> you may also assign values to variables that will be + available to the channel, as if you had performed a + Set(var=value) in the dialplan. More than one Setvar: + maybe specified. + + +The processing of the call file ends when the call is answered and terminated; when +the call was not answered in the initial attempt and subsequent retries; or if +the call file can't be successfully read and parsed. + +To specify what to do with the call file at the end of processing: + +Archive: <yes|no> if "no" the call file is deleted. If set to "yes" the + call file is moved to the "outgoing_done" subdirectory + of the Asterisk spool directory. The default is to + delete the call file. + + +If the call file is archived, Asterisk will append to the call file: + +Status: <exitstatus> can be "Expired", "Completed" or "Failed" + + + +Other lines generated by Asterisk: + +Asterisk keep track of how many retries the call has already attempted, +appending to the call file the following key-pairs in the form: + +StartRetry: <pid> <retrycount> (<time>) +EndRetry: <pid> <retrycount> (<time>) + +With the main process ID (pid) of the Asterisk process, the retry number, and +the attempts start and end times in time_t format. + + + +Directory locations +=================== + +<astspooldir>/outgoing the outgoing dir, where call files are put + for processing + +<astspooldir>/outgoing_done the archive dir + + +<astspooldir> is specified in asterisk.conf, usually /var/spool/asterisk + + + +How to schedule a call +====================== + +Call files that have the time of the last modification in the future are ignored +by Asterisk. This makes it possible to modify the time of a call file to the +wanted time, move to the outgoing directory, and Asterisk will attempt to +create the call at that time. diff --git a/pbx/pbx_spool.c b/pbx/pbx_spool.c index 3bab6c1f5..c04180a76 100644 --- a/pbx/pbx_spool.c +++ b/pbx/pbx_spool.c @@ -58,10 +58,13 @@ enum { * maximum number of retries is exceeded, even if the * modification time of the call file is in the future. */ - SPOOL_FLAG_ALWAYS_DELETE = (1 << 0) + SPOOL_FLAG_ALWAYS_DELETE = (1 << 0), + /* Don't unlink the call file after processing, move in qdonedir */ + SPOOL_FLAG_ARCHIVE = (1 << 1) }; static char qdir[255]; +static char qdonedir[255]; struct outgoing { char fn[256]; @@ -228,6 +231,8 @@ static int apply_outgoing(struct outgoing *o, char *fn, FILE *f) ast_copy_string(o->account, c, sizeof(o->account)); } else if (!strcasecmp(buf, "alwaysdelete")) { ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ALWAYS_DELETE); + } else if (!strcasecmp(buf, "archive")) { + ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ARCHIVE); } else { ast_log(LOG_WARNING, "Unknown keyword '%s' at line %d of %s\n", buf, lineno, fn); } @@ -264,17 +269,59 @@ static void safe_append(struct outgoing *o, time_t now, char *s) } } -static void check_unlink(struct outgoing *o) +/*! + * \brief Remove a call file from the outgoing queue optionally moving it in the archive dir + * + * \param o the pointer to outgoing struct + * \param status the exit status of the call. Can be "Completed", "Failed" or "Expired" + */ +static int remove_from_queue(struct outgoing *o, const char *status) { - if (ast_test_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE)) - unlink(o->fn); - else { + int fd; + FILE *f; + char newfn[256]; + const char *bname; + + if (!ast_test_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE)) { struct stat current_file_status; - + if (!stat(o->fn, ¤t_file_status)) - if (time(NULL) >= current_file_status.st_mtime) - unlink(o->fn); + if (time(NULL) < current_file_status.st_mtime) + return 0; + } + + if (!ast_test_flag(&o->options, SPOOL_FLAG_ARCHIVE)) { + unlink(o->fn); + return 0; + } + if (mkdir(qdonedir, 0700) && (errno != EEXIST)) { + ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool archiving disabled\n", qdonedir); + unlink(o->fn); + return -1; + } + fd = open(o->fn, O_WRONLY|O_APPEND); + if (fd > -1) { + f = fdopen(fd, "a"); + if (f) { + fprintf(f, "Status: %s\n", status); + fclose(f); + } else + close(fd); } + + bname = strrchr(o->fn,'/'); + if (bname == NULL) + bname = o->fn; + else + bname++; + snprintf(newfn, sizeof(newfn), "%s/%s", qdonedir, bname); + /* a existing call file the archive dir is overwritten */ + unlink(newfn); + if (rename(o->fn, newfn) != 0) { + unlink(o->fn); + return -1; + } else + return 0; } static void *attempt_thread(void *data) @@ -295,7 +342,7 @@ static void *attempt_thread(void *data) if (o->retries >= o->maxretries + 1) { /* Max retries exceeded */ 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" : ""); - check_unlink(o); + remove_from_queue(o, "Expired"); } else { /* Notate that the call is still active */ safe_append(o, time(NULL), "EndRetry"); @@ -303,7 +350,7 @@ static void *attempt_thread(void *data) } else { ast_log(LOG_NOTICE, "Call completed to %s/%s\n", o->tech, o->dest); ast_log(LOG_EVENT, "Queued call to %s/%s completed\n", o->tech, o->dest); - check_unlink(o); + remove_from_queue(o, "Completed"); } free_outgoing(o); return NULL; @@ -357,19 +404,19 @@ static int scan_service(char *fn, time_t now, time_t atime) } else { 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" : ""); free_outgoing(o); - unlink(fn); + remove_from_queue(o, "Expired"); return 0; } } else { free_outgoing(o); ast_log(LOG_WARNING, "Invalid file contents in %s, deleting\n", fn); fclose(f); - unlink(fn); + remove_from_queue(o, "Failed"); } } else { free_outgoing(o); ast_log(LOG_WARNING, "Unable to open %s: %s, deleting\n", fn, strerror(errno)); - unlink(fn); + remove_from_queue(o, "Failed"); } } else ast_log(LOG_WARNING, "Out of memory :(\n"); @@ -445,6 +492,7 @@ static int load_module(void *mod) ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool disabled\n", qdir); return 0; } + snprintf(qdonedir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing_done"); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if ((ret = ast_pthread_create(&thread,&attr,scan_thread, NULL)) != 0) { diff --git a/sample.call b/sample.call index b0a13e7f9..6549d7914 100644 --- a/sample.call +++ b/sample.call @@ -69,3 +69,12 @@ Priority: 1 #Set: file2=/tmp/msg #Set: timestamp=20021023104500 #Set: CDR(userfield|r)=42 + +# +# Setting Archive to yes the call file is never deleted, but is moved +# in the subdir "outgoing_done" of the spool directory. In this case +# will be appended a line with "Status: value", where value can be +# Completed, Expired or Failed. +# +#Archive: yes + |