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: 211580 $")
00028
00029 #include <sys/stat.h>
00030 #include <time.h>
00031 #include <utime.h>
00032 #include <dirent.h>
00033
00034 #include "asterisk/paths.h"
00035 #include "asterisk/lock.h"
00036 #include "asterisk/file.h"
00037 #include "asterisk/logger.h"
00038 #include "asterisk/channel.h"
00039 #include "asterisk/callerid.h"
00040 #include "asterisk/pbx.h"
00041 #include "asterisk/module.h"
00042 #include "asterisk/utils.h"
00043 #include "asterisk/options.h"
00044
00045
00046
00047
00048
00049
00050 enum {
00051
00052
00053
00054
00055 SPOOL_FLAG_ALWAYS_DELETE = (1 << 0),
00056
00057 SPOOL_FLAG_ARCHIVE = (1 << 1)
00058 };
00059
00060 static char qdir[255];
00061 static char qdonedir[255];
00062
00063 struct outgoing {
00064 int retries;
00065 int maxretries;
00066 int retrytime;
00067 int waittime;
00068 long callingpid;
00069 int format;
00070 AST_DECLARE_STRING_FIELDS (
00071 AST_STRING_FIELD(fn);
00072 AST_STRING_FIELD(tech);
00073 AST_STRING_FIELD(dest);
00074 AST_STRING_FIELD(app);
00075 AST_STRING_FIELD(data);
00076 AST_STRING_FIELD(exten);
00077 AST_STRING_FIELD(context);
00078 AST_STRING_FIELD(cid_num);
00079 AST_STRING_FIELD(cid_name);
00080 AST_STRING_FIELD(account);
00081 );
00082 int priority;
00083 struct ast_variable *vars;
00084 int maxlen;
00085 struct ast_flags options;
00086 };
00087
00088 static int init_outgoing(struct outgoing *o)
00089 {
00090 o->priority = 1;
00091 o->retrytime = 300;
00092 o->waittime = 45;
00093 o->format = AST_FORMAT_SLINEAR;
00094 ast_set_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE);
00095 if (ast_string_field_init(o, 128)) {
00096 return -1;
00097 }
00098 return 0;
00099 }
00100
00101 static void free_outgoing(struct outgoing *o)
00102 {
00103 if (o->vars) {
00104 ast_variables_destroy(o->vars);
00105 }
00106 ast_string_field_free_memory(o);
00107 ast_free(o);
00108 }
00109
00110 static int apply_outgoing(struct outgoing *o, char *fn, FILE *f)
00111 {
00112 char buf[256];
00113 char *c, *c2;
00114 int lineno = 0;
00115 struct ast_variable *var, *last = o->vars;
00116
00117 while (last && last->next) {
00118 last = last->next;
00119 }
00120
00121 while(fgets(buf, sizeof(buf), f)) {
00122 lineno++;
00123
00124 c = buf;
00125 while ((c = strchr(c, '#'))) {
00126 if ((c == buf) || (*(c-1) == ' ') || (*(c-1) == '\t'))
00127 *c = '\0';
00128 else
00129 c++;
00130 }
00131
00132 c = buf;
00133 while ((c = strchr(c, ';'))) {
00134 if ((c > buf) && (c[-1] == '\\')) {
00135 memmove(c - 1, c, strlen(c) + 1);
00136 c++;
00137 } else {
00138 *c = '\0';
00139 break;
00140 }
00141 }
00142
00143
00144 while(!ast_strlen_zero(buf) && buf[strlen(buf) - 1] < 33)
00145 buf[strlen(buf) - 1] = '\0';
00146 if (!ast_strlen_zero(buf)) {
00147 c = strchr(buf, ':');
00148 if (c) {
00149 *c = '\0';
00150 c++;
00151 while ((*c) && (*c < 33))
00152 c++;
00153 #if 0
00154 printf("'%s' is '%s' at line %d\n", buf, c, lineno);
00155 #endif
00156 if (!strcasecmp(buf, "channel")) {
00157 if ((c2 = strchr(c, '/'))) {
00158 *c2 = '\0';
00159 c2++;
00160 ast_string_field_set(o, tech, c);
00161 ast_string_field_set(o, dest, c2);
00162 } else {
00163 ast_log(LOG_NOTICE, "Channel should be in form Tech/Dest at line %d of %s\n", lineno, fn);
00164 }
00165 } else if (!strcasecmp(buf, "callerid")) {
00166 char cid_name[80] = {0}, cid_num[80] = {0};
00167 ast_callerid_split(c, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
00168 ast_string_field_set(o, cid_num, cid_num);
00169 ast_string_field_set(o, cid_name, cid_name);
00170 } else if (!strcasecmp(buf, "application")) {
00171 ast_string_field_set(o, app, c);
00172 } else if (!strcasecmp(buf, "data")) {
00173 ast_string_field_set(o, data, c);
00174 } else if (!strcasecmp(buf, "maxretries")) {
00175 if (sscanf(c, "%30d", &o->maxretries) != 1) {
00176 ast_log(LOG_WARNING, "Invalid max retries at line %d of %s\n", lineno, fn);
00177 o->maxretries = 0;
00178 }
00179 } else if (!strcasecmp(buf, "codecs")) {
00180 ast_parse_allow_disallow(NULL, &o->format, c, 1);
00181 } else if (!strcasecmp(buf, "context")) {
00182 ast_string_field_set(o, context, c);
00183 } else if (!strcasecmp(buf, "extension")) {
00184 ast_string_field_set(o, exten, c);
00185 } else if (!strcasecmp(buf, "priority")) {
00186 if ((sscanf(c, "%30d", &o->priority) != 1) || (o->priority < 1)) {
00187 ast_log(LOG_WARNING, "Invalid priority at line %d of %s\n", lineno, fn);
00188 o->priority = 1;
00189 }
00190 } else if (!strcasecmp(buf, "retrytime")) {
00191 if ((sscanf(c, "%30d", &o->retrytime) != 1) || (o->retrytime < 1)) {
00192 ast_log(LOG_WARNING, "Invalid retrytime at line %d of %s\n", lineno, fn);
00193 o->retrytime = 300;
00194 }
00195 } else if (!strcasecmp(buf, "waittime")) {
00196 if ((sscanf(c, "%30d", &o->waittime) != 1) || (o->waittime < 1)) {
00197 ast_log(LOG_WARNING, "Invalid waittime at line %d of %s\n", lineno, fn);
00198 o->waittime = 45;
00199 }
00200 } else if (!strcasecmp(buf, "retry")) {
00201 o->retries++;
00202 } else if (!strcasecmp(buf, "startretry")) {
00203 if (sscanf(c, "%30ld", &o->callingpid) != 1) {
00204 ast_log(LOG_WARNING, "Unable to retrieve calling PID!\n");
00205 o->callingpid = 0;
00206 }
00207 } else if (!strcasecmp(buf, "endretry") || !strcasecmp(buf, "abortretry")) {
00208 o->callingpid = 0;
00209 o->retries++;
00210 } else if (!strcasecmp(buf, "delayedretry")) {
00211 } else if (!strcasecmp(buf, "setvar") || !strcasecmp(buf, "set")) {
00212 c2 = c;
00213 strsep(&c2, "=");
00214 if (c2) {
00215 var = ast_variable_new(c, c2, fn);
00216 if (var) {
00217
00218 if (last) {
00219 last->next = var;
00220 } else {
00221 o->vars = var;
00222 }
00223 last = var;
00224 }
00225 } else
00226 ast_log(LOG_WARNING, "Malformed \"%s\" argument. Should be \"%s: variable=value\"\n", buf, buf);
00227 } else if (!strcasecmp(buf, "account")) {
00228 ast_string_field_set(o, account, c);
00229 } else if (!strcasecmp(buf, "alwaysdelete")) {
00230 ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ALWAYS_DELETE);
00231 } else if (!strcasecmp(buf, "archive")) {
00232 ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ARCHIVE);
00233 } else {
00234 ast_log(LOG_WARNING, "Unknown keyword '%s' at line %d of %s\n", buf, lineno, fn);
00235 }
00236 } else
00237 ast_log(LOG_NOTICE, "Syntax error at line %d of %s\n", lineno, fn);
00238 }
00239 }
00240 ast_string_field_set(o, fn, fn);
00241 if (ast_strlen_zero(o->tech) || ast_strlen_zero(o->dest) || (ast_strlen_zero(o->app) && ast_strlen_zero(o->exten))) {
00242 ast_log(LOG_WARNING, "At least one of app or extension must be specified, along with tech and dest in file %s\n", fn);
00243 return -1;
00244 }
00245 return 0;
00246 }
00247
00248 static void safe_append(struct outgoing *o, time_t now, char *s)
00249 {
00250 int fd;
00251 FILE *f;
00252 struct utimbuf tbuf;
00253
00254 if ((fd = open(o->fn, O_WRONLY | O_APPEND)) < 0)
00255 return;
00256
00257 if ((f = fdopen(fd, "a"))) {
00258 fprintf(f, "\n%s: %ld %d (%ld)\n", s, (long)ast_mainpid, o->retries, (long) now);
00259 fclose(f);
00260 } else
00261 close(fd);
00262
00263
00264 tbuf.actime = now;
00265 tbuf.modtime = now + o->retrytime;
00266 if (utime(o->fn, &tbuf))
00267 ast_log(LOG_WARNING, "Unable to set utime on %s: %s\n", o->fn, strerror(errno));
00268 }
00269
00270
00271
00272
00273
00274
00275
00276 static int remove_from_queue(struct outgoing *o, const char *status)
00277 {
00278 int fd;
00279 FILE *f;
00280 char newfn[256];
00281 const char *bname;
00282
00283 if (!ast_test_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE)) {
00284 struct stat current_file_status;
00285
00286 if (!stat(o->fn, ¤t_file_status)) {
00287 if (time(NULL) < current_file_status.st_mtime)
00288 return 0;
00289 }
00290 }
00291
00292 if (!ast_test_flag(&o->options, SPOOL_FLAG_ARCHIVE)) {
00293 unlink(o->fn);
00294 return 0;
00295 }
00296
00297 if (ast_mkdir(qdonedir, 0777)) {
00298 ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool archiving disabled\n", qdonedir);
00299 unlink(o->fn);
00300 return -1;
00301 }
00302
00303 if ((fd = open(o->fn, O_WRONLY | O_APPEND))) {
00304 if ((f = fdopen(fd, "a"))) {
00305 fprintf(f, "Status: %s\n", status);
00306 fclose(f);
00307 } else
00308 close(fd);
00309 }
00310
00311 if (!(bname = strrchr(o->fn, '/')))
00312 bname = o->fn;
00313 else
00314 bname++;
00315 snprintf(newfn, sizeof(newfn), "%s/%s", qdonedir, bname);
00316
00317 unlink(newfn);
00318 if (rename(o->fn, newfn) != 0) {
00319 unlink(o->fn);
00320 return -1;
00321 } else
00322 return 0;
00323 }
00324
00325 static void *attempt_thread(void *data)
00326 {
00327 struct outgoing *o = data;
00328 int res, reason;
00329 if (!ast_strlen_zero(o->app)) {
00330 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);
00331 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);
00332 o->vars = NULL;
00333 } else {
00334 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);
00335 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);
00336 o->vars = NULL;
00337 }
00338 if (res) {
00339 ast_log(LOG_NOTICE, "Call failed to go through, reason (%d) %s\n", reason, ast_channel_reason2str(reason));
00340 if (o->retries >= o->maxretries + 1) {
00341
00342 ast_log(LOG_EVENT, "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" : "");
00343 remove_from_queue(o, "Expired");
00344 } else {
00345
00346 safe_append(o, time(NULL), "EndRetry");
00347 }
00348 } else {
00349 ast_log(LOG_NOTICE, "Call completed to %s/%s\n", o->tech, o->dest);
00350 ast_log(LOG_EVENT, "Queued call to %s/%s completed\n", o->tech, o->dest);
00351 remove_from_queue(o, "Completed");
00352 }
00353 free_outgoing(o);
00354 return NULL;
00355 }
00356
00357 static void launch_service(struct outgoing *o)
00358 {
00359 pthread_t t;
00360 int ret;
00361
00362 if ((ret = ast_pthread_create_detached(&t, NULL, attempt_thread, o))) {
00363 ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
00364 free_outgoing(o);
00365 }
00366 }
00367
00368 static int scan_service(char *fn, time_t now, time_t atime)
00369 {
00370 struct outgoing *o = NULL;
00371 FILE *f;
00372 int res = 0;
00373
00374 if (!(o = ast_calloc(1, sizeof(*o)))) {
00375 ast_log(LOG_WARNING, "Out of memory ;(\n");
00376 return -1;
00377 }
00378
00379 if (init_outgoing(o)) {
00380
00381
00382
00383
00384 ast_free(o);
00385 return -1;
00386 }
00387
00388
00389 if (!(f = fopen(fn, "r+"))) {
00390 remove_from_queue(o, "Failed");
00391 free_outgoing(o);
00392 ast_log(LOG_WARNING, "Unable to open %s: %s, deleting\n", fn, strerror(errno));
00393 return -1;
00394 }
00395
00396
00397 if (apply_outgoing(o, fn, f)) {
00398 remove_from_queue(o, "Failed");
00399 free_outgoing(o);
00400 ast_log(LOG_WARNING, "Invalid file contents in %s, deleting\n", fn);
00401 fclose(f);
00402 return -1;
00403 }
00404
00405 #if 0
00406 printf("Filename: %s, Retries: %d, max: %d\n", fn, o->retries, o->maxretries);
00407 #endif
00408 fclose(f);
00409 if (o->retries <= o->maxretries) {
00410 now += o->retrytime;
00411 if (o->callingpid && (o->callingpid == ast_mainpid)) {
00412 safe_append(o, time(NULL), "DelayedRetry");
00413 ast_log(LOG_DEBUG, "Delaying retry since we're currently running '%s'\n", o->fn);
00414 free_outgoing(o);
00415 } else {
00416
00417 o->retries++;
00418
00419
00420 if (o->callingpid)
00421 safe_append(o, time(NULL), "AbortRetry");
00422
00423 safe_append(o, now, "StartRetry");
00424 launch_service(o);
00425 }
00426 res = now;
00427 } else {
00428 ast_log(LOG_EVENT, "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" : "");
00429 remove_from_queue(o, "Expired");
00430 free_outgoing(o);
00431 }
00432
00433 return res;
00434 }
00435
00436 static void *scan_thread(void *unused)
00437 {
00438 struct stat st;
00439 DIR *dir;
00440 struct dirent *de;
00441 char fn[256];
00442 int res;
00443 time_t last = 0, next = 0, now;
00444 struct timespec ts = { .tv_sec = 1 };
00445
00446 while (!ast_fully_booted) {
00447 nanosleep(&ts, NULL);
00448 }
00449
00450 for(;;) {
00451
00452 nanosleep(&ts, NULL);
00453 time(&now);
00454
00455 if (stat(qdir, &st)) {
00456 ast_log(LOG_WARNING, "Unable to stat %s\n", qdir);
00457 continue;
00458 }
00459
00460
00461 if ((st.st_mtime == last) && (next && (next > now)))
00462 continue;
00463
00464 #if 0
00465 printf("atime: %ld, mtime: %ld, ctime: %ld\n", st.st_atime, st.st_mtime, st.st_ctime);
00466 printf("Ooh, something changed / timeout\n");
00467 #endif
00468 next = 0;
00469 last = st.st_mtime;
00470
00471 if (!(dir = opendir(qdir))) {
00472 ast_log(LOG_WARNING, "Unable to open directory %s: %s\n", qdir, strerror(errno));
00473 continue;
00474 }
00475
00476 while ((de = readdir(dir))) {
00477 snprintf(fn, sizeof(fn), "%s/%s", qdir, de->d_name);
00478 if (stat(fn, &st)) {
00479 ast_log(LOG_WARNING, "Unable to stat %s: %s\n", fn, strerror(errno));
00480 continue;
00481 }
00482 if (!S_ISREG(st.st_mode))
00483 continue;
00484 if (st.st_mtime <= now) {
00485 res = scan_service(fn, now, st.st_atime);
00486 if (res > 0) {
00487
00488 if (!next || (res < next)) {
00489 next = res;
00490 }
00491 } else if (res) {
00492 ast_log(LOG_WARNING, "Failed to scan service '%s'\n", fn);
00493 } else if (!next) {
00494
00495 next = st.st_mtime;
00496 }
00497 } else {
00498
00499 if (!next || (st.st_mtime < next))
00500 next = st.st_mtime;
00501 }
00502 }
00503 closedir(dir);
00504 }
00505 return NULL;
00506 }
00507
00508 static int unload_module(void)
00509 {
00510 return -1;
00511 }
00512
00513 static int load_module(void)
00514 {
00515 pthread_t thread;
00516 int ret;
00517 snprintf(qdir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing");
00518 if (ast_mkdir(qdir, 0777)) {
00519 ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool disabled\n", qdir);
00520 return AST_MODULE_LOAD_DECLINE;
00521 }
00522 snprintf(qdonedir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing_done");
00523
00524 if ((ret = ast_pthread_create_detached_background(&thread, NULL, scan_thread, NULL))) {
00525 ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
00526 return AST_MODULE_LOAD_FAILURE;
00527 }
00528
00529 return AST_MODULE_LOAD_SUCCESS;
00530 }
00531
00532 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Outgoing Spool Support");