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.

Diff revision 2

This is not the most recent revision of the diff. The latest diff is revision 3. See what's changed.

1 2 3
1 2 3

  1. /trunk/pbx/pbx_spool.c: Loading...
/trunk/pbx/pbx_spool.c
Revision 167169 New Change
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
	char fn[256];

   
65
	int retries;                              /*!< Current number of retries */
64
	int retries;                              /*!< Current number of retries */
66
	int maxretries;                           /*!< Maximum number of retries permitted */
65
	int maxretries;                           /*!< Maximum number of retries permitted */
67
	int retrytime;                            /*!< How long to wait between retries (in seconds) */
66
	int retrytime;                            /*!< How long to wait between retries (in seconds) */
68
	int waittime;                             /*!< How long to wait for an answer */
67
	int waittime;                             /*!< How long to wait for an answer */
69
	long callingpid;                          /*!< PID which is currently calling */
68
	long callingpid;                          /*!< PID which is currently calling */
70
	int format;                               /*!< Formats (codecs) for this call */
69
	int format;                               /*!< Formats (codecs) for this call */
71
	
70
	AST_DECLARE_STRING_FIELDS (
72
	char tech[256];                           /*!< Which channel driver to use for outgoing call */
71
		AST_STRING_FIELD(fn);                 /*!< File name of call file */
73
	char dest[256];                           /*!< Which device/line to use for outgoing call */
72
		AST_STRING_FIELD(tech);               /*!< Which channel technology to use for outgoing call */
74

    
   
73
		AST_STRING_FIELD(dest);               /*!< Which device/line to use for outgoing call */
75
	char app[256];                            /*!< If application: Application name */
74
		AST_STRING_FIELD(app);                /*!< If application: Application name */
76
	char data[256];                           /*!< If applicatoin: Application data */
75
		AST_STRING_FIELD(data);               /*!< If application: Application data */
77

    
   
76
		AST_STRING_FIELD(exten);              /*!< If extension/context/priority: Extension in dialplan */
78
	char exten[AST_MAX_EXTENSION];            /*!< If extension/context/priority: Extension in dialplan */
77
		AST_STRING_FIELD(context);            /*!< If extension/context/priority: Dialplan context */
79
	char context[AST_MAX_CONTEXT];            /*!< If extension/context/priority: Dialplan context */
78
		AST_STRING_FIELD(cid_num);            /*!< CallerID Information: Number/extension */

    
   
79
		AST_STRING_FIELD(cid_name);           /*!< CallerID Information: Name */

    
   
80
		AST_STRING_FIELD(account);            /*!< account code */

    
   
81
	);
80
	int priority;                             /*!< If extension/context/priority: Dialplan priority */
82
	int priority;                             /*!< If extension/context/priority: Dialplan priority */
81

    
   

   
82
	char cid_num[256];                        /*!< CallerID Information: Number/extension */

   
83
	char cid_name[256];                       /*!< CallerID Information: Name */

   
84

    
   

   
85
	char account[AST_MAX_ACCOUNT_CODE];       /*!< account code */

   
86

    
   

   
87
	struct ast_variable *vars;                /*!< Variables and Functions */
83
	struct ast_variable *vars;                /*!< Variables and Functions */
88
	

   
89
	int maxlen;                               /*!< Maximum length of call */
84
	int maxlen;                               /*!< Maximum length of call */
90

    
   

   
91
	struct ast_flags options;                 /*!< options */
85
	struct ast_flags options;                 /*!< options */
92
};
86
};
93

    
   
87

   
94
static void init_outgoing(struct outgoing *o)
88
static int init_outgoing(struct outgoing *o)
95
{
89
{
96
	o->priority = 1;
90
	o->priority = 1;
97
	o->retrytime = 300;
91
	o->retrytime = 300;
98
	o->waittime = 45;
92
	o->waittime = 45;
99
	o->format = AST_FORMAT_SLINEAR;
93
	o->format = AST_FORMAT_SLINEAR;
100
	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))) {

    
   
96
		return -1;

    
   
97
	}

    
   
98
	return 0;
