Asterisk - The Open Source Telephony Project GIT-master-d856a3e
Data Structures | Macros | Enumerations | Functions | Variables
pbx_spool.c File Reference

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

#include "asterisk.h"
#include <sys/stat.h>
#include <time.h>
#include <utime.h>
#include <dirent.h>
#include <sys/inotify.h>
#include "asterisk/paths.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/utils.h"
#include "asterisk/options.h"
#include "asterisk/format.h"
#include "asterisk/format_cache.h"
Include dependency graph for pbx_spool.c:

Go to the source code of this file.

Data Structures

struct  createlist
 
struct  direntry
 
struct  dirlist
 
struct  openlist
 
struct  outgoing
 

Macros

#define LINE_BUFFER_SIZE   1024
 

Enumerations

enum  { SPOOL_FLAG_ALWAYS_DELETE = (1 << 0) , SPOOL_FLAG_ARCHIVE = (1 << 1) , SPOOL_FLAG_EARLY_MEDIA = (1 << 2) }
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
static void append_variable (struct outgoing *o, const char *name, const char *value)
 
static int apply_outgoing (struct outgoing *o, FILE *f)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static void * attempt_thread (void *data)
 
static void free_outgoing (struct outgoing *o)
 
static void launch_service (struct outgoing *o)
 
static int load_module (void)
 
static struct outgoingnew_outgoing (const char *fn)
 
static void parse_line (char *line, unsigned int lineno, struct outgoing *o)
 
static void queue_created_files (void)
 
static void queue_file (const char *filename, time_t when)
 
static void queue_file_create (const char *filename)
 
static void queue_file_open (const char *filename)
 
static void queue_file_write (const char *filename)
 
static int remove_from_queue (struct outgoing *o, const char *status)
 Remove a call file from the outgoing queue optionally moving it in the archive dir. More...
 
static void safe_append (struct outgoing *o, time_t now, char *s)
 
static int scan_service (const char *fn, time_t now)
 
static void * scan_thread (void *unused)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Outgoing Spool Support" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, .support_level = AST_MODULE_SUPPORT_CORE, }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static struct createlist createlist = { .first = NULL, .last = NULL, }
 
static struct dirlist dirlist = { .first = NULL, .last = NULL, .lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, {1, 0} } , }
 
static struct openlist openlist = { .first = NULL, .last = NULL, }
 
static char qdir [255]
 
static char qdonedir [255]
 

Detailed Description

Full-featured outgoing call spool support.

Definition in file pbx_spool.c.

Macro Definition Documentation

◆ LINE_BUFFER_SIZE

#define LINE_BUFFER_SIZE   1024

Definition at line 294 of file pbx_spool.c.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum
Enumerator
SPOOL_FLAG_ALWAYS_DELETE 

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.

SPOOL_FLAG_ARCHIVE 
SPOOL_FLAG_EARLY_MEDIA 

Definition at line 62 of file pbx_spool.c.

62 {
63 /*! Always delete the call file after a call succeeds or the
64 * maximum number of retries is exceeded, even if the
65 * modification time of the call file is in the future.
66 */
67 SPOOL_FLAG_ALWAYS_DELETE = (1 << 0),
68 /* Don't unlink the call file after processing, move in qdonedir */
69 SPOOL_FLAG_ARCHIVE = (1 << 1),
70 /* Connect the channel with the outgoing extension once early media is received */
71 SPOOL_FLAG_EARLY_MEDIA = (1 << 2),
72};
@ SPOOL_FLAG_ALWAYS_DELETE
Definition: pbx_spool.c:67
@ SPOOL_FLAG_EARLY_MEDIA
Definition: pbx_spool.c:71
@ SPOOL_FLAG_ARCHIVE
Definition: pbx_spool.c:69

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 965 of file pbx_spool.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 965 of file pbx_spool.c.

◆ append_variable()

static void append_variable ( struct outgoing o,
const char *  name,
const char *  value 
)
static

Definition at line 164 of file pbx_spool.c.

165{
167
168 if (!var) {
169 return;
170 }
171
172 /* Always insert at the end, because some people want to treat the spool
173 * file as a script */
175}
#define var
Definition: ast_expr2f.c:605
static const char name[]
Definition: format_mp3.c:68
#define ast_variable_new(name, value, filename)
#define ast_variable_list_append(head, new_var)
Structure for variables, used for configurations and for channel variables.
struct ast_variable * vars
Definition: pbx_spool.c:97
const ast_string_field fn
Definition: pbx_spool.c:95
int value
Definition: syslog.c:37

References ast_variable_list_append, ast_variable_new, outgoing::fn, name, value, var, and outgoing::vars.

Referenced by apply_outgoing(), and parse_line().

◆ apply_outgoing()

static int apply_outgoing ( struct outgoing o,
FILE *  f 
)
static

Definition at line 296 of file pbx_spool.c.

