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