101
}
99
}
102

    
   
100

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

    
   
106
	ast_string_field_free_memory(o);
108
	ast_free(o);
107
	ast_free(o);
109
}
108
}
110

    
   
109

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

    
   
116

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

    
   
120

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

    
   
131

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

    
   
142

   
144
		/* Trim trailing white space */
143
		/* Trim trailing white space */
145
		while(!ast_strlen_zero(buf) && buf[strlen(buf) - 1] < 33)
144
		while(!ast_strlen_zero(buf) && buf[strlen(buf) - 1] < 33)
146
			buf[strlen(buf) - 1] = '\0';
145
			buf[strlen(buf) - 1] = '\0';
147
		if (!ast_strlen_zero(buf)) {
146
		if (!ast_strlen_zero(buf)) {
148
			c = strchr(buf, ':');
147
			c = strchr(buf, ':');
149
			if (c) {
148
			if (c) {
150
				*c = '\0';
149
				*c = '\0';
151
				c++;
150
				c++;
152
				while ((*c) && (*c < 33))
151
				while ((*c) && (*c < 33))
153
					c++;
152
					c++;
154
#if 0
153
#if 0
155
				printf("'%s' is '%s' at line %d\n", buf, c, lineno);
154
				printf("'%s' is '%s' at line %d\n", buf, c, lineno);
156
#endif
155
#endif
157
				if (!strcasecmp(buf, "channel")) {
156
				if (!strcasecmp(buf, "channel")) {
158
					ast_copy_string(o->tech, c, sizeof(o->tech));
157
					if ((c2 = strchr(c, '/'))) {
159
					if ((c2 = strchr(o->tech, '/'))) {

   
160
						*c2 = '\0';
158
						*c2 = '\0';
161
						c2++;
159
						c2++;
162
						ast_copy_string(o->dest, c2, sizeof(o->dest));
160
						ast_string_field_set(o, tech, c);

    
   
161
						ast_string_field_set(o, dest, c2);
163
					} else {
162
					} else {
164
						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);
165
						o->tech[0] = '\0';

   
166
					}
164
					}
167
				} else if (!strcasecmp(buf, "callerid")) {
165
				} else if (!strcasecmp(buf, "callerid")) {
168
					ast_callerid_split(c, o->cid_name, sizeof(o->cid_name), o->cid_num, sizeof(o->cid_num));
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));

    
   
168
					ast_string_field_set(o, cid_num, cid_num);

    
   
169
					ast_string_field_set(o, cid_name, cid_name);
169
				} else if (!strcasecmp(buf, "application")) {
170
				} else if (!strcasecmp(buf, "application")) {
170
					ast_copy_string(o->app, c, sizeof(o->app));
171
					ast_string_field_set(o, app, c);
171
				} else if (!strcasecmp(buf, "data")) {
172
				} else if (!strcasecmp(buf, "data")) {
172
					ast_copy_string(o->data, c, sizeof(o->data));
173
					ast_string_field_set(o, data, c);
173
				} else if (!strcasecmp(buf, "maxretries")) {
174
				} else if (!strcasecmp(buf, "maxretries")) {
174
					if (sscanf(c, "%d", &o->maxretries) != 1) {
175
					if (sscanf(c, "%d", &o->maxretries) != 1) {
175
						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);
176
						o->maxretries = 0;
177
						o->maxretries = 0;
177
					}
178
					}
178
				} else if (!strcasecmp(buf, "codecs")) {
179
				} else if (!strcasecmp(buf, "codecs")) {
179
					ast_parse_allow_disallow(NULL, &o->format, c, 1);
180
					ast_parse_allow_disallow(NULL, &o->format, c, 1);
180
				} else if (!strcasecmp(buf, "context")) {
181
				} else if (!strcasecmp(buf, "context")) {
181
					ast_copy_string(o->context, c, sizeof(o->context));
182
					ast_string_field_set(o, context, c);
182
				} else if (!strcasecmp(buf, "extension")) {
183
				} else if (!strcasecmp(buf, "extension")) {
183
					ast_copy_string(o->exten, c, sizeof(o->exten));
184
					ast_string_field_set(o, exten, c);
184
				} else if (!strcasecmp(buf, "priority")) {
185
				} else if (!strcasecmp(buf, "priority")) {
185
					if ((sscanf(c, "%d", &o->priority) != 1) || (o->priority < 1)) {
186
					if ((sscanf(c, "%d", &o->priority) != 1) || (o->priority < 1)) {
186
						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);
187
						o->priority = 1;
188
						o->priority = 1;
188
					}
189
					}
189
				} else if (!strcasecmp(buf, "retrytime")) {
190
				} else if (!strcasecmp(buf, "retrytime")) {
190
					if ((sscanf(c, "%d", &o->retrytime) != 1) || (o->retrytime < 1)) {
191
					if ((sscanf(c, "%d", &o->retrytime) != 1) || (o->retrytime < 1)) {
191
						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);
192
						o->retrytime = 300;
193
						o->retrytime = 300;
193
					}
194
					}