297{
298 char buf[LINE_BUFFER_SIZE];
299 unsigned int lineno = 0;
300
301 while (fgets(buf, sizeof(buf), f)) {
302 size_t len = strlen(buf);
303
304 lineno++;
305
306 if (buf[len - 1] == '\n' || feof(f)) {
307 /* We have a line, parse it */
308 parse_line(buf, lineno, o);
309 continue;
310 }
311
312 /* Crazy long line, skip it */
313 ast_log(LOG_WARNING, "Skipping extremely long line at line %d of %s\n", lineno, o->fn);
314
315 /* Consume the rest of the problematic line */
316 while (fgets(buf, sizeof(buf), f)) {
317 len = strlen(buf);
318 if (buf[len - 1] == '\n' || feof(f)) {
319 break;
320 }
321 }
322 }
323
324 if (ast_strlen_zero(o->tech)
325 || ast_strlen_zero(o->dest)
326 || (ast_strlen_zero(o->app) && ast_strlen_zero(o->exten))) {
327 ast_log(LOG_WARNING, "At least one of app or extension must be specified, "
328 "along with tech and dest in file %s\n", o->fn);
329 return -1;
330 }
331
332 if (snprintf(buf, sizeof(buf), "%d", o->retries + 1) < sizeof(buf)) {
333 append_variable(o, "AST_OUTGOING_ATTEMPT", buf);
334 }
335
336 return 0;
337}
#define ast_log
Definition: astobj2.c:42
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define LOG_WARNING
static void append_variable(struct outgoing *o, const char *name, const char *value)
Definition: pbx_spool.c:164
static void parse_line(char *line, unsigned int lineno, struct outgoing *o)
Definition: pbx_spool.c:177
#define LINE_BUFFER_SIZE
Definition: pbx_spool.c:294
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
const ast_string_field tech
Definition: pbx_spool.c:95
const ast_string_field app
Definition: pbx_spool.c:95
int retries
Definition: pbx_spool.c:78
const ast_string_field exten
Definition: pbx_spool.c:95
const ast_string_field dest
Definition: pbx_spool.c:95

References outgoing::app, append_variable(), ast_log, ast_strlen_zero(), buf, outgoing::dest, outgoing::exten, outgoing::fn, len(), LINE_BUFFER_SIZE, ast_variable::lineno, LOG_WARNING, parse_line(), outgoing::retries, and outgoing::tech.

Referenced by scan_service().

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 965 of file pbx_spool.c.

◆ attempt_thread()

static void * attempt_thread ( void *  data)
static

Definition at line 431 of file pbx_spool.c.

432{
433 struct outgoing *o = data;
434 int res, reason;
435 if (!ast_strlen_zero(o->app)) {
436 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);
438 o->waittime * 1000, o->app, o->data, &reason,
440 o->vars, o->account, NULL, NULL);
441 } else {
442 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);
444 o->waittime * 1000, o->context, o->exten, o->priority, &reason,
447 NULL);
448 }
449 if (res) {
450 ast_log(LOG_NOTICE, "Call failed to go through, reason (%d) %s\n", reason, ast_channel_reason2str(reason));
451 if (o->retries >= o->maxretries + 1) {
452 /* Max retries exceeded */
453 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" : "");
454 remove_from_queue(o, "Expired");
455 } else {
456 /* Notate that the call is still active */
457 safe_append(o, time(NULL), "EndRetry");
458#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
459 queue_file(o->fn, time(NULL) + o->retrytime);
460#endif
461 }
462 } else {
463 ast_log(LOG_NOTICE, "Call completed to %s/%s\n", o->tech, o->dest);
464 remove_from_queue(o, "Completed");
465 }
466 free_outgoing(o);
467 return NULL;
468}
const char * ast_channel_reason2str(int reason)
return an english explanation of the code returned thru __ast_request_and_dial's 'outstate' argument
Definition: channel.c:5845
#define ast_verb(level,...)
#define LOG_NOTICE
@ AST_OUTGOING_WAIT_COMPLETE
Definition: pbx.h:1142
int ast_pbx_outgoing_app(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *app, const char *appdata, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, const struct ast_assigned_ids *assignedids)
Synchronously or asynchronously make an outbound call and execute an application on the channel.
Definition: pbx.c:7980
int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *context, const char *exten, int priority, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, int early_media, const struct ast_assigned_ids *assignedids)
Synchronously or asynchronously make an outbound call and send it to a particular extension.
Definition: pbx.c:7916
static void free_outgoing(struct outgoing *o)
Definition: pbx_spool.c:114
static void safe_append(struct outgoing *o, time_t now, char *s)
Definition: pbx_spool.c:339
static void queue_file(const char *filename, time_t when)
Definition: pbx_spool.c:560
static int remove_from_queue(struct outgoing *o, const char *status)
Remove a call file from the outgoing queue optionally moving it in the archive dir.
Definition: pbx_spool.c:363
#define NULL
Definition: resample.c:96
struct ast_format_cap * capabilities
Definition: pbx_spool.c:83
const ast_string_field cid_num
Definition: pbx_spool.c:95
struct ast_flags options
Definition: pbx_spool.c:99
int waittime
Definition: pbx_spool.c:81
const ast_string_field account
Definition: pbx_spool.c:95
const ast_string_field data
Definition: pbx_spool.c:95
const ast_string_field context
Definition: pbx_spool.c:95
int maxretries
Definition: pbx_spool.c:79
const ast_string_field cid_name
Definition: pbx_spool.c:95
int priority
Definition: pbx_spool.c:96
int retrytime
Definition: pbx_spool.c:80
#define ast_test_flag(p, flag)
Definition: utils.h:63

