Review Board 1.7.16


Convert pbx_spool to use string fields

Review Request #168 - Created Feb. 19, 2009 and submitted

Mark Michelson
/trunk
Reviewers
asterisk-dev
Asterisk
Simple. Convert all the static buffers used in pbx_spool to be string fields. This will dramatically lower the amount of memory used when processing a call file.
It compiles.

Changes between revision 1 and 2

1 2 3
1 2 3

  1. /trunk/pbx/pbx_spool.c: Loading...
/trunk/pbx/pbx_spool.c
Diff Revision 1 Diff Revision 2
1
/*
1
/*
2
 * Asterisk -- An open source telephony toolkit.
2
 * Asterisk -- An open source telephony toolkit.
3
 *
3
 *
4
 * Copyright (C) 1999 - 2005, Digium, Inc.
4
 * Copyright (C) 1999 - 2005, Digium, Inc.
5
 *
5
 *
6
 * Mark Spencer <markster@digium.com>
6
 * Mark Spencer <markster@digium.com>
7
 *
7
 *
8
 * See http://www.asterisk.org for more information about
8
 * See http://www.asterisk.org for more information about
9
 * the Asterisk project. Please do not directly contact
9
 * the Asterisk project. Please do not directly contact
10
 * any of the maintainers of this project for assistance;
10
 * any of the maintainers of this project for assistance;
11
 * the project provides a web site, mailing lists and IRC
11
 * the project provides a web site, mailing lists and IRC
12
 * channels for your use.
12
 * channels for your use.
13
 *
13
 *
14
 * This program is free software, distributed under the terms of
14
 * This program is free software, distributed under the terms of
15
 * the GNU General Public License Version 2. See the LICENSE file
15
 * the GNU General Public License Version 2. See the LICENSE file
16
 * at the top of the source tree.
16
 * at the top of the source tree.
17
 */
17
 */
18

    
   
18

   
19
/*! \file
19
/*! \file
20
 *
20
 *
21
 * \brief Full-featured outgoing call spool support
21
 * \brief Full-featured outgoing call spool support
22
 * 
22
 * 
23
 */
23
 */
24

    
   
24

   
25
#include "asterisk.h"
25
#include "asterisk.h"
26

    
   
26

   
27
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
27
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
28

    
   
28

   
29
#include <sys/stat.h>
29
#include <sys/stat.h>
30
#include <time.h>
30
#include <time.h>
31
#include <utime.h>
31
#include <utime.h>
32
#include <dirent.h>
32
#include <dirent.h>
33

    
   
33

   
34
#include "asterisk/paths.h"	/* use ast_config_AST_SPOOL_DIR */
34
#include "asterisk/paths.h"	/* use ast_config_AST_SPOOL_DIR */
35
#include "asterisk/lock.h"
35
#include "asterisk/lock.h"
36
#include "asterisk/file.h"
36
#include "asterisk/file.h"
37
#include "asterisk/logger.h"
37
#include "asterisk/logger.h"
38
#include "asterisk/channel.h"
38
#include "asterisk/channel.h"
39
#include "asterisk/callerid.h"
39
#include "asterisk/callerid.h"
40
#include "asterisk/pbx.h"
40
#include "asterisk/pbx.h"
41
#include "asterisk/module.h"
41
#include "asterisk/module.h"
42
#include "asterisk/utils.h"
42
#include "asterisk/utils.h"
43
#include "asterisk/options.h"
43
#include "asterisk/options.h"
44

    
   
44

   
45
/*
45
/*
46
 * pbx_spool is similar in spirit to qcall, but with substantially enhanced functionality...
46
 * pbx_spool is similar in spirit to qcall, but with substantially enhanced functionality...
47
 * The spool file contains a header 
47
 * The spool file contains a header 
48
 */
48
 */
49

    
   
49

   
50
enum {
50
enum {
51
	/*! Always delete the call file after a call succeeds or the
51
	/*! Always delete the call file after a call succeeds or the
52
	 * maximum number of retries is exceeded, even if the
52
	 * maximum number of retries is exceeded, even if the
53
	 * modification time of the call file is in the future.
53
	 * modification time of the call file is in the future.
54
	 */
54
	 */
55
	SPOOL_FLAG_ALWAYS_DELETE = (1 << 0),
55
	SPOOL_FLAG_ALWAYS_DELETE = (1 << 0),
56
	/* Don't unlink the call file after processing, move in qdonedir */
56
	/* Don't unlink the call file after processing, move in qdonedir */
57
	SPOOL_FLAG_ARCHIVE = (1 << 1)
57
	SPOOL_FLAG_ARCHIVE = (1 << 1)
58
};
58
};
59

    
   
59

   
60
static char qdir[255];
60
static char qdir[255];
61
static char qdonedir[255];
61
static char qdonedir[255];
62

    
   
