Asterisk - The Open Source Telephony Project  18.5.0
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 = "30ef0c93b36035ec78c9cfd712d36d9b" , .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.

Referenced by apply_outgoing().

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

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.

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

Referenced by apply_outgoing(), and parse_line().

165 {
166  struct ast_variable *var = ast_variable_new(name, value, o->fn);
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 */
174  ast_variable_list_append(&o->vars, var);
175 }
Structure for variables, used for configurations and for channel variables.
#define var
Definition: ast_expr2f.c:614
int value
Definition: syslog.c:37
struct ast_variable * vars
Definition: pbx_spool.c:97
#define ast_variable_new(name, value, filename)
static const char name[]
Definition: cdr_mysql.c:74
const ast_string_field fn
Definition: pbx_spool.c:95
#define ast_variable_list_append(head, new_var)

◆ apply_outgoing()

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

Definition at line 296 of file pbx_spool.c.

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().

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 }
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define LOG_WARNING
Definition: logger.h:274
const ast_string_field app
Definition: pbx_spool.c:95
int retries
Definition: pbx_spool.c:78
#define LINE_BUFFER_SIZE
Definition: pbx_spool.c:294
#define ast_strlen_zero(foo)
Definition: strings.h:52
const ast_string_field exten
Definition: pbx_spool.c:95
#define ast_log
Definition: astobj2.c:42
static void parse_line(char *line, unsigned int lineno, struct outgoing *o)
Definition: pbx_spool.c:177
static void append_variable(struct outgoing *o, const char *name, const char *value)
Definition: pbx_spool.c:164
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
const ast_string_field fn
Definition: pbx_spool.c:95
const ast_string_field dest
Definition: pbx_spool.c:95
const ast_string_field tech
Definition: pbx_spool.c:95

◆ 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.

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().

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);
437  res = ast_pbx_outgoing_app(o->tech, o->capabilities, o->dest,
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);
443  res = ast_pbx_outgoing_exten(o->tech, o->capabilities, o->dest,
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 ast_string_field cid_name
Definition: pbx_spool.c:95
#define ast_test_flag(p, flag)
Definition: utils.h:63
int maxretries
Definition: pbx_spool.c:79
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
int priority
Definition: pbx_spool.c:96
int waittime
Definition: pbx_spool.c:81
const ast_string_field cid_num
Definition: pbx_spool.c:95
const ast_string_field app
Definition: pbx_spool.c:95
int retries
Definition: pbx_spool.c:78
static void free_outgoing(struct outgoing *o)
Definition: pbx_spool.c:114
#define NULL
Definition: resample.c:96
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:7951
const ast_string_field context
Definition: pbx_spool.c:95
#define ast_verb(level,...)
Definition: logger.h:463
#define ast_strlen_zero(foo)
Definition: strings.h:52
const ast_string_field exten
Definition: pbx_spool.c:95
static void queue_file(const char *filename, time_t when)
Definition: pbx_spool.c:560
#define ast_log
Definition: astobj2.c:42
const ast_string_field data
Definition: pbx_spool.c:95
struct ast_variable * vars
Definition: pbx_spool.c:97
const ast_string_field account
Definition: pbx_spool.c:95
struct ast_flags options
Definition: pbx_spool.c:99
int retrytime
Definition: pbx_spool.c:80
#define LOG_NOTICE
Definition: logger.h:263
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:8015
const ast_string_field fn
Definition: pbx_spool.c:95
const ast_string_field dest
Definition: pbx_spool.c:95
const ast_string_field tech
Definition: pbx_spool.c:95
static void safe_append(struct outgoing *o, time_t now, char *s)
Definition: pbx_spool.c:339
struct ast_format_cap * capabilities
Definition: pbx_spool.c:83
const char * ast_channel_reason2str(int reason)
return an english explanation of the code returned thru __ast_request_and_dial&#39;s &#39;outstate&#39; argument ...
Definition: channel.c:5913

◆ free_outgoing()

static void free_outgoing ( struct outgoing o)
static

Definition at line 114 of file pbx_spool.c.

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().

115 {
116  if (o->vars) {
118  }
121  ast_free(o);
122 }
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1263
struct ast_variable * vars
Definition: pbx_spool.c:97
#define ast_free(a)
Definition: astmm.h:182
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
struct ast_format_cap * capabilities
Definition: pbx_spool.c:83
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368

◆ launch_service()

static void launch_service ( struct outgoing o)
static

Definition at line 470 of file pbx_spool.c.

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

Referenced by scan_service().

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 }
#define ast_pthread_create_detached(a, b, c, d)
Definition: utils.h:563
#define LOG_WARNING
Definition: logger.h:274
static void * attempt_thread(void *data)
Definition: pbx_spool.c:431
static void free_outgoing(struct outgoing *o)
Definition: pbx_spool.c:114
#define NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42

◆ load_module()

static int load_module ( void  )
static