References outgoing::account, outgoing::app, ast_channel_reason2str(), ast_log, AST_OUTGOING_WAIT_COMPLETE, ast_pbx_outgoing_app(), ast_pbx_outgoing_exten(), ast_strlen_zero(), ast_test_flag, ast_verb, outgoing::capabilities, outgoing::cid_name, outgoing::cid_num, outgoing::context, outgoing::data, outgoing::dest, outgoing::exten, outgoing::fn, free_outgoing(), LOG_NOTICE, outgoing::maxretries, NULL, outgoing::options, outgoing::priority, queue_file(), remove_from_queue(), outgoing::retries, outgoing::retrytime, safe_append(), SPOOL_FLAG_EARLY_MEDIA, outgoing::tech, outgoing::vars, and outgoing::waittime.

Referenced by launch_service().

◆ free_outgoing()

static void free_outgoing ( struct outgoing o)
static

Definition at line 114 of file pbx_spool.c.

115{
116 if (o->vars) {
118 }
121 ast_free(o);
122}
#define ast_free(a)
Definition: astmm.h:180
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374

References ao2_cleanup, ast_free, ast_string_field_free_memory, ast_variables_destroy(), outgoing::capabilities, and outgoing::vars.

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

◆ launch_service()

static void launch_service ( struct outgoing o)
static

Definition at line 470 of file pbx_spool.c.

471{
472 pthread_t t;
473 int ret;
474
475 if ((ret = ast_pthread_create_detached(&t, NULL, attempt_thread, o))) {
476 ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
477 free_outgoing(o);
478 }
479}
static void * attempt_thread(void *data)
Definition: pbx_spool.c:431
#define ast_pthread_create_detached(a, b, c, d)
Definition: utils.h:588

References ast_log, ast_pthread_create_detached, attempt_thread(), free_outgoing(), LOG_WARNING, and NULL.

Referenced by scan_service().

◆ load_module()

static int load_module ( void  )
static

Definition at line 946 of file pbx_spool.c.