62

   
63
struct outgoing {
63
struct outgoing {
64
	int retries;                              /*!< Current number of retries */
64
	int retries;                              /*!< Current number of retries */
65
	int maxretries;                           /*!< Maximum number of retries permitted */
65
	int maxretries;                           /*!< Maximum number of retries permitted */
66
	int retrytime;                            /*!< How long to wait between retries (in seconds) */
66
	int retrytime;                            /*!< How long to wait between retries (in seconds) */
67
	int waittime;                             /*!< How long to wait for an answer */
67
	int waittime;                             /*!< How long to wait for an answer */
68
	long callingpid;                          /*!< PID which is currently calling */
68
	long callingpid;                          /*!< PID which is currently calling */
69
	int format;                               /*!< Formats (codecs) for this call */
69
	int format;                               /*!< Formats (codecs) for this call */
70
	AST_DECLARE_STRING_FIELDS (
70
	AST_DECLARE_STRING_FIELDS (
71
		AST_STRING_FIELD(fn);                 /*!< File name of call file */
71
		AST_STRING_FIELD(fn);                 /*!< File name of call file */
72
		AST_STRING_FIELD(tech);               /*!< Which channel technology to use for outgoing call */
72
		AST_STRING_FIELD(tech);               /*!< Which channel technology to use for outgoing call */
73
		AST_STRING_FIELD(dest);               /*!< Which device/line to use for outgoing call */
73
		AST_STRING_FIELD(dest);               /*!< Which device/line to use for outgoing call */
74
		AST_STRING_FIELD(app);                /*!< If application: Application name */
74
		AST_STRING_FIELD(app);                /*!< If application: Application name */
75
		AST_STRING_FIELD(data);               /*!< If application: Application data */
75
		AST_STRING_FIELD(data);               /*!< If application: Application data */
76
		AST_STRING_FIELD(exten);              /*!< If extension/context/priority: Extension in dialplan */
76
		AST_STRING_FIELD(exten);              /*!< If extension/context/priority: Extension in dialplan */
77
		AST_STRING_FIELD(context);            /*!< If extension/context/priority: Dialplan context */
77
		AST_STRING_FIELD(context);            /*!< If extension/context/priority: Dialplan context */
78
		AST_STRING_FIELD(cid_num);            /*!< CallerID Information: Number/extension */
78
		AST_STRING_FIELD(cid_num);            /*!< CallerID Information: Number/extension */
79
		AST_STRING_FIELD(cid_name);           /*!< CallerID Information: Name */
79
		AST_STRING_FIELD(cid_name);           /*!< CallerID Information: Name */
80
		AST_STRING_FIELD(account);            /*!< account code */
80
		AST_STRING_FIELD(account);            /*!< account code */
81
	);
81
	);
82
	int priority;                             /*!< If extension/context/priority: Dialplan priority */
82
	int priority;                             /*!< If extension/context/priority: Dialplan priority */
83
	struct ast_variable *vars;                /*!< Variables and Functions */
83
	struct ast_variable *vars;                /*!< Variables and Functions */
84
	int maxlen;                               /*!< Maximum length of call */
84
	int maxlen;                               /*!< Maximum length of call */
85
	struct ast_flags options;                 /*!< options */
85
	struct ast_flags options;                 /*!< options */
86
};
86
};
87

    
   
87

   
88
static int init_outgoing(struct outgoing *o)
88
static int init_outgoing(struct outgoing *o)
89
{
89
{
90
	o->priority = 1;
90
	o->priority = 1;
91
	o->retrytime = 300;
91
	o->retrytime = 300;
92
	o->waittime = 45;
92
	o->waittime = 45;
93
	o->format = AST_FORMAT_SLINEAR;
93
	o->format = AST_FORMAT_SLINEAR;
94
	ast_set_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE);
94
	ast_set_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE);
95
	if (!(ast_string_field_init(o, 128))) {
95
	if (!(ast_string_field_init(o, 128))) {
96
		return -1;
96
		return -1;
97
	}
97
	}
98
	return 0;
98
	return 0;
99
}
99
}
100

    
   
100

   
101
static void free_outgoing(struct outgoing *o)
101
static void free_outgoing(struct outgoing *o)
102
{
102
{
103
	if (o->vars) {
103
	if (o->vars) {
104
		ast_variables_destroy(o->vars);
104
		ast_variables_destroy(o->vars);
105
	}
105
	}
106
	ast_string_field_free_memory(o);
106
	ast_string_field_free_memory(o);
107
	ast_free(o);
107
	ast_free(o);
108
}
108
}
109

    
   
