00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "asterisk.h"
00030
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 383121 $")
00032
00033 #include <sys/stat.h>
00034 #include <time.h>
00035 #include <utime.h>
00036 #include <dirent.h>
00037 #ifdef HAVE_INOTIFY
00038 #include <sys/inotify.h>
00039 #elif defined(HAVE_KQUEUE)
00040 #include <sys/types.h>
00041 #include <sys/time.h>
00042 #include <sys/event.h>
00043 #include <fcntl.h>
00044 #endif
00045
00046 #include "asterisk/paths.h"
00047 #include "asterisk/lock.h"
00048 #include "asterisk/file.h"
00049 #include "asterisk/logger.h"
00050 #include "asterisk/channel.h"
00051 #include "asterisk/callerid.h"
00052 #include "asterisk/pbx.h"
00053 #include "asterisk/module.h"
00054 #include "asterisk/utils.h"
00055 #include "asterisk/options.h"
00056
00057
00058
00059
00060
00061
00062 enum {
00063
00064
00065
00066
00067 SPOOL_FLAG_ALWAYS_DELETE = (1 << 0),
00068
00069 SPOOL_FLAG_ARCHIVE = (1 << 1),
00070
00071 SPOOL_FLAG_EARLY_MEDIA = (1 << 2),
00072 };
00073
00074 static char qdir[255];
00075 static char qdonedir[255];
00076
00077 struct outgoing {
00078 int retries;
00079 int maxretries;
00080 int retrytime;
00081 int waittime;
00082 long callingpid;
00083 struct ast_format_cap *capabilities;
00084 AST_DECLARE_STRING_FIELDS (
00085 AST_STRING_FIELD(fn);
00086 AST_STRING_FIELD(tech);
00087 AST_STRING_FIELD(dest);
00088 AST_STRING_FIELD(app);
00089 AST_STRING_FIELD(data);
00090 AST_STRING_FIELD(exten);
00091 AST_STRING_FIELD(context);
00092 AST_STRING_FIELD(cid_num);
00093 AST_STRING_FIELD(cid_name);
00094 AST_STRING_FIELD(account);
00095 );
00096 int priority;
00097 struct ast_variable *vars;
00098 int maxlen;
00099 struct ast_flags options;
00100 };
00101
00102 #if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
00103 static void queue_file(const char *filename, time_t when);
00104 #endif
00105
00106 static void free_outgoing(struct outgoing *o)
00107 {
00108 if (o->vars) {
00109 ast_variables_destroy(o->vars);
00110 }
00111 o->capabilities = ast_format_cap_destroy(o->capabilities);
00112 ast_string_field_free_memory(o);
00113 ast_free(o);
00114 }
00115
00116 static struct outgoing *new_outgoing(const char *fn)
00117 {
00118 struct outgoing *o;
00119 struct ast_format tmpfmt;
00120
00121 o = ast_calloc(1, sizeof(*o));
00122 if (!o) {
00123 return NULL;
00124 }
00125
00126
00127 o->priority = 1;
00128 o->retrytime = 300;
00129 o->waittime = 45;
00130 ast_set_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE);
00131 if (ast_string_field_init(o, 128)) {
00132
00133
00134
00135
00136
00137 ast_free(o);
00138 return NULL;
00139 }
00140 ast_string_field_set(o, fn, fn);
00141 if (ast_strlen_zero(o->fn)) {
00142
00143 free_outgoing(o);
00144 return NULL;
00145 }
00146
00147 o->capabilities = ast_format_cap_alloc_nolock();
00148 if (!o->capabilities) {
00149 free_outgoing(o);
00150 return NULL;
00151 }
00152 ast_format_cap_add(o->capabilities, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
00153
00154 return o;
00155 }
00156
00157 static int apply_outgoing(struct outgoing *o, FILE *f)
00158 {
00159 char buf[256];
00160 char *c, *c2;
00161 int lineno = 0;
00162 struct ast_variable *var, *last = o->vars;
00163
00164 while (last && last->next) {
00165 last = last->next;
00166 }
00167
00168 while(fgets(buf, sizeof(buf), f)) {
00169 lineno++;
00170
00171 c = buf;
00172 while ((c = strchr(c, '#'))) {
00173 if ((c == buf) || (*(c-1) == ' ') || (*(c-1) == '\t'))
00174 *c = '\0';
00175 else
00176 c++;
00177 }
00178
00179 c = buf;
00180 while ((c = strchr(c, ';'))) {
00181 if ((c > buf) && (c[-1] == '\\')) {
00182 memmove(c - 1, c, strlen(c) + 1);
00183 c++;
00184 } else {
00185 *c = '\0';
00186 break;
00187 }
00188 }
00189
00190
00191 ast_trim_blanks(buf);
00192 if (ast_strlen_zero(buf)) {
00193 continue;
00194 }
00195 c = strchr(buf, ':');
00196 if (!c) {
00197 ast_log(LOG_NOTICE, "Syntax error at line %d of %s\n", lineno, o->fn);
00198 continue;
00199 }
00200 *c = '\0';
00201 c = ast_skip_blanks(c + 1);
00202 #if 0
00203 printf("'%s' is '%s' at line %d\n", buf, c, lineno);
00204 #endif
00205 if (!strcasecmp(buf, "channel")) {
00206 if ((c2 = strchr(c, '/'))) {
00207 *c2 = '\0';
00208 c2++;
00209 ast_string_field_set(o, tech, c);
00210 ast_string_field_set(o, dest, c2);
00211 } else {
00212 ast_log(LOG_NOTICE, "Channel should be in form Tech/Dest at line %d of %s\n", lineno, o->fn);
00213 }
00214 } else if (!strcasecmp(buf, "callerid")) {
00215 char cid_name[80] = {0}, cid_num[80] = {0};
00216 ast_callerid_split(c, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
00217 ast_string_field_set(o, cid_num, cid_num);
00218 ast_string_field_set(o, cid_name, cid_name);
00219 } else if (!strcasecmp(buf, "application")) {
00220 ast_string_field_set(o, app, c);
00221 } else if (!strcasecmp(buf, "data")) {
00222 ast_string_field_set(o, data, c);
00223 } else if (!strcasecmp(buf, "maxretries")) {
00224 if (sscanf(c, "%30d", &o->maxretries) != 1) {
00225 ast_log(LOG_WARNING, "Invalid max retries at line %d of %s\n", lineno, o->fn);
00226 o->maxretries = 0;
00227 }
00228 } else if (!strcasecmp(buf, "codecs")) {
00229 ast_parse_allow_disallow(NULL, o->capabilities, c, 1);
00230 } else if (!strcasecmp(buf, "context")) {
00231 ast_string_field_set(o, context, c);
00232 } else if (!strcasecmp(buf, "extension")) {
00233 ast_string_field_set(o, exten, c);
00234 } else if (!strcasecmp(buf, "priority")) {
00235 if ((sscanf(c, "%30d", &o->priority) != 1) || (o->priority < 1)) {
00236 ast_log(LOG_WARNING, "Invalid priority at line %d of %s\n", lineno, o->fn);
00237 o->priority = 1;
00238 }
00239 } else if (!strcasecmp(buf, "retrytime")) {
00240 if ((sscanf(c, "%30d", &o->retrytime) != 1) || (o->retrytime < 1)) {
00241 ast_log(LOG_WARNING, "Invalid retrytime at line %d of %s\n", lineno, o->fn);
00242 o->retrytime = 300;
00243 }
00244 } else if (!strcasecmp(buf, "waittime")) {
00245 if ((sscanf(c, "%30d", &o->waittime) != 1) || (o->waittime < 1)) {
00246 ast_log(LOG_WARNING, "Invalid waittime at line %d of %s\n", lineno, o->fn);
00247 o->waittime = 45;
00248 }
00249 } else if (!strcasecmp(buf, "retry")) {
00250 o->retries++;
00251 } else if (!strcasecmp(buf, "startretry")) {
00252 if (sscanf(c, "%30ld", &o->callingpid) != 1) {
00253 ast_log(LOG_WARNING, "Unable to retrieve calling PID!\n");
00254 o->callingpid = 0;
00255 }
00256 } else if (!strcasecmp(buf, "endretry") || !strcasecmp(buf, "abortretry")) {
00257 o->callingpid = 0;
00258 o->retries++;
00259 } else if (!strcasecmp(buf, "delayedretry")) {
00260 } else if (!strcasecmp(buf, "setvar") || !strcasecmp(buf, "set")) {
00261 c2 = c;
00262 strsep(&c2, "=");
00263 if (c2) {
00264 var = ast_variable_new(c, c2, o->fn);
00265 if (var) {
00266
00267 if (last) {
00268 last->next = var;
00269 } else {
00270 o->vars = var;
00271 }
00272 last = var;
00273 }
00274 } else
00275 ast_log(LOG_WARNING, "Malformed \"%s\" argument. Should be \"%s: variable=value\"\n", buf, buf);
00276 } else if (!strcasecmp(buf, "account")) {
00277 ast_string_field_set(o, account, c);
00278 } else if (!strcasecmp(buf, "alwaysdelete")) {
00279 ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ALWAYS_DELETE);
00280 } else if (!strcasecmp(buf, "archive")) {
00281 ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ARCHIVE);
00282 } else if (!strcasecmp(buf, "early_media")) {
00283 ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_EARLY_MEDIA);
00284 } else {
00285 ast_log(LOG_WARNING, "Unknown keyword '%s' at line %d of %s\n", buf, lineno, o->fn);
00286 }
00287 }
00288 if (ast_strlen_zero(o->tech) || ast_strlen_zero(o->dest) || (ast_strlen_zero(o->app) && ast_strlen_zero(o->exten))) {
00289 ast_log(LOG_WARNING, "At least one of app or extension must be specified, along with tech and dest in file %s\n", o->fn);
00290 return -1;
00291 }
00292 return 0;
00293 }
00294
00295 static void safe_append(struct outgoing *o, time_t now, char *s)
00296 {
00297 FILE *f;
00298 struct utimbuf tbuf = { .actime = now, .modtime = now + o->retrytime };
00299
00300 ast_debug(1, "Outgoing %s/%s: %s\n", o->tech, o->dest, s);
00301
00302 if ((f = fopen(o->fn, "a"))) {
00303 fprintf(f, "\n%s: %ld %d (%ld)\n", s, (long)ast_mainpid, o->retries, (long) now);
00304 fclose(f);
00305 }
00306
00307
00308 if (utime(o->fn, &tbuf)) {
00309 ast_log(LOG_WARNING, "Unable to set utime on %s: %s\n", o->fn, strerror(errno));
00310 }
00311 }
00312
00313
00314
00315
00316
00317
00318
00319 static int remove_from_queue(struct outgoing *o, const char *status)
00320 {
00321 FILE *f;
00322 char newfn[256];
00323 const char *bname;
00324
00325 if (!ast_test_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE)) {
00326 struct stat current_file_status;
00327
00328 if (!stat(o->fn, ¤t_file_status)) {
00329 if (time(NULL) < current_file_status.st_mtime) {
00330 return 0;
00331 }
00332 }
00333 }
00334
00335 if (!ast_test_flag(&o->options, SPOOL_FLAG_ARCHIVE)) {
00336 unlink(o->fn);
00337 return 0;
00338 }
00339
00340 if (ast_mkdir(qdonedir, 0777)) {
00341 ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool archiving disabled\n", qdonedir);
00342 unlink(o->fn);
00343 return -1;
00344 }
00345
00346 if (!(bname = strrchr(o->fn, '/'))) {
00347 bname = o->fn;
00348 } else {
00349 bname++;
00350 }
00351
00352 snprintf(newfn, sizeof(newfn), "%s/%s", qdonedir, bname);
00353
00354 unlink(newfn);
00355 if (rename(o->fn, newfn) != 0) {
00356 unlink(o->fn);
00357 return -1;
00358 }
00359
00360
00361
00362 if ((f = fopen(newfn, "a"))) {
00363 fprintf(f, "Status: %s\n", status);
00364 fclose(f);
00365 }
00366
00367 return 0;
00368 }
00369
00370 static void *attempt_thread(void *data)
00371 {
00372 struct outgoing *o = data;
00373 int res, reason;
00374 if (!ast_strlen_zero(o->app)) {
00375 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);
00376 res = ast_pbx_outgoing_app(o->tech, o->capabilities, o->dest, o->waittime * 1000,
00377 o->app, o->data, &reason, 2 , o->cid_num, o->cid_name,
00378 o->vars, o->account, NULL);
00379 o->vars = NULL;
00380 } else {
00381 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);
00382 res = ast_pbx_outgoing_exten(o->tech, o->capabilities, o->dest,
00383 o->waittime * 1000, o->context, o->exten, o->priority, &reason,
00384 2 , o->cid_num, o->cid_name, o->vars, o->account, NULL,
00385 ast_test_flag(&o->options, SPOOL_FLAG_EARLY_MEDIA));
00386 o->vars = NULL;
00387 }
00388 if (res) {
00389 ast_log(LOG_NOTICE, "Call failed to go through, reason (%d) %s\n", reason, ast_channel_reason2str(reason));
00390 if (o->retries >= o->maxretries + 1) {
00391
00392 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" : "");
00393 remove_from_queue(o, "Expired");
00394 } else {
00395
00396 safe_append(o, time(NULL), "EndRetry");
00397 #if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
00398 queue_file(o->fn, time(NULL) + o->retrytime);
00399 #endif
00400 }
00401 } else {
00402 ast_log(LOG_NOTICE, "Call completed to %s/%s\n", o->tech, o->dest);
00403 remove_from_queue(o, "Completed");
00404 }
00405 free_outgoing(o);
00406 return NULL;
00407 }
00408
00409 static void launch_service(struct outgoing *o)
00410 {
00411 pthread_t t;
00412 int ret;
00413
00414 if ((ret = ast_pthread_create_detached(&t, NULL, attempt_thread, o))) {
00415 ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
00416 free_outgoing(o);
00417 }
00418 }
00419
00420
00421 static int scan_service(const char *fn, time_t now)
00422 {
00423 struct outgoing *o;
00424 FILE *f;
00425 int res;
00426
00427 o = new_outgoing(fn);
00428 if (!o) {
00429 return -1;
00430 }
00431
00432
00433 f = fopen(o->fn, "r");
00434 if (!f) {
00435 #if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
00436
00437
00438
00439
00440
00441 if (errno != ENOENT)
00442 #endif
00443 {
00444 ast_log(LOG_WARNING, "Unable to open %s: '%s'(%d), deleting\n",
00445 o->fn, strerror(errno), (int) errno);
00446 }
00447 remove_from_queue(o, "Failed");
00448 free_outgoing(o);
00449 return -1;
00450 }
00451
00452
00453 res = apply_outgoing(o, f);
00454 fclose(f);
00455 if (res) {
00456 ast_log(LOG_WARNING, "Invalid file contents in %s, deleting\n", o->fn);
00457 remove_from_queue(o, "Failed");
00458 free_outgoing(o);
00459 return -1;
00460 }
00461
00462 ast_debug(1, "Filename: %s, Retries: %d, max: %d\n", o->fn, o->retries, o->maxretries);
00463 if (o->retries <= o->maxretries) {
00464 now += o->retrytime;
00465 if (o->callingpid && (o->callingpid == ast_mainpid)) {
00466 safe_append(o, time(NULL), "DelayedRetry");
00467 ast_debug(1, "Delaying retry since we're currently running '%s'\n", o->fn);
00468 free_outgoing(o);
00469 } else {
00470
00471 o->retries++;
00472
00473
00474 if (o->callingpid)
00475 safe_append(o, time(NULL), "AbortRetry");
00476
00477 safe_append(o, now, "StartRetry");
00478 launch_service(o);
00479 }
00480 return now;
00481 }
00482
00483 ast_log(LOG_NOTICE, "Queued call to %s/%s expired without completion after %d attempt%s\n",
00484 o->tech, o->dest, o->retries - 1, ((o->retries - 1) != 1) ? "s" : "");
00485 remove_from_queue(o, "Expired");
00486 free_outgoing(o);
00487 return 0;
00488 }
00489
00490 #if defined(HAVE_INOTIFY) || defined(HAVE_KQUEUE)
00491 struct direntry {
00492 AST_LIST_ENTRY(direntry) list;
00493 time_t mtime;
00494 char name[0];
00495 };
00496
00497 static AST_LIST_HEAD_STATIC(dirlist, direntry);
00498
00499 #if defined(HAVE_INOTIFY)
00500
00501 static AST_LIST_HEAD_NOLOCK_STATIC(createlist, direntry);
00502 static AST_LIST_HEAD_NOLOCK_STATIC(openlist, direntry);
00503 #endif
00504
00505 static void queue_file(const char *filename, time_t when)
00506 {
00507 struct stat st;
00508 struct direntry *cur, *new;
00509 int res;
00510 time_t now = time(NULL);
00511
00512 if (!strchr(filename, '/')) {
00513 char *fn = ast_alloca(strlen(qdir) + strlen(filename) + 2);
00514 sprintf(fn, "%s/%s", qdir, filename);
00515 filename = fn;
00516 }
00517
00518 if (when == 0) {
00519 if (stat(filename, &st)) {
00520 ast_log(LOG_WARNING, "Unable to stat %s: %s\n", filename, strerror(errno));
00521 return;
00522 }
00523
00524 if (!S_ISREG(st.st_mode)) {
00525 return;
00526 }
00527
00528 when = st.st_mtime;
00529 }
00530
00531
00532 AST_LIST_LOCK(&dirlist);
00533 AST_LIST_TRAVERSE(&dirlist, cur, list) {
00534 if (cur->mtime == when && !strcmp(filename, cur->name)) {
00535 AST_LIST_UNLOCK(&dirlist);
00536 return;
00537 }
00538 }
00539
00540 if ((res = when) > now || (res = scan_service(filename, now)) > 0) {
00541 if (!(new = ast_calloc(1, sizeof(*new) + strlen(filename) + 1))) {
00542 AST_LIST_UNLOCK(&dirlist);
00543 return;
00544 }
00545 new->mtime = res;
00546 strcpy(new->name, filename);
00547
00548 if (AST_LIST_EMPTY(&dirlist)) {
00549 AST_LIST_INSERT_HEAD(&dirlist, new, list);
00550 } else {
00551 int found = 0;
00552 AST_LIST_TRAVERSE_SAFE_BEGIN(&dirlist, cur, list) {
00553 if (cur->mtime > new->mtime) {
00554 AST_LIST_INSERT_BEFORE_CURRENT(new, list);
00555 found = 1;
00556 break;
00557 }
00558 }
00559 AST_LIST_TRAVERSE_SAFE_END
00560 if (!found) {
00561 AST_LIST_INSERT_TAIL(&dirlist, new, list);
00562 }
00563 }
00564 }
00565 AST_LIST_UNLOCK(&dirlist);
00566 }
00567
00568 #ifdef HAVE_INOTIFY
00569 static void queue_file_create(const char *filename)
00570 {
00571 struct direntry *cur;
00572
00573 AST_LIST_TRAVERSE(&createlist, cur, list) {
00574 if (!strcmp(cur->name, filename)) {
00575 return;
00576 }
00577 }
00578
00579 if (!(cur = ast_calloc(1, sizeof(*cur) + strlen(filename) + 1))) {
00580 return;
00581 }
00582 strcpy(cur->name, filename);
00583
00584 cur->mtime = time(NULL) + 2;
00585 AST_LIST_INSERT_TAIL(&createlist, cur, list);
00586 }
00587
00588 static void queue_file_open(const char *filename)
00589 {
00590 struct direntry *cur;
00591
00592 AST_LIST_TRAVERSE_SAFE_BEGIN(&createlist, cur, list) {
00593 if (!strcmp(cur->name, filename)) {
00594 AST_LIST_REMOVE_CURRENT(list);
00595 AST_LIST_INSERT_TAIL(&openlist, cur, list);
00596 break;
00597 }
00598 }
00599 AST_LIST_TRAVERSE_SAFE_END
00600 }
00601
00602 static void queue_created_files(void)
00603 {
00604 struct direntry *cur;
00605 time_t now = time(NULL);
00606
00607 AST_LIST_TRAVERSE_SAFE_BEGIN(&createlist, cur, list) {
00608 if (cur->mtime > now) {
00609 break;
00610 }
00611
00612 AST_LIST_REMOVE_CURRENT(list);
00613 queue_file(cur->name, 0);
00614 ast_free(cur);
00615 }
00616 AST_LIST_TRAVERSE_SAFE_END
00617 }
00618
00619 static void queue_file_write(const char *filename)
00620 {
00621 struct direntry *cur;
00622
00623 AST_LIST_TRAVERSE_SAFE_BEGIN(&openlist, cur, list) {
00624 if (!strcmp(cur->name, filename)) {
00625 AST_LIST_REMOVE_CURRENT(list);
00626 ast_free(cur);
00627 queue_file(filename, 0);
00628 break;
00629 }
00630 }
00631 AST_LIST_TRAVERSE_SAFE_END
00632 }
00633 #endif
00634
00635 static void *scan_thread(void *unused)
00636 {
00637 DIR *dir;
00638 struct dirent *de;
00639 time_t now;
00640 struct timespec ts = { .tv_sec = 1 };
00641 #ifdef HAVE_INOTIFY
00642 ssize_t res;
00643 int inotify_fd = inotify_init();
00644 struct inotify_event *iev;
00645 char buf[8192] __attribute__((aligned (sizeof(int))));
00646 struct pollfd pfd = { .fd = inotify_fd, .events = POLLIN };
00647 #else
00648 struct timespec nowait = { .tv_sec = 0, .tv_nsec = 1 };
00649 int inotify_fd = kqueue();
00650 struct kevent kev;
00651 struct kevent event;
00652 #endif
00653 struct direntry *cur;
00654
00655 while (!ast_fully_booted) {
00656 nanosleep(&ts, NULL);
00657 }
00658
00659 if (inotify_fd < 0) {
00660 ast_log(LOG_ERROR, "Unable to initialize "
00661 #ifdef HAVE_INOTIFY
00662 "inotify(7)"
00663 #else
00664 "kqueue(2)"
00665 #endif
00666 "\n");
00667 return NULL;
00668 }
00669
00670 #ifdef HAVE_INOTIFY
00671 inotify_add_watch(inotify_fd, qdir, IN_CREATE | IN_OPEN | IN_CLOSE_WRITE | IN_MOVED_TO);
00672 #endif
00673
00674
00675 if (!(dir = opendir(qdir))) {
00676 ast_log(LOG_ERROR, "Unable to open directory %s: %s\n", qdir, strerror(errno));
00677 return NULL;
00678 }
00679
00680 #ifndef HAVE_INOTIFY
00681 EV_SET(&kev, dirfd(dir), EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, NOTE_WRITE, 0, NULL);
00682 if (kevent(inotify_fd, &kev, 1, &event, 1, &nowait) < 0 && errno != 0) {
00683 ast_log(LOG_ERROR, "Unable to watch directory %s: %s\n", qdir, strerror(errno));
00684 }
00685 #endif
00686 now = time(NULL);
00687 while ((de = readdir(dir))) {
00688 queue_file(de->d_name, 0);
00689 }
00690
00691 #ifdef HAVE_INOTIFY
00692
00693 closedir(dir);
00694 #endif
00695
00696
00697 for (;;) {
00698 time_t next = AST_LIST_EMPTY(&dirlist) ? INT_MAX : AST_LIST_FIRST(&dirlist)->mtime;
00699
00700 time(&now);
00701 if (next > now) {
00702 #ifdef HAVE_INOTIFY
00703 int stage = 0;
00704
00705
00706 int waittime = next == INT_MAX ? -1 : (next - now) * 1000;
00707 if (!AST_LIST_EMPTY(&createlist)) {
00708 waittime = 1000;
00709 }
00710
00711 if ((res = poll(&pfd, 1, waittime)) > 0 && (stage = 1) &&
00712 (res = read(inotify_fd, &buf, sizeof(buf))) >= sizeof(*iev)) {
00713 ssize_t len = 0;
00714
00715 for (iev = (void *) buf; res >= sizeof(*iev); iev = (struct inotify_event *) (((char *) iev) + len)) {
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732 if (iev->mask & IN_CREATE) {
00733 queue_file_create(iev->name);
00734 } else if (iev->mask & IN_OPEN) {
00735 queue_file_open(iev->name);
00736 } else if (iev->mask & IN_CLOSE_WRITE) {
00737 queue_file_write(iev->name);
00738 } else if (iev->mask & IN_MOVED_TO) {
00739 queue_file(iev->name, 0);
00740 } else {
00741 ast_log(LOG_ERROR, "Unexpected event %d for file '%s'\n", (int) iev->mask, iev->name);
00742 }
00743
00744 len = sizeof(*iev) + iev->len;
00745 res -= len;
00746 }
00747 } else if (res < 0 && errno != EINTR && errno != EAGAIN) {
00748 ast_debug(1, "Got an error back from %s(2): %s\n", stage ? "read" : "poll", strerror(errno));
00749 }
00750 time(&now);
00751 }
00752 queue_created_files();
00753 #else
00754 int num_events;
00755
00756 if (next == INT_MAX) {
00757 num_events = kevent(inotify_fd, &kev, 1, &event, 1, NULL);
00758 } else {
00759 struct timespec ts2 = { .tv_sec = (unsigned long int)(next - now), .tv_nsec = 0 };
00760 num_events = kevent(inotify_fd, &kev, 1, &event, 1, &ts2);
00761 }
00762 if ((num_events < 0) || (event.flags == EV_ERROR)) {
00763 ast_debug(10, "KEvent error %s\n", strerror(errno));
00764 continue;
00765 } else if (num_events == 0) {
00766
00767 continue;
00768 } else {
00769
00770 rewinddir(dir);
00771 while ((de = readdir(dir))) {
00772 queue_file(de->d_name, 0);
00773 }
00774 }
00775 time(&now);
00776 }
00777 #endif
00778
00779
00780 AST_LIST_LOCK(&dirlist);
00781 while (!AST_LIST_EMPTY(&dirlist) && AST_LIST_FIRST(&dirlist)->mtime <= now) {
00782 cur = AST_LIST_REMOVE_HEAD(&dirlist, list);
00783 queue_file(cur->name, cur->mtime);
00784 ast_free(cur);
00785 }
00786 AST_LIST_UNLOCK(&dirlist);
00787 }
00788 return NULL;
00789 }
00790
00791 #else
00792 static void *scan_thread(void *unused)
00793 {
00794 struct stat st;
00795 DIR *dir;
00796 struct dirent *de;
00797 char fn[256];
00798 int res;
00799 int force_poll = 1;
00800 time_t last = 0;
00801 time_t next = 0;
00802 time_t now;
00803 struct timespec ts = { .tv_sec = 1 };
00804
00805 while (!ast_fully_booted) {
00806 nanosleep(&ts, NULL);
00807 }
00808
00809 for (;;) {
00810
00811 nanosleep(&ts, NULL);
00812 time(&now);
00813
00814 if (stat(qdir, &st)) {
00815 ast_log(LOG_WARNING, "Unable to stat %s\n", qdir);
00816 continue;
00817 }
00818
00819
00820 if (!force_poll && st.st_mtime == last && (!next || now < next)) {
00821
00822
00823
00824
00825 continue;
00826 }
00827
00828 #if 0
00829 printf("atime: %ld, mtime: %ld, ctime: %ld\n", st.st_atime, st.st_mtime, st.st_ctime);
00830 printf("Ooh, something changed / timeout\n");
00831 #endif
00832
00833 if (!(dir = opendir(qdir))) {
00834 ast_log(LOG_WARNING, "Unable to open directory %s: %s\n", qdir, strerror(errno));
00835 continue;
00836 }
00837
00838
00839
00840
00841
00842
00843
00844 force_poll = (st.st_mtime == now);
00845
00846 next = 0;
00847 last = st.st_mtime;
00848 while ((de = readdir(dir))) {
00849 snprintf(fn, sizeof(fn), "%s/%s", qdir, de->d_name);
00850 if (stat(fn, &st)) {
00851 ast_log(LOG_WARNING, "Unable to stat %s: %s\n", fn, strerror(errno));
00852 continue;
00853 }
00854 if (!S_ISREG(st.st_mode)) {
00855
00856 continue;
00857 }
00858 if (st.st_mtime <= now) {
00859 res = scan_service(fn, now);
00860 if (res > 0) {
00861
00862 if (!next || res < next) {
00863
00864 next = res;
00865 }
00866 } else if (res) {
00867 ast_log(LOG_WARNING, "Failed to scan service '%s'\n", fn);
00868 } else if (!next) {
00869
00870 next = st.st_mtime;
00871 }
00872 } else {
00873
00874 if (!next || st.st_mtime < next) {
00875
00876 next = st.st_mtime;
00877 }
00878 }
00879 }
00880 closedir(dir);
00881 }
00882 return NULL;
00883 }
00884 #endif
00885
00886 static int unload_module(void)
00887 {
00888 return -1;
00889 }
00890
00891 static int load_module(void)
00892 {
00893 pthread_t thread;
00894 int ret;
00895 snprintf(qdir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing");
00896 if (ast_mkdir(qdir, 0777)) {
00897 ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool disabled\n", qdir);
00898 return AST_MODULE_LOAD_DECLINE;
00899 }
00900 snprintf(qdonedir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing_done");
00901
00902 if ((ret = ast_pthread_create_detached_background(&thread, NULL, scan_thread, NULL))) {
00903 ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
00904 return AST_MODULE_LOAD_FAILURE;
00905 }
00906
00907 return AST_MODULE_LOAD_SUCCESS;
00908 }
00909
00910 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Outgoing Spool Support");