947{
948 pthread_t thread;
949 int ret;
950 snprintf(qdir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing");
951 if (ast_mkdir(qdir, 0777)) {
952 ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool disabled\n", qdir);
954 }
955 snprintf(qdonedir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing_done");
956
958 ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
960 }
961
963}
pthread_t thread
Definition: app_sla.c:329
@ AST_MODULE_LOAD_FAILURE
Module could not be loaded properly.
Definition: module.h:102
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
const char * ast_config_AST_SPOOL_DIR
Definition: options.c:154
static char qdir[255]
Definition: pbx_spool.c:74
static void * scan_thread(void *unused)
Definition: pbx_spool.c:690
static char qdonedir[255]
Definition: pbx_spool.c:75
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2479
#define ast_pthread_create_detached_background(a, b, c, d)
Definition: utils.h:597

References ast_config_AST_SPOOL_DIR, ast_log, ast_mkdir(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_SUCCESS, ast_pthread_create_detached_background, LOG_WARNING, NULL, qdir, qdonedir, scan_thread(), and thread.

◆ new_outgoing()

static struct outgoing * new_outgoing ( const char *  fn)
static

Definition at line 124 of file pbx_spool.c.

125{
126 struct outgoing *o;
127
128 o = ast_calloc(1, sizeof(*o));
129 if (!o) {
130 return NULL;
131 }
132
133 /* Initialize the new object. */
134 o->priority = 1;
135 o->retrytime = 300;
136 o->waittime = 45;
138 if (ast_string_field_init(o, 128)) {
139 /*
140 * No need to call free_outgoing here since the failure was to
141 * allocate string fields and no variables have been allocated
142 * yet.
143 */
144 ast_free(o);
145 return NULL;
146 }
148 if (ast_strlen_zero(o->fn)) {
149 /* String field set failed. Since this string is important we must fail. */
150 free_outgoing(o);
151 return NULL;
152 }
153
155 if (!o->capabilities) {
156 free_outgoing(o);
157 return NULL;
158 }
160
161 return o;
162}
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
@ AST_FORMAT_CAP_FLAG_DEFAULT
Definition: format_cap.h:38
#define ast_format_cap_append(cap, format, framing)
Add format capability to capabilities structure.
Definition: format_cap.h:99
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition: format_cap.h:49
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_set_flag(p, flag)
Definition: utils.h:70

References ast_calloc, ast_format_cap_alloc, ast_format_cap_append, AST_FORMAT_CAP_FLAG_DEFAULT, ast_format_slin, ast_free, ast_set_flag, ast_string_field_init, ast_string_field_set, ast_strlen_zero(), outgoing::capabilities, outgoing::fn, free_outgoing(), NULL, outgoing::options, outgoing::priority, outgoing::retrytime, SPOOL_FLAG_ALWAYS_DELETE, and outgoing::waittime.

Referenced by scan_service().

◆ parse_line()

static void parse_line ( char *  line,
unsigned int  lineno,
struct outgoing o 
)
static

Definition at line 177 of file pbx_spool.c.

178{
179 char *c;
180
181 /* Trim comments */
182 c = line;
183 while ((c = strchr(c, '#'))) {
184 if ((c == line) || (*(c-1) == ' ') || (*(c-1) == '\t')) {
185 *c = '\0';
186 break;
187 }
188 c++;
189 }
190
191 c = line;
192 while ((c = strchr(c, ';'))) {
193 if ((c > line) && (c[-1] == '\\')) {
194 memmove(c - 1, c, strlen(c) + 1);
195 } else {
196 *c = '\0';
197 break;
198 }
199 }
200
201 /* Trim trailing white space */
202 ast_trim_blanks(line);
203 if (ast_strlen_zero(line)) {
204 return;
205 }
206 c = strchr(line, ':');
207 if (!c) {
208 ast_log(LOG_NOTICE, "Syntax error at line %d of %s\n", lineno, o->fn);
209 return;
210 }
211 *c = '\0';
212 c = ast_skip_blanks(c + 1);
213#if 0
214 printf("'%s' is '%s' at line %d\n", line, c, lineno);
215#endif
216 if (!strcasecmp(line, "channel")) {
217 char *c2;
218 if ((c2 = strchr(c, '/'))) {
219 *c2 = '\0';
220 c2++;
221 ast_string_field_set(o, tech, c);
222 ast_string_field_set(o, dest, c2);
223 } else {
224 ast_log(LOG_NOTICE, "Channel should be in form Tech/Dest at line %d of %s\n", lineno, o->fn);
225 }
226 } else if (!strcasecmp(line, "callerid")) {
227 char cid_name[80] = {0}, cid_num[80] = {0};
228 ast_callerid_split(c, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
229 ast_string_field_set(o, cid_num, cid_num);
230 ast_string_field_set(o, cid_name, cid_name);
231 } else if (!strcasecmp(line, "application")) {
233 } else if (!strcasecmp(line, "data")) {
234 ast_string_field_set(o, data, c);
235 } else if (!strcasecmp(line, "maxretries")) {
236 if (sscanf(c, "%30d", &o->maxretries) != 1) {
237 ast_log(LOG_WARNING, "Invalid max retries at line %d of %s\n", lineno, o->fn);
238 o->maxretries = 0;
239 }
240 } else if (!strcasecmp(line, "codecs")) {
242 } else if (!strcasecmp(line, "context")) {
244 } else if (!strcasecmp(line, "extension")) {
245 ast_string_field_set(o, exten, c);
246 } else if (!strcasecmp(line, "priority")) {
247 if ((sscanf(c, "%30d", &o->priority) != 1) || (o->priority < 1)) {
248 ast_log(LOG_WARNING, "Invalid priority at line %d of %s\n", lineno, o->fn);
249 o->priority = 1;
250 }
251 } else if (!strcasecmp(line, "retrytime")) {
252 if ((sscanf(c, "%30d", &o->retrytime) != 1) || (o->retrytime < 1)) {
253 ast_log(LOG_WARNING, "Invalid retrytime at line %d of %s\n", lineno, o->fn);
254 o->retrytime = 300;
255 }
256 } else if (!strcasecmp(line, "waittime")) {
257 if ((sscanf(c, "%30d", &o->waittime) != 1) || (o->waittime < 1)) {
258 ast_log(LOG_WARNING, "Invalid waittime at line %d of %s\n", lineno, o->fn);
259 o->waittime = 45;
260 }
261 } else if (!strcasecmp(line, "retry")) {
262 o->retries++;
263 } else if (!strcasecmp(line, "startretry")) {
264 if (sscanf(c, "%30ld", &o->callingpid) != 1) {
265 ast_log(LOG_WARNING, "Unable to retrieve calling PID!\n");
266 o->callingpid = 0;
267 }
268 } else if (!strcasecmp(line, "endretry") || !strcasecmp(line, "abortretry")) {
269 o->callingpid = 0;
270 o->retries++;
271 } else if (!strcasecmp(line, "delayedretry")) {
272 } else if (!strcasecmp(line, "setvar") || !strcasecmp(line, "set")) {
273 char *c2 = c;
274
275 strsep(&c2, "=");
276 if (c2) {
277 append_variable(o, c, c2);
278 } else {
279 ast_log(LOG_WARNING, "Malformed \"%s\" argument. Should be \"%s: variable=value\"\n", line, line);
280 }
281 } else if (!strcasecmp(line, "account")) {
282 ast_string_field_set(o, account, c);
283 } else if (!strcasecmp(line, "alwaysdelete")) {
285 } else if (!strcasecmp(line, "archive")) {
287 } else if (!strcasecmp(line, "early_media")) {
289 } else {
290 ast_log(LOG_WARNING, "Unknown keyword '%s' at line %d of %s\n", line, lineno, o->fn);
291 }
292}
static const char app[]
Definition: app_adsiprog.c:56
int ast_callerid_split(const char *src, char *name, int namelen, char *num, int numlen)
Definition: callerid.c:1292
int ast_format_cap_update_by_allow_disallow(struct ast_format_cap *cap, const char *list, int allowing)
Parse an "allow" or "deny" list and modify a format capabilities structure accordingly.
Definition: format_cap.c:320
char * strsep(char **str, const char *delims)
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
char * ast_trim_blanks(char *str)
Trims trailing whitespace characters from a string.
Definition: strings.h:186
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:161
long callingpid
Definition: pbx_spool.c:82
static struct test_val c
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94

References app, append_variable(), ast_callerid_split(), ast_format_cap_update_by_allow_disallow(), ast_log, ast_set2_flag, ast_skip_blanks(), ast_string_field_set, ast_strlen_zero(), ast_trim_blanks(), ast_true(), c, outgoing::callingpid, outgoing::capabilities, voicemailpwcheck::context, outgoing::fn, ast_variable::lineno, LOG_NOTICE, LOG_WARNING, outgoing::maxretries, outgoing::options, outgoing::priority, outgoing::retries, outgoing::retrytime, SPOOL_FLAG_ALWAYS_DELETE, SPOOL_FLAG_ARCHIVE, SPOOL_FLAG_EARLY_MEDIA, strsep(), and outgoing::waittime.

Referenced by apply_outgoing().

◆ queue_created_files()

static void queue_created_files ( void  )
static

Definition at line 657 of file pbx_spool.c.

658{
659 struct direntry *cur;
660 time_t now = time(NULL);
661
663 if (cur->mtime > now) {
664 break;
665 }
666
668 queue_file(cur->name, 0);
669 ast_free(cur);
670 }
672}
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
time_t mtime
Definition: pbx_spool.c:105
struct direntry::@420 list
char name[0]
Definition: pbx_spool.c:106

References ast_free, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, direntry::list, direntry::mtime, direntry::name, NULL, and queue_file().

Referenced by scan_thread().

◆ queue_file()

static void queue_file ( const char *  filename,
time_t  when 
)
static

Definition at line 560 of file pbx_spool.c.

561{
562 struct stat st;
563 struct direntry *cur, *new;
564 int res;
565 time_t now = time(NULL);
566
567 if (!strchr(filename, '/')) {
568 char *fn = ast_alloca(strlen(qdir) + strlen(filename) + 2);
569 sprintf(fn, "%s/%s", qdir, filename); /* SAFE */
570 filename = fn;
571 }
572
573 if (when == 0) {
574 if (stat(filename, &st)) {
575 ast_log(LOG_WARNING, "Unable to stat %s: %s\n", filename, strerror(errno));
576 return;
577 }
578
579 if (!S_ISREG(st.st_mode)) {
580 return;
581 }
582
583 when = st.st_mtime;
584 }
585
586 /* Need to check the existing list in order to avoid duplicates. */
589 if (cur->mtime == when && !strcmp(filename, cur->name)) {
591 return;
592 }
593 }
594
595 if ((res = when) > now || (res = scan_service(filename, now)) > 0) {
596 if (!(new = ast_calloc(1, sizeof(*new) + strlen(filename) + 1))) {
598 return;
599 }
600 new->mtime = res;
601 strcpy(new->name, filename);
602 /* List is ordered by mtime */
603 if (AST_LIST_EMPTY(&dirlist)) {
605 } else {
606 int found = 0;
608 if (cur->mtime > new->mtime) {
610 found = 1;
611 break;
612 }
613 }
615 if (!found) {
617 }
618 }
619 }
621}
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:599
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
int errno
static int scan_service(const char *fn, time_t now)
Definition: pbx_spool.c:482