194
				} else if (!strcasecmp(buf, "waittime")) {
195
				} else if (!strcasecmp(buf, "waittime")) {
195
					if ((sscanf(c, "%d", &o->waittime) != 1) || (o->waittime < 1)) {
196
					if ((sscanf(c, "%d", &o->waittime) != 1) || (o->waittime < 1)) {
196
						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);
197
						o->waittime = 45;
198
						o->waittime = 45;
198
					}
199
					}
199
				} else if (!strcasecmp(buf, "retry")) {
200
				} else if (!strcasecmp(buf, "retry")) {
200
					o->retries++;
201
					o->retries++;
201
				} else if (!strcasecmp(buf, "startretry")) {
202
				} else if (!strcasecmp(buf, "startretry")) {
202
					if (sscanf(c, "%ld", &o->callingpid) != 1) {
203
					if (sscanf(c, "%ld", &o->callingpid) != 1) {
203
						ast_log(LOG_WARNING, "Unable to retrieve calling PID!\n");
204
						ast_log(LOG_WARNING, "Unable to retrieve calling PID!\n");
204
						o->callingpid = 0;
205
						o->callingpid = 0;
205
					}
206
					}
206
				} else if (!strcasecmp(buf, "endretry") || !strcasecmp(buf, "abortretry")) {
207
				} else if (!strcasecmp(buf, "endretry") || !strcasecmp(buf, "abortretry")) {
207
					o->callingpid = 0;
208
					o->callingpid = 0;
208
					o->retries++;
209
					o->retries++;
209
				} else if (!strcasecmp(buf, "delayedretry")) {
210
				} else if (!strcasecmp(buf, "delayedretry")) {
210
				} else if (!strcasecmp(buf, "setvar") || !strcasecmp(buf, "set")) {
211
				} else if (!strcasecmp(buf, "setvar") || !strcasecmp(buf, "set")) {
211
					c2 = c;
212
					c2 = c;
212
					strsep(&c2, "=");
213
					strsep(&c2, "=");
213
					if (c2) {
214
					if (c2) {
214
						var = ast_variable_new(c, c2, fn);
215
						var = ast_variable_new(c, c2, fn);
215
						if (var) {
216
						if (var) {
216
							/* 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 */
217
							if (last) {
218
							if (last) {
218
								last->next = var;
219
								last->next = var;
219
							} else {
220
							} else {
220
								o->vars = var;
221
								o->vars = var;
221
							}
222
							}
222
							last = var;
223
							last = var;
223
						}
224
						}
224
					} else
225
					} else
225
						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);
226
				} else if (!strcasecmp(buf, "account")) {
227
				} else if (!strcasecmp(buf, "account")) {
227
					ast_copy_string(o->account, c, sizeof(o->account));
228
					ast_string_field_set(o, account, c);
228
				} else if (!strcasecmp(buf, "alwaysdelete")) {
229
				} else if (!strcasecmp(buf, "alwaysdelete")) {
229
					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);
230
				} else if (!strcasecmp(buf, "archive")) {
231
				} else if (!strcasecmp(buf, "archive")) {
231
					ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ARCHIVE);
