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