References ast_alloca, ast_calloc, AST_LIST_EMPTY, AST_LIST_INSERT_BEFORE_CURRENT, AST_LIST_INSERT_HEAD, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log, errno, direntry::list, LOG_WARNING, direntry::mtime, direntry::name, NULL, qdir, and scan_service().

Referenced by attempt_thread(), queue_created_files(), queue_file_write(), and scan_thread().

◆ queue_file_create()

static void queue_file_create ( const char *  filename)
static

Definition at line 624 of file pbx_spool.c.

625{
626 struct direntry *cur;
627
629 if (!strcmp(cur->name, filename)) {
630 return;
631 }
632 }
633
634 if (!(cur = ast_calloc(1, sizeof(*cur) + strlen(filename) + 1))) {
635 return;
636 }
637 strcpy(cur->name, filename);
638 /* We'll handle this file unless an IN_OPEN event occurs within 2 seconds */
639 cur->mtime = time(NULL) + 2;
641}

References ast_calloc, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, direntry::list, direntry::mtime, direntry::name, and NULL.

Referenced by scan_thread().

◆ queue_file_open()

static void queue_file_open ( const char *  filename)
static

Definition at line 643 of file pbx_spool.c.

644{
645 struct direntry *cur;
646
648 if (!strcmp(cur->name, filename)) {
651 break;
652 }
653 }
655}

References AST_LIST_INSERT_TAIL, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, direntry::list, and direntry::name.

Referenced by scan_thread().

◆ queue_file_write()

static void queue_file_write ( const char *  filename)
static

Definition at line 674 of file pbx_spool.c.