232
					ast_set2_flag(&o->options, ast_true(c), SPOOL_FLAG_ARCHIVE);
232
				} else {
233
				} else {
233
					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);
234
				}
235
				}
235
			} else
236
			} else
236
				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);
237
		}
238
		}
238
	}
239
	}
239
	ast_copy_string(o->fn, fn, sizeof(o->fn));
240
	ast_string_field_set(o, fn, fn);
240
	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))) {
241
		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);
242
		return -1;
243
		return -1;
243
	}
244
	}
244
	return 0;
245
	return 0;
245
}
246
}
246

    
   
247

   
247
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)
248
{
249
{
249
	int fd;
250
	int fd;
250
	FILE *f;
251
	FILE *f;
251
	struct utimbuf tbuf;
252
	struct utimbuf tbuf;
252

    
   
253

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

    
   
256

   
256
	if ((f = fdopen(fd, "a"))) {
257
	if ((f = fdopen(fd, "a"))) {
257
		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);
258
		fclose(f);
259
		fclose(f);
259
	} else
260
	} else
260
		close(fd);
261
		close(fd);
261

    
   
262

   
262
	/* Update the file time */
263
	/* Update the file time */
263
	tbuf.actime = now;
264
	tbuf.actime = now;
264
	tbuf.modtime = now + o->retrytime;
265
	tbuf.modtime = now + o->retrytime;
265
	if (utime(o->fn, &tbuf))
266
	if (utime(o->fn, &tbuf))
266
		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));
267
}
268
}
268

    
   
269

   
269
/*!
270
/*!
270
 * \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
271
 *
272
 *
272
 * \param o the pointer to outgoing struct
273
 * \param o the pointer to outgoing struct
273
 * \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"
274
 */
275
 */
275
static int remove_from_queue(struct outgoing *o, const char *status)
276
static int remove_from_queue(struct outgoing *o, const char *status)
276
{
277
{
277
	int fd;
278
	int fd;
278
	FILE *f;
279
	FILE *f;
279
	char newfn[256];
280
	char newfn[256];
280
	const char *bname;
281
	const char *bname;
281

    
   
282

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

    
   
285

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

    
   
291

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

    
   
296

   
296
	if (ast_mkdir(qdonedir, 0777)) {
297
	if (ast_mkdir(qdonedir, 0777)) {
297
		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);
298
		unlink(o->fn);
299
		unlink(o->fn);
299
		return -1;
300
		return -1;
300
	}
301
	}
301

    
   
302

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

    
   
310

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

    
   
324

   
324
static void *attempt_thread(void *data)
325
static void *attempt_thread(void *data)
325
{
326
{
326
	struct outgoing *o = data;
327
	struct outgoing *o = data;
327
	int res, reason;
328
	int res, reason;
328
	if (!ast_strlen_zero(o->app)) {
329
	if (!ast_strlen_zero(o->app)) {
329
		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);
330
		res = ast_pbx_outgoing_app(o->tech, o->format, 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);
331
		o->vars = NULL;
332
		o->vars = NULL;
332
	} else {
333
	} else {
333
		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);
334
		res = ast_pbx_outgoing_exten(o->tech, o->format, 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);
335
		o->vars = NULL;
336
		o->vars = NULL;
336
	}
337
	}
337
	if (res) {
338
	if (res) {
338
		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));
339
		if (o->retries >= o->maxretries + 1) {
340
		if (o->retries >= o->maxretries + 1) {
340
			/* Max retries exceeded */
341
			/* Max retries exceeded */
341
			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" : "");
342
			remove_from_queue(o, "Expired");
343
			remove_from_queue(o, "Expired");
343
		} else {
344
		} else {
344
			/* Notate that the call is still active */
345
			/* Notate that the call is still active */
345
			safe_append(o, time(NULL), "EndRetry");
346
			safe_append(o, time(NULL), "EndRetry");
346
		}
347
		}
347
	} else {
348
	} else {
348
		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);
349
		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);
350
		remove_from_queue(o, "Completed");
351
		remove_from_queue(o, "Completed");
351
	}
