2003-02-04 15:48:42 +00:00
/*
2005-09-14 20:46:50 +00:00
* Asterisk - - An open source telephony toolkit .
2003-02-04 15:48:42 +00:00
*
2010-03-27 06:09:26 +00:00
* Copyright ( C ) 1999 - 2010 , Digium , Inc .
2003-02-04 15:48:42 +00:00
*
* Mark Spencer < markster @ digium . com >
*
2005-09-14 20:46:50 +00:00
* See http : //www.asterisk.org for more information about
* the Asterisk project . Please do not directly contact
* any of the maintainers of this project for assistance ;
* the project provides a web site , mailing lists and IRC
* channels for your use .
*
2003-02-04 15:48:42 +00:00
* This program is free software , distributed under the terms of
2005-09-14 20:46:50 +00:00
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree .
*/
2005-10-24 20:12:06 +00:00
/*! \file
2005-09-14 20:46:50 +00:00
*
2005-10-24 20:12:06 +00:00
* \ brief Full - featured outgoing call spool support
2005-09-14 20:46:50 +00:00
*
2003-02-04 15:48:42 +00:00
*/
2006-06-07 18:54:56 +00:00
# include "asterisk.h"
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
2003-02-04 15:48:42 +00:00
# include <sys/stat.h>
# include <time.h>
# include <utime.h>
# include <dirent.h>
2010-03-27 06:09:26 +00:00
# ifdef HAVE_INOTIFY
# include <sys/inotify.h>
2010-05-13 15:33:49 +00:00
# elif defined(HAVE_KQUEUE)
2010-05-13 05:37:31 +00:00
# include <sys/types.h>
# include <sys/time.h>
# include <sys/event.h>
# include <fcntl.h>
2010-03-27 06:09:26 +00:00
# endif
2005-06-06 22:12:19 +00:00
2007-11-20 23:16:15 +00:00
# include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
2005-06-06 22:12:19 +00:00
# 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/utils.h"
2008-12-03 17:01:03 +00:00
# include "asterisk/options.h"
2005-06-06 22:12:19 +00:00
2003-02-04 15:48:42 +00:00
/*
* pbx_spool is similar in spirit to qcall , but with substantially enhanced functionality . . .
* The spool file contains a header
*/
2006-05-19 22:51:44 +00:00
enum {
/*! Always delete the call file after a call succeeds or the
* maximum number of retries is exceeded , even if the
* modification time of the call file is in the future .
*/
2006-05-25 17:58:55 +00:00
SPOOL_FLAG_ALWAYS_DELETE = ( 1 < < 0 ) ,
/* Don't unlink the call file after processing, move in qdonedir */
SPOOL_FLAG_ARCHIVE = ( 1 < < 1 )
2006-05-19 22:51:44 +00:00
} ;
2003-02-04 15:48:42 +00:00
static char qdir [ 255 ] ;
2006-05-25 17:58:55 +00:00
static char qdonedir [ 255 ] ;
2003-02-04 15:48:42 +00:00
struct outgoing {
2008-05-14 08:16:25 +00:00
int retries ; /*!< Current number of retries */
int maxretries ; /*!< Maximum number of retries permitted */
int retrytime ; /*!< How long to wait between retries (in seconds) */
int waittime ; /*!< How long to wait for an answer */
long callingpid ; /*!< PID which is currently calling */
2009-11-04 14:05:12 +00:00
format_t format ; /*!< Formats (codecs) for this call */
2009-03-03 17:03:47 +00:00
AST_DECLARE_STRING_FIELDS (
AST_STRING_FIELD ( fn ) ; /*!< File name of call file */
AST_STRING_FIELD ( tech ) ; /*!< Which channel technology to use for outgoing call */
AST_STRING_FIELD ( dest ) ; /*!< Which device/line to use for outgoing call */
AST_STRING_FIELD ( app ) ; /*!< If application: Application name */
AST_STRING_FIELD ( data ) ; /*!< If application: Application data */
AST_STRING_FIELD ( exten ) ; /*!< If extension/context/priority: Extension in dialplan */
AST_STRING_FIELD ( context ) ; /*!< If extension/context/priority: Dialplan context */
AST_STRING_FIELD ( cid_num ) ; /*!< CallerID Information: Number/extension */
AST_STRING_FIELD ( cid_name ) ; /*!< CallerID Information: Name */
AST_STRING_FIELD ( account ) ; /*!< account code */
) ;
2008-05-14 08:16:25 +00:00
int priority ; /*!< If extension/context/priority: Dialplan priority */
struct ast_variable * vars ; /*!< Variables and Functions */
int maxlen ; /*!< Maximum length of call */
struct ast_flags options ; /*!< options */
2003-02-04 15:48:42 +00:00
} ;
2009-03-03 17:03:47 +00:00
static int init_outgoing ( struct outgoing * o )
2003-02-04 15:48:42 +00:00
{
o - > priority = 1 ;
o - > retrytime = 300 ;
o - > waittime = 45 ;
2008-05-14 12:32:57 +00:00
o - > format = AST_FORMAT_SLINEAR ;
2006-05-19 22:51:44 +00:00
ast_set_flag ( & o - > options , SPOOL_FLAG_ALWAYS_DELETE ) ;
2009-03-03 17:03:47 +00:00
if ( ast_string_field_init ( o , 128 ) ) {
return - 1 ;
}
return 0 ;
2003-02-04 15:48:42 +00:00
}
2005-10-11 20:44:23 +00:00
static void free_outgoing ( struct outgoing * o )
{
2008-10-14 22:38:06 +00:00
if ( o - > vars ) {
ast_variables_destroy ( o - > vars ) ;
}
2009-03-03 17:03:47 +00:00
ast_string_field_free_memory ( o ) ;
2007-07-11 20:40:33 +00:00
ast_free ( o ) ;
2005-10-11 20:44:23 +00:00
}
2010-03-27 06:09:26 +00:00
static int apply_outgoing ( struct outgoing * o , const char * fn , FILE * f )
2003-02-04 15:48:42 +00:00
{
char buf [ 256 ] ;
char * c , * c2 ;
int lineno = 0 ;
2008-06-18 20:23:58 +00:00
struct ast_variable * var , * last = o - > vars ;
while ( last & & last - > next ) {
last = last - > next ;
}
2005-07-15 23:24:51 +00:00
2005-04-01 21:31:12 +00:00
while ( fgets ( buf , sizeof ( buf ) , f ) ) {
2003-02-04 15:48:42 +00:00
lineno + + ;
2005-07-15 00:15:14 +00:00
/* Trim comments */
c = buf ;
while ( ( c = strchr ( c , ' # ' ) ) ) {
if ( ( c = = buf ) | | ( * ( c - 1 ) = = ' ' ) | | ( * ( c - 1 ) = = ' \t ' ) )
* c = ' \0 ' ;
else
c + + ;
}
2005-10-11 20:52:37 +00:00
c = buf ;
while ( ( c = strchr ( c , ' ; ' ) ) ) {
if ( ( c > buf ) & & ( c [ - 1 ] = = ' \\ ' ) ) {
memmove ( c - 1 , c , strlen ( c ) + 1 ) ;
c + + ;
} else {
* c = ' \0 ' ;
break ;
}
}
2003-02-04 15:48:42 +00:00
2005-07-15 00:15:14 +00:00
/* Trim trailing white space */
while ( ! ast_strlen_zero ( buf ) & & buf [ strlen ( buf ) - 1 ] < 33 )
buf [ strlen ( buf ) - 1 ] = ' \0 ' ;
if ( ! ast_strlen_zero ( buf ) ) {
c = strchr ( buf , ' : ' ) ;
if ( c ) {
* c = ' \0 ' ;
c + + ;
while ( ( * c ) & & ( * c < 33 ) )
2003-02-04 15:48:42 +00:00
c + + ;
#if 0
2005-07-15 00:15:14 +00:00
printf ( " '%s' is '%s' at line %d \n " , buf , c , lineno ) ;
# endif
if ( ! strcasecmp ( buf , " channel " ) ) {
2009-03-03 17:03:47 +00:00
if ( ( c2 = strchr ( c , ' / ' ) ) ) {
2005-07-15 00:15:14 +00:00
* c2 = ' \0 ' ;
c2 + + ;
2009-03-03 17:03:47 +00:00
ast_string_field_set ( o , tech , c ) ;
ast_string_field_set ( o , dest , c2 ) ;
2003-02-04 15:48:42 +00:00
} else {
2005-07-15 00:15:14 +00:00
ast_log ( LOG_NOTICE , " Channel should be in form Tech/Dest at line %d of %s \n " , lineno , fn ) ;
}
} else if ( ! strcasecmp ( buf , " callerid " ) ) {
2009-03-03 17:03:47 +00:00
char cid_name [ 80 ] = { 0 } , cid_num [ 80 ] = { 0 } ;
ast_callerid_split ( c , cid_name , sizeof ( cid_name ) , cid_num , sizeof ( cid_num ) ) ;
ast_string_field_set ( o , cid_num , cid_num ) ;
ast_string_field_set ( o , cid_name , cid_name ) ;
2005-07-15 00:15:14 +00:00
} else if ( ! strcasecmp ( buf , " application " ) ) {
2009-03-03 17:03:47 +00:00
ast_string_field_set ( o , app , c ) ;
2005-07-15 00:15:14 +00:00
} else if ( ! strcasecmp ( buf , " data " ) ) {
2009-03-03 17:03:47 +00:00
ast_string_field_set ( o , data , c ) ;
2005-07-15 00:15:14 +00:00
} else if ( ! strcasecmp ( buf , " maxretries " ) ) {
2009-08-10 19:20:57 +00:00
if ( sscanf ( c , " %30d " , & o - > maxretries ) ! = 1 ) {
2005-07-15 00:15:14 +00:00
ast_log ( LOG_WARNING , " Invalid max retries at line %d of %s \n " , lineno , fn ) ;
o - > maxretries = 0 ;
}
2008-05-14 12:32:57 +00:00
} else if ( ! strcasecmp ( buf , " codecs " ) ) {
ast_parse_allow_disallow ( NULL , & o - > format , c , 1 ) ;
2005-07-15 00:15:14 +00:00
} else if ( ! strcasecmp ( buf , " context " ) ) {
2009-03-03 17:03:47 +00:00
ast_string_field_set ( o , context , c ) ;
2005-07-15 00:15:14 +00:00
} else if ( ! strcasecmp ( buf , " extension " ) ) {
2009-03-03 17:03:47 +00:00
ast_string_field_set ( o , exten , c ) ;
2005-07-15 00:15:14 +00:00
} else if ( ! strcasecmp ( buf , " priority " ) ) {
2009-08-10 19:20:57 +00:00
if ( ( sscanf ( c , " %30d " , & o - > priority ) ! = 1 ) | | ( o - > priority < 1 ) ) {
2005-07-15 00:15:14 +00:00
ast_log ( LOG_WARNING , " Invalid priority at line %d of %s \n " , lineno , fn ) ;
o - > priority = 1 ;
}
} else if ( ! strcasecmp ( buf , " retrytime " ) ) {
2009-08-10 19:20:57 +00:00
if ( ( sscanf ( c , " %30d " , & o - > retrytime ) ! = 1 ) | | ( o - > retrytime < 1 ) ) {
2005-07-15 00:15:14 +00:00
ast_log ( LOG_WARNING , " Invalid retrytime at line %d of %s \n " , lineno , fn ) ;
o - > retrytime = 300 ;
2003-02-04 15:48:42 +00:00
}
2005-07-15 00:15:14 +00:00
} else if ( ! strcasecmp ( buf , " waittime " ) ) {
2009-08-10 19:20:57 +00:00
if ( ( sscanf ( c , " %30d " , & o - > waittime ) ! = 1 ) | | ( o - > waittime < 1 ) ) {
2007-09-13 23:12:16 +00:00
ast_log ( LOG_WARNING , " Invalid waittime at line %d of %s \n " , lineno , fn ) ;
2005-07-15 00:15:14 +00:00
o - > waittime = 45 ;
}
} else if ( ! strcasecmp ( buf , " retry " ) ) {
o - > retries + + ;
} else if ( ! strcasecmp ( buf , " startretry " ) ) {
2009-08-10 19:20:57 +00:00
if ( sscanf ( c , " %30ld " , & o - > callingpid ) ! = 1 ) {
2005-07-15 00:15:14 +00:00
ast_log ( LOG_WARNING , " Unable to retrieve calling PID! \n " ) ;
o - > callingpid = 0 ;
}
} else if ( ! strcasecmp ( buf , " endretry " ) | | ! strcasecmp ( buf , " abortretry " ) ) {
o - > callingpid = 0 ;
o - > retries + + ;
} else if ( ! strcasecmp ( buf , " delayedretry " ) ) {
2005-07-15 23:24:51 +00:00
} else if ( ! strcasecmp ( buf , " setvar " ) | | ! strcasecmp ( buf , " set " ) ) {
c2 = c ;
strsep ( & c2 , " = " ) ;
2005-11-20 10:32:28 +00:00
if ( c2 ) {
2007-08-29 20:55:40 +00:00
var = ast_variable_new ( c , c2 , fn ) ;
2005-11-20 06:59:42 +00:00
if ( var ) {
2008-06-18 20:23:58 +00:00
/* Always insert at the end, because some people want to treat the spool file as a script */
if ( last ) {
last - > next = var ;
} else {
o - > vars = var ;
}
last = var ;
2005-11-20 06:59:42 +00:00
}
2005-11-20 10:32:28 +00:00
} else
2005-11-20 06:59:42 +00:00
ast_log ( LOG_WARNING , " Malformed \" %s \" argument. Should be \" %s: variable=value \" \n " , buf , buf ) ;
2005-07-15 00:15:14 +00:00
} else if ( ! strcasecmp ( buf , " account " ) ) {
2009-03-03 17:03:47 +00:00
ast_string_field_set ( o , account , c ) ;
2006-05-19 22:51:44 +00:00
} else if ( ! strcasecmp ( buf , " alwaysdelete " ) ) {
ast_set2_flag ( & o - > options , ast_true ( c ) , SPOOL_FLAG_ALWAYS_DELETE ) ;
2006-05-25 17:58:55 +00:00
} else if ( ! strcasecmp ( buf , " archive " ) ) {
ast_set2_flag ( & o - > options , ast_true ( c ) , SPOOL_FLAG_ARCHIVE ) ;
2005-07-15 00:15:14 +00:00
} else {
ast_log ( LOG_WARNING , " Unknown keyword '%s' at line %d of %s \n " , buf , lineno , fn ) ;
}
} else
ast_log ( LOG_NOTICE , " Syntax error at line %d of %s \n " , lineno , fn ) ;
}
2003-02-04 15:48:42 +00:00
}
2009-03-03 17:03:47 +00:00
ast_string_field_set ( o , fn , fn ) ;
2004-05-08 08:07:47 +00:00
if ( ast_strlen_zero ( o - > tech ) | | ast_strlen_zero ( o - > dest ) | | ( ast_strlen_zero ( o - > app ) & & ast_strlen_zero ( o - > exten ) ) ) {
2003-02-04 15:48:42 +00:00
ast_log ( LOG_WARNING , " At least one of app or extension must be specified, along with tech and dest in file %s \n " , fn ) ;
return - 1 ;
}
return 0 ;
}
2004-03-20 21:13:12 +00:00
static void safe_append ( struct outgoing * o , time_t now , char * s )
{
int fd ;
FILE * f ;
2004-04-05 00:00:23 +00:00
struct utimbuf tbuf ;
2007-07-11 20:40:33 +00:00
if ( ( fd = open ( o - > fn , O_WRONLY | O_APPEND ) ) < 0 )
return ;
if ( ( f = fdopen ( fd , " a " ) ) ) {
fprintf ( f , " \n %s: %ld %d (%ld) \n " , s , ( long ) ast_mainpid , o - > retries , ( long ) now ) ;
fclose ( f ) ;
} else
close ( fd ) ;
/* Update the file time */
tbuf . actime = now ;
tbuf . modtime = now + o - > retrytime ;
if ( utime ( o - > fn , & tbuf ) )
ast_log ( LOG_WARNING , " Unable to set utime on %s: %s \n " , o - > fn , strerror ( errno ) ) ;
2004-03-20 21:13:12 +00:00
}
2006-05-25 17:58:55 +00:00
/*!
* \ 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 )
2006-05-19 22:51:44 +00:00
{
2006-05-25 17:58:55 +00:00
int fd ;
FILE * f ;
char newfn [ 256 ] ;
const char * bname ;
if ( ! ast_test_flag ( & o - > options , SPOOL_FLAG_ALWAYS_DELETE ) ) {
2006-05-19 22:51:44 +00:00
struct stat current_file_status ;
2006-05-25 17:58:55 +00:00
2007-07-11 20:40:33 +00:00
if ( ! stat ( o - > fn , & current_file_status ) ) {
2006-05-25 17:58:55 +00:00
if ( time ( NULL ) < current_file_status . st_mtime )
return 0 ;
2007-07-11 20:40:33 +00:00
}
2006-05-25 17:58:55 +00:00
}
if ( ! ast_test_flag ( & o - > options , SPOOL_FLAG_ARCHIVE ) ) {
unlink ( o - > fn ) ;
return 0 ;
}
2007-07-11 20:40:33 +00:00
2007-06-22 04:35:12 +00:00
if ( ast_mkdir ( qdonedir , 0777 ) ) {
2006-05-25 17:58:55 +00:00
ast_log ( LOG_WARNING , " Unable to create queue directory %s -- outgoing spool archiving disabled \n " , qdonedir ) ;
unlink ( o - > fn ) ;
return - 1 ;
}
2007-07-11 20:40:33 +00:00
if ( ( fd = open ( o - > fn , O_WRONLY | O_APPEND ) ) ) {
if ( ( f = fdopen ( fd , " a " ) ) ) {
2006-05-25 17:58:55 +00:00
fprintf ( f , " Status: %s \n " , status ) ;
fclose ( f ) ;
} else
close ( fd ) ;
2006-05-19 22:51:44 +00:00
}
2006-05-25 17:58:55 +00:00
2007-07-11 20:40:33 +00:00
if ( ! ( bname = strrchr ( o - > fn , ' / ' ) ) )
2006-05-25 17:58:55 +00:00
bname = o - > fn ;
2007-07-11 20:40:33 +00:00
else
2006-05-25 17:58:55 +00:00
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 ;
2006-05-19 22:51:44 +00:00
}
2003-02-04 15:48:42 +00:00
static void * attempt_thread ( void * data )
{
struct outgoing * o = data ;
int res , reason ;
2004-05-08 08:07:47 +00:00
if ( ! ast_strlen_zero ( o - > app ) ) {
2007-07-26 15:49:18 +00:00
ast_verb ( 3 , " Attempting call on %s/%s for application %s(%s) (Retry %d) \n " , o - > tech , o - > dest , o - > app , o - > data , o - > retries ) ;
2009-03-03 17:03:47 +00:00
res = ast_pbx_outgoing_app ( o - > tech , o - > format , ( void * ) 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 ) ;
2008-10-14 22:38:06 +00:00
o - > vars = NULL ;
2003-02-04 15:48:42 +00:00
} else {
2007-07-26 15:49:18 +00:00
ast_verb ( 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 ) ;
2009-03-03 17:03:47 +00:00
res = ast_pbx_outgoing_exten ( o - > tech , o - > format , ( void * ) 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 ) ;
2008-10-14 22:38:06 +00:00
o - > vars = NULL ;
2003-02-04 15:48:42 +00:00
}
if ( res ) {
2007-08-10 21:03:06 +00:00
ast_log ( LOG_NOTICE , " Call failed to go through, reason (%d) %s \n " , reason , ast_channel_reason2str ( reason ) ) ;
2003-02-04 15:48:42 +00:00
if ( o - > retries > = o - > maxretries + 1 ) {
/* Max retries exceeded */
2009-05-02 19:02:22 +00:00
ast_log ( LOG_NOTICE , " 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 " : " " ) ;
2006-05-25 17:58:55 +00:00
remove_from_queue ( o , " Expired " ) ;
2004-03-20 21:13:12 +00:00
} else {
/* Notate that the call is still active */
safe_append ( o , time ( NULL ) , " EndRetry " ) ;
2003-02-04 15:48:42 +00:00
}
} else {
ast_log ( LOG_NOTICE , " Call completed to %s/%s \n " , o - > tech , o - > dest ) ;
2006-05-25 17:58:55 +00:00
remove_from_queue ( o , " Completed " ) ;
2003-02-04 15:48:42 +00:00
}
2005-10-11 20:44:23 +00:00
free_outgoing ( o ) ;
2003-02-04 15:48:42 +00:00
return NULL ;
}
static void launch_service ( struct outgoing * o )
{
pthread_t t ;
2006-01-07 07:31:08 +00:00
int ret ;
2007-05-24 18:30:19 +00:00
if ( ( ret = ast_pthread_create_detached ( & t , NULL , attempt_thread , o ) ) ) {
2006-01-07 07:31:08 +00:00
ast_log ( LOG_WARNING , " Unable to create thread :( (returned error: %d) \n " , ret ) ;
2005-10-11 20:44:23 +00:00
free_outgoing ( o ) ;
2003-02-04 15:48:42 +00:00
}
}
2010-09-07 21:21:00 +00:00
/* Called from scan_thread or queue_file */
2010-03-27 06:09:26 +00:00
static int scan_service ( const char * fn , time_t now )
2003-02-04 15:48:42 +00:00
{
2007-07-11 20:40:33 +00:00
struct outgoing * o = NULL ;
2003-02-04 15:48:42 +00:00
FILE * f ;
2007-07-11 20:40:33 +00:00
int res = 0 ;
if ( ! ( o = ast_calloc ( 1 , sizeof ( * o ) ) ) ) {
ast_log ( LOG_WARNING , " Out of memory ;( \n " ) ;
return - 1 ;
}
2009-03-03 17:03:47 +00:00
if ( init_outgoing ( o ) ) {
/* No need to call free_outgoing here since we know the failure
* was to allocate string fields and no variables have been allocated
* yet .
*/
ast_free ( o ) ;
return - 1 ;
}
2007-07-11 20:40:33 +00:00
/* Attempt to open the file */
if ( ! ( f = fopen ( fn , " r+ " ) ) ) {
remove_from_queue ( o , " Failed " ) ;
free_outgoing ( o ) ;
ast_log ( LOG_WARNING , " Unable to open %s: %s, deleting \n " , fn , strerror ( errno ) ) ;
return - 1 ;
}
/* Read in and verify the contents */
if ( apply_outgoing ( o , fn , f ) ) {
remove_from_queue ( o , " Failed " ) ;
free_outgoing ( o ) ;
ast_log ( LOG_WARNING , " Invalid file contents in %s, deleting \n " , fn ) ;
fclose ( f ) ;
return - 1 ;
}
2003-02-04 15:48:42 +00:00
#if 0
2007-07-11 20:40:33 +00:00
printf ( " Filename: %s, Retries: %d, max: %d \n " , fn , o - > retries , o - > maxretries ) ;
2003-02-04 15:48:42 +00:00
# endif
2007-07-11 20:40:33 +00:00
fclose ( f ) ;
if ( o - > retries < = o - > maxretries ) {
now + = o - > retrytime ;
if ( o - > callingpid & & ( o - > callingpid = = ast_mainpid ) ) {
safe_append ( o , time ( NULL ) , " DelayedRetry " ) ;
ast_log ( LOG_DEBUG , " Delaying retry since we're currently running '%s' \n " , o - > fn ) ;
2005-10-11 20:44:23 +00:00
free_outgoing ( o ) ;
2007-07-11 20:40:33 +00:00
} else {
/* Increment retries */
o - > retries + + ;
/* If someone else was calling, they're presumably gone now
so abort their retry and continue as we were . . . */
if ( o - > callingpid )
safe_append ( o , time ( NULL ) , " AbortRetry " ) ;
safe_append ( o , now , " StartRetry " ) ;
launch_service ( o ) ;
2003-02-04 15:48:42 +00:00
}
2007-07-11 20:40:33 +00:00
res = now ;
} else {
2009-05-02 19:02:22 +00:00
ast_log ( LOG_NOTICE , " 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 " : " " ) ;
2007-07-11 20:40:33 +00:00
remove_from_queue ( o , " Expired " ) ;
free_outgoing ( o ) ;
}
return res ;
2003-02-04 15:48:42 +00:00
}
2010-05-13 05:37:31 +00:00
# if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
2010-03-27 06:09:26 +00:00
struct direntry {
AST_LIST_ENTRY ( direntry ) list ;
time_t mtime ;
char name [ 0 ] ;
} ;
/* Only one thread is accessing this list, so no lock is necessary */
static AST_LIST_HEAD_NOLOCK_STATIC ( dirlist , direntry ) ;
static void queue_file ( const char * filename , time_t when )
{
struct stat st ;
struct direntry * cur , * new ;
int res ;
time_t now = time ( NULL ) ;
if ( filename [ 0 ] ! = ' / ' ) {
char * fn = alloca ( strlen ( qdir ) + strlen ( filename ) + 2 ) ;
sprintf ( fn , " %s/%s " , qdir , filename ) ; /* SAFE */
filename = fn ;
}
if ( when = = 0 ) {
if ( stat ( filename , & st ) ) {
ast_log ( LOG_WARNING , " Unable to stat %s: %s \n " , filename , strerror ( errno ) ) ;
return ;
}
if ( ! S_ISREG ( st . st_mode ) ) {
return ;
}
when = st . st_mtime ;
}
2010-05-13 05:37:31 +00:00
# ifndef HAVE_INOTIFY
/* Need to check the existing list for kqueue(2), in order to avoid duplicates. */
AST_LIST_TRAVERSE ( & dirlist , cur , list ) {
if ( cur - > mtime = = when & & ! strcmp ( filename , cur - > name ) ) {
return ;
}
}
# endif
2010-03-27 06:09:26 +00:00
if ( ( res = when ) > now | | ( res = scan_service ( filename , now ) ) > 0 ) {
if ( ! ( new = ast_calloc ( 1 , sizeof ( * new ) + strlen ( filename ) + 1 ) ) ) {
return ;
}
new - > mtime = res ;
strcpy ( new - > name , filename ) ;
/* List is ordered by mtime */
if ( AST_LIST_EMPTY ( & dirlist ) ) {
AST_LIST_INSERT_HEAD ( & dirlist , new , list ) ;
} else {
int found = 0 ;
AST_LIST_TRAVERSE_SAFE_BEGIN ( & dirlist , cur , list ) {
if ( cur - > mtime > new - > mtime ) {
AST_LIST_INSERT_BEFORE_CURRENT ( new , list ) ;
found = 1 ;
break ;
}
}
AST_LIST_TRAVERSE_SAFE_END
if ( ! found ) {
AST_LIST_INSERT_TAIL ( & dirlist , new , list ) ;
}
}
}
}
static void * scan_thread ( void * unused )
{
DIR * dir ;
struct dirent * de ;
time_t now ;
struct timespec ts = { . tv_sec = 1 } ;
2010-05-13 05:37:31 +00:00
# ifdef HAVE_INOTIFY
int res ;
2010-03-27 06:09:26 +00:00
int inotify_fd = inotify_init ( ) ;
struct {
struct inotify_event iev ;
/* It may not look like we're using this element, but when we read
* from inotify_fd , the event is typically larger than the first
* struct , and overflows into this second one . */
char name [ FILENAME_MAX + 1 ] ;
} buf ;
struct pollfd pfd = { . fd = inotify_fd , . events = POLLIN } ;
2010-05-13 05:37:31 +00:00
# else
struct timespec nowait = { 0 , 1 } ;
int inotify_fd = kqueue ( ) ;
struct kevent kev ;
# endif
struct direntry * cur ;
2010-03-27 06:09:26 +00:00
while ( ! ast_fully_booted ) {
nanosleep ( & ts , NULL ) ;
}
if ( inotify_fd < 0 ) {
2010-05-13 05:37:31 +00:00
ast_log ( LOG_ERROR , " Unable to initialize "
# ifdef HAVE_INOTIFY
" inotify(7) "
# else
" kqueue(2) "
# endif
" \n " ) ;
2010-03-27 06:09:26 +00:00
return NULL ;
}
2010-05-13 05:37:31 +00:00
# ifdef HAVE_INOTIFY
2010-09-07 21:21:00 +00:00
inotify_add_watch ( inotify_fd , qdir , IN_CREATE | IN_MOVED_TO ) ;
2010-05-13 05:37:31 +00:00
# endif
2010-03-27 06:09:26 +00:00
/* First, run through the directory and clear existing entries */
if ( ! ( dir = opendir ( qdir ) ) ) {
ast_log ( LOG_ERROR , " Unable to open directory %s: %s \n " , qdir , strerror ( errno ) ) ;
return NULL ;
}
2010-05-13 05:37:31 +00:00
# ifndef HAVE_INOTIFY
EV_SET ( & kev , dirfd ( dir ) , EVFILT_VNODE , EV_ADD | EV_ENABLE , NOTE_WRITE | NOTE_EXTEND | NOTE_DELETE | NOTE_REVOKE | NOTE_ATTRIB , 0 , NULL ) ;
if ( kevent ( inotify_fd , & kev , 1 , NULL , 0 , & nowait ) < 0 & & errno ! = 0 ) {
ast_log ( LOG_ERROR , " Unable to watch directory %s: %s \n " , qdir , strerror ( errno ) ) ;
}
# endif
2010-03-27 06:09:26 +00:00
now = time ( NULL ) ;
while ( ( de = readdir ( dir ) ) ) {
queue_file ( de - > d_name , 0 ) ;
}
2010-05-13 05:37:31 +00:00
# ifdef HAVE_INOTIFY
/* Directory needs to remain open for kqueue(2) */
2010-03-27 06:09:26 +00:00
closedir ( dir ) ;
2010-05-13 05:37:31 +00:00
# endif
2010-03-27 06:09:26 +00:00
/* Wait for either a) next timestamp to occur, or b) a change to happen */
for ( ; /* ever */ ; ) {
time_t next = AST_LIST_EMPTY ( & dirlist ) ? INT_MAX : AST_LIST_FIRST ( & dirlist ) - > mtime ;
time ( & now ) ;
if ( next > now ) {
2010-05-13 05:37:31 +00:00
# ifdef HAVE_INOTIFY
2010-03-27 06:09:26 +00:00
int stage = 0 ;
/* Convert from seconds to milliseconds, unless there's nothing
* in the queue already , in which case , we wait forever . */
int waittime = next = = INT_MAX ? - 1 : ( next - now ) * 1000 ;
/* When a file arrives, add it to the queue, in mtime order. */
if ( ( res = poll ( & pfd , 1 , waittime ) ) > 0 & & ( stage = 1 ) & &
( res = read ( inotify_fd , & buf , sizeof ( buf ) ) ) > = sizeof ( buf . iev ) ) {
2010-09-07 21:21:00 +00:00
/* File(s) added to directory, add them to my list */
do {
queue_file ( buf . iev . name , 0 ) ;
res - = sizeof ( buf . iev ) + buf . iev . len ;
if ( res > = sizeof ( buf . iev ) ) {
memmove ( & buf . iev , & buf . iev . name [ buf . iev . len ] , res ) ;
continue ;
}
break ;
} while ( 1 ) ;
2010-03-27 06:09:26 +00:00
} else if ( res < 0 & & errno ! = EINTR & & errno ! = EAGAIN ) {
ast_debug ( 1 , " Got an error back from %s(2): %s \n " , stage ? " read " : " poll " , strerror ( errno ) ) ;
}
2010-05-13 05:37:31 +00:00
# else
struct timespec ts2 = { next - now , 0 } ;
if ( kevent ( inotify_fd , NULL , 0 , & kev , 1 , & ts2 ) < = 0 ) {
/* Interrupt or timeout, restart calculations */
continue ;
} else {
/* Directory changed, rescan */
rewinddir ( dir ) ;
while ( ( de = readdir ( dir ) ) ) {
queue_file ( de - > d_name , 0 ) ;
}
}
# endif
2010-03-27 06:09:26 +00:00
time ( & now ) ;
}
/* Empty the list of all entries ready to be processed */
while ( ! AST_LIST_EMPTY ( & dirlist ) & & AST_LIST_FIRST ( & dirlist ) - > mtime < = now ) {
cur = AST_LIST_REMOVE_HEAD ( & dirlist , list ) ;
queue_file ( cur - > name , cur - > mtime ) ;
ast_free ( cur ) ;
}
}
return NULL ;
}
# else
2003-02-04 15:48:42 +00:00
static void * scan_thread ( void * unused )
{
struct stat st ;
DIR * dir ;
struct dirent * de ;
2006-02-24 10:50:43 +00:00
char fn [ 256 ] ;
2003-02-04 15:48:42 +00:00
int res ;
time_t last = 0 , next = 0 , now ;
2008-12-03 17:01:03 +00:00
struct timespec ts = { . tv_sec = 1 } ;
while ( ! ast_fully_booted ) {
nanosleep ( & ts , NULL ) ;
}
2007-07-11 20:40:33 +00:00
2003-02-04 15:48:42 +00:00
for ( ; ; ) {
/* Wait a sec */
2008-12-03 17:01:03 +00:00
nanosleep ( & ts , NULL ) ;
2003-02-04 15:48:42 +00:00
time ( & now ) ;
2007-07-11 20:40:33 +00:00
if ( stat ( qdir , & st ) ) {
ast_log ( LOG_WARNING , " Unable to stat %s \n " , qdir ) ;
continue ;
}
/* Make sure it is time for us to execute our check */
if ( ( st . st_mtime = = last ) & & ( next & & ( next > now ) ) )
continue ;
2003-02-04 15:48:42 +00:00
#if 0
2007-07-11 20:40:33 +00:00
printf ( " atime: %ld, mtime: %ld, ctime: %ld \n " , st . st_atime , st . st_mtime , st . st_ctime ) ;
printf ( " Ooh, something changed / timeout \n " ) ;
2008-12-03 17:38:59 +00:00
# endif
2007-07-11 20:40:33 +00:00
next = 0 ;
last = st . st_mtime ;
if ( ! ( dir = opendir ( qdir ) ) ) {
ast_log ( LOG_WARNING , " Unable to open directory %s: %s \n " , qdir , strerror ( errno ) ) ;
continue ;
}
while ( ( de = readdir ( dir ) ) ) {
snprintf ( fn , sizeof ( fn ) , " %s/%s " , qdir , de - > d_name ) ;
if ( stat ( fn , & st ) ) {
ast_log ( LOG_WARNING , " Unable to stat %s: %s \n " , fn , strerror ( errno ) ) ;
continue ;
}
if ( ! S_ISREG ( st . st_mode ) )
continue ;
if ( st . st_mtime < = now ) {
2010-03-27 06:09:26 +00:00
res = scan_service ( fn , now ) ;
2007-07-11 20:40:33 +00:00
if ( res > 0 ) {
/* Update next service time */
if ( ! next | | ( res < next ) ) {
next = res ;
2003-02-04 15:48:42 +00:00
}
2008-12-03 17:38:59 +00:00
} else if ( res ) {
2007-07-11 20:40:33 +00:00
ast_log ( LOG_WARNING , " Failed to scan service '%s' \n " , fn ) ;
2008-12-03 17:38:59 +00:00
} else if ( ! next ) {
/* Expired entry: must recheck on the next go-around */
next = st . st_mtime ;
}
2007-07-11 20:40:33 +00:00
} else {
/* Update "next" update if necessary */
if ( ! next | | ( st . st_mtime < next ) )
next = st . st_mtime ;
2003-02-04 15:48:42 +00:00
}
2007-07-11 20:40:33 +00:00
}
closedir ( dir ) ;
2003-02-04 15:48:42 +00:00
}
return NULL ;
}
2010-03-27 06:09:26 +00:00
# endif
2003-02-04 15:48:42 +00:00
2006-08-21 02:11:39 +00:00
static int unload_module ( void )
2003-02-04 15:48:42 +00:00
{
return - 1 ;
}
2006-08-21 02:11:39 +00:00
static int load_module ( void )
2003-02-04 15:48:42 +00:00
{
pthread_t thread ;
2006-01-07 07:31:08 +00:00
int ret ;
2004-07-14 13:57:15 +00:00
snprintf ( qdir , sizeof ( qdir ) , " %s/%s " , ast_config_AST_SPOOL_DIR , " outgoing " ) ;
2007-06-22 04:35:12 +00:00
if ( ast_mkdir ( qdir , 0777 ) ) {
2003-02-04 15:48:42 +00:00
ast_log ( LOG_WARNING , " Unable to create queue directory %s -- outgoing spool disabled \n " , qdir ) ;
2007-12-26 20:02:27 +00:00
return AST_MODULE_LOAD_DECLINE ;
2003-02-04 15:48:42 +00:00
}
2006-05-25 17:58:55 +00:00
snprintf ( qdonedir , sizeof ( qdir ) , " %s/%s " , ast_config_AST_SPOOL_DIR , " outgoing_done " ) ;
2007-05-24 18:30:19 +00:00
if ( ( ret = ast_pthread_create_detached_background ( & thread , NULL , scan_thread , NULL ) ) ) {
2006-01-07 07:31:08 +00:00
ast_log ( LOG_WARNING , " Unable to create thread :( (returned error: %d) \n " , ret ) ;
2007-12-26 20:02:27 +00:00
return AST_MODULE_LOAD_FAILURE ;
2003-02-04 15:48:42 +00:00
}
2007-05-24 18:30:19 +00:00
2007-12-26 20:02:27 +00:00
return AST_MODULE_LOAD_SUCCESS ;
2003-02-04 15:48:42 +00:00
}
2006-08-21 02:11:39 +00:00
AST_MODULE_INFO_STANDARD ( ASTERISK_GPL_KEY , " Outgoing Spool Support " ) ;