675{
676 struct direntry *cur;
677 /* Only queue entries where an IN_CREATE preceded the IN_CLOSE_WRITE */
679 if (!strcmp(cur->name, filename)) {
681 ast_free(cur);
682 queue_file(filename, 0);
683 break;
684 }
685 }
687}

References ast_free, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, direntry::list, direntry::name, and queue_file().

Referenced by scan_thread().

◆ remove_from_queue()

static int remove_from_queue ( struct outgoing o,
const char *  status 
)
static

Remove a call file from the outgoing queue optionally moving it in the archive dir.

Parameters
othe pointer to outgoing struct
statusthe exit status of the call. Can be "Completed", "Failed" or "Expired"

Definition at line 363 of file pbx_spool.c.

364{
365 FILE *f;
366 char newfn[256];
367 const char *bname;
368
369#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
370 struct direntry *cur;
371#endif
372
374 struct stat current_file_status;
375
376 if (!stat(o->fn, &current_file_status)) {
377 if (time(NULL) < current_file_status.st_mtime) {
378 return 0;
379 }
380 }
381 }
382
383#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
386 if (!strcmp(cur->name, o->fn)) {
388 ast_free(cur);
389 break;
390 }
391 }
394#endif
395
397 unlink(o->fn);
398 return 0;
399 }
400
401 if (ast_mkdir(qdonedir, 0777)) {
402 ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool archiving disabled\n", qdonedir);
403 unlink(o->fn);
404 return -1;
405 }
406
407 if (!(bname = strrchr(o->fn, '/'))) {
408 bname = o->fn;
409 } else {
410 bname++;
411 }
412
413 snprintf(newfn, sizeof(newfn), "%s/%s", qdonedir, bname);
414 /* If there is already a call file with the name in the archive dir, it will be overwritten. */
415 unlink(newfn);
416 if (rename(o->fn, newfn) != 0) {
417 unlink(o->fn);
418 return -1;
419 }
420
421 /* Only append to the file AFTER we move it out of the watched directory,
422 * otherwise the fclose() causes another event for inotify(7) */
423 if ((f = fopen(newfn, "a"))) {
424 fprintf(f, "Status: %s\n", status);
425 fclose(f);
426 }
427
428 return 0;
429}
jack_status_t status
Definition: app_jack.c:146

References ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log, ast_mkdir(), ast_test_flag, outgoing::fn, LOG_WARNING, direntry::name, NULL, outgoing::options, qdonedir, SPOOL_FLAG_ALWAYS_DELETE, SPOOL_FLAG_ARCHIVE, and status.

Referenced by attempt_thread(), and scan_service().

◆ safe_append()

static void safe_append ( struct outgoing o,
time_t  now,
char *  s 
)
static

Definition at line 339 of file pbx_spool.c.

340{
341 FILE *f;
342 struct utimbuf tbuf = { .actime = now, .modtime = now + o->retrytime };
343
344 ast_debug(1, "Outgoing %s/%s: %s\n", o->tech, o->dest, s);
345
346 if ((f = fopen(o->fn, "a"))) {
347 fprintf(f, "\n%s: %ld %d (%ld)\n", s, (long)ast_mainpid, o->retries, (long) now);
348 fclose(f);
349 }
350
351 /* Update the file time */
352 if (utime(o->fn, &tbuf)) {
353 ast_log(LOG_WARNING, "Unable to set utime on %s: %s\n", o->fn, strerror(errno));
354 }
355}
#define ast_debug(level,...)
Log a DEBUG message.
pid_t ast_mainpid
Definition: asterisk.c:315

References ast_debug, ast_log, ast_mainpid, outgoing::dest, errno, outgoing::fn, LOG_WARNING, outgoing::retries, outgoing::retrytime, and outgoing::tech.

Referenced by attempt_thread(), and scan_service().

◆ scan_service()

static int scan_service ( const char *  fn,
time_t  now 
)
static
Todo:
XXX There is some odd delayed duplicate servicing of call files going on. We need to suppress the error message if the file does not exist as a result.

Definition at line 482 of file pbx_spool.c.