352
	}
352
	free_outgoing(o);
353
	free_outgoing(o);
353
	return NULL;
354
	return NULL;
354
}
355
}
355

    
   
356

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

    
   
361

   
361
	if ((ret = ast_pthread_create_detached(&t, NULL, attempt_thread, o))) {
362
	if ((ret = ast_pthread_create_detached(&t, NULL, attempt_thread, o))) {
362
		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);
363
		free_outgoing(o);
364
		free_outgoing(o);
364
	}
365
	}
365
}
366
}
366

    
   
367

   
367
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)
368
{
369
{
369
	struct outgoing *o = NULL;
370
	struct outgoing *o = NULL;
370
	FILE *f;
371
	FILE *f;
371
	int res = 0;
372
	int res = 0;
372

    
   
373

   
373
	if (!(o = ast_calloc(1, sizeof(*o)))) {
374
	if (!(o = ast_calloc(1, sizeof(*o)))) {
374
		ast_log(LOG_WARNING, "Out of memory ;(\n");
375
		ast_log(LOG_WARNING, "Out of memory ;(\n");
375
		return -1;
376
		return -1;
376
	}
377
	}
377
	
378
	
378
	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);

    
   
385
		return -1;

    
   
386
	}
379

    
   
387

   
380
	/* Attempt to open the file */
388
	/* Attempt to open the file */
381
	if (!(f = fopen(fn, "r+"))) {
389
	if (!(f = fopen(fn, "r+"))) {
382
		remove_from_queue(o, "Failed");
390
		remove_from_queue(o, "Failed");
383
		free_outgoing(o);
391
		free_outgoing(o);
384
		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));
385
		return -1;
393
		return -1;
386
	}
394
	}
387

    
   
395

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

    
   
432

   
425
	return res;
433
	return res;
426
}
434
}
427

    
   
435

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

    
   
449

   
442
	for(;;) {
450
	for(;;) {
443
		/* Wait a sec */
451
		/* Wait a sec */
444
		nanosleep(&ts, NULL);
452
		nanosleep(&ts, NULL);
445
		time(&now);
453
		time(&now);
446

    
   
454

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

    
   
459

   
452
		/* Make sure it is time for us to execute our check */
460
		/* Make sure it is time for us to execute our check */
453
		if ((st.st_mtime == last) && (next && (next > now)))
461
		if ((st.st_mtime == last) && (next && (next > now)))
454
			continue;
462
			continue;
455
		
463
		
456
#if 0
464
#if 0
457
		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);
458
		printf("Ooh, something changed / timeout\n");
466
		printf("Ooh, something changed / timeout\n");
459
#endif
467
#endif
460
		next = 0;
468
		next = 0;
461
		last = st.st_mtime;
469
		last = st.st_mtime;
462

    
   
470

   
463
		if (!(dir = opendir(qdir))) {
471
		if (!(dir = opendir(qdir))) {
464
			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));
465
			continue;
473
			continue;
466
		}
474
		}
467

    
   
475

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

    
   
507

   
500
static int unload_module(void)
508
static int unload_module(void)
501
{
509
{
502
	return -1;
510
	return -1;
503
}
511
}
504

    
   
512

   
505
static int load_module(void)
513
static int load_module(void)
506
{
514
{
507
	pthread_t thread;
515
	pthread_t thread;
508
	int ret;
516
	int ret;
509
	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");
510
	if (ast_mkdir(qdir, 0777)) {
518
	if (ast_mkdir(qdir, 0777)) {
511
		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);
512
		return AST_MODULE_LOAD_DECLINE;
520
		return AST_MODULE_LOAD_DECLINE;
513
	}
521
	}
514
	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");
515

    
   
523

   
516
	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))) {
517
		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);
518
		return AST_MODULE_LOAD_FAILURE;
526
		return AST_MODULE_LOAD_FAILURE;
519
	}
527
	}
520

    
   
528

   
521
	return AST_MODULE_LOAD_SUCCESS;
529
	return AST_MODULE_LOAD_SUCCESS;
522
}
530
}
523

    
   
531

   
524
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.