109

   
110
static int apply_outgoing(struct outgoing *o, char *fn, FILE *f)
110
static int apply_outgoing(struct outgoing *o, char *fn, FILE *f)
111
{
111
{
112
	char buf[256];
112
	char buf[256];
113
	char *c, *c2;
113
	char *c, *c2;
114
	int lineno = 0;
114
	int lineno = 0;
115
	struct ast_variable *var, *last = o->vars;
115
	struct ast_variable *var, *last = o->vars;
116

    
   
116

   
117
	while (last && last->next) {
117
	while (last && last->next) {
118
		last = last->next;
118
		last = last->next;
119
	}
119
	}
120

    
   
120

   
121
	while(fgets(buf, sizeof(buf), f)) {
121
	while(fgets(buf, sizeof(buf), f)) {
122
		lineno++;
122
		lineno++;
123
		/* Trim comments */
123
		/* Trim comments */
124
		c = buf;
124
		c = buf;
125
		while ((c = strchr(c, '#'))) {
125
		while ((c = strchr(c, '#'))) {
126
			if ((c == buf) || (*(c-1) == ' ') || (*(c-1) == '\t'))
126
			if ((c == buf) || (*(c-1) == ' ') || (*(c-1) == '\t'))
127
				*c = '\0';
127
				*c = '\0';
128
			else
128
			else
129
				c++;
129
				c++;
130
		}
130
		}
131

    
   
131

   
132
		c = buf;
132
		c = buf;
133
		while ((c = strchr(c, ';'))) {
133
		while ((c = strchr(c, ';'))) {
134
			if ((c > buf) && (c[-1] == '\\')) {
134
			if ((c > buf) && (c[-1] == '\\')) {
135
				memmove(c - 1, c, strlen(c) + 1);
135
				memmove(c - 1, c, strlen(c) + 1);
136
				c++;
136
				c++;
137
			} else {
137
			} else {
138
				*c = '\0';
138
				*c = '\0';
139
				break;
139
				break;
140
			}
140
			}
141
		}
141
		}
142

    
   
142

   
143
		/* Trim trailing white space */
143
		/* Trim trailing white space */
144
		while(!ast_strlen_zero(buf) && buf[strlen(buf) - 1] < 33)
144
		while(!ast_strlen_zero(buf) && buf[strlen(buf) - 1] < 33)
145
			buf[strlen(buf) - 1] = '\0';
145
			buf[strlen(buf) - 1] = '\0';
146
		if (!ast_strlen_zero(buf)) {
146
		if (!ast_strlen_zero(buf)) {
147
			c = strchr(buf, ':');
147
			c = strchr(buf, ':');
148
			if (c) {
148
			if (c) {
149
				*c = '\0';
149
				*c = '\0';
150
				c++;
150
				c++;
151
				while ((*c) && (*c < 33))
151
				while ((*c) && (*c < 33))
152
					c++;
152
					c++;
153
#if 0
153
#if 0
154
				printf("'%s' is '%s' at line %d\n", buf, c, lineno);
154
				printf("'%s' is '%s' at line %d\n", buf, c, lineno);
155
#endif
155
#endif
156
				if (!strcasecmp(buf, "channel")) {
156
				if (!strcasecmp(buf, "channel")) {
157
					if ((c2 = strchr(c, '/'))) {
157
					if ((c2 = strchr(c, '/'))) {
158
						*c2 = '\0';
158
						*c2 = '\0';
159
						c2++;
159
						c2++;
160
						ast_string_field_set(o, tech, c);
160
						ast_string_field_set(o, tech, c);
161
						ast_string_field_set(o, dest, c2);
161
						ast_string_field_set(o, dest, c2);
162
					} else {
162
					} else {
163
						ast_log(LOG_NOTICE, "Channel should be in form Tech/Dest at line %d of %s\n", lineno, fn);
163
						ast_log(LOG_NOTICE, "Channel should be in form Tech/Dest at line %d of %s\n", lineno, fn);
164
					}
164
					}
165
				} else if (!strcasecmp(buf, "callerid")) {
165
				} else if (!strcasecmp(buf, "callerid")) {
166
					char cid_name[80] = {0}, cid_num[80] = {0};
166
					char cid_name[80] = {0}, cid_num[80] = {0};
167
					ast_callerid_split(c, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
167
					ast_callerid_split(c, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
168
					ast_string_field_set(o, cid_num, cid_num);
168
					ast_string_field_set(o, cid_num, cid_num);
169
					ast_string_field_set(o, cid_name, cid_name);
169
					ast_string_field_set(o, cid_name, cid_name);
170
				} else if (!strcasecmp(buf, "application")) {
170
				} else if (!strcasecmp(buf, "application")) {
171
					ast_string_field_set(o, app, c);
171
					ast_string_field_set(o, app, c);
172
				} else if (!strcasecmp(buf, "data")) {
172
				} else if (!strcasecmp(buf, "data")) {
173
					ast_string_field_set(o, data, c);
173
					ast_string_field_set(o, data, c);
174
				} else if (!strcasecmp(buf, "maxretries")) {
174
				} else if (!strcasecmp(buf, "maxretries")) {
175
					if (sscanf(c, "%d", &o->maxretries) != 1) {
175
					if (sscanf(c, "%d", &o->maxretries) != 1) {
176
						ast_log(LOG_WARNING, "Invalid max retries at line %d of %s\n", lineno, fn);
176
						ast_log(LOG_WARNING, "Invalid max retries at line %d of %s\n", lineno, fn);
177
						o->maxretries = 0;
177
						o->maxretries = 0;
178
					}
178
					}
179
				} else if (!strcasecmp(buf, "codecs")) {
179
				} else if (!strcasecmp(buf, "codecs")) {
180
					ast_parse_allow_disallow(NULL, &o->format, c, 1);
180
					ast_parse_allow_disallow(NULL, &o->format, c, 1);
181
				} else if (!strcasecmp(buf, "context")) {
181
				} else if (!strcasecmp(buf, "context")) {
182
					ast_string_field_set(o, context, c);
182
					ast_string_field_set(o, context, c);
183
				} else if (!strcasecmp(buf, "extension")) {
183
				} else if (!strcasecmp(buf, "extension")) {
184
					ast_string_field_set(o, exten, c);
184
					ast_string_field_set(o, exten, c);
185
				} else if (!strcasecmp(buf, "priority")) {
185
				} else if (!strcasecmp(buf, "priority")) {
186
					if ((sscanf(c, "%d", &o->priority) != 1) || (o->priority < 1)) {
186
					if ((sscanf(c, "%d", &o->priority) != 1) || (o->priority < 1)) {
187
						ast_log(LOG_WARNING, "Invalid priority at line %d of %s\n", lineno, fn);
187
						ast_log(LOG_WARNING, "Invalid priority at line %d of %s\n", lineno, fn);
188
						o->priority = 1;
188
						o->priority = 1;
189
					}
189
					}
190
				} else if (!strcasecmp(buf, "retrytime")) {
190
				} else if (!strcasecmp(buf, "retrytime")) {
191
					if ((sscanf(c, "%d", &o->retrytime) != 1) || (o->retrytime < 1)) {
191
					if ((sscanf(c, "%d", &o->retrytime) != 1) || (o->retrytime < 1)) {
192
						ast_log(LOG_WARNING, "Invalid retrytime at line %d of %s\n", lineno, fn);
192
						ast_log(LOG_WARNING, "Invalid retrytime at line %d of %s\n", lineno, fn);
193
						o->retrytime = 300;
193
						o->retrytime = 300;
194
					}
194
					}
195
				} else if (!strcasecmp(buf, "waittime")) {
195
				} else if (!strcasecmp(buf, "waittime")) {
196
					if ((sscanf(c, "%d", &o->waittime) != 1) || (o->waittime < 1)) {
196
					if ((sscanf(c, "%d", &o->waittime) != 1) || (o->waittime < 1)) {
197
						ast_log(LOG_WARNING, "Invalid waittime at line %d of %s\n", lineno, fn);
197
						ast_log(LOG_WARNING, "Invalid waittime at line %d of %s\n", lineno, fn);
198
						o->waittime = 45;
198
						o->waittime = 45;
199
					}
199
					}
200
				} else if (!strcasecmp(buf, "retry")) {
200
				} else if (!strcasecmp(buf, "retry")) {
201
					o->retries++;
201
					o->retries++;
202
				} else if (!strcasecmp(buf, "startretry")) {
202
				} else if (!strcasecmp(buf, "startretry")) {
203
					if (sscanf(c, "%ld", &o->callingpid) != 1) {
203
					if (sscanf(c, "%ld", &o->callingpid) != 1) {
204
						ast_log(LOG_WARNING, "Unable to retrieve calling PID!\n");
204
						ast_log(LOG_WARNING, "Unable to retrieve calling PID!\n");
205
						o->callingpid = 0;
205
						o->callingpid = 0;
206
					}
206
					}
207
				} else if (!strcasecmp(buf, "endretry") || !strcasecmp(buf, "abortretry")) {
207
				} else if (!strcasecmp(buf, "endretry") || !strcasecmp(buf, "abortretry")) {
208
					o->callingpid = 0;
208
					o->callingpid = 0;
209
					o->retries++;
209
					o->retries++;
210
				} else if (!strcasecmp(buf, "delayedretry")) {
210
				} else if (!strcasecmp(buf, "delayedretry")) {
211
				} else if (!strcasecmp(buf, "setvar") || !strcasecmp(buf, "set")) {
211
				} else if (!strcasecmp(buf, "setvar") || !strcasecmp(buf, "set")) {
212
					c2 = c;
212
					c2 = c;
213
					strsep(&c2, "=");
213
					strsep(&c2, "=");
214
					if (c2) {
214
					if (c2) {
215
						var = ast_variable_new(c, c2, fn);
215
						var = ast_variable_new(c, c2, fn);
216
						if (var) {
216
						if (var) {
217
							/* Always insert at the end, because some people want to treat the spool file as a script */
217
							/* Always insert at the end, because some people want to treat the spool file as a script */
218
							if (last) {
218
							if (last) {
219
								last->next = var;
219
								last->next = var;
220
							} else {
220
							} else {
221
								o->vars = var;
221
								o->vars = var;
222
							}
222
							}
223
							last = var;
223
							last = var;
224
						}
224
						}
225
					} else
225
					} else
226
						ast_log(LOG_WARNING, "Malformed \"%s\" argument.  Should be \"%s: variable=value\"\n", buf, buf);
226
						ast_log(LOG_WARNING, "Malformed \"%s\" argument.  Should be \"%s: variable=value\"\n", buf, buf);
227
				} else if (!strcasecmp(buf, "account")) {
227
				} else if (!strcasecmp(buf, "account")) {
228
					ast_string_field_set(o, account, c);
228
					ast_string_field_set(o, account, c);
229
				} else if (!strcasecmp(buf, "alwaysdelete")) {
229
				} else if (!strcasecmp(buf, "alwaysdelete")) {
230
					ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ALWAYS_DELETE);
230
					ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ALWAYS_DELETE);
231
				} else if (!strcasecmp(buf, "archive")) {
231
				} else if (!strcasecmp(buf, "archive")) {
232
					ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ARCHIVE);
232
					ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ARCHIVE);
233
				} else {
233
				} else {
234
					ast_log(LOG_WARNING, "Unknown keyword '%s' at line %d of %s\n", buf, lineno, fn);
234
					ast_log(LOG_WARNING, "Unknown keyword '%s' at line %d of %s\n", buf, lineno, fn);
235
				}
235
				}
236
			} else
236
			} else
237
				ast_log(LOG_NOTICE, "Syntax error at line %d of %s\n", lineno, fn);
237
				ast_log(LOG_NOTICE, "Syntax error at line %d of %s\n", lineno, fn);
238
		}
238
		}
239
	}
239
	}