483{
484 struct outgoing *o;
485 FILE *f;
486 int res;
487
488 o = new_outgoing(fn);
489 if (!o) {
490 return -1;
491 }
492
493 /* Attempt to open the file */
494 f = fopen(o->fn, "r");
495 if (!f) {
496#if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
497 /*!
498 * \todo XXX There is some odd delayed duplicate servicing of
499 * call files going on. We need to suppress the error message
500 * if the file does not exist as a result.
501 */
502 if (errno != ENOENT)
503#endif
504 {
505 ast_log(LOG_WARNING, "Unable to open %s: '%s'(%d), deleting\n",
506 o->fn, strerror(errno), (int) errno);
507 }
508 remove_from_queue(o, "Failed");
509 free_outgoing(o);
510 return -1;
511 }
512
513 /* Read in and verify the contents */
514 res = apply_outgoing(o, f);
515 fclose(f);
516 if (res) {
517 ast_log(LOG_WARNING, "Invalid file contents in %s, deleting\n", o->fn);
518 remove_from_queue(o, "Failed");
519 free_outgoing(o);
520 return -1;
521 }
522
523 ast_debug(1, "Filename: %s, Retries: %d, max: %d\n", o->fn, o->retries, o->maxretries);
524 if (o->retries <= o->maxretries) {
525 now += o->retrytime;
526 if (o->callingpid && (o->callingpid == ast_mainpid)) {
527 safe_append(o, time(NULL), "DelayedRetry");
528 ast_debug(1, "Delaying retry since we're currently running '%s'\n", o->fn);
529 free_outgoing(o);
530 } else {
531 /* Increment retries */
532 o->retries++;
533 /* If someone else was calling, they're presumably gone now
534 so abort their retry and continue as we were... */
535 if (o->callingpid)
536 safe_append(o, time(NULL), "AbortRetry");
537
538 safe_append(o, now, "StartRetry");
540 }
541 return now;
542 }
543
544 ast_log(LOG_NOTICE, "Queued call to %s/%s expired without completion after %d attempt%s\n",
545 o->tech, o->dest, o->retries - 1, ((o->retries - 1) != 1) ? "s" : "");
546 remove_from_queue(o, "Expired");
547 free_outgoing(o);
548 return 0;
549}
static struct outgoing * new_outgoing(const char *fn)
Definition: pbx_spool.c:124
static void launch_service(struct outgoing *o)
Definition: pbx_spool.c:470
static int apply_outgoing(struct outgoing *o, FILE *f)
Definition: pbx_spool.c:296

References apply_outgoing(), ast_debug, ast_log, ast_mainpid, outgoing::callingpid, outgoing::dest, errno, outgoing::fn, free_outgoing(), launch_service(), LOG_NOTICE, LOG_WARNING, outgoing::maxretries, new_outgoing(), NULL, remove_from_queue(), outgoing::retries, outgoing::retrytime, safe_append(), and outgoing::tech.

Referenced by queue_file().

◆ scan_thread()

static void * scan_thread ( void *  unused)
static

Definition at line 690 of file pbx_spool.c.