Definition at line 946 of file pbx_spool.c.

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, scan_thread(), and thread.

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_meetme.c:1089
static char qdonedir[255]
Definition: pbx_spool.c:75
#define LOG_WARNING
Definition: logger.h:274
static char qdir[255]
Definition: pbx_spool.c:74
#define NULL
Definition: resample.c:96
#define ast_pthread_create_detached_background(a, b, c, d)
Definition: utils.h:572
#define ast_log
Definition: astobj2.c:42
Module could not be loaded properly.
Definition: module.h:102
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 void * scan_thread(void *unused)
Definition: pbx_spool.c:690
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: main/utils.c:2231

◆ new_outgoing()

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

Definition at line 124 of file pbx_spool.c.

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().

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_set_flag(p, flag)
Definition: utils.h:70
int priority
Definition: pbx_spool.c:96
int waittime
Definition: pbx_spool.c:81
static void free_outgoing(struct outgoing *o)
Definition: pbx_spool.c:114
#define NULL
Definition: resample.c:96
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:353
#define ast_format_cap_append(cap, format, framing)
Definition: format_cap.h:103
#define ast_format_cap_alloc(flags)
Definition: format_cap.h:52
struct ast_flags options
Definition: pbx_spool.c:99
int retrytime
Definition: pbx_spool.c:80
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
const ast_string_field fn
Definition: pbx_spool.c:95
struct ast_format_cap * capabilities
Definition: pbx_spool.c:83
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:514

◆ parse_line()

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

Definition at line 177 of file pbx_spool.c.

References outgoing::account, outgoing::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, outgoing::cid_name, outgoing::cid_num, outgoing::context, outgoing::data, outgoing::dest, outgoing::exten, outgoing::fn, 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(), outgoing::tech, and outgoing::waittime.

Referenced by apply_outgoing().

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));
230  ast_string_field_set(o, cid_name, cid_name);
231  } else if (!strcasecmp(line, "application")) {
232  ast_string_field_set(o, app, c);
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")) {
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")) {
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 char exten[AST_MAX_EXTENSION]
Definition: chan_alsa.c:118
int ast_callerid_split(const char *src, char *name, int namelen, char *num, int numlen)
Definition: callerid.c:1092
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
#define LOG_WARNING
Definition: logger.h:274
int maxretries
Definition: pbx_spool.c:79
int priority
Definition: pbx_spool.c:96
int waittime
Definition: pbx_spool.c:81
int retries
Definition: pbx_spool.c:78
static struct test_val c
static char cid_num[AST_MAX_EXTENSION]
Definition: chan_mgcp.c:164
#define ast_strlen_zero(foo)
Definition: strings.h:52
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
#define ast_log
Definition: astobj2.c:42
const ast_string_field data
Definition: pbx_spool.c:95
const ast_string_field account
Definition: pbx_spool.c:95
static void append_variable(struct outgoing *o, const char *name, const char *value)
Definition: pbx_spool.c:164
struct ast_flags options
Definition: pbx_spool.c:99
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: main/utils.c:1951
int retrytime
Definition: pbx_spool.c:80
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:157
#define LOG_NOTICE
Definition: logger.h:263
char * ast_trim_blanks(char *str)
Trims trailing whitespace characters from a string.
Definition: strings.h:182
const ast_string_field fn
Definition: pbx_spool.c:95
static char cid_name[AST_MAX_EXTENSION]
Definition: chan_mgcp.c:165
const ast_string_field dest
Definition: pbx_spool.c:95
const ast_string_field tech
Definition: pbx_spool.c:95
char * strsep(char **str, const char *delims)
long callingpid
Definition: pbx_spool.c:82
struct ast_format_cap * capabilities
Definition: pbx_spool.c:83
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:116
static const char app[]
Definition: app_mysql.c:62
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:514

◆ queue_created_files()

static void queue_created_files ( void  )
static

Definition at line 657 of file pbx_spool.c.

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().

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 }
struct direntry::@445 list
#define NULL
Definition: resample.c:96
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
static void queue_file(const char *filename, time_t when)
Definition: pbx_spool.c:560
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
char name[0]
Definition: pbx_spool.c:106
#define ast_free(a)
Definition: astmm.h:182
time_t mtime
Definition: pbx_spool.c:105
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528

◆ queue_file()

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

Definition at line 560 of file pbx_spool.c.

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, outgoing::fn, direntry::list, LOG_WARNING, direntry::mtime, direntry::name, NULL, and scan_service().

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

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. */
588  AST_LIST_TRAVERSE(&dirlist, cur, list) {
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 }
static int scan_service(const char *fn, time_t now)
Definition: pbx_spool.c:482
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
#define LOG_WARNING
Definition: logger.h:274
static char qdir[255]
Definition: pbx_spool.c:74
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
struct direntry::@445 list
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
#define NULL
Definition: resample.c:96
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
#define ast_log
Definition: astobj2.c:42
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
int errno
char name[0]
Definition: pbx_spool.c:106
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:710
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
time_t mtime
Definition: pbx_spool.c:105
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:598

