[asterisk-commits] mmichelson: branch mmichelson/queue_refcount r80415 - in /team/mmichelson/que...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Wed Aug 22 16:44:07 CDT 2007
Author: mmichelson
Date: Wed Aug 22 16:44:07 2007
New Revision: 80415
URL: http://svn.digium.com/view/asterisk?view=rev&rev=80415
Log:
Fixing automerge for this branch
Modified:
team/mmichelson/queue_refcount/ (props changed)
team/mmichelson/queue_refcount/UPGRADE.txt
team/mmichelson/queue_refcount/channels/chan_iax2.c
team/mmichelson/queue_refcount/channels/chan_sip.c
team/mmichelson/queue_refcount/doc/iax.txt
team/mmichelson/queue_refcount/include/asterisk/astobj2.h
team/mmichelson/queue_refcount/main/Makefile
team/mmichelson/queue_refcount/main/astobj2.c
team/mmichelson/queue_refcount/res/res_agi.c
Propchange: team/mmichelson/queue_refcount/
------------------------------------------------------------------------------
automerge = *
Propchange: team/mmichelson/queue_refcount/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Wed Aug 22 16:44:07 2007
@@ -1,1 +1,1 @@
-/branches/1.4:1-80339
+/branches/1.4:1-80413
Modified: team/mmichelson/queue_refcount/UPGRADE.txt
URL: http://svn.digium.com/view/asterisk/team/mmichelson/queue_refcount/UPGRADE.txt?view=diff&rev=80415&r1=80414&r2=80415
==============================================================================
--- team/mmichelson/queue_refcount/UPGRADE.txt (original)
+++ team/mmichelson/queue_refcount/UPGRADE.txt Wed Aug 22 16:44:07 2007
@@ -314,6 +314,21 @@
The IAX2 channel:
+* It is possible that previous configurations depended on the order in which
+ peers and users were specified in iax.conf for forcing the order in which
+ chan_iax2 matched against them. This behavior is going away and is considered
+ deprecated in this version. Avoid having ambiguous peer and user entries and
+ to make things easy on yourself, always set the "username" option for users
+ so that the remote end can match on that exactly instead of trying to infer
+ which user you want based on host.
+
+ If you would like to go ahead and use the new behavior which doesn't use the
+ order in the config file to influence matching order, then change the
+ MAX_PEER_BUCKETS define in chan_iax2.c to a value greater than one. An
+ example is provided there. By changing this, you will get *much* better
+ performance on systems that do a lot of peer and user lookups as they will be
+ stored in memory in a much more efficient manner.
+
* The "mailboxdetail" option has been deprecated. Previously, if this option
was not enabled, the 2 byte MSGCOUNT information element would be set to all
1's to indicate there there is some number of messages waiting. With this
Modified: team/mmichelson/queue_refcount/channels/chan_iax2.c
URL: http://svn.digium.com/view/asterisk/team/mmichelson/queue_refcount/channels/chan_iax2.c?view=diff&rev=80415&r1=80414&r2=80415
==============================================================================
--- team/mmichelson/queue_refcount/channels/chan_iax2.c (original)
+++ team/mmichelson/queue_refcount/channels/chan_iax2.c Wed Aug 22 16:44:07 2007
@@ -94,6 +94,7 @@
#include "asterisk/netsock.h"
#include "asterisk/stringfields.h"
#include "asterisk/linkedlists.h"
+#include "asterisk/astobj2.h"
#include "iax2.h"
#include "iax2-parser.h"
@@ -305,7 +306,6 @@
struct ast_ha *ha;
struct iax2_context *contexts;
struct ast_variable *vars;
- AST_LIST_ENTRY(iax2_user) entry;
};
struct iax2_peer {
@@ -358,7 +358,6 @@
int smoothing; /*!< Sample over how many units to determine historic ms */
struct ast_ha *ha;
- AST_LIST_ENTRY(iax2_peer) entry;
};
#define IAX2_TRUNK_PREFACE (sizeof(struct iax_frame) + sizeof(struct ast_iax2_meta_hdr) + sizeof(struct ast_iax2_meta_trunk_hdr))
@@ -632,9 +631,24 @@
int count;
} iaxq;
-static AST_LIST_HEAD_STATIC(users, iax2_user);
-
-static AST_LIST_HEAD_STATIC(peers, iax2_peer);
+/*!
+ * This module will get much higher performance when doing a lot of
+ * user and peer lookups if the number of buckets is increased from 1.
+ * However, to maintain old behavior for Asterisk 1.4, these are set to
+ * 1 by default. When using multiple buckets, search order through these
+ * containers is considered random, so you will not be able to depend on
+ * the order the entires are specified in iax.conf for matching order. */
+#ifdef LOW_MEMORY
+#define MAX_PEER_BUCKETS 1
+/* #define MAX_PEER_BUCKETS 17 */
+#else
+#define MAX_PEER_BUCKETS 1
+/* #define MAX_PEER_BUCKETS 563 */
+#endif
+static ao2_container *peers;
+
+#define MAX_USER_BUCKETS MAX_PEER_BUCKETS
+static ao2_container *users;
static struct ast_firmware_list {
struct iax_firmware *wares;
@@ -675,7 +689,6 @@
static void reg_source_db(struct iax2_peer *p);
static struct iax2_peer *realtime_peer(const char *peername, struct sockaddr_in *sin);
-static void destroy_peer(struct iax2_peer *peer);
static int ast_cli_netstats(struct mansession *s, int fd, int limit_fmt);
#define IAX_IOSTATE_IDLE 0
@@ -828,7 +841,6 @@
static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly);
static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly);
static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, time_t regtime);
-static void destroy_user(struct iax2_user *user);
static void prune_peers(void);
static const struct ast_channel_tech iax2_tech = {
@@ -1043,51 +1055,112 @@
}
/*!
+ * \note The only member of the peer passed here guaranteed to be set is the name field
+ */
+static int peer_hash_cb(const void *obj, const int flags)
+{
+ const struct iax2_peer *peer = obj;
+
+ return ast_str_hash(peer->name);
+}
+
+/*!
+ * \note The only member of the peer passed here guaranteed to be set is the name field
+ */
+static int peer_cmp_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_peer *peer = obj, *peer2 = arg;
+
+ return !strcasecmp(peer->name, peer2->name) ? CMP_MATCH : 0;
+}
+
+/*!
+ * \note The only member of the user passed here guaranteed to be set is the name field
+ */
+static int user_hash_cb(const void *obj, const int flags)
+{
+ const struct iax2_user *user = obj;
+
+ return ast_str_hash(user->name);
+}
+
+/*!
+ * \note The only member of the user passed here guaranteed to be set is the name field
+ */
+static int user_cmp_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_user *user = obj, *user2 = arg;
+
+ return !strcasecmp(user->name, user2->name) ? CMP_MATCH : 0;
+}
+
+/*!
* \note This funtion calls realtime_peer -> reg_source_db -> iax2_poke_peer -> find_callno,
* so do not call it with a pvt lock held.
*/
static struct iax2_peer *find_peer(const char *name, int realtime)
{
struct iax2_peer *peer = NULL;
-
- /* Grab peer from linked list */
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, peer, entry) {
- if (!strcasecmp(peer->name, name)) {
- break;
- }
- }
- AST_LIST_UNLOCK(&peers);
+ struct iax2_peer tmp_peer = {
+ .name = name,
+ };
+
+ peer = ao2_find(peers, &tmp_peer, OBJ_POINTER);
/* Now go for realtime if applicable */
if(!peer && realtime)
peer = realtime_peer(name, NULL);
+
return peer;
}
-static int iax2_getpeername(struct sockaddr_in sin, char *host, int len, int lockpeer)
+static struct iax2_peer *peer_ref(struct iax2_peer *peer)
+{
+ ao2_ref(peer, +1);
+ return peer;
+}
+
+static inline struct iax2_peer *peer_unref(struct iax2_peer *peer)
+{
+ ao2_ref(peer, -1);
+ return NULL;
+}
+
+static inline struct iax2_user *user_ref(struct iax2_user *user)
+{
+ ao2_ref(user, +1);
+ return user;
+}
+
+static inline struct iax2_user *user_unref(struct iax2_user *user)
+{
+ ao2_ref(user, -1);
+ return NULL;
+}
+
+static int iax2_getpeername(struct sockaddr_in sin, char *host, int len)
{
struct iax2_peer *peer = NULL;
int res = 0;
-
- if (lockpeer)
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, peer, entry) {
+ ao2_iterator i;
+
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
if ((peer->addr.sin_addr.s_addr == sin.sin_addr.s_addr) &&
(peer->addr.sin_port == sin.sin_port)) {
ast_copy_string(host, peer->name, len);
+ peer_unref(peer);
res = 1;
break;
}
- }
- if (lockpeer)
- AST_LIST_UNLOCK(&peers);
+ peer_unref(peer);
+ }
+
if (!peer) {
peer = realtime_peer(NULL, &sin);
if (peer) {
ast_copy_string(host, peer->name, len);
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
+ peer_unref(peer);
res = 1;
}
}
@@ -1095,7 +1168,7 @@
return res;
}
-static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, int lockpeer, const char *host)
+static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, const char *host)
{
struct chan_iax2_pvt *tmp;
jb_conf jbconf;
@@ -1251,7 +1324,7 @@
/*!
* \note Calling this function while holding another pvt lock can cause a deadlock.
*/
-static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int lockpeer, int sockfd)
+static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int sockfd)
{
int res = 0;
int x;
@@ -1288,7 +1361,7 @@
* this is just checking for a peer that has that IP/port and
* assuming that we have a user of the same name. This isn't always
* correct, but it will be changed if needed after authentication. */
- if (!iax2_getpeername(*sin, host, sizeof(host), lockpeer))
+ if (!iax2_getpeername(*sin, host, sizeof(host)))
snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
gettimeofday(&now, NULL);
for (x=1;x<TRUNK_CALL_START;x++) {
@@ -1302,7 +1375,7 @@
ast_log(LOG_WARNING, "No more space\n");
return 0;
}
- iaxs[x] = new_iax(sin, lockpeer, host);
+ iaxs[x] = new_iax(sin, host);
update_max_nontrunk();
if (iaxs[x]) {
if (option_debug && iaxdebug)
@@ -1804,18 +1877,19 @@
static void iax2_destroy_helper(struct chan_iax2_pvt *pvt)
{
- struct iax2_user *user = NULL;
-
/* Decrement AUTHREQ count if needed */
if (ast_test_flag(pvt, IAX_MAXAUTHREQ)) {
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
- if (!strcmp(user->name, pvt->username)) {
- user->curauthreq--;
- break;
- }
- }
- AST_LIST_UNLOCK(&users);
+ struct iax2_user *user;
+ struct iax2_user tmp_user = {
+ .name = pvt->username,
+ };
+
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
+ if (user) {
+ ast_atomic_fetchadd_int(&user->curauthreq, -1);
+ user_unref(user);
+ }
+
ast_clear_flag(pvt, IAX_MAXAUTHREQ);
}
/* No more pings or lagrq's */
@@ -2054,6 +2128,7 @@
} else {
ast_cli(fd, "SORRY peer %s is not eligible for this operation.\n", argv[3]);
}
+ peer_unref(peer);
} else {
ast_cli(fd, "SORRY peer %s was not found in the cache.\n", argv[3]);
}
@@ -2180,8 +2255,7 @@
ast_cli(fd, "%s\n",status);
ast_cli(fd, " Qualify : every %dms when OK, every %dms when UNREACHABLE (sample smoothing %s)\n", peer->pokefreqok, peer->pokefreqnotok, peer->smoothing ? "On" : "Off");
ast_cli(fd,"\n");
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
+ peer_unref(peer);
} else {
ast_cli(fd,"Peer %s not found.\n", argv[3]);
ast_cli(fd,"\n");
@@ -2193,20 +2267,23 @@
static char *complete_iax2_show_peer(const char *line, const char *word, int pos, int state)
{
int which = 0;
- struct iax2_peer *p = NULL;
+ struct iax2_peer *peer;
char *res = NULL;
int wordlen = strlen(word);
+ ao2_iterator i;
/* 0 - iax2; 1 - show; 2 - peer; 3 - <peername> */
- if (pos == 3) {
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, p, entry) {
- if (!strncasecmp(p->name, word, wordlen) && ++which > state) {
- res = ast_strdup(p->name);
- break;
- }
- }
- AST_LIST_UNLOCK(&peers);
+ if (pos != 3)
+ return NULL;
+
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
+ if (!strncasecmp(peer->name, word, wordlen) && ++which > state) {
+ res = ast_strdup(peer->name);
+ peer_unref(peer);
+ break;
+ }
+ peer_unref(peer);
}
return res;
@@ -2620,8 +2697,7 @@
if (strcasecmp(tmp->value, "friend") &&
strcasecmp(tmp->value, "peer")) {
/* Whoops, we weren't supposed to exist! */
- destroy_peer(peer);
- peer = NULL;
+ peer = peer_unref(peer);
break;
}
} else if (!strcasecmp(tmp->name, "regseconds")) {
@@ -2648,9 +2724,7 @@
ast_sched_del(sched, peer->expire);
peer->expire = ast_sched_add(sched, (global_rtautoclear) * 1000, expire_registry, (void*)peer->name);
}
- AST_LIST_LOCK(&peers);
- AST_LIST_INSERT_HEAD(&peers, peer, entry);
- AST_LIST_UNLOCK(&peers);
+ ao2_link(peers, peer_ref(peer));
if (ast_test_flag(peer, IAX_DYNAMIC))
reg_source_db(peer);
} else {
@@ -2707,9 +2781,7 @@
if (ast_test_flag((&globalflags), IAX_RTCACHEFRIENDS)) {
ast_set_flag(user, IAX_RTCACHEFRIENDS);
- AST_LIST_LOCK(&users);
- AST_LIST_INSERT_HEAD(&users, user, entry);
- AST_LIST_UNLOCK(&users);
+ ao2_link(users, user_ref(user));
} else {
ast_set_flag(user, IAX_TEMPONLY);
}
@@ -2753,6 +2825,7 @@
struct ast_hostent ahp;
struct hostent *hp;
struct iax2_peer *peer;
+ int res = -1;
ast_clear_flag(cai, IAX_SENDANI | IAX_TRUNK);
cai->sockfd = defaultsockfd;
@@ -2778,18 +2851,12 @@
cai->found = 1;
/* if the peer has no address (current or default), return failure */
- if (!(peer->addr.sin_addr.s_addr || peer->defaddr.sin_addr.s_addr)) {
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
- return -1;
- }
+ if (!(peer->addr.sin_addr.s_addr || peer->defaddr.sin_addr.s_addr))
+ goto return_unref;
/* if the peer is being monitored and is currently unreachable, return failure */
- if (peer->maxms && ((peer->lastms > peer->maxms) || (peer->lastms < 0))) {
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
- return -1;
- }
+ if (peer->maxms && ((peer->lastms > peer->maxms) || (peer->lastms < 0)))
+ goto return_unref;
ast_copy_flags(cai, peer, IAX_SENDANI | IAX_TRUNK | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
cai->maxtime = peer->maxms;
@@ -2817,9 +2884,7 @@
*key++ = '\0';
if (!key || ast_db_get(family, key, cai->secret, sizeof(cai->secret))) {
ast_log(LOG_WARNING, "Unable to retrieve database password for family/key '%s'!\n", peer->dbsecret);
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
- return -1;
+ goto return_unref;
}
}
@@ -2831,10 +2896,12 @@
sin->sin_port = peer->defaddr.sin_port;
}
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
-
- return 0;
+ res = 0;
+
+return_unref:
+ peer_unref(peer);
+
+ return res;
}
static void __auto_congest(void *nothing)
@@ -3410,18 +3477,20 @@
static int iax2_getpeertrunk(struct sockaddr_in sin)
{
- struct iax2_peer *peer = NULL;
+ struct iax2_peer *peer;
int res = 0;
-
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, peer, entry) {
+ ao2_iterator i;
+
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
if ((peer->addr.sin_addr.s_addr == sin.sin_addr.s_addr) &&
(peer->addr.sin_port == sin.sin_port)) {
res = ast_test_flag(peer, IAX_TRUNK);
+ peer_unref(peer);
break;
}
- }
- AST_LIST_UNLOCK(&peers);
+ peer_unref(peer);
+ }
return res;
}
@@ -4139,6 +4208,7 @@
struct iax2_user *user = NULL;
char auth[90];
char *pstr = "";
+ ao2_iterator i;
switch (argc) {
case 5:
@@ -4155,8 +4225,9 @@
}
ast_cli(fd, FORMAT, "Username", "Secret", "Authen", "Def.Context", "A/C","Codec Pref");
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
+ i = ao2_iterator_init(users, 0);
+ for (user = ao2_iterator_next(&i); user;
+ user_unref(user), user = ao2_iterator_next(&i)) {
if (havepattern && regexec(®exbuf, user->name, 0, NULL, 0))
continue;
@@ -4177,9 +4248,7 @@
ast_cli(fd, FORMAT2, user->name, auth, user->authmethods,
user->contexts ? user->contexts->context : context,
user->ha ? "Yes" : "No", pstr);
-
- }
- AST_LIST_UNLOCK(&users);
+ }
if (havepattern)
regfree(®exbuf);
@@ -4197,6 +4266,7 @@
int online_peers = 0;
int offline_peers = 0;
int unmonitored_peers = 0;
+ ao2_iterator i;
#define FORMAT2 "%-15.15s %-15.15s %s %-15.15s %-8s %s %-10s%s"
#define FORMAT "%-15.15s %-15.15s %s %-15.15s %-5d%s %s %-10s%s"
@@ -4245,8 +4315,9 @@
else
ast_cli(fd, FORMAT2, "Name/Username", "Host", " ", "Mask", "Port", " ", "Status", term);
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, peer, entry) {
+ i = ao2_iterator_init(peers, 0);
+ for (peer = ao2_iterator_next(&i); peer;
+ peer_unref(peer), peer = ao2_iterator_next(&i)) {
char nm[20];
char status[20];
char srch[2000];
@@ -4295,7 +4366,6 @@
peer->encmethods ? "(E)" : " ", status, term);
total_peers++;
}
- AST_LIST_UNLOCK(&peers);
if (s)
astman_append(s,"%d iax2 peers [%d online, %d offline, %d unmonitored]%s", total_peers, online_peers, offline_peers, unmonitored_peers, term);
@@ -4761,6 +4831,7 @@
int bestscore = 0;
int gotcapability = 0;
struct ast_variable *v = NULL, *tmpvar = NULL;
+ ao2_iterator i;
if (!iaxs[callno])
return res;
@@ -4815,8 +4886,8 @@
return res;
}
/* Search the userlist for a compatible entry, and fill in the rest */
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
+ i = ao2_iterator_init(users, 0);
+ while ((user = ao2_iterator_next(&i))) {
if ((ast_strlen_zero(iaxs[callno]->username) || /* No username specified */
!strcmp(iaxs[callno]->username, user->name)) /* Or this username specified */
&& ast_apply_ha(user->ha, sin) /* Access is permitted from this IP */
@@ -4824,6 +4895,8 @@
apply_context(user->contexts, iaxs[callno]->context))) { /* Context is permitted */
if (!ast_strlen_zero(iaxs[callno]->username)) {
/* Exact match, stop right now. */
+ if (best)
+ user_unref(best);
best = user;
break;
} else if (ast_strlen_zero(user->secret) && ast_strlen_zero(user->inkeys)) {
@@ -4832,13 +4905,19 @@
/* There was host authentication and we passed, bonus! */
if (bestscore < 4) {
bestscore = 4;
+ if (best)
+ user_unref(best);
best = user;
+ continue;
}
} else {
/* No host access, but no secret, either, not bad */
if (bestscore < 3) {
bestscore = 3;
+ if (best)
+ user_unref(best);
best = user;
+ continue;
}
}
} else {
@@ -4846,26 +4925,31 @@
/* Authentication, but host access too, eh, it's something.. */
if (bestscore < 2) {
bestscore = 2;
+ if (best)
+ user_unref(best);
best = user;
+ continue;
}
} else {
/* Authentication and no host access... This is our baseline */
if (bestscore < 1) {
bestscore = 1;
+ if (best)
+ user_unref(best);
best = user;
+ continue;
}
}
}
}
- }
- AST_LIST_UNLOCK(&users);
+ user_unref(user);
+ }
user = best;
if (!user && !ast_strlen_zero(iaxs[callno]->username)) {
user = realtime_user(iaxs[callno]->username);
if (user && !ast_strlen_zero(iaxs[callno]->context) && /* No context specified */
!apply_context(user->contexts, iaxs[callno]->context)) { /* Context is permitted */
- destroy_user(user);
- user = NULL;
+ user = user_unref(user);
}
}
if (user) {
@@ -4944,9 +5028,8 @@
ast_string_field_set(iaxs[callno], secret, buf);
} else
ast_string_field_set(iaxs[callno], secret, user->secret);
- if (ast_test_flag(user, IAX_TEMPONLY))
- destroy_user(user);
res = 0;
+ user = user_unref(user);
}
ast_set2_flag(iaxs[callno], iax2_getpeertrunk(*sin), IAX_TRUNK);
return res;
@@ -4992,7 +5075,6 @@
*/
static int authenticate_request(int call_num)
{
- struct iax2_user *user = NULL;
struct iax_ie_data ied;
int res = -1, authreq_restrict = 0;
char challenge[10];
@@ -5002,17 +5084,18 @@
/* If an AUTHREQ restriction is in place, make sure we can send an AUTHREQ back */
if (ast_test_flag(p, IAX_MAXAUTHREQ)) {
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
- if (!strcmp(user->name, p->username)) {
- if (user->curauthreq == user->maxauthreq)
- authreq_restrict = 1;
- else
- user->curauthreq++;
- break;
- }
- }
- AST_LIST_UNLOCK(&users);
+ struct iax2_user *user, tmp_user = {
+ .name = p->username,
+ };
+
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
+ if (user) {
+ if (user->curauthreq == user->maxauthreq)
+ authreq_restrict = 1;
+ else
+ user->curauthreq++;
+ user = user_unref(user);
+ }
}
/* If the AUTHREQ limit test failed, send back an error */
@@ -5051,21 +5134,19 @@
char rsasecret[256] = "";
int res = -1;
int x;
- struct iax2_user *user = NULL;
-
- AST_LIST_LOCK(&users);
- AST_LIST_TRAVERSE(&users, user, entry) {
- if (!strcmp(user->name, p->username))
- break;
- }
+ struct iax2_user *user, tmp_user = {
+ .name = p->username,
+ };
+
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
if (user) {
if (ast_test_flag(p, IAX_MAXAUTHREQ)) {
- user->curauthreq--;
+ ast_atomic_fetchadd_int(&user->curauthreq, -1);
ast_clear_flag(p, IAX_MAXAUTHREQ);
}
ast_string_field_set(p, host, user->name);
- }
- AST_LIST_UNLOCK(&users);
+ user = user_unref(user);
+ }
if (!ast_test_flag(&p->state, IAX_STATE_AUTHENTICATED))
return res;
@@ -5127,11 +5208,12 @@
char md5secret[256] = "";
char rsasecret[256] = "";
char secret[256] = "";
- struct iax2_peer *p;
+ struct iax2_peer *p = NULL;
struct ast_key *key;
char *keyn;
int x;
int expire = 0;
+ int res = -1;
ast_clear_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED | IAX_STATE_UNCHANGED);
/* iaxs[callno]->peer[0] = '\0'; not necc. any more-- stringfield is pre-inited to null string */
@@ -5158,23 +5240,19 @@
if (!p || !iaxs[callno]) {
if (authdebug && !p)
ast_log(LOG_NOTICE, "No registration for peer '%s' (from %s)\n", peer, ast_inet_ntoa(sin->sin_addr));
- return -1;
+ goto return_unref;
}
if (!ast_test_flag(p, IAX_DYNAMIC)) {
if (authdebug)
ast_log(LOG_NOTICE, "Peer '%s' is not dynamic (from %s)\n", peer, ast_inet_ntoa(sin->sin_addr));
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ goto return_unref;
}
if (!ast_apply_ha(p->ha, sin)) {
if (authdebug)
ast_log(LOG_NOTICE, "Host %s denied access to register peer '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name);
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ goto return_unref;
}
if (!inaddrcmp(&p->addr, sin))
ast_set_flag(&iaxs[callno]->state, IAX_STATE_UNCHANGED);
@@ -5200,16 +5278,12 @@
if (!keyn) {
if (authdebug)
ast_log(LOG_NOTICE, "Host %s failed RSA authentication with inkeys '%s'\n", peer, p->inkeys);
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ goto return_unref;
}
} else {
if (authdebug)
ast_log(LOG_NOTICE, "Host '%s' trying to do RSA authentication, but we have no inkeys\n", peer);
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ goto return_unref;
}
} else if (!ast_strlen_zero(md5secret) && (p->authmethods & IAX_AUTH_MD5) && !ast_strlen_zero(iaxs[callno]->challenge)) {
struct MD5Context md5;
@@ -5233,26 +5307,20 @@
} else {
if (authdebug)
ast_log(LOG_NOTICE, "Host %s failed MD5 authentication for '%s' (%s != %s)\n", ast_inet_ntoa(sin->sin_addr), p->name, requeststr, md5secret);
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ goto return_unref;
}
} else if (!ast_strlen_zero(secret) && (p->authmethods & IAX_AUTH_PLAINTEXT)) {
/* They've provided a plain text password and we support that */
if (strcmp(secret, p->secret)) {
if (authdebug)
ast_log(LOG_NOTICE, "Host %s did not provide proper plaintext password for '%s'\n", ast_inet_ntoa(sin->sin_addr), p->name);
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ goto return_unref;
} else
ast_set_flag(&iaxs[callno]->state, IAX_STATE_AUTHENTICATED);
} else if (!ast_strlen_zero(md5secret) || !ast_strlen_zero(secret)) {
if (authdebug)
ast_log(LOG_NOTICE, "Inappropriate authentication received\n");
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
+ goto return_unref;
}
ast_string_field_set(iaxs[callno], peer, peer);
/* Choose lowest expiry number */
@@ -5261,10 +5329,13 @@
ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
-
- return 0;
+ res = 0;
+
+return_unref:
+ if (p)
+ peer_unref(p);
+
+ return res;
}
static int authenticate(const char *challenge, const char *secret, const char *keyn, int authmethods, struct iax_ie_data *ied, struct sockaddr_in *sin, aes_encrypt_ctx *ecx, aes_decrypt_ctx *dcx)
@@ -5351,8 +5422,8 @@
/* Normal password authentication */
res = authenticate(p->challenge, override, okey, authmethods, &ied, sin, &p->ecx, &p->dcx);
} else {
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE(&peers, peer, entry) {
+ ao2_iterator i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
if ((ast_strlen_zero(p->peer) || !strcmp(p->peer, peer->name))
/* No peer specified at our end, or this is the peer */
&& (ast_strlen_zero(peer->username) || (!strcmp(peer->username, p->username)))
@@ -5361,11 +5432,11 @@
/* No specified host, or this is our host */
) {
res = authenticate(p->challenge, peer->secret, peer->outkey, authmethods, &ied, sin, &p->ecx, &p->dcx);
+ peer_unref(peer);
if (!res)
break;
}
}
- AST_LIST_UNLOCK(&peers);
if (!peer) {
/* We checked our list and didn't find one. It's unlikely, but possible,
that we're trying to authenticate *to* a realtime peer */
@@ -5374,13 +5445,11 @@
if ((peer = realtime_peer(peer_name, NULL))) {
ast_mutex_lock(&iaxsl[callno]);
if (!(p = iaxs[callno])) {
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
+ peer_unref(peer);
return -1;
}
res = authenticate(p->challenge, peer->secret,peer->outkey, authmethods, &ied, sin, &p->ecx, &p->dcx);
- if (ast_test_flag(peer, IAX_TEMPONLY))
- destroy_peer(peer);
+ peer_unref(peer);
}
if (!peer) {
ast_mutex_lock(&iaxsl[callno]);
@@ -5684,42 +5753,36 @@
static void __expire_registry(void *data)
{
char *name = data;
- struct iax2_peer *p = NULL;
-
- /* Go through and grab this peer... and if it needs to be removed... then do it */
- AST_LIST_LOCK(&peers);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&peers, p, entry) {
- if (!strcasecmp(p->name, name)) {
- p->expire = -1;
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END
- AST_LIST_UNLOCK(&peers);
-
- /* Peer is already gone for whatever reason */
- if (!p)
+ struct iax2_peer *peer = NULL;
+ struct iax2_peer tmp_peer = {
+ .name = name,
+ };
+
+ peer = ao2_find(peers, &tmp_peer, OBJ_POINTER);
+ if (!peer)
return;
- ast_log(LOG_DEBUG, "Expiring registration for peer '%s'\n", p->name);
- if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(p, IAX_TEMPONLY|IAX_RTCACHEFRIENDS)))
- realtime_update_peer(p->name, &p->addr, 0);
- manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", p->name);
+ peer->expire = -1;
+
+ ast_log(LOG_DEBUG, "Expiring registration for peer '%s'\n", peer->name);
+ if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(peer, IAX_TEMPONLY|IAX_RTCACHEFRIENDS)))
+ realtime_update_peer(peer->name, &peer->addr, 0);
+ manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
/* Reset the address */
- memset(&p->addr, 0, sizeof(p->addr));
+ memset(&peer->addr, 0, sizeof(peer->addr));
/* Reset expiry value */
- p->expiry = min_reg_expire;
- if (!ast_test_flag(p, IAX_TEMPONLY))
- ast_db_del("IAX/Registry", p->name);
- register_peer_exten(p, 0);
- ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
+ peer->expiry = min_reg_expire;
+ if (!ast_test_flag(peer, IAX_TEMPONLY))
+ ast_db_del("IAX/Registry", peer->name);
+ register_peer_exten(peer, 0);
+ ast_device_state_changed("IAX2/%s", peer->name); /* Activate notification */
if (iax2_regfunk)
- iax2_regfunk(p->name, 0);
-
- if (ast_test_flag(p, IAX_RTAUTOCLEAR)) {
- ast_set_flag(p, IAX_DELME);
- prune_peers();
- }
+ iax2_regfunk(peer->name, 0);
+
+ if (ast_test_flag(peer, IAX_RTAUTOCLEAR))
+ ao2_unlink(peers, peer);
+
+ peer_unref(peer);
}
static int expire_registry(void *data)
@@ -5786,6 +5849,7 @@
char data[80];
int version;
const char *peer_name;
+ int res = -1;
memset(&ied, 0, sizeof(ied));
@@ -5799,11 +5863,8 @@
return -1;
}
ast_mutex_lock(&iaxsl[callno]);
- if (!iaxs[callno]) {
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
- }
+ if (!iaxs[callno])
+ goto return_unref;
if (ast_test_flag((&globalflags), IAX_RTUPDATE) && (ast_test_flag(p, IAX_TEMPONLY|IAX_RTCACHEFRIENDS))) {
if (sin->sin_addr.s_addr) {
@@ -5844,9 +5905,8 @@
/* Make sure our call still exists, an INVAL at the right point may make it go away */
if (!iaxs[callno]) {
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return 0;
+ res = 0;
+ goto return_unref;
}
/* Store socket fd */
@@ -5893,9 +5953,13 @@
version = iax_check_version(devtype);
if (version)
iax_ie_append_short(&ied, IAX_IE_FIRMWAREVER, version);
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGACK, 0, ied.buf, ied.pos, -1);
+
+ res = 0;
+
+return_unref:
+ peer_unref(p);
+
+ return res ? res : send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGACK, 0, ied.buf, ied.pos, -1);
}
static int registry_authrequest(int callno)
@@ -5904,6 +5968,7 @@
struct iax2_peer *p;
char challenge[10];
const char *peer_name;
+ int res = -1;
peer_name = ast_strdupa(iaxs[callno]->peer);
@@ -5911,28 +5976,29 @@
ast_mutex_unlock(&iaxsl[callno]);
p = find_peer(peer_name, 1);
ast_mutex_lock(&iaxsl[callno]);
- if (!iaxs[callno]) {
- if (p && ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return -1;
- }
- if (p) {
- memset(&ied, 0, sizeof(ied));
- iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods);
- if (p->authmethods & (IAX_AUTH_RSA | IAX_AUTH_MD5)) {
- /* Build the challenge */
- snprintf(challenge, sizeof(challenge), "%d", (int)ast_random());
- ast_string_field_set(iaxs[callno], challenge, challenge);
- /* snprintf(iaxs[callno]->challenge, sizeof(iaxs[callno]->challenge), "%d", (int)ast_random()); */
- iax_ie_append_str(&ied, IAX_IE_CHALLENGE, iaxs[callno]->challenge);
- }
- iax_ie_append_str(&ied, IAX_IE_USERNAME, peer_name);
- if (ast_test_flag(p, IAX_TEMPONLY))
- destroy_peer(p);
- return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1);;
- }
- ast_log(LOG_WARNING, "No such peer '%s'\n", peer_name);
- return 0;
+ if (!iaxs[callno])
+ goto return_unref;
+ if (!p) {
+ ast_log(LOG_WARNING, "No such peer '%s'\n", peer_name);
+ goto return_unref;
+ }
+
+ memset(&ied, 0, sizeof(ied));
+ iax_ie_append_short(&ied, IAX_IE_AUTHMETHODS, p->authmethods);
+ if (p->authmethods & (IAX_AUTH_RSA | IAX_AUTH_MD5)) {
+ /* Build the challenge */
+ snprintf(challenge, sizeof(challenge), "%d", (int)ast_random());
+ ast_string_field_set(iaxs[callno], challenge, challenge);
+ iax_ie_append_str(&ied, IAX_IE_CHALLENGE, iaxs[callno]->challenge);
+ }
+ iax_ie_append_str(&ied, IAX_IE_USERNAME, peer_name);
+
+ res = 0;
+
+return_unref:
+ peer_unref(p);
+
+ return res ? res : send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGAUTH, 0, ied.buf, ied.pos, -1);;
}
static int registry_rerequest(struct iax_ies *ies, int callno, struct sockaddr_in *sin)
@@ -6643,7 +6709,7 @@
}
/* This is a video frame, get call number */
- fr->callno = find_callno(ntohs(vh->callno) & ~0x8000, dcallno, &sin, new, 1, fd);
+ fr->callno = find_callno(ntohs(vh->callno) & ~0x8000, dcallno, &sin, new, fd);
minivid = 1;
} else if ((meta->zeros == 0) && !(ntohs(meta->metacmd) & 0x8000)) {
unsigned char metatype;
@@ -6701,7 +6767,7 @@
/* Stop if we don't have enough data */
if (len > res)
break;
- fr->callno = find_callno(callno & ~IAX_FLAG_FULL, 0, &sin, NEW_PREVENT, 1, fd);
+ fr->callno = find_callno(callno & ~IAX_FLAG_FULL, 0, &sin, NEW_PREVENT, fd);
if (fr->callno) {
ast_mutex_lock(&iaxsl[fr->callno]);
/* If it's a valid call, deliver the contents. If not, we
@@ -6788,7 +6854,7 @@
}
if (!fr->callno)
- fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, 1, fd);
+ fr->callno = find_callno(ntohs(mh->callno) & ~IAX_FLAG_FULL, dcallno, &sin, new, fd);
if (fr->callno > 0)
ast_mutex_lock(&iaxsl[fr->callno]);
@@ -8239,7 +8305,7 @@
if (!reg->callno) {
if (option_debug)
ast_log(LOG_DEBUG, "Allocate call number\n");
- reg->callno = find_callno(0, 0, ®->addr, NEW_FORCE, 1, defaultsockfd);
+ reg->callno = find_callno(0, 0, ®->addr, NEW_FORCE, defaultsockfd);
if (reg->callno < 1) {
ast_log(LOG_WARNING, "Unable to create call for registration\n");
return -1;
@@ -8299,7 +8365,7 @@
memset(&ied, 0, sizeof(ied));
iax_ie_append_raw(&ied, IAX_IE_PROVISIONING, provdata.buf, provdata.pos);
- callno = find_callno(0, 0, &sin, NEW_FORCE, 1, cai.sockfd);
+ callno = find_callno(0, 0, &sin, NEW_FORCE, cai.sockfd);
if (!callno)
return -1;
@@ -8411,6 +8477,15 @@
return 0;
}
+static int iax2_poke_peer_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_peer *peer = obj;
+
+ iax2_poke_peer(peer, 0);
+
+ return 0;
+}
+
static int iax2_poke_peer(struct iax2_peer *peer, int heldcall)
{
if (!peer->maxms || !peer->addr.sin_addr.s_addr) {
@@ -8430,7 +8505,7 @@
}
if (heldcall)
ast_mutex_unlock(&iaxsl[heldcall]);
- peer->callno = find_callno(0, 0, &peer->addr, NEW_FORCE, 0, peer->sockfd);
+ peer->callno = find_callno(0, 0, &peer->addr, NEW_FORCE, peer->sockfd);
if (heldcall)
ast_mutex_lock(&iaxsl[heldcall]);
if (peer->callno < 1) {
@@ -8504,7 +8579,7 @@
if (pds.port)
sin.sin_port = htons(atoi(pds.port));
- callno = find_callno(0, 0, &sin, NEW_FORCE, 1, cai.sockfd);
+ callno = find_callno(0, 0, &sin, NEW_FORCE, cai.sockfd);
if (callno < 1) {
ast_log(LOG_WARNING, "Unable to create call\n");
*cause = AST_CAUSE_CONGESTION;
@@ -8801,7 +8876,31 @@
}
}
-
+static void peer_destructor(void *obj)
+{
+ struct iax2_peer *peer = obj;
+
+ ast_free_ha(peer->ha);
+
+ /* Delete it, it needs to disappear */
+ if (peer->expire > -1)
+ ast_sched_del(sched, peer->expire);
+ if (peer->pokeexpire > -1)
+ ast_sched_del(sched, peer->pokeexpire);
+ if (peer->callno > 0) {
+ ast_mutex_lock(&iaxsl[peer->callno]);
+ iax2_destroy(peer->callno);
+ ast_mutex_unlock(&iaxsl[peer->callno]);
+ }
+
+ register_peer_exten(peer, 0);
+
+ if (peer->dnsmgr)
+ ast_dnsmgr_release(peer->dnsmgr);
+
+ ast_string_field_free_pools(peer);
+}
+
/*! \brief Create peer structure based on configuration */
static struct iax2_peer *build_peer(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly)
{
@@ -8810,38 +8909,31 @@
int maskfound=0;
int found=0;
int firstpass=1;
-
- AST_LIST_LOCK(&peers);
+ struct iax2_peer tmp_peer = {
+ .name = name,
+ };
+
if (!temponly) {
- AST_LIST_TRAVERSE(&peers, peer, entry) {
- if (!strcmp(peer->name, name)) {
- if (!ast_test_flag(peer, IAX_DELME))
- firstpass = 0;
- break;
- }
- }
- } else
- peer = NULL;
+ peer = ao2_find(peers, &tmp_peer, OBJ_POINTER);
+ if (peer && !ast_test_flag(peer, IAX_DELME))
+ firstpass = 0;
+ }
+
if (peer) {
found++;
if (firstpass) {
oldha = peer->ha;
peer->ha = NULL;
}
- AST_LIST_REMOVE(&peers, peer, entry);
- AST_LIST_UNLOCK(&peers);
- } else {
- AST_LIST_UNLOCK(&peers);
- if ((peer = ast_calloc(1, sizeof(*peer)))) {
- peer->expire = -1;
- peer->pokeexpire = -1;
- peer->sockfd = defaultsockfd;
- if (ast_string_field_init(peer, 32)) {
- free(peer);
- peer = NULL;
- }
- }
- }
+ ao2_unlink(peers, peer);
+ } else if ((peer = ao2_alloc(sizeof(*peer), peer_destructor))) {
+ peer->expire = -1;
+ peer->pokeexpire = -1;
+ peer->sockfd = defaultsockfd;
+ if (ast_string_field_init(peer, 32))
+ peer = peer_unref(peer);
+ }
+
if (peer) {
if (firstpass) {
ast_copy_flags(peer, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
@@ -8927,8 +9019,7 @@
ast_clear_flag(peer, IAX_DYNAMIC);
if (ast_dnsmgr_lookup(v->value, &peer->addr.sin_addr, &peer->dnsmgr)) {
ast_string_field_free_pools(peer);
- free(peer);
- return NULL;
+ return peer_unref(peer);
}
if (!peer->addr.sin_port)
peer->addr.sin_port = htons(IAX_DEFAULT_PORTNO);
@@ -8938,8 +9029,7 @@
} else if (!strcasecmp(v->name, "defaultip")) {
if (ast_get_ip(&peer->defaddr, v->value)) {
ast_string_field_free_pools(peer);
- free(peer);
- return NULL;
+ return peer_unref(peer);
}
} else if (!strcasecmp(v->name, "sourceaddress")) {
peer_set_srcaddr(peer, v->value);
@@ -9045,6 +9135,19 @@
return peer;
}
+static void user_destructor(void *obj)
+{
+ struct iax2_user *user = obj;
+
+ ast_free_ha(user->ha);
+ free_context(user->contexts);
+ if(user->vars) {
+ ast_variables_destroy(user->vars);
+ user->vars = NULL;
+ }
+ ast_string_field_free_pools(user);
+}
+
/*! \brief Create in-memory user structure from configuration */
static struct iax2_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int temponly)
{
@@ -9057,18 +9160,15 @@
int oldcurauthreq = 0;
char *varname = NULL, *varval = NULL;
struct ast_variable *tmpvar = NULL;
-
- AST_LIST_LOCK(&users);
+ struct iax2_user tmp_user = {
+ .name = name,
+ };
+
if (!temponly) {
- AST_LIST_TRAVERSE(&users, user, entry) {
- if (!strcmp(user->name, name)) {
- if (!ast_test_flag(user, IAX_DELME))
- firstpass = 0;
- break;
- }
- }
- } else
- user = NULL;
+ user = ao2_find(users, &tmp_user, OBJ_POINTER);
+ if (user && !ast_test_flag(user, IAX_DELME))
+ firstpass = 0;
+ }
if (user) {
if (firstpass) {
@@ -9079,12 +9179,9 @@
user->contexts = NULL;
}
/* Already in the list, remove it and it will be added back (or FREE'd) */
- AST_LIST_REMOVE(&users, user, entry);
- AST_LIST_UNLOCK(&users);
+ ao2_unlink(users, user);
} else {
- AST_LIST_UNLOCK(&users);
- /* This is going to memset'd to 0 in the next block */
- user = ast_calloc(sizeof(*user),1);
+ user = ao2_alloc(sizeof(*user), user_destructor);
}
if (user) {
@@ -9092,8 +9189,8 @@
ast_string_field_free_pools(user);
memset(user, 0, sizeof(struct iax2_user));
if (ast_string_field_init(user, 32)) {
- free(user);
- user = NULL;
+ user = user_unref(user);
+ goto cleanup;
}
user->maxauthreq = maxauthreq;
user->curauthreq = oldcurauthreq;
@@ -9257,6 +9354,7 @@
}
ast_clear_flag(user, IAX_DELME);
}
+cleanup:
if (oldha)
ast_free_ha(oldha);
if (oldcon)
@@ -9264,16 +9362,29 @@
return user;
}
+static int peer_delme_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_peer *peer = obj;
+
+ ast_set_flag(peer, IAX_DELME);
+
+ return 0;
+}
+
+static int user_delme_cb(void *obj, void *arg, int flags)
+{
+ struct iax2_user *user = obj;
+
+ ast_set_flag(user, IAX_DELME);
+
+ return 0;
+}
+
static void delete_users(void)
{
[... 916 lines stripped ...]
More information about the asterisk-commits
mailing list