240
	ast_string_field_set(o, fn, fn);
240
	ast_string_field_set(o, fn, fn);
241
	if (ast_strlen_zero(o->tech) || ast_strlen_zero(o->dest) || (ast_strlen_zero(o->app) && ast_strlen_zero(o->exten))) {
241
	if (ast_strlen_zero(o->tech) || ast_strlen_zero(o->dest) || (ast_strlen_zero(o->app) && ast_strlen_zero(o->exten))) {
242
		ast_log(LOG_WARNING, "At least one of app or extension must be specified, along with tech and dest in file %s\n", fn);
242
		ast_log(LOG_WARNING, "At least one of app or extension must be specified, along with tech and dest in file %s\n", fn);
243
		return -1;
243
		return -1;
244
	}
244
	}
245
	return 0;
245
	return 0;
246
}
246
}
247

    
   
247

   
248
static void safe_append(struct outgoing *o, time_t now, char *s)
248
static void safe_append(struct outgoing *o, time_t now, char *s)
249
{
249
{
250
	int fd;
250
	int fd;
251
	FILE *f;
251
	FILE *f;
252
	struct utimbuf tbuf;
252
	struct utimbuf tbuf;
253

    
   
253

   
254
	if ((fd = open(o->fn, O_WRONLY | O_APPEND)) < 0)
254
	if ((fd = open(o->fn, O_WRONLY | O_APPEND)) < 0)
255
		return;
255
		return;
256

    
   
256

   
257
	if ((f = fdopen(fd, "a"))) {
257
	if ((f = fdopen(fd, "a"))) {
258
		fprintf(f, "\n%s: %ld %d (%ld)\n", s, (long)ast_mainpid, o->retries, (long) now);
258
		fprintf(f, "\n%s: %ld %d (%ld)\n", s, (long)ast_mainpid, o->retries, (long) now);
259
		fclose(f);
259
		fclose(f);
260
	} else
260
	} else
261
		close(fd);
261
		close(fd);
262

    
   
262

   
263
	/* Update the file time */
263
	/* Update the file time */
264
	tbuf.actime = now;
264
	tbuf.actime = now;
265
	tbuf.modtime = now + o->retrytime;
265
	tbuf.modtime = now + o->retrytime;
266
	if (utime(o->fn, &tbuf))
266
	if (utime(o->fn, &tbuf))
267
		ast_log(LOG_WARNING, "Unable to set utime on %s: %s\n", o->fn, strerror(errno));
267
		ast_log(LOG_WARNING, "Unable to set utime on %s: %s\n", o->fn, strerror(errno));
268
}
268
}
269

    
   