◆ queue_file_create()

static void queue_file_create ( const char *  filename)
static

Definition at line 624 of file pbx_spool.c.

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

Referenced by scan_thread().

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 }
struct direntry::@445 list
#define NULL
Definition: resample.c:96
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
char name[0]
Definition: pbx_spool.c:106
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
time_t mtime
Definition: pbx_spool.c:105

◆ queue_file_open()

static void queue_file_open ( const char *  filename)
static

Definition at line 643 of file pbx_spool.c.

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().

644 {
645  struct direntry *cur;
646 
648  if (!strcmp(cur->name, filename)) {
651  break;
652  }
653  }
655 }
struct direntry::@445 list
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
char name[0]
Definition: pbx_spool.c:106
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528

◆ queue_file_write()

static void queue_file_write ( const char *  filename)
static

Definition at line 674 of file pbx_spool.c.

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().

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 }
struct direntry::@445 list
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
static void queue_file(const char *filename, time_t when)
Definition: pbx_spool.c:560
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
char name[0]
Definition: pbx_spool.c:106
#define ast_free(a)
Definition: astmm.h:182
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528

◆ 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.

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, SPOOL_FLAG_ALWAYS_DELETE, and SPOOL_FLAG_ARCHIVE.

Referenced by attempt_thread(), and scan_service().

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 }
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
static char qdonedir[255]
Definition: pbx_spool.c:75
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define LOG_WARNING
Definition: logger.h:274
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
#define NULL
Definition: resample.c:96
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
#define ast_log
Definition: astobj2.c:42
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
struct ast_flags options
Definition: pbx_spool.c:99
char name[0]
Definition: pbx_spool.c:106
#define ast_free(a)
Definition: astmm.h:182
const ast_string_field fn
Definition: pbx_spool.c:95
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
jack_status_t status
Definition: app_jack.c:146
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: main/utils.c:2231

◆ safe_append()

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

Definition at line 339 of file pbx_spool.c.

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().

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 LOG_WARNING
Definition: logger.h:274
int retries
Definition: pbx_spool.c:78
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
int retrytime
Definition: pbx_spool.c:80
int errno
const ast_string_field fn
Definition: pbx_spool.c:95
const ast_string_field dest
Definition: pbx_spool.c:95
const ast_string_field tech
Definition: pbx_spool.c:95
pid_t ast_mainpid
Definition: asterisk.c:315

◆ 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.

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(), and scan_thread().

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");
539  launch_service(o);
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 int apply_outgoing(struct outgoing *o, FILE *f)
Definition: pbx_spool.c:296
#define LOG_WARNING
Definition: logger.h:274
int maxretries
Definition: pbx_spool.c:79
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
int retries
Definition: pbx_spool.c:78
static void free_outgoing(struct outgoing *o)
Definition: pbx_spool.c:114
#define NULL
Definition: resample.c:96
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
static void launch_service(struct outgoing *o)
Definition: pbx_spool.c:470
int retrytime
Definition: pbx_spool.c:80
int errno
#define LOG_NOTICE
Definition: logger.h:263
const ast_string_field fn
Definition: pbx_spool.c:95
const ast_string_field dest
Definition: pbx_spool.c:95
const ast_string_field tech
Definition: pbx_spool.c:95
long callingpid
Definition: pbx_spool.c:82
pid_t ast_mainpid
Definition: asterisk.c:315
static void safe_append(struct outgoing *o, time_t now, char *s)
Definition: pbx_spool.c:339

◆ scan_thread()

static void* scan_thread ( void *  unused)
static

Definition at line 690 of file pbx_spool.c.

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, outgoing::fn, HAVE_INOTIFY, inotify_fd, last, len(), LOG_ERROR, LOG_WARNING, direntry::mtime, direntry::name, direntry::next, NULL, queue_created_files(), queue_file(), queue_file_create(), queue_file_open(), queue_file_write(), and scan_service().

Referenced by load_module().

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 AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
static void queue_created_files(void)
Definition: pbx_spool.c:657
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static char qdir[255]
Definition: pbx_spool.c:74
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
#define HAVE_INOTIFY
Definition: autoconfig.h:391
Definition: astman.c:222
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
#define NULL
Definition: resample.c:96
static void queue_file_write(const char *filename)
Definition: pbx_spool.c:674
static void queue_file(const char *filename, time_t when)
Definition: pbx_spool.c:560
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
static int inotify_fd
Definition: localtime.c:351
#define ast_fully_booted
Definition: options.h:115
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
struct direntry * next
Definition: pbx_spool.c:104
#define LOG_ERROR
Definition: logger.h:285
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
int errno
char name[0]
Definition: pbx_spool.c:106
#define ast_free(a)
Definition: astmm.h:182
time_t mtime
Definition: pbx_spool.c:105
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

◆ 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 = "30ef0c93b36035ec78c9cfd712d36d9b" , .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.

◆ qdonedir

char qdonedir[255]
static

Definition at line 75 of file pbx_spool.c.