691{
692 DIR *dir;
693 struct dirent *de;
694 time_t now;
695 struct timespec ts = { .tv_sec = 1 };
696#ifdef HAVE_INOTIFY
697 ssize_t res;
698 int inotify_fd = inotify_init();
699 struct inotify_event *iev;
700 char buf[8192] __attribute__((aligned (sizeof(int))));
701 struct pollfd pfd = { .fd = inotify_fd, .events = POLLIN };
702#else
703 struct timespec nowait = { .tv_sec = 0, .tv_nsec = 1 };
704 int inotify_fd = kqueue();
705 struct kevent kev;
706 struct kevent event;
707#endif
708 struct direntry *cur;
709
710 while (!ast_fully_booted) {
711 nanosleep(&ts, NULL);
712 }
713
714 if (inotify_fd < 0) {
715 ast_log(LOG_ERROR, "Unable to initialize "
716#ifdef HAVE_INOTIFY
717 "inotify(7)"
718#else
719 "kqueue(2)"
720#endif
721 "\n");
722 return NULL;
723 }
724
725#ifdef HAVE_INOTIFY
726 inotify_add_watch(inotify_fd, qdir, IN_CREATE | IN_OPEN | IN_CLOSE_WRITE | IN_MOVED_TO);
727#endif
728
729 /* First, run through the directory and clear existing entries */
730 if (!(dir = opendir(qdir))) {
731 ast_log(LOG_ERROR, "Unable to open directory %s: %s\n", qdir, strerror(errno));
732 return NULL;
733 }
734
735#ifndef HAVE_INOTIFY
736 EV_SET(&kev, dirfd(dir), EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, NOTE_WRITE, 0, NULL);
737 if (kevent(inotify_fd, &kev, 1, &event, 1, &nowait) < 0 && errno != 0) {
738 ast_log(LOG_ERROR, "Unable to watch directory %s: %s\n", qdir, strerror(errno));
739 }
740#endif
741 now = time(NULL);
742 while ((de = readdir(dir))) {
743 queue_file(de->d_name, 0);
744 }
745
746#ifdef HAVE_INOTIFY
747 /* Directory needs to remain open for kqueue(2) */
748 closedir(dir);
749#endif
750
751 /* Wait for either a) next timestamp to occur, or b) a change to happen */
752 for (;/* ever */;) {
753 time_t next = AST_LIST_EMPTY(&dirlist) ? INT_MAX : AST_LIST_FIRST(&dirlist)->mtime;
754
755 time(&now);
756 if (next > now) {
757#ifdef HAVE_INOTIFY
758 int stage = 0;
759 /* Convert from seconds to milliseconds, unless there's nothing
760 * in the queue already, in which case, we wait forever. */
761 int waittime = next == INT_MAX ? -1 : (next - now) * 1000;
762 if (!AST_LIST_EMPTY(&createlist)) {
763 waittime = 1000;
764 }
765 /* When a file arrives, add it to the queue, in mtime order. */
766 if ((res = poll(&pfd, 1, waittime)) > 0 && (stage = 1) &&
767 (res = read(inotify_fd, &buf, sizeof(buf))) >= sizeof(*iev)) {
768 ssize_t len = 0;
769 /* File(s) added to directory, add them to my list */
770 for (iev = (void *) buf; res >= sizeof(*iev); iev = (struct inotify_event *) (((char *) iev) + len)) {
771 /* For an IN_MOVED_TO event, simply process the file. However, if
772 * we get an IN_CREATE event it *might* be an open(O_CREAT) or it
773 * might be a hardlink (like smsq does, since rename() might
774 * overwrite an existing file). So we have to see if we get a
775 * subsequent IN_OPEN event on the same file. If we do, keep it
776 * on the openlist and wait for the corresponding IN_CLOSE_WRITE.
777 * If we *don't* see an IN_OPEN event, then it was a hard link so
778 * it can be processed immediately.
779 *
780 * Unfortunately, although open(O_CREAT) is an atomic file system
781 * operation, the inotify subsystem doesn't give it to us in a
782 * single event with both IN_CREATE|IN_OPEN set. It's two separate
783 * events, and the kernel doesn't even give them to us at the same
784 * time. We can read() from inotify_fd after the IN_CREATE event,
785 * and get *nothing* from it. The IN_OPEN arrives only later! So
786 * we have a very short timeout of 2 seconds. */
787 if (iev->mask & IN_CREATE) {
788 queue_file_create(iev->name);
789 } else if (iev->mask & IN_OPEN) {
790 queue_file_open(iev->name);
791 } else if (iev->mask & IN_CLOSE_WRITE) {
792 queue_file_write(iev->name);
793 } else if (iev->mask & IN_MOVED_TO) {
794 queue_file(iev->name, 0);
795 } else {
796 ast_log(LOG_ERROR, "Unexpected event %d for file '%s'\n", (int) iev->mask, iev->name);
797 }
798
799 len = sizeof(*iev) + iev->len;
800 res -= len;
801 }
802 } else if (res < 0 && errno != EINTR && errno != EAGAIN) {
803 ast_debug(1, "Got an error back from %s(2): %s\n", stage ? "read" : "poll", strerror(errno));
804 }
805 time(&now);
806 }
808#else
809 int num_events;
810 /* If queue empty then wait forever */
811 if (next == INT_MAX) {
812 num_events = kevent(inotify_fd, &kev, 1, &event, 1, NULL);
813 } else {
814 struct timespec ts2 = { .tv_sec = (unsigned long int)(next - now), .tv_nsec = 0 };
815 num_events = kevent(inotify_fd, &kev, 1, &event, 1, &ts2);
816 }
817 if ((num_events < 0) || (event.flags == EV_ERROR)) {
818 ast_debug(10, "KEvent error %s\n", strerror(errno));
819 continue;
820 } else if (num_events == 0) {
821 /* Interrupt or timeout, restart calculations */
822 continue;
823 } else {
824 /* Directory changed, rescan */
825 rewinddir(dir);
826 while ((de = readdir(dir))) {
827 queue_file(de->d_name, 0);
828 }
829 }
830 time(&now);
831 }
832#endif
833
834 /* Empty the list of all entries ready to be processed */
836 while (!AST_LIST_EMPTY(&dirlist) && AST_LIST_FIRST(&dirlist)->mtime <= now) {
837 cur = AST_LIST_REMOVE_HEAD(&dirlist, list);
838 queue_file(cur->name, cur->mtime);
839 ast_free(cur);
840 }
842 }
843 return NULL;
844}
#define HAVE_INOTIFY
Definition: autoconfig.h:386
#define LOG_ERROR
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421
static int inotify_fd
Definition: localtime.c:351
#define ast_fully_booted
Definition: options.h:117
static void queue_file_write(const char *filename)
Definition: pbx_spool.c:674
static void queue_file_create(const char *filename)
Definition: pbx_spool.c:624
static void queue_file_open(const char *filename)
Definition: pbx_spool.c:643
static void queue_created_files(void)
Definition: pbx_spool.c:657
struct direntry * next
Definition: pbx_spool.c:104
Definition: astman.c:222

References ast_debug, ast_free, ast_fully_booted, AST_LIST_EMPTY, AST_LIST_FIRST, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_log, buf, errno, HAVE_INOTIFY, inotify_fd, len(), LOG_ERROR, direntry::mtime, direntry::name, direntry::next, NULL, qdir, queue_created_files(), queue_file(), queue_file_create(), queue_file_open(), and queue_file_write().

Referenced by load_module().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 941 of file pbx_spool.c.

942{
943 return -1;
944}

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Outgoing Spool Support" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, .support_level = AST_MODULE_SUPPORT_CORE, }
static

Definition at line 965 of file pbx_spool.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 965 of file pbx_spool.c.

◆ createlist

struct createlist createlist = { .first = NULL, .last = NULL, }
static

◆ dirlist

struct dirlist dirlist = { .first = NULL, .last = NULL, .lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, {1, 0} } , }
static

◆ openlist

struct openlist openlist = { .first = NULL, .last = NULL, }
static

◆ qdir

char qdir[255]
static

Definition at line 74 of file pbx_spool.c.

Referenced by load_module(), queue_file(), and scan_thread().

◆ qdonedir

char qdonedir[255]
static

Definition at line 75 of file pbx_spool.c.

Referenced by load_module(), and remove_from_queue().