269

   
270
/*!
270
/*!
271
 * \brief Remove a call file from the outgoing queue optionally moving it in the archive dir
271
 * \brief Remove a call file from the outgoing queue optionally moving it in the archive dir
272
 *
272
 *
273
 * \param o the pointer to outgoing struct
273
 * \param o the pointer to outgoing struct
274
 * \param status the exit status of the call. Can be "Completed", "Failed" or "Expired"
274
 * \param status the exit status of the call. Can be "Completed", "Failed" or "Expired"
275
 */
275
 */
276
static int remove_from_queue(struct outgoing *o, const char *status)
276
static int remove_from_queue(struct outgoing *o, const char *status)
277
{
277
{
278
	int fd;
278
	int fd;
279
	FILE *f;
279
	FILE *f;
280
	char newfn[256];
280
	char newfn[256];
281
	const char *bname;
281
	const char *bname;
282

    
   
282

   
283
	if (!ast_test_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE)) {
283
	if (!ast_test_flag(&o->options, SPOOL_FLAG_ALWAYS_DELETE)) {
284
		struct stat current_file_status;
284
		struct stat current_file_status;
285

    
   
285

   
286
		if (!stat(o->fn, &current_file_status)) {
286
		if (!stat(o->fn, &current_file_status)) {
287
			if (time(NULL) < current_file_status.st_mtime)
287
			if (time(NULL) < current_file_status.st_mtime)
288
				return 0;
288
				return 0;
289
		}
289
		}
290
	}
290
	}
291

    
   
291

   
292
	if (!ast_test_flag(&o->options, SPOOL_FLAG_ARCHIVE)) {
292
	if (!ast_test_flag(&o->options, SPOOL_FLAG_ARCHIVE)) {
293
		unlink(o->fn);
293
		unlink(o->fn);
294
		return 0;
294
		return 0;
295
	}
295
	}
296

    
   
296

   
297
	if (ast_mkdir(qdonedir, 0777)) {
297
	if (ast_mkdir(qdonedir, 0777)) {
298
		ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool archiving disabled\n", qdonedir);
298
		ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool archiving disabled\n", qdonedir);
299
		unlink(o->fn);
299
		unlink(o->fn);
300
		return -1;
300
		return -1;
301
	}
301
	}
302

    
   
302

   
303
	if ((fd = open(o->fn, O_WRONLY | O_APPEND))) {
303
	if ((fd = open(o->fn, O_WRONLY | O_APPEND))) {
304
		if ((f = fdopen(fd, "a"))) {
304
		if ((f = fdopen(fd, "a"))) {
305
			fprintf(f, "Status: %s\n", status);
305
			fprintf(f, "Status: %s\n", status);
306
			fclose(f);
306
			fclose(f);
307
		} else
307
		} else
308
			close(fd);
308
			close(fd);
309
	}
309
	}
310

    
   
310

   
311
	if (!(bname = strrchr(o->fn, '/')))
311
	if (!(bname = strrchr(o->fn, '/')))
312
		bname = o->fn;
312
		bname = o->fn;
313
	else
313
	else
314
		bname++;	
314
		bname++;	
315
	snprintf(newfn, sizeof(newfn), "%s/%s", qdonedir, bname);
315
	snprintf(newfn, sizeof(newfn), "%s/%s", qdonedir, bname);
316
	/* a existing call file the archive dir is overwritten */
316
	/* a existing call file the archive dir is overwritten */
317
	unlink(newfn);
317
	unlink(newfn);
318
	if (rename(o->fn, newfn) != 0) {
318
	if (rename(o->fn, newfn) != 0) {
319
		unlink(o->fn);
319
		unlink(o->fn);
320
		return -1;
320
		return -1;
321
	} else
321
	} else
322
		return 0;
322
		return 0;
323
}
323
}
324

    
   
