aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/app_osplookup.c8
-rw-r--r--channels/chan_local.c32
-rw-r--r--channels/chan_mgcp.c2
-rw-r--r--channels/chan_sip.c5
-rw-r--r--configs/cdr.conf.sample31
-rw-r--r--configs/sip.conf.sample2
-rw-r--r--include/asterisk/pbx.h8
-rw-r--r--main/cdr.c39
-rw-r--r--main/cli.c8
-rw-r--r--main/pbx.c31
-rw-r--r--main/utils.c16
-rw-r--r--res/res_fax.c128
12 files changed, 241 insertions, 69 deletions
diff --git a/apps/app_osplookup.c b/apps/app_osplookup.c
index ea20cee77..239d6d501 100644
--- a/apps/app_osplookup.c
+++ b/apps/app_osplookup.c
@@ -1493,6 +1493,7 @@ static int osp_lookup(
char callingnum[OSP_SIZE_NORSTR];
char callednum[OSP_SIZE_NORSTR];
char destination[OSP_SIZE_NORSTR];
+ char* tmp;
unsigned int tokenlen;
char token[OSP_SIZE_TOKSTR];
char src[OSP_SIZE_NORSTR];
@@ -1565,6 +1566,11 @@ static int osp_lookup(
}
}
+ ast_copy_string(callednum, called, sizeof(callednum));
+ if((tmp = strchr(callednum, ';')) != NULL) {
+ *tmp = '\0';
+ }
+
callidnum = 0;
callids[0] = NULL;
for (i = 0; i < OSP_CALLID_MAXNUM; i++) {
@@ -1605,7 +1611,7 @@ static int osp_lookup(
dev,
calling ? calling : "",
OSPC_NFORMAT_E164,
- called,
+ callednum,
OSPC_NFORMAT_E164,
NULL,
callidnum,
diff --git a/channels/chan_local.c b/channels/chan_local.c
index d1f66f8dd..aebca441a 100644
--- a/channels/chan_local.c
+++ b/channels/chan_local.c
@@ -409,6 +409,38 @@ static void check_bridge(struct local_pvt *p)
p->chan->audiohooks = p->owner->audiohooks;
p->owner->audiohooks = audiohooks_swapper;
}
+
+ /* If any Caller ID was set, preserve it after masquerade like above. We must check
+ * to see if Caller ID was set because otherwise we'll mistakingly copy info not
+ * set from the dialplan and will overwrite the real channel Caller ID. The reason
+ * for this whole preswapping action is because the Caller ID is set on the channel
+ * thread (which is the to be masqueraded away local channel) before both local
+ * channels are optimized away.
+ */
+ if (p->owner->caller.id.name.valid || p->owner->caller.id.number.valid
+ || p->owner->caller.id.subaddress.valid || p->owner->caller.ani.name.valid
+ || p->owner->caller.ani.number.valid || p->owner->caller.ani.subaddress.valid) {
+ struct ast_party_caller tmp;
+ tmp = p->owner->caller;
+ p->owner->caller = p->chan->_bridge->caller;
+ p->chan->_bridge->caller = tmp;
+ }
+ if (p->owner->redirecting.from.name.valid || p->owner->redirecting.from.number.valid
+ || p->owner->redirecting.from.subaddress.valid || p->owner->redirecting.to.name.valid
+ || p->owner->redirecting.to.number.valid || p->owner->redirecting.to.subaddress.valid) {
+ struct ast_party_redirecting tmp;
+ tmp = p->owner->redirecting;
+ p->owner->redirecting = p->chan->_bridge->redirecting;
+ p->chan->_bridge->redirecting = tmp;
+ }
+ if (p->owner->dialed.number.str || p->owner->dialed.subaddress.valid) {
+ struct ast_party_dialed tmp;
+ tmp = p->owner->dialed;
+ p->owner->dialed = p->chan->_bridge->dialed;
+ p->chan->_bridge->dialed = tmp;
+ }
+
+
ast_app_group_update(p->chan, p->owner);
ast_channel_masquerade(p->owner, p->chan->_bridge);
ast_set_flag(p, LOCAL_ALREADY_MASQED);
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index 485b39aad..a787eab72 100644
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -3580,7 +3580,7 @@ static int find_and_retrans(struct mgcp_subchannel *sub, struct mgcp_request *re
if (sscanf(req->identifier, "%30d", &seqno) != 1) {
seqno = 0;
}
- for (cur = sub->parent->parent->responses, next = cur->next; cur; cur = next, next = cur->next) {
+ for (cur = sub->parent->parent->responses, next = cur ? cur->next : NULL; cur; cur = next, next = cur ? cur->next : NULL) {
if (now - cur->whensent > RESPONSE_TIMEOUT) {
/* Delete this entry */
if (prev)
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 47dc2610d..cdf598a66 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -12652,7 +12652,6 @@ static int expire_register(const void *data)
peer->expire = -1;
peer->portinuri = 0;
- memset(&peer->addr, 0, sizeof(peer->addr));
destroy_association(peer); /* remove registration data from storage */
set_socket_transport(&peer->socket, peer->default_outbound_transport);
@@ -12681,6 +12680,10 @@ static int expire_register(const void *data)
}
}
+ /* Only clear the addr after we check for destruction. The addr must remain
+ * in order to unlink from the peers_by_ip container correctly */
+ memset(&peer->addr, 0, sizeof(peer->addr));
+
unref_peer(peer, "removing peer ref for expire_register");
return 0;
diff --git a/configs/cdr.conf.sample b/configs/cdr.conf.sample
index 3866ab1fa..b0c38c6cf 100644
--- a/configs/cdr.conf.sample
+++ b/configs/cdr.conf.sample
@@ -29,6 +29,22 @@
; channel.)
;unanswered = no
+; Normally, CDR's are not closed out until after all extensions are finished
+; executing. By enabling this option, the CDR will be ended before executing
+; the "h" extension so that CDR values such as "end" and "billsec" may be
+; retrieved inside of of this extension. The default value is "no".
+;endbeforehexten=no
+
+; Normally, the 'billsec' field logged to the backends (text files or databases)
+; is simply the end time (hangup time) minus the answer time in seconds. Internally,
+; asterisk stores the time in terms of microseconds and seconds. By setting
+; initiatedseconds to 'yes', you can force asterisk to report any seconds
+; that were initiated (a sort of round up method). Technically, this is
+; when the microsecond part of the end time is greater than the microsecond
+; part of the answer time, then the billsec time is incremented one second.
+; The default value is "no".
+;initiatedseconds=no
+
; Define the CDR batch mode, where instead of posting the CDR at the end of
; every call, the data will be stored in a buffer to help alleviate load on the
; asterisk server. Default is "no".
@@ -65,21 +81,6 @@
; is "yes".
;safeshutdown=yes
-; Normally, CDR's are not closed out until after all extensions are finished
-; executing. By enabling this option, the CDR will be ended before executing
-; the "h" extension so that CDR values such as "end" and "billsec" may be
-; retrieved inside of of this extension.
-;endbeforehexten=no
-
-; Normally, the 'billsec' field logged to the backends (text files or databases)
-; is simply the end time (hangup time) minus the answer time in seconds. Internally,
-; asterisk stores the time in terms of microseconds and seconds. By setting
-; initiatedseconds to 'yes', you can force asterisk to report any seconds
-; that were initiated (a sort of round up method). Technically, this is
-; when the microsecond part of the end time is greater than the microsecond
-; part of the answer time, then the billsec time is incremented one second.
-;initiatedseconds=no
-
;
;
; CHOOSING A CDR "BACKEND" (what kind of output to generate)
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index 01fd29b00..35f4f682e 100644
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -144,6 +144,8 @@ allowoverlap=no ; Disable overlap dialing support. (Default is y
; d) Listen on the IPv4 and IPv6 wildcards. Example: bindaddr=::
; (You can choose independently for UDP, TCP, and TLS, by specifying different values for
; "udpbindaddr", "tcpbindaddr", and "tlsbindaddr".)
+; (Note that using bindaddr=:: will show only a single IPv6 socket in netstat.
+; IPv4 is supported at the same time using IPv4-mapped IPv6 addresses.)
;
; You may optionally add a port number. (The default is port 5060 for UDP and TCP, 5061
; for TLS).
diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h
index ac09f009b..a0ef3d476 100644
--- a/include/asterisk/pbx.h
+++ b/include/asterisk/pbx.h
@@ -1220,14 +1220,6 @@ struct ast_exten *pbx_find_extension(struct ast_channel *chan,
const char *context, const char *exten, int priority,
const char *label, const char *callerid, enum ext_match_t action);
-
-/* every time a write lock is obtained for contexts,
- a counter is incremented. You can check this via the
- following func */
-
-int ast_wrlock_contexts_version(void);
-
-
/*! \brief hashtable functions for contexts */
/*! @{ */
int ast_hashtab_compare_contexts(const void *ah_a, const void *ah_b);
diff --git a/main/cdr.c b/main/cdr.c
index a6096976d..c9b58238b 100644
--- a/main/cdr.c
+++ b/main/cdr.c
@@ -84,18 +84,26 @@ static struct sched_context *sched;
static int cdr_sched = -1;
static pthread_t cdr_thread = AST_PTHREADT_NULL;
-#define BATCH_SIZE_DEFAULT 100
-#define BATCH_TIME_DEFAULT 300
-#define BATCH_SCHEDULER_ONLY_DEFAULT 0
-#define BATCH_SAFE_SHUTDOWN_DEFAULT 1
+static int enabled;
+static const int ENABLED_DEFAULT = 1;
-static int enabled; /*! Is the CDR subsystem enabled ? */
-static int unanswered;
static int batchmode;
+static const int BATCHMODE_DEFAULT = 0;
+
+static int unanswered;
+static const int UNANSWERED_DEFAULT = 0;
+
static int batchsize;
+static const int BATCH_SIZE_DEFAULT = 100;
+
static int batchtime;
+static const int BATCH_TIME_DEFAULT = 300;
+
static int batchscheduleronly;
+static const int BATCH_SCHEDULER_ONLY_DEFAULT = 0;
+
static int batchsafeshutdown;
+static const int BATCH_SAFE_SHUTDOWN_DEFAULT = 1;
AST_MUTEX_DEFINE_STATIC(cdr_batch_lock);
@@ -1492,22 +1500,27 @@ static int do_reload(int reload)
int res=0;
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
- if ((config = ast_config_load2("cdr.conf", "cdr", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
- return 0;
- if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEUNCHANGED || config == CONFIG_STATUS_FILEINVALID) {
+ if ((config = ast_config_load2("cdr.conf", "cdr", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
return 0;
}
ast_mutex_lock(&cdr_batch_lock);
+ was_enabled = enabled;
+ was_batchmode = batchmode;
+
batchsize = BATCH_SIZE_DEFAULT;
batchtime = BATCH_TIME_DEFAULT;
batchscheduleronly = BATCH_SCHEDULER_ONLY_DEFAULT;
batchsafeshutdown = BATCH_SAFE_SHUTDOWN_DEFAULT;
- was_enabled = enabled;
- was_batchmode = batchmode;
- enabled = 1;
- batchmode = 0;
+ enabled = ENABLED_DEFAULT;
+ batchmode = BATCHMODE_DEFAULT;
+ unanswered = UNANSWERED_DEFAULT;
+
+ if (config == CONFIG_STATUS_FILEMISSING || config == CONFIG_STATUS_FILEINVALID) {
+ ast_mutex_unlock(&cdr_batch_lock);
+ return 0;
+ }
/* don't run the next scheduled CDR posting while reloading */
AST_SCHED_DEL(sched, cdr_sched);
diff --git a/main/cli.c b/main/cli.c
index c9dbc6d02..ebb7deb6d 100644
--- a/main/cli.c
+++ b/main/cli.c
@@ -1485,7 +1485,13 @@ char *ast_complete_channels(const char *line, const char *word, int pos, int sta
return NULL;
}
- if (!(iter = ast_channel_iterator_by_name_new(word, strlen(word)))) {
+ if (ast_strlen_zero(word)) {
+ iter = ast_channel_iterator_all_new();
+ } else {
+ iter = ast_channel_iterator_by_name_new(word, strlen(word));
+ }
+
+ if (!iter) {
return NULL;
}
diff --git a/main/pbx.c b/main/pbx.c
index 4216b853a..f9476fd1d 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -1164,7 +1164,11 @@ static struct pbx_builtin {
static struct ast_context *contexts;
static struct ast_hashtab *contexts_table = NULL;
-AST_RWLOCK_DEFINE_STATIC(conlock); /*!< Lock for the ast_context list */
+/*!\brief Lock for the ast_context list
+ * This lock MUST be recursive, or a deadlock on reload may result. See
+ * https://issues.asterisk.org/view.php?id=17643
+ */
+AST_MUTEX_DEFINE_STATIC(conlock);
static AST_RWLIST_HEAD_STATIC(apps, ast_app);
@@ -7016,7 +7020,6 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
*/
struct timeval begintime, writelocktime, endlocktime, enddeltime;
- int wrlock_ver;
begintime = ast_tvnow();
ast_rdlock_contexts();
@@ -7025,15 +7028,6 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
context_merge(extcontexts, exttable, tmp, registrar);
}
ast_hashtab_end_traversal(iter);
- wrlock_ver = ast_wrlock_contexts_version();
-
- ast_unlock_contexts(); /* this feels real retarded, but you must do
- what you must do If this isn't done, the following
- wrlock is a guraranteed deadlock */
- ast_wrlock_contexts();
- if (ast_wrlock_contexts_version() > wrlock_ver+1) {
- ast_log(LOG_WARNING,"==================!!!!!!!!!!!!!!!Something changed the contexts in the middle of merging contexts!\n");
- }
AST_RWLIST_WRLOCK(&hints);
writelocktime = ast_tvnow();
@@ -9836,32 +9830,23 @@ int load_pbx(void)
return 0;
}
-static int conlock_wrlock_version = 0;
-
-int ast_wrlock_contexts_version(void)
-{
- return conlock_wrlock_version;
-}
/*
* Lock context list functions ...
*/
int ast_wrlock_contexts()
{
- int res = ast_rwlock_wrlock(&conlock);
- if (!res)
- ast_atomic_fetchadd_int(&conlock_wrlock_version, 1);
- return res;
+ return ast_mutex_lock(&conlock);
}
int ast_rdlock_contexts()
{
- return ast_rwlock_rdlock(&conlock);
+ return ast_mutex_lock(&conlock);
}
int ast_unlock_contexts()
{
- return ast_rwlock_unlock(&conlock);
+ return ast_mutex_unlock(&conlock);
}
/*
diff --git a/main/utils.c b/main/utils.c
index 5961a7339..6f2c884d0 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -1615,7 +1615,8 @@ ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr
size_t space = (*pool_head)->size - (*pool_head)->used;
size_t to_alloc = needed + sizeof(ast_string_field_allocation);
- if (__builtin_expect(to_alloc > space, 0)) {
+ /* This +1 accounts for alignment on SPARC */
+ if (__builtin_expect(to_alloc + 1 > space, 0)) {
size_t new_size = (*pool_head)->size;
while (new_size < to_alloc) {
@@ -1632,6 +1633,13 @@ ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr
}
result = (*pool_head)->base + (*pool_head)->used;
+#ifdef __sparc__
+ /* SPARC requires that the allocation field be aligned. */
+ if ((long) result % sizeof(ast_string_field_allocation)) {
+ result++;
+ (*pool_head)->used++;
+ }
+#endif
(*pool_head)->used += to_alloc;
(*pool_head)->active += needed;
result += sizeof(ast_string_field_allocation);
@@ -1706,6 +1714,12 @@ void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
}
} else {
target = (*pool_head)->base + (*pool_head)->used + sizeof(ast_string_field_allocation);
+#ifdef __sparc__
+ if ((long) target % sizeof(ast_string_field_allocation)) {
+ target++;
+ space--;
+ }
+#endif
available = space - sizeof(ast_string_field_allocation);
}
diff --git a/res/res_fax.c b/res/res_fax.c
index 03fd128a7..3da2d02f8 100644
--- a/res/res_fax.c
+++ b/res/res_fax.c
@@ -387,9 +387,6 @@ static struct ast_fax_session_details *session_details_new(void)
d->modems = general_options.modems;
d->minrate = general_options.minrate;
d->maxrate = general_options.maxrate;
- ast_string_field_set(d, result, "FAILED");
- ast_string_field_set(d, resultstr, "error starting fax session");
- ast_string_field_set(d, error, "INIT_ERROR");
return d;
}
@@ -500,6 +497,45 @@ static int ast_fax_modem_to_str(enum ast_fax_modems bits, char *tbuf, size_t buf
return 0;
}
+static int check_modem_rate(enum ast_fax_modems modems, unsigned int rate)
+{
+ switch (rate) {
+ case 2400:
+ if (!(modems & (AST_FAX_MODEM_V27 | AST_FAX_MODEM_V34))) {
+ return 1;
+ }
+ break;
+ case 4800:
+ if (!(modems & (AST_FAX_MODEM_V27 | AST_FAX_MODEM_V34))) {
+ return 1;
+ }
+ break;
+ case 7200:
+ case 9600:
+ if (!(modems & (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V29 | AST_FAX_MODEM_V34))) {
+ return 1;
+ }
+ break;
+ case 12000:
+ case 14400:
+ if (!(modems & (AST_FAX_MODEM_V17 | AST_FAX_MODEM_V34))) {
+ return 1;
+ }
+ break;
+ case 28800:
+ case 33600:
+ if (!(modems & AST_FAX_MODEM_V34)) {
+ return 1;
+ }
+ break;
+ default:
+ /* this should never happen */
+ return 1;
+ }
+
+ return 0;
+}
+
/*! \brief register a FAX technology module */
int ast_fax_tech_register(struct ast_fax_tech *tech)
{
@@ -1363,7 +1399,7 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_
/*! \brief initiate a receive FAX session */
static int receivefax_exec(struct ast_channel *chan, const char *data)
{
- char *parse;
+ char *parse, modems[128] = "";
int channel_alive;
struct ast_fax_session_details *details;
struct ast_fax_document *doc;
@@ -1390,8 +1426,40 @@ static int receivefax_exec(struct ast_channel *chan, const char *data)
return -1;
}
+ ast_string_field_set(details, result, "FAILED");
+ ast_string_field_set(details, resultstr, "error starting fax session");
+ ast_string_field_set(details, error, "INIT_ERROR");
set_channel_variables(chan, details);
+ if (details->maxrate < details->minrate) {
+ ast_string_field_set(details, error, "INVALID_ARGUMENTS");
+ ast_string_field_set(details, resultstr, "maxrate is less than minrate");
+ set_channel_variables(chan, details);
+ ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", details->maxrate, details->minrate);
+ ao2_ref(details, -1);
+ return -1;
+ }
+
+ if (check_modem_rate(details->modems, details->minrate)) {
+ ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
+ ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, details->minrate);
+ ast_string_field_set(details, error, "INVALID_ARGUMENTS");
+ ast_string_field_set(details, resultstr, "incompatible 'modems' and 'minrate' settings");
+ set_channel_variables(chan, details);
+ ao2_ref(details, -1);
+ return -1;
+ }
+
+ if (check_modem_rate(details->modems, details->maxrate)) {
+ ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
+ ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, details->maxrate);
+ ast_string_field_set(details, error, "INVALID_ARGUMENTS");
+ ast_string_field_set(details, resultstr, "incompatible 'modems' and 'maxrate' settings");
+ set_channel_variables(chan, details);
+ ao2_ref(details, -1);
+ return -1;
+ }
+
if (ast_strlen_zero(data)) {
ast_string_field_set(details, error, "INVALID_ARGUMENTS");
ast_string_field_set(details, resultstr, "invalid arguments");
@@ -1768,7 +1836,7 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det
/*! \brief initiate a send FAX session */
static int sendfax_exec(struct ast_channel *chan, const char *data)
{
- char *parse, *filenames, *c;
+ char *parse, *filenames, *c, modems[128] = "";
int channel_alive, file_count;
struct ast_fax_session_details *details;
struct ast_fax_document *doc;
@@ -1795,8 +1863,40 @@ static int sendfax_exec(struct ast_channel *chan, const char *data)
return -1;
}
+ ast_string_field_set(details, result, "FAILED");
+ ast_string_field_set(details, resultstr, "error starting fax session");
+ ast_string_field_set(details, error, "INIT_ERROR");
set_channel_variables(chan, details);
+ if (details->maxrate < details->minrate) {
+ ast_string_field_set(details, error, "INVALID_ARGUMENTS");
+ ast_string_field_set(details, resultstr, "maxrate is less than minrate");
+ set_channel_variables(chan, details);
+ ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", details->maxrate, details->minrate);
+ ao2_ref(details, -1);
+ return -1;
+ }
+
+ if (check_modem_rate(details->modems, details->minrate)) {
+ ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
+ ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, details->minrate);
+ ast_string_field_set(details, error, "INVALID_ARGUMENTS");
+ ast_string_field_set(details, resultstr, "incompatible 'modems' and 'minrate' settings");
+ set_channel_variables(chan, details);
+ ao2_ref(details, -1);
+ return -1;
+ }
+
+ if (check_modem_rate(details->modems, details->maxrate)) {
+ ast_fax_modem_to_str(details->modems, modems, sizeof(modems));
+ ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, details->maxrate);
+ ast_string_field_set(details, error, "INVALID_ARGUMENTS");
+ ast_string_field_set(details, resultstr, "incompatible 'modems' and 'maxrate' settings");
+ set_channel_variables(chan, details);
+ ao2_ref(details, -1);
+ return -1;
+ }
+
if (ast_strlen_zero(data)) {
ast_string_field_set(details, error, "INVALID_ARGUMENTS");
ast_string_field_set(details, resultstr, "invalid arguments");
@@ -2292,6 +2392,7 @@ static int set_config(const char *config_file)
struct ast_config *cfg;
struct ast_variable *v;
struct ast_flags config_flags = { 0 };
+ char modems[128] = "";
/* set defaults */
general_options.minrate = RES_FAX_MINRATE;
@@ -2342,6 +2443,23 @@ static int set_config(const char *config_file)
ast_config_destroy(cfg);
+ if (general_options.maxrate < general_options.minrate) {
+ ast_log(LOG_ERROR, "maxrate %d is less than minrate %d\n", general_options.maxrate, general_options.minrate);
+ return -1;
+ }
+
+ if (check_modem_rate(general_options.modems, general_options.minrate)) {
+ ast_fax_modem_to_str(general_options.modems, modems, sizeof(modems));
+ ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'minrate' setting %d\n", modems, general_options.minrate);
+ return -1;
+ }
+
+ if (check_modem_rate(general_options.modems, general_options.maxrate)) {
+ ast_fax_modem_to_str(general_options.modems, modems, sizeof(modems));
+ ast_log(LOG_ERROR, "'modems' setting '%s' is incompatible with 'maxrate' setting %d\n", modems, general_options.maxrate);
+ return -1;
+ }
+
return 0;
}