324

   
325
static void *attempt_thread(void *data)
325
static void *attempt_thread(void *data)
326
{
326
{
327
	struct outgoing *o = data;
327
	struct outgoing *o = data;
328
	int res, reason;
328
	int res, reason;
329
	if (!ast_strlen_zero(o->app)) {
329
	if (!ast_strlen_zero(o->app)) {
330
		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);
330
		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);
331
		res = ast_pbx_outgoing_app(o->tech, o->format, (void *) o->dest, o->waittime * 1000, o->app, o->data, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL);
331
		res = ast_pbx_outgoing_app(o->tech, o->format, (void *) o->dest, o->waittime * 1000, o->app, o->data, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL);
332
		o->vars = NULL;
332
		o->vars = NULL;
333
	} else {
333
	} else {
334
		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);
334
		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);
335
		res = ast_pbx_outgoing_exten(o->tech, o->format, (void *) o->dest, o->waittime * 1000, o->context, o->exten, o->priority, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL);
335
		res = ast_pbx_outgoing_exten(o->tech, o->format, (void *) o->dest, o->waittime * 1000, o->context, o->exten, o->priority, &reason, 2 /* wait to finish */, o->cid_num, o->cid_name, o->vars, o->account, NULL);
336
		o->vars = NULL;
336
		o->vars = NULL;
337
	}
337
	}
338
	if (res) {
338
	if (res) {
339
		ast_log(LOG_NOTICE, "Call failed to go through, reason (%d) %s\n", reason, ast_channel_reason2str(reason));
339
		ast_log(LOG_NOTICE, "Call failed to go through, reason (%d) %s\n", reason, ast_channel_reason2str(reason));
340
		if (o->retries >= o->maxretries + 1) {
340
		if (o->retries >= o->maxretries + 1) {
341
			/* Max retries exceeded */
341
			/* Max retries exceeded */
342
			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" : "");
342
			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" : "");
343
			remove_from_queue(o, "Expired");
343
			remove_from_queue(o, "Expired");
344
		} else {
344
		} else {
345
			/* Notate that the call is still active */
345
			/* Notate that the call is still active */
346
			safe_append(o, time(NULL), "EndRetry");
346
			safe_append(o, time(NULL), "EndRetry");
347
		}
347
		}
348
	} else {
348
	} else {
349
		ast_log(LOG_NOTICE, "Call completed to %s/%s\n", o->tech, o->dest);
349
		ast_log(LOG_NOTICE, "Call completed to %s/%s\n", o->tech, o->dest);
350
		ast_log(LOG_EVENT, "Queued call to %s/%s completed\n", o->tech, o->dest);
350
		ast_log(LOG_EVENT, "Queued call to %s/%s completed\n", o->tech, o->dest);
351
		remove_from_queue(o, "Completed");
351
		remove_from_queue(o, "Completed");
352
	}
352
	}
353
	free_outgoing(o);
353
	free_outgoing(o);
354
	return NULL;
354
	return NULL;
355
}
355
}
356

    
   
356

   
357
static void launch_service(struct outgoing *o)
357
static void launch_service(struct outgoing *o)
358
{
358
{
359
	pthread_t t;
359
	pthread_t t;
360
	int ret;
360
	int ret;
361

    
   
361

   
362
	if ((ret = ast_pthread_create_detached(&t, NULL, attempt_thread, o))) {
362
	if ((ret = ast_pthread_create_detached(&t, NULL, attempt_thread, o))) {
363
		ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
363
		ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
364
		free_outgoing(o);
364
		free_outgoing(o);
365
	}
365
	}
366
}
366
}
367

    
   
367

   
368
static int scan_service(char *fn, time_t now, time_t atime)
368
static int scan_service(char *fn, time_t now, time_t atime)
369
{
369
{
370
	struct outgoing *o = NULL;
370
	struct outgoing *o = NULL;
371
	FILE *f;
371
	FILE *f;
372
	int res = 0;
372
	int res = 0;
373

    
   
373

   
374
	if (!(o = ast_calloc(1, sizeof(*o)))) {
374
	if (!(o = ast_calloc(1, sizeof(*o)))) {
375
		ast_log(LOG_WARNING, "Out of memory ;(\n");
375
		ast_log(LOG_WARNING, "Out of memory ;(\n");
376
		return -1;
376
		return -1;
377
	}
377
	}
378
	
378
	
379
	if (init_outgoing(o)) {
379
	if (init_outgoing(o)) {

    
   
380
		/* No need to call free_outgoing here since we know the failure

    
   
381
		 * was to allocate string fields and no variables have been allocated

    
   
382
		 * yet.

    
   
383
		 */

    
   
384
		ast_free(o);
380
		return -1;
385
		return -1;
381
	}
386
	}
382

    
   
387

   
383
	/* Attempt to open the file */
388
	/* Attempt to open the file */
384
	if (!(f = fopen(fn, "r+"))) {
389
	if (!(f = fopen(fn, "r+"))) {
385
		remove_from_queue(o, "Failed");
390
		remove_from_queue(o, "Failed");
386
		free_outgoing(o);
391
		free_outgoing(o);
387
		ast_log(LOG_WARNING, "Unable to open %s: %s, deleting\n", fn, strerror(errno));
392
		ast_log(LOG_WARNING, "Unable to open %s: %s, deleting\n", fn, strerror(errno));
388
		return -1;
393
		return -1;
389
	}
394
	}
390

    
   
395

   
391
	/* Read in and verify the contents */
396
	/* Read in and verify the contents */
392
	if (apply_outgoing(o, fn, f)) {
397
	if (apply_outgoing(o, fn, f)) {
393
		remove_from_queue(o, "Failed");
398
		remove_from_queue(o, "Failed");
394
		free_outgoing(o);
399
		free_outgoing(o);
395
		ast_log(LOG_WARNING, "Invalid file contents in %s, deleting\n", fn);
400
		ast_log(LOG_WARNING, "Invalid file contents in %s, deleting\n", fn);
396
		fclose(f);
401
		fclose(f);
397
		return -1;
402
		return -1;
398
	}
403
	}
399
	
404
	
400
#if 0
405
#if 0
401
	printf("Filename: %s, Retries: %d, max: %d\n", fn, o->retries, o->maxretries);
406
	printf("Filename: %s, Retries: %d, max: %d\n", fn, o->retries, o->maxretries);
402
#endif
407
#endif
403
	fclose(f);
408
	fclose(f);
404
	if (o->retries <= o->maxretries) {
409
	if (o->retries <= o->maxretries) {
405
		now += o->retrytime;
410
		now += o->retrytime;
406
		if (o->callingpid && (o->callingpid == ast_mainpid)) {
411
		if (o->callingpid && (o->callingpid == ast_mainpid)) {
407
			safe_append(o, time(NULL), "DelayedRetry");
412
			safe_append(o, time(NULL), "DelayedRetry");
408
			ast_log(LOG_DEBUG, "Delaying retry since we're currently running '%s'\n", o->fn);
413
			ast_log(LOG_DEBUG, "Delaying retry since we're currently running '%s'\n", o->fn);
409
			free_outgoing(o);
414
			free_outgoing(o);
410
		} else {
415
		} else {
411
			/* Increment retries */
416
			/* Increment retries */
412
			o->retries++;
417
			o->retries++;
413
			/* If someone else was calling, they're presumably gone now
418
			/* If someone else was calling, they're presumably gone now
414
			   so abort their retry and continue as we were... */
419
			   so abort their retry and continue as we were... */
415
			if (o->callingpid)
420
			if (o->callingpid)
416
				safe_append(o, time(NULL), "AbortRetry");
421
				safe_append(o, time(NULL), "AbortRetry");
417
			
422
			
418
			safe_append(o, now, "StartRetry");
423
			safe_append(o, now, "StartRetry");
419
			launch_service(o);
424
			launch_service(o);
420
		}
425
		}
421
		res = now;
426
		res = now;
422
	} else {
427
	} else {
423
		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" : "");
428
		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" : "");
424
		remove_from_queue(o, "Expired");
429
		remove_from_queue(o, "Expired");
425
		free_outgoing(o);
430
		free_outgoing(o);
426
	}
431
	}
427

    
   
432

   
428
	return res;
433
	return res;
429
}
434
}
430

    
   
435

   
431
static void *scan_thread(void *unused)
436
static void *scan_thread(void *unused)
432
{
437
{
433
	struct stat st;
438
	struct stat st;
434
	DIR *dir;
439
	DIR *dir;
435
	struct dirent *de;
440
	struct dirent *de;
436
	char fn[256];
441
	char fn[256];
437
	int res;
442
	int res;
438
	time_t last = 0, next = 0, now;
443
	time_t last = 0, next = 0, now;
439
	struct timespec ts = { .tv_sec = 1 };
444
	struct timespec ts = { .tv_sec = 1 };
440
  
445
  
441
	while (!ast_fully_booted) {
446
	while (!ast_fully_booted) {
442
		nanosleep(&ts, NULL);
447
		nanosleep(&ts, NULL);
443
	}
448
	}
444

    
   
449

   
445
	for(;;) {
450
	for(;;) {
446
		/* Wait a sec */
451
		/* Wait a sec */
447
		nanosleep(&ts, NULL);
452
		nanosleep(&ts, NULL);
448
		time(&now);
453
		time(&now);
449

    
   
454

   
450
		if (stat(qdir, &st)) {
455
		if (stat(qdir, &st)) {
451
			ast_log(LOG_WARNING, "Unable to stat %s\n", qdir);
456
			ast_log(LOG_WARNING, "Unable to stat %s\n", qdir);
452
			continue;
457
			continue;
453
		}
458
		}
454

    
   
459

   
455
		/* Make sure it is time for us to execute our check */
460
		/* Make sure it is time for us to execute our check */
456
		if ((st.st_mtime == last) && (next && (next > now)))
461
		if ((st.st_mtime == last) && (next && (next > now)))
457
			continue;
462
			continue;
458
		
463
		
459
#if 0
464
#if 0
460
		printf("atime: %ld, mtime: %ld, ctime: %ld\n", st.st_atime, st.st_mtime, st.st_ctime);
465
		printf("atime: %ld, mtime: %ld, ctime: %ld\n", st.st_atime, st.st_mtime, st.st_ctime);
461
		printf("Ooh, something changed / timeout\n");
466
		printf("Ooh, something changed / timeout\n");
462
#endif
467
#endif
463
		next = 0;
468
		next = 0;
464
		last = st.st_mtime;
469
		last = st.st_mtime;
465

    
   
470

   
466
		if (!(dir = opendir(qdir))) {
471
		if (!(dir = opendir(qdir))) {
467
			ast_log(LOG_WARNING, "Unable to open directory %s: %s\n", qdir, strerror(errno));
472
			ast_log(LOG_WARNING, "Unable to open directory %s: %s\n", qdir, strerror(errno));
468
			continue;
473
			continue;
469
		}
474
		}
470

    
   
475

   
471
		while ((de = readdir(dir))) {
476
		while ((de = readdir(dir))) {
472
			snprintf(fn, sizeof(fn), "%s/%s", qdir, de->d_name);
477
			snprintf(fn, sizeof(fn), "%s/%s", qdir, de->d_name);
473
			if (stat(fn, &st)) {
478
			if (stat(fn, &st)) {
474
				ast_log(LOG_WARNING, "Unable to stat %s: %s\n", fn, strerror(errno));
479
				ast_log(LOG_WARNING, "Unable to stat %s: %s\n", fn, strerror(errno));
475
				continue;
480
				continue;
476
			}
481
			}
477
			if (!S_ISREG(st.st_mode))
482
			if (!S_ISREG(st.st_mode))
478
				continue;
483
				continue;
479
			if (st.st_mtime <= now) {
484
			if (st.st_mtime <= now) {
480
				res = scan_service(fn, now, st.st_atime);
485
				res = scan_service(fn, now, st.st_atime);
481
				if (res > 0) {
486
				if (res > 0) {
482
					/* Update next service time */
487
					/* Update next service time */
483
					if (!next || (res < next)) {
488
					if (!next || (res < next)) {
484
						next = res;
489
						next = res;
485
					}
490
					}
486
				} else if (res) {
491
				} else if (res) {
487
					ast_log(LOG_WARNING, "Failed to scan service '%s'\n", fn);
492
					ast_log(LOG_WARNING, "Failed to scan service '%s'\n", fn);
488
				} else if (!next) {
493
				} else if (!next) {
489
					/* Expired entry: must recheck on the next go-around */
494
					/* Expired entry: must recheck on the next go-around */
490
					next = st.st_mtime;
495
					next = st.st_mtime;
491
				}
496
				}
492
			} else {
497
			} else {
493
				/* Update "next" update if necessary */
498
				/* Update "next" update if necessary */
494
				if (!next || (st.st_mtime < next))
499
				if (!next || (st.st_mtime < next))
495
					next = st.st_mtime;
500
					next = st.st_mtime;
496
			}
501
			}
497
		}
502
		}
498
		closedir(dir);
503
		closedir(dir);
499
	}
504
	}
500
	return NULL;
505
	return NULL;
501
}
506
}
502

    
   
507

   
503
static int unload_module(void)
508
static int unload_module(void)
504
{
509
{
505
	return -1;
510
	return -1;
506
}
511
}
507

    
   
512

   
508
static int load_module(void)
513
static int load_module(void)
509
{
514
{
510
	pthread_t thread;
515
	pthread_t thread;
511
	int ret;
516
	int ret;
512
	snprintf(qdir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing");
517
	snprintf(qdir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing");
513
	if (ast_mkdir(qdir, 0777)) {
518
	if (ast_mkdir(qdir, 0777)) {
514
		ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool disabled\n", qdir);
519
		ast_log(LOG_WARNING, "Unable to create queue directory %s -- outgoing spool disabled\n", qdir);
515
		return AST_MODULE_LOAD_DECLINE;
520
		return AST_MODULE_LOAD_DECLINE;
516
	}
521
	}
517
	snprintf(qdonedir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing_done");
522
	snprintf(qdonedir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "outgoing_done");
518

    
   
523

   
519
	if ((ret = ast_pthread_create_detached_background(&thread, NULL, scan_thread, NULL))) {
524
	if ((ret = ast_pthread_create_detached_background(&thread, NULL, scan_thread, NULL))) {
520
		ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
525
		ast_log(LOG_WARNING, "Unable to create thread :( (returned error: %d)\n", ret);
521
		return AST_MODULE_LOAD_FAILURE;
526
		return AST_MODULE_LOAD_FAILURE;
522
	}
527
	}
523

    
   
528

   
524
	return AST_MODULE_LOAD_SUCCESS;
529
	return AST_MODULE_LOAD_SUCCESS;
525
}
530
}
526

    
   
531

   
527
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Outgoing Spool Support");
532
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Outgoing Spool Support");
  1. /trunk/pbx/pbx_spool.c: Loading...

https://reviewboard.asterisk.org/ runs on a server provided by Digium, Inc. and uses bandwidth donated to the open source Asterisk community by API Digital Communications in Huntsville, AL USA.
Please report problems with this site to asteriskteam@digium.com.