summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Murphy <murf@digium.com>2008-04-16 23:53:27 +0000
committerSteve Murphy <murf@digium.com>2008-04-16 23:53:27 +0000
commit5fb4b1bbe53307c1266eae71113c2a8a5c9708a7 (patch)
tree779b6e2fb75cceb98ec617df7591d41f0136a947
parent123ac5fd646cb7a0ed3b7055cda609db93c20487 (diff)
This is the scariest commit I've done in a long time. This is the astobj2-ification of chan_sip. I've tested a number of scenarios like crazy. It used to have 4x the call setup/teardown performance of trunk, but now it's roughly at parity. I will attempt to find the bottlenecks and get it back to the 4x mark. The changes made were somewhat invasive, but the value to the community of these upgrades outweighs waiting further for more testing. Every change being made to chan_sip was lousing this code up when we tried to merge. Peers, Users, Dialogs, are all now astobj2 objects, indexed via hashtables. Refcounting is used to track objects and free them at the bitter end of their lives. Please file issues on bugs.digium.com, and PLEASE, please, please be patient. One natural advantage to all the hash-table work is that loading large sip.conf files full of thousands of peers now goes much faster. One more please: PLEASE help thrash this code and test it.
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@114190 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r--CHANGES7
-rw-r--r--channels/chan_sip.c2189
-rw-r--r--configs/sip.conf.sample18
-rw-r--r--doc/chan_sip-perf-testing.txt110
4 files changed, 1684 insertions, 640 deletions
diff --git a/CHANGES b/CHANGES
index 53accaa79..ca167fe21 100644
--- a/CHANGES
+++ b/CHANGES
@@ -35,6 +35,13 @@ SIP Changes
* The ATTENDED_TRANSFER_COMPLETE_SOUND can now be set using setvar to cause a given
audio file to be played upon completion of an attended transfer.
* Added DNS manager support to registrations for peers referencing peer entries.
+ * Performance improvements via using hash tables (astobj2) and doubly-linked lists to improve
+ load/reload of large numbers of peers/users by ~40x (for large lists of peers.
+ Initially, we saw 4x improvement in call setup/destruction, but at the time
+ of merging, this gain has disappeared; further research will be done to try
+ and restore this performance improvement. Astobj2 refcounting is now used
+ for users, peers, and dialogs. Users are encouraged to assist in regression
+ testing and problem reporting!
IAX Changes
-----------
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 44eb1c163..df0806740 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1,4 +1,4 @@
-/*
+ /*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
@@ -136,6 +136,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <signal.h>
#include <sys/signal.h>
#include <regex.h>
+#include <time.h>
#include "asterisk/network.h"
#include "asterisk/paths.h" /* need ast_config_AST_SYSTEM_NAME */
@@ -163,6 +164,19 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/utils.h"
#include "asterisk/file.h"
#include "asterisk/astobj.h"
+/*
+ Uncomment the define below, if you are having refcount related memory leaks.
+ With this uncommented, this module will generate a file, /tmp/refs, which contains
+ a history of the ao2_ref() calls. To be useful, all calls to ao2_* functions should
+ be modified to ao2_t_* calls, and include a tag describing what is happening with
+ enough detail, to make pairing up a reference count increment with its corresponding decrement.
+ The refcounter program in utils/ can be invaluable in highlighting objects that are not
+ balanced, along with the complete history for that object.
+ In normal operation, the macros defined will throw away the tags, so they do not
+ affect the speed of the program at all. They can be considered to be documentation.
+*/
+/* #define REF_DEBUG 1 */
+#include "asterisk/astobj2.h"
#include "asterisk/dnsmgr.h"
#include "asterisk/devicestate.h"
#include "asterisk/linkedlists.h"
@@ -1161,7 +1175,6 @@ struct sip_st_cfg {
*/
struct sip_pvt {
struct sip_pvt *next; /*!< Next dialog in chain */
- ast_mutex_t pvt_lock; /*!< Dialog private lock */
enum invitestates invitestate; /*!< Track state of SIP_INVITEs */
int method; /*!< SIP method that opened this dialog */
AST_DECLARE_STRING_FIELDS(
@@ -1314,43 +1327,48 @@ struct sip_pvt {
* the container and individual items, and functions to add/remove
* references to the individual items.
*/
-static struct sip_pvt *dialoglist = NULL;
-
-/*! \brief Protect the SIP dialog list (of sip_pvt's) */
-AST_MUTEX_DEFINE_STATIC(dialoglock);
+struct ao2_container *dialogs;
-#ifndef DETECT_DEADLOCKS
-/*! \brief hide the way the list is locked/unlocked */
-static void dialoglist_lock(void)
+/*!
+ * when we create or delete references, make sure to use these
+ * functions so we keep track of the refcounts.
+ * To simplify the code, we allow a NULL to be passed to dialog_unref().
+ */
+#ifdef REF_DEBUG
+#define dialog_ref(arg1,arg2) dialog_ref_debug((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+#define dialog_unref(arg1,arg2) dialog_unref_debug((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__)
+static struct sip_pvt *dialog_ref_debug(struct sip_pvt *p, char *tag, char *file, int line, const char *func)
{
- ast_mutex_lock(&dialoglock);
+ if (p)
+ _ao2_ref_debug(p, 1, tag, file, line, func);
+ else
+ ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n");
+ return p;
}
-static void dialoglist_unlock(void)
+static struct sip_pvt *dialog_unref_debug(struct sip_pvt *p, char *tag, char *file, int line, const char *func)
{
- ast_mutex_unlock(&dialoglock);
+ if (p)
+ _ao2_ref_debug(p, -1, tag, file, line, func);
+ return NULL;
}
#else
-/* we don't want to HIDE the information about where the lock was requested if trying to debug
- * deadlocks! So, just make these macros! */
-#define dialoglist_lock(x) ast_mutex_lock(&dialoglock)
-#define dialoglist_unlock(x) ast_mutex_unlock(&dialoglock)
-#endif
-
-/*!
- * when we create or delete references, make sure to use these
- * functions so we keep track of the refcounts.
- * To simplify the code, we allow a NULL to be passed to dialog_unref().
- */
-static struct sip_pvt *dialog_ref(struct sip_pvt *p)
+static struct sip_pvt *dialog_ref(struct sip_pvt *p, char *tag)
{
+ if (p)
+ ao2_ref(p, 1);
+ else
+ ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n");
return p;
}
-static struct sip_pvt *dialog_unref(struct sip_pvt *p)
+static struct sip_pvt *dialog_unref(struct sip_pvt *p, char *tag)
{
+ if (p)
+ ao2_ref(p, -1);
return NULL;
}
+#endif
/*! \brief sip packet - raw format for outbound packets that are sent or scheduled for transmission
* Packets are linked in a list, whose head is in the struct sip_pvt they belong to.
@@ -1376,7 +1394,7 @@ struct sip_pkt {
/*! \brief Structure for SIP user data. User's place calls to us */
struct sip_user {
/* Users who can access various contexts */
- ASTOBJ_COMPONENTS(struct sip_user);
+ char name[80];
char secret[80]; /*!< Password */
char md5secret[80]; /*!< Password in md5 */
char context[AST_MAX_CONTEXT]; /*!< Default context for incoming calls */
@@ -1396,6 +1414,7 @@ struct sip_user {
/* things that don't belong in flags */
char is_realtime; /*!< this is a 'realtime' user */
+ unsigned int the_mark:1; /*!< moved out of the ASTOBJ fields; that which bears the_mark should be deleted! */
int amaflags; /*!< AMA flags for billing */
int callingpres; /*!< Calling id presentation */
@@ -1427,8 +1446,7 @@ struct sip_mailbox {
/*! \brief Structure for SIP peer data, we place calls to peers if registered or fixed IP address (host) */
/* XXX field 'name' must be first otherwise sip_addrcmp() will fail */
struct sip_peer {
- ASTOBJ_COMPONENTS(struct sip_peer); /*!< name, refcount, objflags, object pointers */
- /*!< peer->name is the unique name of this object */
+ char name[80]; /*!< peer->name is the unique name of this object */
struct sip_socket socket; /*!< Socket used for this peer */
char secret[80]; /*!< Password */
char md5secret[80]; /*!< Password in MD5 */
@@ -1470,6 +1488,7 @@ struct sip_peer {
char rt_fromcontact; /*!< P: copy fromcontact from realtime */
char host_dynamic; /*!< P: Dynamic Peers register with Asterisk */
char selfdestruct; /*!< P: Automatic peers need to destruct themselves */
+ char the_mark; /*!< moved out of ASTOBJ into struct proper; That which bears the_mark should be deleted! */
int expire; /*!< When to expire this peer registration */
int capability; /*!< Codec capability */
@@ -1560,20 +1579,27 @@ struct sip_threadinfo {
AST_LIST_ENTRY(sip_threadinfo) list;
};
-/* --- Linked lists of various objects --------*/
+/* --- Hash tables of various objects --------*/
+
+#ifdef LOW_MEMORY
+static int hash_peer_size = 17;
+static int hash_dialog_size = 17;
+static int hash_user_size = 17;
+#else
+static int hash_peer_size = 563;
+static int hash_dialog_size = 563;
+static int hash_user_size = 563;
+#endif
/*! \brief The thread list of TCP threads */
static AST_LIST_HEAD_STATIC(threadl, sip_threadinfo);
/*! \brief The user list: Users and friends */
-static struct ast_user_list {
- ASTOBJ_CONTAINER_COMPONENTS(struct sip_user);
-} userl;
+static struct ao2_container *users;
/*! \brief The peer list: Peers and Friends */
-static struct ast_peer_list {
- ASTOBJ_CONTAINER_COMPONENTS(struct sip_peer);
-} peerl;
+struct ao2_container *peers;
+struct ao2_container *peers_by_ip;
/*! \brief The register list: Other SIP proxies we register with and place calls to */
static struct ast_register_list {
@@ -1581,6 +1607,102 @@ static struct ast_register_list {
int recheck;
} regl;
+/*!
+ * \note The only member of the peer used here is the name field
+ */
+static int peer_hash_cb(const void *obj, const int flags)
+{
+ const struct sip_peer *peer = obj;
+
+ return ast_str_hash(peer->name);
+}
+
+/*!
+ * \note The only member of the peer used here is the name field
+ */
+static int peer_cmp_cb(void *obj, void *arg, int flags)
+{
+ struct sip_peer *peer = obj, *peer2 = arg;
+
+ return !strcasecmp(peer->name, peer2->name) ? CMP_MATCH : 0;
+}
+
+/*!
+ * \note the peer's addr struct provides to fields combined to make a key: the sin_addr.s_addr and sin_port fields.
+ */
+static int peer_iphash_cb(const void *obj, const int flags)
+{
+ const struct sip_peer *peer = obj;
+ int ret1 = peer->addr.sin_addr.s_addr;
+ if (ret1 < 0)
+ ret1 = -ret1;
+
+ if (ast_test_flag(&peer->flags[0], SIP_INSECURE_PORT)) {
+ return ret1;
+ } else {
+ return ret1 + peer->addr.sin_port;
+ }
+}
+
+/*!
+ * \note the peer's addr struct provides to fields combined to make a key: the sin_addr.s_addr and sin_port fields.
+ */
+static int peer_ipcmp_cb(void *obj, void *arg, int flags)
+{
+ struct sip_peer *peer = obj, *peer2 = arg;
+
+ if (peer->addr.sin_addr.s_addr != peer2->addr.sin_addr.s_addr)
+ return 0;
+
+ if (!ast_test_flag(&peer->flags[0], SIP_INSECURE_PORT) && !ast_test_flag(&peer2->flags[0], SIP_INSECURE_PORT)) {
+ if (peer->addr.sin_port == peer2->addr.sin_port)
+ return CMP_MATCH;
+ else
+ return 0;
+ }
+ return CMP_MATCH;
+}
+
+/*!
+ * \note The only member of the user used here is the name field
+ */
+static int user_hash_cb(const void *obj, const int flags)
+{
+ const struct sip_user *user = obj;
+
+ return ast_str_hash(user->name);
+}
+
+/*!
+ * \note The only member of the user used here is the name field
+ */
+static int user_cmp_cb(void *obj, void *arg, int flags)
+{
+ struct sip_user *user = obj, *user2 = arg;
+
+ return !strcasecmp(user->name, user2->name) ? CMP_MATCH : 0;
+}
+
+/*!
+ * \note The only member of the dialog used here callid string
+ */
+static int dialog_hash_cb(const void *obj, const int flags)
+{
+ const struct sip_pvt *pvt = obj;
+
+ return ast_str_hash(pvt->callid);
+}
+
+/*!
+ * \note The only member of the dialog used here callid string
+ */
+static int dialog_cmp_cb(void *obj, void *arg, int flags)
+{
+ struct sip_pvt *pvt = obj, *pvt2 = arg;
+
+ return !strcasecmp(pvt->callid, pvt2->callid) ? CMP_MATCH : 0;
+}
+
static int temp_pvt_init(void *);
static void temp_pvt_cleanup(void *);
@@ -1741,7 +1863,9 @@ static int __sip_autodestruct(const void *data);
static void sip_scheddestroy(struct sip_pvt *p, int ms);
static int sip_cancel_destroy(struct sip_pvt *p);
static struct sip_pvt *sip_destroy(struct sip_pvt *p);
-static int __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist);
+static void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdialoglist);
+static void *registry_unref(struct sip_registry *reg, char *tag);
+static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist);
static void __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod);
static void __sip_pretend_ack(struct sip_pvt *p);
static int __sip_semi_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod);
@@ -1799,12 +1923,14 @@ static int clear_realm_authentication(struct sip_auth *authlist); /* Clear realm
static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, const char *realm);
/*--- Misc functions */
+static void check_rtp_timeout(struct sip_pvt *dialog, time_t t);
static int sip_do_reload(enum channelreloadreason reason);
static int reload_config(enum channelreloadreason reason);
static int expire_register(const void *data);
static void *do_monitor(void *data);
static int restart_monitor(void);
-static int sip_addrcmp(char *name, struct sockaddr_in *sin); /* Support for peer matching */
+static void peer_mailboxes_to_str(struct ast_str **mailbox_str, struct sip_peer *peer);
+/* static int sip_addrcmp(char *name, struct sockaddr_in *sin); Support for peer matching */
static int sip_refer_allocate(struct sip_pvt *p);
static void ast_quiet_chan(struct ast_channel *chan);
static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target);
@@ -1825,8 +1951,11 @@ static char *transfermode2str(enum transfermodes mode) attribute_const;
static const char *nat2str(int nat) attribute_const;
static int peer_status(struct sip_peer *peer, char *status, int statuslen);
static char *sip_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_show_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static char * _sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[]);
static char *sip_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *_sip_dbdump(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[]);
+static char *sip_dbdump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static char *sip_show_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static void print_group(int fd, ast_group_t group, int crlf);
static const char *dtmfmode2str(int mode) attribute_const;
@@ -1887,7 +2016,9 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
static struct sip_user *build_user(const char *name, struct ast_variable *v, struct ast_variable *alt, int realtime);
static int update_call_counter(struct sip_pvt *fup, int event);
static void sip_destroy_peer(struct sip_peer *peer);
+static void sip_destroy_peer_fn(void *peer);
static void sip_destroy_user(struct sip_user *user);
+static void sip_destroy_user_fn(void *user);
static int sip_poke_peer(struct sip_peer *peer);
static void set_peer_defaults(struct sip_peer *peer);
static struct sip_peer *temp_peer(const char *name);
@@ -1919,6 +2050,7 @@ static int sip_reregister(const void *data);
static int __sip_do_register(struct sip_registry *r);
static int sip_reg_timeout(const void *data);
static void sip_send_all_registers(void);
+static int sip_reinvite_retry(const void *data);
/*--- Parsing SIP requests and responses */
static void append_date(struct sip_request *req); /* Append date to SIP packet */
@@ -2267,26 +2399,93 @@ cleanup2:
return NULL;
}
-#define sip_pvt_lock(x) ast_mutex_lock(&x->pvt_lock)
-#define sip_pvt_trylock(x) ast_mutex_trylock(&x->pvt_lock)
-#define sip_pvt_unlock(x) ast_mutex_unlock(&x->pvt_lock)
+#define sip_pvt_lock(x) ao2_lock(x)
+#define sip_pvt_trylock(x) ao2_trylock(x)
+#define sip_pvt_unlock(x) ao2_unlock(x)
/*!
* helper functions to unreference various types of objects.
* By handling them this way, we don't have to declare the
* destructor on each call, which removes the chance of errors.
*/
-static void unref_peer(struct sip_peer *peer)
+static void *unref_peer(struct sip_peer *peer, char *tag)
{
- ASTOBJ_UNREF(peer, sip_destroy_peer);
+ ao2_t_ref(peer, -1, tag);
+ return NULL;
}
-static void unref_user(struct sip_user *user)
+static void *unref_user(struct sip_user *user, char *tag)
{
- ASTOBJ_UNREF(user, sip_destroy_user);
+ ao2_t_ref(user, -1, tag);
+ return NULL;
}
-static void *registry_unref(struct sip_registry *reg)
+static struct sip_peer *ref_peer(struct sip_peer *peer, char *tag)
+{
+ ao2_t_ref(peer, 1,tag);
+ return peer;
+}
+
+/*!
+ * \brief Unlink a dialog from the dialogs container, as well as any other places
+ * that it may be currently stored.
+ *
+ * \note A reference to the dialog must be held before calling this function, and this
+ * function does not release that reference.
+ */
+static void *dialog_unlink_all(struct sip_pvt *dialog, int lockowner, int lockdialoglist)
+{
+ struct sip_pkt *cp;
+
+ dialog_ref(dialog, "Let's bump the count in the unlink so it doesn't accidentally become dead before we are done");
+
+ ao2_t_unlink(dialogs, dialog, "unlinking dialog via ao2_unlink");
+
+ /* Unlink us from the owner (channel) if we have one */
+ if (dialog->owner) {
+ if (lockowner)
+ ast_channel_lock(dialog->owner);
+ ast_debug(1, "Detaching from channel %s\n", dialog->owner->name);
+ dialog->owner->tech_pvt = dialog_unref(dialog->owner->tech_pvt, "resetting channel dialog ptr in unlink_all");
+ if (lockowner)
+ ast_channel_unlock(dialog->owner);
+ }
+ if (dialog->registry) {
+ if (dialog->registry->call == dialog)
+ dialog->registry->call = dialog_unref(dialog->registry->call, "nulling out the registry's call dialog field in unlink_all");
+ dialog->registry = registry_unref(dialog->registry, "delete dialog->registry");
+ }
+ if (dialog->stateid > -1) {
+ ast_extension_state_del(dialog->stateid, NULL);
+ dialog_unref(dialog, "removing extension_state, should unref the associated dialog ptr that was stored there.");
+ dialog->stateid = -1; /* shouldn't we 'zero' this out? */
+ }
+ /* Remove link from peer to subscription of MWI */
+ if (dialog->relatedpeer && dialog->relatedpeer->mwipvt)
+ dialog->relatedpeer->mwipvt = dialog_unref(dialog->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt");
+ if (dialog->relatedpeer && dialog->relatedpeer->call == dialog)
+ dialog->relatedpeer->call = dialog_unref(dialog->relatedpeer->call, "unset the relatedpeer->call field in tandem with relatedpeer field itself");
+
+ /* remove all current packets in this dialog */
+ while((cp = dialog->packets)) {
+ dialog->packets = dialog->packets->next;
+ AST_SCHED_DEL(sched, cp->retransid);
+ dialog_unref(cp->owner, "remove all current packets in this dialog, and the pointer to the dialog too as part of __sip_destroy");
+ ast_free(cp);
+ }
+
+ AST_SCHED_DEL_UNREF(sched, dialog->waitid, dialog_unref(dialog, "when you delete the waitid sched, you should dec the refcount for the stored dialog ptr"));
+
+ AST_SCHED_DEL_UNREF(sched, dialog->initid, dialog_unref(dialog, "when you delete the initid sched, you should dec the refcount for the stored dialog ptr"));
+
+ if (dialog->autokillid > -1)
+ AST_SCHED_DEL_UNREF(sched, dialog->autokillid, dialog_unref(dialog, "when you delete the autokillid sched, you should dec the refcount for the stored dialog ptr"));
+
+ dialog_unref(dialog, "Let's unbump the count in the unlink so the poor pvt can disappear if it is time");
+ return NULL;
+}
+
+static void *registry_unref(struct sip_registry *reg, char *tag)
{
ast_debug(3, "SIP Registry %s: refcount now %d\n", reg->hostname, reg->refcount - 1);
ASTOBJ_UNREF(reg, sip_registry_destroy);
@@ -2294,7 +2493,7 @@ static void *registry_unref(struct sip_registry *reg)
}
/*! \brief Add object reference to SIP registry */
-static struct sip_registry *registry_addref(struct sip_registry *reg)
+static struct sip_registry *registry_addref(struct sip_registry *reg, char *tag)
{
ast_debug(3, "SIP Registry %s: refcount now %d\n", reg->hostname, reg->refcount + 1);
return ASTOBJ_REF(reg); /* Add pointer to registry in packet */
@@ -2689,7 +2888,7 @@ static int retrans_pkt(const void *data)
struct sip_pkt *pkt = (struct sip_pkt *)data, *prev, *cur = NULL;
int reschedule = DEFAULT_RETRANS;
int xmitres = 0;
-
+
/* Lock channel PVT */
sip_pvt_lock(pkt->owner);
@@ -2792,8 +2991,11 @@ static int retrans_pkt(const void *data)
if (cur == pkt) {
UNLINK(cur, pkt->owner->packets, prev);
sip_pvt_unlock(pkt->owner);
+ if (pkt->owner)
+ pkt->owner = dialog_unref(pkt->owner,"pkt is being freed, its dialog ref is dead now");
if (pkt->data)
ast_free(pkt->data);
+ pkt->data = NULL;
ast_free(pkt);
return 0;
}
@@ -2822,7 +3024,7 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res
/* I removed the code from retrans_pkt that does the same thing so it doesn't get loaded into the scheduler */
/* According to the RFC some packets need to be retransmitted even if its TCP, so this needs to get revisited */
if (!(p->socket.type & SIP_TRANSPORT_UDP)) {
- xmitres = __sip_xmit(dialog_ref(p), data, len); /* Send packet */
+ xmitres = __sip_xmit(dialog_ref(p, "pasing dialog ptr into callback..."), data, len); /* Send packet */
if (xmitres == XMIT_ERROR) { /* Serious network trouble, no need to try again */
append_history(p, "XmitErr", "%s", fatal ? "(Critical)" : "(Non-critical)");
return AST_FAILURE;
@@ -2844,7 +3046,7 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res
pkt->seqno = seqno;
pkt->is_resp = resp;
pkt->is_fatal = fatal;
- pkt->owner = dialog_ref(p);
+ pkt->owner = dialog_ref(p, "__sip_reliable_xmit: setting pkt->owner");
pkt->next = p->packets;
p->packets = pkt; /* Add it to the queue */
pkt->timer_t1 = p->timer_t1; /* Set SIP timer T1 */
@@ -2861,6 +3063,7 @@ static enum sip_result __sip_reliable_xmit(struct sip_pvt *p, int seqno, int res
if (xmitres == XMIT_ERROR) { /* Serious network trouble, no need to try again */
append_history(pkt->owner, "XmitErr", "%s", pkt->is_fatal ? "(Critical)" : "(Non-critical)");
+ ast_log(LOG_ERROR, "Serious Network Trouble; __sip_xmit returns error for pkt data\n");
if (pkt->data)
ast_free(pkt->data);
return AST_FAILURE;
@@ -2896,7 +3099,7 @@ static int __sip_autodestruct(const void *data)
if (p->subscribed == MWI_NOTIFICATION)
if (p->relatedpeer)
- unref_peer(p->relatedpeer); /* Remove link to peer. If it's realtime, make sure it's gone from memory) */
+ p->relatedpeer = unref_peer(p->relatedpeer, "__sip_autodestruct: unref peer p->relatedpeer"); /* Remove link to peer. If it's realtime, make sure it's gone from memory) */
/* Reset schedule ID */
p->autokillid = -1;
@@ -2904,19 +3107,20 @@ static int __sip_autodestruct(const void *data)
if (p->owner) {
ast_log(LOG_WARNING, "Autodestruct on dialog '%s' with owner in place (Method: %s)\n", p->callid, sip_methods[p->method].text);
ast_queue_hangup(p->owner);
- dialog_unref(p);
} else if (p->refer) {
ast_debug(3, "Finally hanging up channel after transfer: %s\n", p->callid);
transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
append_history(p, "ReferBYE", "Sending BYE on transferer call leg %s", p->callid);
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- dialog_unref(p);
} else {
append_history(p, "AutoDestroy", "%s", p->callid);
ast_debug(3, "Auto destroying SIP dialog '%s'\n", p->callid);
- sip_destroy(p); /* Go ahead and destroy dialog. All attempts to recover is done */
+ dialog_unlink_all(p, TRUE, TRUE); /* once it's unlinked and unrefd everywhere, it'll be freed automagically */
+ /* dialog_unref(p, "unref dialog-- no other matching conditions"); -- unlink all now should finish off the dialog's references and free it. */
+ /* sip_destroy(p); */ /* Go ahead and destroy dialog. All attempts to recover is done */
/* sip_destroy also absorbs the reference */
}
+ dialog_unref(p, "The ref to a dialog passed to this sched callback is going out of scope; unref it.");
return 0;
}
@@ -2937,7 +3141,7 @@ static void sip_scheddestroy(struct sip_pvt *p, int ms)
if (p->do_history)
append_history(p, "SchedDestroy", "%d ms", ms);
- p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, dialog_ref(p));
+ p->autokillid = ast_sched_add(sched, ms, __sip_autodestruct, dialog_ref(p, "setting ref as passing into ast_sched_add for __sip_autodestruct"));
if (p->stimer && p->stimer->st_active == TRUE && p->stimer->st_schedid > 0)
stop_session_timer(p);
@@ -2951,11 +3155,13 @@ static int sip_cancel_destroy(struct sip_pvt *p)
{
int res = 0;
if (p->autokillid > -1) {
- if (!(res = ast_sched_del(sched, p->autokillid))) {
+ int res3;
+
+ if (!(res3 = ast_sched_del(sched, p->autokillid))) {
append_history(p, "CancelDestroy", "");
p->autokillid = -1;
+ dialog_unref(p, "dialog unrefd because autokillid is de-sched'd");
}
- dialog_unref(p);
}
return res;
}
@@ -3010,7 +3216,7 @@ static void __sip_ack(struct sip_pvt *p, int seqno, int resp, int sipmethod)
sip_pvt_lock(p);
}
UNLINK(cur, p->packets, prev);
- dialog_unref(cur->owner);
+ dialog_unref(cur->owner, "unref pkt cur->owner dialog from sip ack before freeing pkt");
if (cur->data)
ast_free(cur->data);
ast_free(cur);
@@ -3491,27 +3697,36 @@ static void clear_peer_mailboxes(struct sip_peer *peer)
destroy_mailbox(mailbox);
}
+static void sip_destroy_peer_fn(void *peer)
+{
+ sip_destroy_peer(peer);
+}
+
/*! \brief Destroy peer object from memory */
static void sip_destroy_peer(struct sip_peer *peer)
{
ast_debug(3, "Destroying SIP peer %s\n", peer->name);
-
if (peer->outboundproxy)
ast_free(peer->outboundproxy);
peer->outboundproxy = NULL;
/* Delete it, it needs to disappear */
- if (peer->call)
- peer->call = sip_destroy(peer->call);
-
- if (peer->mwipvt) /* We have an active subscription, delete it */
- peer->mwipvt = sip_destroy(peer->mwipvt);
+ if (peer->call) {
+ dialog_unlink_all(peer->call, TRUE, TRUE);
+ peer->call = dialog_unref(peer->call, "peer->call is being unset");
+ }
+
+ if (peer->mwipvt) { /* We have an active subscription, delete it */
+ dialog_unlink_all(peer->mwipvt, TRUE, TRUE);
+ peer->mwipvt = dialog_unref(peer->mwipvt, "unreffing peer->mwipvt");
+ }
+
if (peer->chanvars) {
ast_variables_destroy(peer->chanvars);
peer->chanvars = NULL;
}
-
+
/* If the schedule delete fails, that means the schedule is currently
* running, which means we should wait for that thread to complete.
* Otherwise, there's a crashable race condition.
@@ -3524,18 +3739,17 @@ static void sip_destroy_peer(struct sip_peer *peer)
register_peer_exten(peer, FALSE);
ast_free_ha(peer->ha);
if (peer->selfdestruct)
- apeerobjs--;
+ ast_atomic_fetchadd_int(&apeerobjs, -1);
else if (peer->is_realtime) {
- rpeerobjs--;
+ ast_atomic_fetchadd_int(&rpeerobjs, -1);
ast_debug(3, "-REALTIME- peer Destroyed. Name: %s. Realtime Peer objects: %d\n", peer->name, rpeerobjs);
} else
- speerobjs--;
+ ast_atomic_fetchadd_int(&speerobjs, -1);
clear_realm_authentication(peer->auth);
peer->auth = NULL;
if (peer->dnsmgr)
ast_dnsmgr_release(peer->dnsmgr);
clear_peer_mailboxes(peer);
- ast_free(peer);
}
/*! \brief Update peer data in database (if used) */
@@ -3578,6 +3792,8 @@ static const char *get_name_from_variable(struct ast_variable *var, const char *
/*! \brief realtime_peer: Get peer from realtime storage
* Checks the "sippeers" realtime family from extconfig.conf
* Checks the "sipregs" realtime family from extconfig.conf if it's configured.
+ * This returns a pointer to a peer and because we use build_peer, we can rest
+ * assured that the refcount is bumped.
*/
static struct sip_peer *realtime_peer(const char *newpeername, struct sockaddr_in *sin)
{
@@ -3743,8 +3959,14 @@ static struct sip_peer *realtime_peer(const char *newpeername, struct sockaddr_i
ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_RTAUTOCLEAR|SIP_PAGE2_RTCACHEFRIENDS);
if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
AST_SCHED_REPLACE(peer->expire, sched, global_rtautoclear * 1000, expire_register, (void *) peer);
+ /* we could be incr. its refcount right here, but I guess, since
+ peers hang around until module unload time anyway, it's not worth the trouble */
+ }
+ ao2_t_link(peers, peer, "link peer into peers table");
+ if (peer->addr.sin_addr.s_addr) {
+ ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
}
- ASTOBJ_CONTAINER_LINK(&peerl, peer);
+
} else {
peer->is_realtime = 1;
}
@@ -3758,30 +3980,33 @@ static struct sip_peer *realtime_peer(const char *newpeername, struct sockaddr_i
return peer;
}
-/*! \brief Support routine for find_peer */
-static int sip_addrcmp(char *name, struct sockaddr_in *sin)
-{
- /* We know name is the first field, so we can cast */
- struct sip_peer *p = (struct sip_peer *) name;
- return !(!inaddrcmp(&p->addr, sin) ||
- (ast_test_flag(&p->flags[0], SIP_INSECURE_PORT) &&
- (p->addr.sin_addr.s_addr == sin->sin_addr.s_addr)));
-}
-
/*! \brief Locate peer by name or ip address
* This is used on incoming SIP message to find matching peer on ip
or outgoing message to find matching peer on name
- \note Avoid using this function in new functions if there's a way to avoid it, i
- since it causes a database lookup or a traversal of the in-memory peer list.
+ \note Avoid using this function in new functions if there is a way to avoid it, i
+ since it might cause a database lookup.
*/
static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int realtime)
{
struct sip_peer *p = NULL;
-
- if (peer)
- p = ASTOBJ_CONTAINER_FIND(&peerl, peer);
- else
- p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp);
+ struct sip_peer tmp_peer;
+
+ if (peer) {
+ ast_copy_string(tmp_peer.name, peer, sizeof(tmp_peer.name));
+ p = ao2_t_find(peers, &tmp_peer, OBJ_POINTER, "ao2_find in peers table");
+ } else if (sin) { /* search by addr? */
+ tmp_peer.addr.sin_addr.s_addr = sin->sin_addr.s_addr;
+ tmp_peer.addr.sin_port = sin->sin_port;
+ tmp_peer.flags[0].flags = 0;
+ p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table"); /* WAS: p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */
+ if (!p) {
+ ast_set_flag(&tmp_peer.flags[0], SIP_INSECURE_PORT);
+ p = ao2_t_find(peers_by_ip, &tmp_peer, OBJ_POINTER, "ao2_find in peers_by_ip table 2"); /* WAS: p = ASTOBJ_CONTAINER_FIND_FULL(&peerl, sin, name, sip_addr_hashfunc, 1, sip_addrcmp); */
+ if (p) {
+ return p;
+ }
+ }
+ }
if (!p && realtime)
p = realtime_peer(peer, sin);
@@ -3789,6 +4014,12 @@ static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin, int
return p;
}
+static void sip_destroy_user_fn(void *user)
+{
+ sip_destroy_user(user);
+}
+
+
/*! \brief Remove user object from in-memory storage */
static void sip_destroy_user(struct sip_user *user)
{
@@ -3800,14 +4031,14 @@ static void sip_destroy_user(struct sip_user *user)
user->chanvars = NULL;
}
if (user->is_realtime)
- ruserobjs--;
+ ast_atomic_fetchadd_int(&ruserobjs, -1);
else
- suserobjs--;
- ast_free(user);
+ ast_atomic_fetchadd_int(&suserobjs, -1);
}
/*! \brief Load user from realtime storage
* Loads user from "sipusers" category in realtime (extconfig.conf)
+ * returns a refcounted pointer to a sip_user structure.
* Users are matched on From: user name (the domain in skipped) */
static struct sip_user *realtime_user(const char *username)
{
@@ -3837,12 +4068,12 @@ static struct sip_user *realtime_user(const char *username)
if (ast_test_flag(&global_flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
ast_set_flag(&user->flags[1], SIP_PAGE2_RTCACHEFRIENDS);
- suserobjs++;
- ASTOBJ_CONTAINER_LINK(&userl, user);
+ ast_atomic_fetchadd_int(&suserobjs, 1);
+ ao2_t_link(users, user, "link user into users table");
} else {
/* Move counter from s to r... */
- suserobjs--;
- ruserobjs++;
+ ast_atomic_fetchadd_int(&suserobjs, -1);
+ ast_atomic_fetchadd_int(&ruserobjs, 1);
user->is_realtime = 1;
}
ast_variables_destroy(var);
@@ -3852,10 +4083,20 @@ static struct sip_user *realtime_user(const char *username)
/*! \brief Locate user by name
* Locates user by name (From: sip uri user name part) first
* from in-memory list (static configuration) then from
- * realtime storage (defined in extconfig.conf) */
+ * realtime storage (defined in extconfig.conf)
+ * \return a refcounted pointer to a user. Make sure the
+ * the ref count is decremented when this pointer is nulled, changed,
+ * or goes out of scope!
+ */
+
static struct sip_user *find_user(const char *name, int realtime)
{
- struct sip_user *u = ASTOBJ_CONTAINER_FIND(&userl, name);
+ struct sip_user tmp;
+ struct sip_user *u;
+
+ ast_copy_string(tmp.name, name, sizeof(tmp.name));
+ u = ao2_t_find(users, &tmp, OBJ_POINTER, "ao2_find in users table");
+
if (!u && realtime)
u = realtime_user(name);
return u;
@@ -3935,6 +4176,7 @@ static void set_t38_capabilities(struct sip_pvt *p)
* again from memory or database during the life time of the dialog.
*
* \return -1 on error, 0 on success.
+ *
*/
static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
{
@@ -4018,11 +4260,13 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
if (!dialog->initreq.headers) {
char *c;
char *tmpcall = ast_strdupa(dialog->callid);
-
+ /* this sure looks to me like we are going to change the callid on this dialog!! */
c = strchr(tmpcall, '@');
if (c) {
*c = '\0';
+ ao2_t_unlink(dialogs, dialog, "About to change the callid -- remove the old name");
ast_string_field_build(dialog, callid, "%s@%s", tmpcall, peer->fromdomain);
+ ao2_t_link(dialogs, dialog, "New dialog callid -- inserted back into table");
}
}
}
@@ -4082,7 +4326,7 @@ static int create_addr(struct sip_pvt *dialog, const char *opeer, struct sockadd
if (peer) {
int res = create_addr_from_peer(dialog, peer);
- unref_peer(peer);
+ unref_peer(peer, "create_addr: unref peer from find_peer hashtab lookup");
return res;
} else {
/* Setup default parameters for this dialog's socket. Currently we only support regular UDP SIP as the default */
@@ -4150,14 +4394,13 @@ static int auto_congest(const void *arg)
if (p->owner) {
/* XXX fails on possible deadlock */
if (!ast_channel_trylock(p->owner)) {
- ast_log(LOG_NOTICE, "Auto-congesting %s\n", p->owner->name);
append_history(p, "Cong", "Auto-congesting (timer)");
ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
ast_channel_unlock(p->owner);
}
}
sip_pvt_unlock(p);
- dialog_unref(p);
+ dialog_unref(p, "unreffing arg passed into auto_congest callback (p->initid)");
return 0;
}
@@ -4245,9 +4488,11 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout)
p->invitestate = INV_CALLING;
/* Initialize auto-congest time */
- AST_SCHED_REPLACE(p->initid, sched, p->timer_b, auto_congest, dialog_ref(p));
+ AST_SCHED_REPLACE_UNREF(p->initid, sched, p->timer_b, auto_congest, p,
+ dialog_unref(_data, "dialog ptr dec when SCHED_REPLACE del op succeeded"),
+ dialog_unref(p, "dialog ptr dec when SCHED_REPLACE add failed"),
+ dialog_ref(p, "dialog ptr inc when SCHED_REPLACE add succeeded") );
}
-
return res;
}
@@ -4261,35 +4506,25 @@ static void sip_registry_destroy(struct sip_registry *reg)
if (reg->call) {
/* Clear registry before destroying to ensure
we don't get reentered trying to grab the registry lock */
- reg->call->registry = NULL;
+ reg->call->registry = registry_unref(reg->call->registry, "destroy reg->call->registry");
ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", reg->username, reg->hostname);
- reg->call = sip_destroy(reg->call);
+ dialog_unlink_all(reg->call, TRUE, TRUE);
+ reg->call = dialog_unref(reg->call, "unref reg->call");
+ /* reg->call = sip_destroy(reg->call); */
}
- AST_SCHED_DEL(sched, reg->expire);
+ AST_SCHED_DEL(sched, reg->expire);
AST_SCHED_DEL(sched, reg->timeout);
+
ast_string_field_free_memory(reg);
- regobjs--;
+ ast_atomic_fetchadd_int(&regobjs, -1);
ast_dnsmgr_release(reg->dnsmgr);
ast_free(reg);
}
/*! \brief Execute destruction of SIP dialog structure, release memory */
-static int __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
+static void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
{
- struct sip_pvt *cur, *prev = NULL;
- struct sip_pkt *cp;
-
- /* We absolutely cannot destroy the rtp struct while a bridge is active or we WILL crash */
- if (p->rtp && ast_rtp_get_bridged(p->rtp)) {
- ast_verbose("Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
- return -1;
- }
-
- if (p->vrtp && ast_rtp_get_bridged(p->vrtp)) {
- ast_verbose("Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
- return -1;
- }
if (sip_debug_test_pvt(p))
ast_verbose("Really destroying SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
@@ -4315,21 +4550,26 @@ static int __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
}
/* Remove link from peer to subscription of MWI */
- if (p->relatedpeer && p->relatedpeer->mwipvt)
- p->relatedpeer->mwipvt = dialog_unref(p->relatedpeer->mwipvt);
-
+ if (p->relatedpeer && p->relatedpeer->mwipvt)
+ p->relatedpeer->mwipvt = dialog_unref(p->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt");
+ if (p->relatedpeer && p->relatedpeer->call == p)
+ p->relatedpeer->call = dialog_unref(p->relatedpeer->call, "unset the relatedpeer->call field in tandem with relatedpeer field itself");
+
+ if (p->relatedpeer)
+ p->relatedpeer = unref_peer(p->relatedpeer,"unsetting a dialog relatedpeer field in sip_destroy");
+
+ if (p->registry) {
+ if (p->registry->call == p)
+ p->registry->call = dialog_unref(p->registry->call, "nulling out the registry's call dialog field in unlink_all");
+ p->registry = registry_unref(p->registry, "delete p->registry");
+ }
+
if (dumphistory)
sip_dump_history(p);
if (p->options)
ast_free(p->options);
- if (p->stateid > -1)
- ast_extension_state_del(p->stateid, NULL);
- AST_SCHED_DEL(sched, p->initid);
- AST_SCHED_DEL(sched, p->waitid);
- AST_SCHED_DEL(sched, p->autokillid);
-
if (p->rtp) {
ast_rtp_destroy(p->rtp);
}
@@ -4349,11 +4589,6 @@ static int __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
free_old_route(p->route);
p->route = NULL;
}
- if (p->registry) {
- if (p->registry->call == p)
- p->registry->call = NULL;
- p->registry = registry_unref(p->registry);
- }
if (p->initreq.data)
ast_free(p->initreq.data);
@@ -4362,6 +4597,7 @@ static int __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
if (p->stimer->st_active == TRUE && p->stimer->st_schedid > -1)
ast_sched_del(sched, p->stimer->st_schedid);
ast_free(p->stimer);
+ p->stimer = NULL;
}
/* Clear history */
@@ -4375,41 +4611,13 @@ static int __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
p->history = NULL;
}
- /* Lock dialog list before removing ourselves from the list */
- if (lockdialoglist)
- dialoglist_lock();
- for (prev = NULL, cur = dialoglist; cur; prev = cur, cur = cur->next) {
- if (cur == p) {
- UNLINK(cur, dialoglist, prev);
- break;
- }
- }
- if (lockdialoglist)
- dialoglist_unlock();
- if (!cur) {
- ast_log(LOG_WARNING, "Trying to destroy \"%s\", not found in dialog list?!?! \n", p->callid);
- return 0;
- }
-
- /* remove all current packets in this dialog */
- while((cp = p->packets)) {
- p->packets = p->packets->next;
- AST_SCHED_DEL(sched, cp->retransid);
- dialog_unref(cp->owner);
- if (cp->data)
- ast_free(cp->data);
- ast_free(cp);
- }
if (p->chanvars) {
ast_variables_destroy(p->chanvars);
p->chanvars = NULL;
}
- ast_mutex_destroy(&p->pvt_lock);
ast_string_field_free_memory(p);
-
- ast_free(p);
- return 0;
+ return;
}
/*! \brief update_call_counter: Handle call_limit for SIP users
@@ -4490,9 +4698,9 @@ static int update_call_counter(struct sip_pvt *fup, int event)
if (*inuse >= *call_limit) {
ast_log(LOG_WARNING, "Call %s %s '%s' rejected due to usage limit of %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *call_limit);
if (u)
- unref_user(u);
+ unref_user(u, "update_call_counter: unref user u call limit exceeded");
else
- unref_peer(p);
+ unref_peer(p, "update_call_counter: unref peer p, call limit exceeded");
return -1;
}
}
@@ -4522,12 +4730,18 @@ static int update_call_counter(struct sip_pvt *fup, int event)
}
if (p) {
ast_device_state_changed("SIP/%s", p->name);
- unref_peer(p);
+ unref_peer(p, "update_call_counter: unref_peer from call counter");
} else /* u must be set */
- unref_user(u);
+ unref_user(u, "update_call_counter: unref_user from call counter");
return 0;
}
+
+static void sip_destroy_fn(void *p)
+{
+ sip_destroy(p);
+}
+
/*! \brief Destroy SIP call structure.
* Make it return NULL so the caller can do things like
* foo = sip_destroy(foo);
@@ -4731,8 +4945,10 @@ static int sip_hangup(struct ast_channel *ast)
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER); /* Really hang up next time */
p->needdestroy = 0;
- p->owner->tech_pvt = dialog_unref(p->owner->tech_pvt);
+ p->owner->tech_pvt = dialog_unref(p->owner->tech_pvt, "unref p->owner->tech_pvt");
+ sip_pvt_lock(p);
p->owner = NULL; /* Owner will be gone after we return, so take it away */
+ sip_pvt_unlock(p);
return 0;
}
@@ -4773,7 +4989,7 @@ static int sip_hangup(struct ast_channel *ast)
ast_dsp_free(p->vad);
p->owner = NULL;
- ast->tech_pvt = dialog_unref(ast->tech_pvt);
+ ast->tech_pvt = dialog_unref(ast->tech_pvt, "unref ast->tech_pvt");
ast_module_unref(ast_module_info->self);
/* Do not destroy this pvt until we have timeout or
@@ -4857,7 +5073,7 @@ static int sip_hangup(struct ast_channel *ast)
but we can't send one while we have "INVITE" outstanding. */
ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
ast_clear_flag(&p->flags[0], SIP_NEEDREINVITE);
- AST_SCHED_DEL(sched, p->waitid);
+ AST_SCHED_DEL_UNREF(sched, p->waitid, dialog_unref(p, "when you delete the waitid sched, you should dec the refcount for the stored dialog ptr"));
if (sip_cancel_destroy(p))
ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
}
@@ -5357,7 +5573,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
tmp->rawwriteformat = fmt;
tmp->readformat = fmt;
tmp->rawreadformat = fmt;
- tmp->tech_pvt = dialog_ref(i);
+ tmp->tech_pvt = dialog_ref(i, "sip_new: set chan->tech_pvt to i");
tmp->callgroup = i->callgroup;
tmp->pickupgroup = i->pickupgroup;
@@ -5743,16 +5959,15 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si
{
struct sip_pvt *p;
- if (!(p = ast_calloc(1, sizeof(*p))))
+ if (!(p = ao2_t_alloc(sizeof(*p), sip_destroy_fn, "allocate a dialog(pvt) struct")))
return NULL;
if (ast_string_field_init(p, 512)) {
+ ao2_t_ref(p, -1, "failed to string_field_init, drop p");
ast_free(p);
return NULL;
}
- ast_mutex_init(&p->pvt_lock);
-
p->socket.fd = -1;
p->socket.type = SIP_TRANSPORT_UDP;
p->method = intended_method;
@@ -5802,12 +6017,11 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si
ast_log(LOG_WARNING, "Unable to create RTP audio %s%ssession: %s\n",
ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "and video " : "",
ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "and text " : "", strerror(errno));
- ast_mutex_destroy(&p->pvt_lock);
if (p->chanvars) {
ast_variables_destroy(p->chanvars);
p->chanvars = NULL;
}
- ast_free(p);
+ ao2_t_ref(p, -1, "failed to create RTP audio session, drop p");
return NULL;
}
ast_rtp_setqos(p->rtp, global_tos_audio, global_cos_audio, "SIP RTP");
@@ -5865,14 +6079,60 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si
/* Add to active dialog list */
- dialoglist_lock();
- p->next = dialoglist;
- dialoglist = dialog_ref(p);
- dialoglist_unlock();
- ast_debug(1, "Allocating new SIP dialog for %s - %s (%s)\n", callid ? callid : "(No Call-ID)", sip_methods[intended_method].text, p->rtp ? "With RTP" : "No RTP");
+
+ ao2_t_link(dialogs, p, "link pvt into dialogs table");
+
+ ast_debug(1, "Allocating new SIP dialog for %s - %s (%s)\n", callid ? callid : p->callid, sip_methods[intended_method].text, p->rtp ? "With RTP" : "No RTP");
return p;
}
+/*! \brief argument to the helper function to identify a call */
+struct find_call_cb_arg {
+ enum sipmethod method;
+ const char *callid;
+ const char *fromtag;
+ const char *totag;
+ const char *tag;
+};
+
+/*!
+ * code to determine whether this is the pvt that we are looking for.
+ * Return FALSE if not found, true otherwise. p is unlocked.
+ */
+static int find_call_cb(void *__p, void *__arg, int flags)
+{
+ struct sip_pvt *p = __p;
+ struct find_call_cb_arg *arg = __arg;
+ /* In pedantic, we do not want packets with bad syntax to be connected to a PVT */
+ int found = FALSE;
+
+ if (!ast_strlen_zero(p->callid)) { /* XXX double check, do we allow match on empty p->callid ? */
+ if (arg->method == SIP_REGISTER)
+ found = (!strcmp(p->callid, arg->callid));
+ else
+ found = (!strcmp(p->callid, arg->callid) &&
+ (!pedanticsipchecking || ast_strlen_zero(arg->tag) || ast_strlen_zero(p->theirtag) || !strcmp(p->theirtag, arg->tag))) ;
+
+
+ ast_debug(5, "= %s Their Call ID: %s Their Tag %s Our tag: %s\n", found ? "Found" : "No match", p->callid, p->theirtag, p->tag);
+
+ /* If we get a new request within an existing to-tag - check the to tag as well */
+ if (pedanticsipchecking && found && arg->method != SIP_RESPONSE) { /* SIP Request */
+ if (p->tag[0] == '\0' && arg->totag[0]) {
+ /* We have no to tag, but they have. Wrong dialog */
+ found = FALSE;
+ } else if (arg->totag[0]) { /* Both have tags, compare them */
+ if (strcmp(arg->totag, p->tag)) {
+ found = FALSE; /* This is not our packet */
+ }
+ }
+ if (!found)
+ ast_debug(5, "= Being pedantic: This is not our match on request: Call ID: %s Ourtag <null> Totag %s Method %s\n", p->callid, arg->totag, sip_methods[arg->method].text);
+ }
+ }
+ return found;
+}
+
/*! \brief find or create a dialog structure for an incoming SIP message.
* Connect incoming SIP message to current dialog or create new dialog structure
* Returns a reference to the sip_pvt object, remember to give it back once done.
@@ -5884,10 +6144,17 @@ static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *si
char *tag = ""; /* note, tag is never NULL */
char totag[128];
char fromtag[128];
+ struct find_call_cb_arg arg;
const char *callid = get_header(req, "Call-ID");
const char *from = get_header(req, "From");
const char *to = get_header(req, "To");
const char *cseq = get_header(req, "Cseq");
+ struct sip_pvt *sip_pvt_ptr;
+
+ callid = get_header(req, "Call-ID");
+ from = get_header(req, "From");
+ to = get_header(req, "To");
+ cseq = get_header(req, "Cseq");
/* Call-ID, to, from and Cseq are required by RFC 3261. (Max-forwards and via too - ignored now) */
/* get_header always returns non-NULL so we must use ast_strlen_zero() */
@@ -5895,6 +6162,12 @@ static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *si
ast_strlen_zero(from) || ast_strlen_zero(cseq))
return NULL; /* Invalid packet */
+ arg.method = req->method;
+ arg.callid = callid;
+ arg.fromtag = fromtag;
+ arg.totag = totag;
+ arg.tag = ""; /* make sure tag is never NULL */
+
if (pedanticsipchecking) {
/* In principle Call-ID's uniquely identify a call, but with a forking SIP proxy
we need more to identify a branch - so we have to check branch, from
@@ -5922,50 +6195,32 @@ static struct sip_pvt *find_call(struct sip_request *req, struct sockaddr_in *si
}
}
- dialoglist_lock();
restartsearch:
- for (p = dialoglist; p; p = p->next) {
- /* In pedantic, we do not want packets with bad syntax to be connected to a PVT */
- int found = FALSE;
- if (ast_strlen_zero(p->callid))
- continue;
- if (req->method == SIP_REGISTER)
- found = (!strcmp(p->callid, callid));
- else
- found = (!strcmp(p->callid, callid) &&
- (!pedanticsipchecking || ast_strlen_zero(tag) || ast_strlen_zero(p->theirtag) || !strcmp(p->theirtag, tag))) ;
-
- ast_debug(5, "= %s Their Call ID: %s Their Tag %s Our tag: %s\n", found ? "Found" : "No match", p->callid, p->theirtag, p->tag);
-
- /* If we get a new request within an existing to-tag - check the to tag as well */
- if (pedanticsipchecking && found && req->method != SIP_RESPONSE) { /* SIP Request */
- if (p->tag[0] == '\0' && totag[0]) {
- /* We have no to tag, but they have. Wrong dialog */
- found = FALSE;
- } else if (totag[0]) { /* Both have tags, compare them */
- if (strcmp(totag, p->tag)) {
- found = FALSE; /* This is not our packet */
- }
- }
- if (!found)
- ast_debug(5, "= Being pedantic: This is not our match on request: Call ID: %s Ourtag <null> Totag %s Method %s\n", p->callid, totag, sip_methods[req->method].text);
- }
-
-
- if (found) {
+ if (!pedanticsipchecking) {
+ struct sip_pvt tmp_dialog = {
+ .callid = callid,
+ };
+ sip_pvt_ptr = ao2_t_find(dialogs, &tmp_dialog, OBJ_POINTER, "ao2_find in dialogs");
+ if (sip_pvt_ptr) { /* well, if we don't find it-- what IS in there? */
/* Found the call */
+ sip_pvt_lock(sip_pvt_ptr);
+ return sip_pvt_ptr;
+ }
+ } else { /* in pedantic mode! -- do the fancy linear search */
+ ao2_lock(dialogs);
+ p = ao2_t_callback(dialogs, 0 /* single, data */, find_call_cb, &arg, "pedantic linear search for dialog");
+ if (p) {
if (sip_pvt_trylock(p)) {
- dialoglist_unlock();
+ ao2_unlock(dialogs);
usleep(1);
- dialoglist_lock();
+ ao2_lock(dialogs);
goto restartsearch;
}
- dialoglist_unlock();
return p;
}
+ ao2_unlock(dialogs);
}
- dialoglist_unlock();
-
+
/* See if the method is capable of creating a dialog */
if (sip_methods[intended_method].can_create == CAN_CREATE_DIALOG) {
if (intended_method == SIP_REFER) {
@@ -6008,7 +6263,7 @@ restartsearch:
if (intended_method == SIP_RESPONSE)
ast_debug(2, "That's odd... Got a response on a call we dont know about. Callid %s\n", callid ? callid : "<unknown>");
- return p;
+ return NULL;
}
/*! \brief Parse register=> line in sip.conf and add to registry */
@@ -6092,7 +6347,7 @@ static int sip_register(const char *value, int lineno)
return -1;
}
- regobjs++;
+ ast_atomic_fetchadd_int(&regobjs, 1);
ASTOBJ_INIT(reg);
ast_string_field_set(reg, callback, callback);
if (!ast_strlen_zero(username))
@@ -6111,8 +6366,8 @@ static int sip_register(const char *value, int lineno)
reg->portno = portnum;
reg->callid_valid = FALSE;
reg->ocseq = INITIAL_CSEQ;
- ASTOBJ_CONTAINER_LINK(&regl, reg); /* Add the new registry entry to the list */
- registry_unref(reg); /* release the reference given by ASTOBJ_INIT. The container has another reference */
+ ASTOBJ_CONTAINER_LINK(&regl, reg); /* Add the new registry entry to the list */
+ registry_unref(reg, "unref the reg pointer"); /* release the reference given by ASTOBJ_INIT. The container has another reference */
return 0;
}
@@ -7531,7 +7786,7 @@ static int transmit_response_using_temp(ast_string_field callid, struct sockaddr
struct sip_pvt *p = NULL;
if (!(p = ast_threadstorage_get(&ts_temp_pvt, sizeof(*p)))) {
- ast_log(LOG_NOTICE, "Failed to get temporary pvt\n");
+ ast_log(LOG_ERROR, "Failed to get temporary pvt\n");
return -1;
}
@@ -8436,7 +8691,7 @@ static int determine_firstline_parts(struct sip_request *req)
static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int oldsdp)
{
struct sip_request req;
-
+
reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1);
add_header(&req, "Allow", ALLOWED_METHODS);
@@ -8859,7 +9114,7 @@ static int transmit_state_notify(struct sip_pvt *p, int state, int full, int tim
enum state { NOTIFY_OPEN, NOTIFY_INUSE, NOTIFY_CLOSED } local_state = NOTIFY_OPEN;
char *pidfstate = "--";
char *pidfnote= "Ready";
-
+
memset(from, 0, sizeof(from));
memset(to, 0, sizeof(to));
@@ -9039,7 +9294,7 @@ static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs,
{
struct sip_request req;
struct ast_str *out = ast_str_alloca(500);
-
+
initreqprep(&req, p, SIP_NOTIFY);
add_header(&req, "Event", "message-summary");
add_header(&req, "Content-Type", default_notifymime);
@@ -9081,7 +9336,7 @@ static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *messa
{
struct sip_request req;
char tmp[SIPBUFSIZE/2];
-
+
reqprep(&req, p, SIP_NOTIFY, 0, 1);
snprintf(tmp, sizeof(tmp), "refer;id=%d", cseq);
add_header(&req, "Event", tmp);
@@ -9129,7 +9384,7 @@ static const char *regstate2str(enum sipregistrystate regstate)
static int sip_reregister(const void *data)
{
/* if we are here, we know that we need to reregister. */
- struct sip_registry *r= registry_addref((struct sip_registry *) data);
+ struct sip_registry *r= (struct sip_registry *) data;
/* if we couldn't get a reference to the registry object, punt */
if (!r)
@@ -9144,7 +9399,7 @@ static int sip_reregister(const void *data)
r->expire = -1;
__sip_do_register(r);
- registry_unref(r);
+ registry_unref(r, "unreg the re-registered");
return 0;
}
@@ -9167,7 +9422,7 @@ static int sip_reg_timeout(const void *data)
{
/* if we are here, our registration timed out, so we'll just do it over */
- struct sip_registry *r = registry_addref((struct sip_registry *) data);
+ struct sip_registry *r = (struct sip_registry *)data; /* the ref count should have been bumped when the sched item was added */
struct sip_pvt *p;
int res;
@@ -9198,8 +9453,8 @@ static int sip_reg_timeout(const void *data)
/* decouple the two objects */
/* p->registry == r, so r has 2 refs, and the unref won't take the object away */
if (p->registry)
- p->registry = registry_unref(p->registry);
- r->call = dialog_unref(r->call);
+ p->registry = registry_unref(p->registry, "p->registry unreffed");
+ r->call = dialog_unref(r->call, "unrefing r->call");
}
/* If we have a limit, stop registration and give up */
if (global_regattempts_max && r->regattempts > global_regattempts_max) {
@@ -9214,7 +9469,7 @@ static int sip_reg_timeout(const void *data)
res=transmit_register(r, SIP_REGISTER, NULL, NULL);
}
manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelType: SIP\r\nUsername: %s\r\nDomain: %s\r\nStatus: %s\r\n", r->username, r->hostname, regstate2str(r->regstate));
- registry_unref(r);
+ registry_unref(r, "unreffing registry_unref r");
return 0;
}
@@ -9229,6 +9484,7 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char *
char tmp[80];
char addr[80];
struct sip_pvt *p;
+ int res;
/* exit if we are already in process with this registrar ?*/
if (r == NULL || ((auth == NULL) && (r->regstate == REG_STATE_REGSENT || r->regstate == REG_STATE_AUTHSENT))) {
@@ -9247,7 +9503,7 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char *
ast_log(LOG_WARNING, "Already have a REGISTER going on to %s@%s?? \n", r->username, r->hostname);
return 0;
} else {
- p = r->call;
+ p = dialog_ref(r->call, "getting a copy of the r->call dialog in transmit_register");
make_our_tag(p->tag, sizeof(p->tag)); /* create a new local tag for every register attempt */
ast_string_field_set(p, theirtag, NULL); /* forget their old tag, so we don't match tags when getting response */
}
@@ -9276,12 +9532,16 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char *
if (create_addr(p, r->hostname, &r->us)) {
/* we have what we hope is a temporary network error,
* probably DNS. We need to reschedule a registration try */
- sip_destroy(p);
+ dialog_unlink_all(p, TRUE, TRUE);
+ p = dialog_unref(p, "unref dialog after unlink_all");
if (r->timeout > -1) {
- AST_SCHED_REPLACE(r->timeout, sched, global_reg_timeout * 1000, sip_reg_timeout, r);
+ AST_SCHED_REPLACE_UNREF(r->timeout, sched, global_reg_timeout * 1000, sip_reg_timeout, r,
+ registry_unref(_data, "del for REPLACE of registry ptr"),
+ registry_unref(r, "object ptr dec when SCHED_REPLACE add failed"),
+ registry_addref(r,"add for REPLACE registry ptr"));
ast_log(LOG_WARNING, "Still have a registration timeout for %s@%s (create_addr() error), %d\n", r->username, r->hostname, r->timeout);
} else {
- r->timeout = ast_sched_add(sched, global_reg_timeout * 1000, sip_reg_timeout, r);
+ r->timeout = ast_sched_add(sched, global_reg_timeout * 1000, sip_reg_timeout, registry_addref(r, "add for REPLACE registry ptr"));
ast_log(LOG_WARNING, "Probably a DNS error for registration to %s@%s, trying REGISTER again (after %d seconds)\n", r->username, r->hostname, global_reg_timeout);
}
r->regattempts++;
@@ -9292,13 +9552,13 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char *
ast_string_field_set(r, callid, p->callid);
if (!r->dnsmgr && r->portno) {
p->sa.sin_port = htons(r->portno);
- p->recv.sin_port = htons(r->portno);
+ p->recv.sin_port = htons(r->portno);
} else { /* Set registry port to the port set from the peer definition/srv or default */
r->portno = ntohs(p->sa.sin_port);
}
ast_set_flag(&p->flags[0], SIP_OUTGOING); /* Registration is outgoing call */
- r->call = dialog_ref(p); /* Save pointer to SIP dialog */
- p->registry = registry_addref(r); /* Add pointer to registry in packet */
+ r->call = dialog_ref(p, "copying dialog into registry r->call"); /* Save pointer to SIP dialog */
+ p->registry = registry_addref(r, "transmit_register: addref to p->registry in transmit_register"); /* Add pointer to registry in packet */
if (!ast_strlen_zero(r->secret)) /* Secret (password) */
ast_string_field_set(p, peersecret, r->secret);
if (!ast_strlen_zero(r->md5secret))
@@ -9336,7 +9596,10 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char *
if (auth == NULL) {
if (r->timeout > -1)
ast_log(LOG_WARNING, "Still have a registration timeout, #%d - deleting it\n", r->timeout);
- AST_SCHED_REPLACE(r->timeout, sched, global_reg_timeout * 1000, sip_reg_timeout, r);
+ AST_SCHED_REPLACE_UNREF(r->timeout, sched, global_reg_timeout * 1000, sip_reg_timeout, r,
+ registry_unref(_data,"reg ptr unrefed from del in SCHED_REPLACE"),
+ registry_unref(r,"reg ptr unrefed from add failure in SCHED_REPLACE"),
+ registry_addref(r,"reg ptr reffed from add in SCHED_REPLACE"));
ast_debug(1, "Scheduled a registration timeout for %s id #%d \n", r->hostname, r->timeout);
}
@@ -9427,15 +9690,16 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char *
r->regstate = auth ? REG_STATE_AUTHSENT : REG_STATE_REGSENT;
r->regattempts++; /* Another attempt */
ast_debug(4, "REGISTER attempt %d to %s@%s\n", r->regattempts, r->username, r->hostname);
-
- return send_request(p, &req, XMIT_CRITICAL, p->ocseq);
+ res = send_request(p, &req, XMIT_CRITICAL, p->ocseq);
+ dialog_unref(p, "p is finished here at the end of transmit_register");
+ return res;
}
/*! \brief Transmit text with SIP MESSAGE method */
static int transmit_message_with_text(struct sip_pvt *p, const char *text)
{
struct sip_request req;
-
+
reqprep(&req, p, SIP_MESSAGE, 0, 1);
add_text(&req, text);
return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
@@ -9514,6 +9778,7 @@ static int transmit_refer(struct sip_pvt *p, const char *dest)
add_header(&req, "Referred-By", p->our_contact);
return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
+
/* We should propably wait for a NOTIFY here until we ack the transfer */
/* Maybe fork a new thread and wait for a STATUS of REFER_200OK on the refer status before returning to app_transfer */
@@ -9529,7 +9794,7 @@ static int transmit_refer(struct sip_pvt *p, const char *dest)
static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigned int duration)
{
struct sip_request req;
-
+
reqprep(&req, p, SIP_INFO, 0, 1);
add_digit(&req, digit, duration, (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_SHORTINFO));
return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
@@ -9539,7 +9804,7 @@ static int transmit_info_with_digit(struct sip_pvt *p, const char digit, unsigne
static int transmit_info_with_vidupdate(struct sip_pvt *p)
{
struct sip_request req;
-
+
reqprep(&req, p, SIP_INFO, 0, 1);
add_vidupdate(&req);
return send_request(p, &req, XMIT_RELIABLE, p->ocseq);
@@ -9551,7 +9816,7 @@ static int transmit_info_with_vidupdate(struct sip_pvt *p)
static int transmit_request(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch)
{
struct sip_request resp;
-
+
if (sipmethod == SIP_ACK)
p->invitestate = INV_CONFIRMED;
@@ -9582,7 +9847,7 @@ static void auth_headers(enum sip_auth_type code, char **header, char **resphead
static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int seqno, enum xmittype reliable, int newbranch)
{
struct sip_request resp;
-
+
reqprep(&resp, p, sipmethod, seqno, newbranch);
if (!ast_strlen_zero(p->realm)) {
char digest[1024];
@@ -9632,13 +9897,13 @@ static int expire_register(const void *data)
if (!peer) /* Hmmm. We have no peer. Weird. */
return 0;
+ peer->expire = -1;
memset(&peer->addr, 0, sizeof(peer->addr));
destroy_association(peer); /* remove registration data from storage */
manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
register_peer_exten(peer, FALSE); /* Remove regexten */
- peer->expire = -1;
ast_device_state_changed("SIP/%s", peer->name);
/* Do we need to release this peer from memory?
@@ -9649,8 +9914,11 @@ static int expire_register(const void *data)
if (peer->selfdestruct ||
ast_test_flag(&peer->flags[1], SIP_PAGE2_RTAUTOCLEAR)) {
- peer = ASTOBJ_CONTAINER_UNLINK(&peerl, peer); /* Remove from peer list */
- unref_peer(peer); /* Remove from memory */
+ ao2_t_unlink(peers, peer, "ao2_unlink of peer from peers table");
+ if (peer->addr.sin_addr.s_addr) {
+ ao2_t_unlink(peers_by_ip, peer, "ao2_unlink of peer from peers_by_ip table");
+ }
+
}
return 0;
@@ -10256,7 +10524,8 @@ static void sip_peer_hold(struct sip_pvt *p, int hold)
/* Request device state update */
ast_device_state_changed("SIP/%s", peer->name);
-
+ unref_peer(peer, "sip_peer_hold: from find_peer operation");
+
return;
}
@@ -10265,9 +10534,9 @@ static void mwi_event_cb(const struct ast_event *event, void *userdata)
{
struct sip_peer *peer = userdata;
- ASTOBJ_RDLOCK(peer);
+ ao2_lock(peer);
sip_send_mwi_to_peer(peer, event, 0);
- ASTOBJ_UNLOCK(peer);
+ ao2_unlock(peer);
}
/*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
@@ -10398,7 +10667,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr
if (!(peer && ast_apply_ha(peer->ha, sin))) {
/* Peer fails ACL check */
if (peer) {
- unref_peer(peer);
+ unref_peer(peer, "register_verify: unref_peer: from find_peer operation");
peer = NULL;
res = AUTH_ACL_FAILED;
} else
@@ -10452,7 +10721,11 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr
/* Create peer if we have autocreate mode enabled */
peer = temp_peer(name);
if (peer) {
- ASTOBJ_CONTAINER_LINK(&peerl, peer);
+ ao2_t_link(peers, peer, "link peer into peer table");
+ if (peer->addr.sin_addr.s_addr) {
+ ao2_t_link(peers_by_ip, peer, "link peer into peers-by-ip table");
+ }
+
if (sip_cancel_destroy(p))
ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
switch (parse_register_contact(p, peer, req)) {
@@ -10524,7 +10797,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr
}
}
if (peer)
- unref_peer(peer);
+ unref_peer(peer, "register_verify: unref_peer: tossing stack peer pointer at end of func");
return res;
}
@@ -10757,55 +11030,41 @@ static int get_destination(struct sip_pvt *p, struct sip_request *oreq)
- Their tag is fromtag, our tag is to-tag
- This means that in some transactions, totag needs to be their tag :-)
depending upon the direction
- Returns a reference, remember to release it when done XXX
+ \return a reference, remember to release it when done
*/
static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *totag, const char *fromtag)
{
struct sip_pvt *sip_pvt_ptr;
-
+ struct sip_pvt tmp_dialog = {
+ .callid = callid,
+ };
if (totag)
ast_debug(4, "Looking for callid %s (fromtag %s totag %s)\n", callid, fromtag ? fromtag : "<no fromtag>", totag ? totag : "<no totag>");
/* Search dialogs and find the match */
- dialoglist_lock();
- for (sip_pvt_ptr = dialoglist; sip_pvt_ptr; sip_pvt_ptr = sip_pvt_ptr->next) {
- if (!strcmp(sip_pvt_ptr->callid, callid)) {
- int match = 1;
- char *ourtag = sip_pvt_ptr->tag;
-
- /* Go ahead and lock it (and its owner) before returning */
- sip_pvt_lock(sip_pvt_ptr);
-
- /* Check if tags match. If not, this is not the call we want
- (With a forking SIP proxy, several call legs share the
- call id, but have different tags)
- */
- if (pedanticsipchecking && (strcmp(fromtag, sip_pvt_ptr->theirtag) || (!ast_strlen_zero(totag) && strcmp(totag, ourtag))))
- match = 0;
-
- if (!match) {
- sip_pvt_unlock(sip_pvt_ptr);
- continue;
- }
-
- if (totag)
- ast_debug(4, "Matched %s call - their tag is %s Our tag is %s\n",
- ast_test_flag(&sip_pvt_ptr->flags[0], SIP_OUTGOING) ? "OUTGOING": "INCOMING",
- sip_pvt_ptr->theirtag, sip_pvt_ptr->tag);
-
- /* deadlock avoidance... */
- while (sip_pvt_ptr->owner && ast_channel_trylock(sip_pvt_ptr->owner)) {
- sip_pvt_unlock(sip_pvt_ptr);
- usleep(1);
- sip_pvt_lock(sip_pvt_ptr);
- }
- break;
+
+ sip_pvt_ptr = ao2_t_find(dialogs, &tmp_dialog, OBJ_POINTER, "ao2_find of dialog in dialogs table");
+ if (sip_pvt_ptr) {
+ char *ourtag = sip_pvt_ptr->tag;
+ /* Go ahead and lock it (and its owner) before returning */
+ sip_pvt_lock(sip_pvt_ptr);
+
+ if (pedanticsipchecking && (strcmp(fromtag, sip_pvt_ptr->theirtag) || (!ast_strlen_zero(totag) && strcmp(totag, ourtag)))) {
+ sip_pvt_unlock(sip_pvt_ptr);
+ ast_debug(4, "Matched %s call for callid=%s - But the pedantic check rejected the match; their tag is %s Our tag is %s\n",
+ ast_test_flag(&sip_pvt_ptr->flags[0], SIP_OUTGOING) ? "OUTGOING": "INCOMING", sip_pvt_ptr->callid,
+ sip_pvt_ptr->theirtag, sip_pvt_ptr->tag);
+ return 0;
}
+ sip_pvt_unlock(sip_pvt_ptr);
+
+ if (totag)
+ ast_debug(4, "Matched %s call - their tag is %s Our tag is %s\n",
+ ast_test_flag(&sip_pvt_ptr->flags[0], SIP_OUTGOING) ? "OUTGOING": "INCOMING",
+ sip_pvt_ptr->theirtag, sip_pvt_ptr->tag);
}
- dialoglist_unlock();
- if (!sip_pvt_ptr)
- ast_debug(4, "Found no match for callid %s to-tag %s from-tag %s\n", callid, totag, fromtag);
+
return sip_pvt_ptr;
}
@@ -11042,7 +11301,7 @@ static int get_also_info(struct sip_pvt *p, struct sip_request *oreq)
ast_copy_string(referdata->refer_to, c, sizeof(referdata->refer_to));
ast_copy_string(referdata->referred_by, "", sizeof(referdata->referred_by));
ast_copy_string(referdata->refer_contact, "", sizeof(referdata->refer_contact));
- referdata->refer_call = dialog_unref(referdata->refer_call);
+ referdata->refer_call = dialog_unref(referdata->refer_call, "unreffing referdata->refer_call");
/* Set new context */
ast_string_field_set(p, context, transfer_context);
return 0;
@@ -11269,7 +11528,7 @@ static enum check_auth_result check_user_ok(struct sip_pvt *p, char *of,
if (debug)
ast_verbose("Found user '%s' for '%s', but fails host access\n",
user->name, of);
- unref_user(user);
+ unref_user(user, "unref_user: from check_user_ok from find_user call, early return of AUTH_DONT_KNOW.");
return AUTH_DONT_KNOW;
}
if (debug)
@@ -11351,7 +11610,7 @@ static enum check_auth_result check_user_ok(struct sip_pvt *p, char *of,
p->trtp = NULL;
}
}
- unref_user(user);
+ unref_user(user, "unref_user from check_auth_result, at end");
return res;
}
@@ -11436,7 +11695,8 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
/* copy channel vars */
p->chanvars = copy_vars(peer->chanvars);
if (authpeer) {
- (*authpeer) = ASTOBJ_REF(peer); /* Add a ref to the object here, to keep it in memory a bit longer if it is realtime */
+ ao2_t_ref(peer, 1, "copy pointer into (*authpeer)");
+ (*authpeer) = peer; /* Add a ref to the object here, to keep it in memory a bit longer if it is realtime */
}
if (!ast_strlen_zero(peer->username)) {
@@ -11486,7 +11746,7 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
if (p->t38.peercapability)
p->t38.jointcapability &= p->t38.peercapability;
}
- unref_peer(peer);
+ unref_peer(peer, "check_peer_ok: unref_peer: tossing temp ptr to peer from find_peer");
return res;
}
@@ -11703,7 +11963,10 @@ static char *sip_show_inuse(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
char ilimits[40];
char iused[40];
int showall = FALSE;
-
+ struct ao2_iterator i;
+ struct sip_user *user;
+ struct sip_peer *peer;
+
switch (cmd) {
case CLI_INIT:
e->command = "sip show inuse";
@@ -11723,31 +11986,39 @@ static char *sip_show_inuse(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
showall = TRUE;
ast_cli(a->fd, FORMAT, "* User name", "In use", "Limit");
- ASTOBJ_CONTAINER_TRAVERSE(&userl, 1, do {
- ASTOBJ_RDLOCK(iterator);
- if (iterator->call_limit)
- snprintf(ilimits, sizeof(ilimits), "%d", iterator->call_limit);
+
+ i = ao2_iterator_init(users, 0);
+
+ while ((user = ao2_t_iterator_next(&i, "iterate thru user table"))) {
+ ao2_lock(user);
+
+ if (user->call_limit)
+ snprintf(ilimits, sizeof(ilimits), "%d", user->call_limit);
else
ast_copy_string(ilimits, "N/A", sizeof(ilimits));
- snprintf(iused, sizeof(iused), "%d", iterator->inUse);
- if (showall || iterator->call_limit)
- ast_cli(a->fd, FORMAT2, iterator->name, iused, ilimits);
- ASTOBJ_UNLOCK(iterator);
- } while (0) );
+ snprintf(iused, sizeof(iused), "%d", user->inUse);
+ if (showall || user->call_limit)
+ ast_cli(a->fd, FORMAT2, user->name, iused, ilimits);
+ ao2_unlock(user);
+ unref_user(user, "toss iterator pointer");
+ }
ast_cli(a->fd, FORMAT, "* Peer name", "In use", "Limit");
- ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do {
- ASTOBJ_RDLOCK(iterator);
- if (iterator->call_limit)
- snprintf(ilimits, sizeof(ilimits), "%d", iterator->call_limit);
+ i = ao2_iterator_init(peers, 0);
+
+ while ((peer = ao2_t_iterator_next(&i, "iterate thru peer table"))) {
+ ao2_lock(peer);
+ if (peer->call_limit)
+ snprintf(ilimits, sizeof(ilimits), "%d", peer->call_limit);
else
ast_copy_string(ilimits, "N/A", sizeof(ilimits));
- snprintf(iused, sizeof(iused), "%d/%d/%d", iterator->inUse, iterator->inRinging, iterator->onHold);
- if (showall || iterator->call_limit)
- ast_cli(a->fd, FORMAT2, iterator->name, iused, ilimits);
- ASTOBJ_UNLOCK(iterator);
- } while (0) );
+ snprintf(iused, sizeof(iused), "%d/%d/%d", peer->inUse, peer->inRinging, peer->onHold);
+ if (showall || peer->call_limit)
+ ast_cli(a->fd, FORMAT2, peer->name, iused, ilimits);
+ ao2_unlock(peer);
+ unref_peer(peer, "toss iterator pointer");
+ }
return CLI_SUCCESS;
#undef FORMAT
@@ -11773,12 +12044,26 @@ static struct _map_x_s natmodes[] = {
{ -1, NULL}, /* terminator */
};
+static struct _map_x_s natcfgmodes[] = {
+ { SIP_NAT_NEVER, "never"},
+ { SIP_NAT_ROUTE, "route"},
+ { SIP_NAT_ALWAYS, "yes"},
+ { SIP_NAT_RFC3581, "no"},
+ { -1, NULL}, /* terminator */
+};
+
/*! \brief Convert NAT setting to text string */
static const char *nat2str(int nat)
{
return map_x_s(natmodes, nat, "Unknown");
}
+/*! \brief Convert NAT setting to text string appropriate for config files */
+static const char *nat2strconfig(int nat)
+{
+ return map_x_s(natcfgmodes, nat, "Unknown");
+}
+
/*! \brief Report Peer status in character string
* \return 0 if peer is unreachable, 1 if peer is online, -1 if unmonitored
*/
@@ -11896,7 +12181,10 @@ static char *sip_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
{
regex_t regexbuf;
int havepattern = FALSE;
-
+ int count = 0;
+ struct sip_user *user;
+ struct ao2_iterator i;
+
#define FORMAT "%-25.25s %-15.15s %-15.15s %-15.15s %-5.5s%-10.10s\n"
switch (cmd) {
@@ -11926,27 +12214,32 @@ static char *sip_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
}
ast_cli(a->fd, FORMAT, "Username", "Secret", "Accountcode", "Def.Context", "ACL", "NAT");
- ASTOBJ_CONTAINER_TRAVERSE(&userl, 1, do {
- ASTOBJ_RDLOCK(iterator);
- if (havepattern && regexec(&regexbuf, iterator->name, 0, NULL, 0)) {
- ASTOBJ_UNLOCK(iterator);
+ i = ao2_iterator_init(users, 0);
+
+ while ((user = ao2_t_iterator_next(&i, "iterate thru user table"))) {
+ ao2_lock(user);
+ count++;
+ if (havepattern && regexec(&regexbuf, user->name, 0, NULL, 0)) {
+ ao2_unlock(user);
+ unref_user(user, "toss iterator pointer via a continue in iterator loop");
continue;
}
- ast_cli(a->fd, FORMAT, iterator->name,
- iterator->secret,
- iterator->accountcode,
- iterator->context,
- cli_yesno(iterator->ha != NULL),
- nat2str(ast_test_flag(&iterator->flags[0], SIP_NAT)));
- ASTOBJ_UNLOCK(iterator);
- } while (0)
- );
+ ast_cli(a->fd, FORMAT, user->name,
+ user->secret,
+ user->accountcode,
+ user->context,
+ cli_yesno(user->ha != NULL),
+ nat2str(ast_test_flag(&user->flags[0], SIP_NAT)));
+ ao2_unlock(user);
+ unref_user(user, "toss iterator pointer");
+ }
if (havepattern)
regfree(&regexbuf);
-
+ ast_cli(a->fd, "Total of %d user entries\n", count);
+
return CLI_SUCCESS;
#undef FORMAT
}
@@ -12054,12 +12347,299 @@ static char *sip_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
return _sip_show_peers(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
}
+static char *sip_dbdump(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip dbdump";
+ e->usage =
+ "Usage: sip dbdump [<file>]\n"
+ " dumps user/peer info to the screen in SQL INSERT command form\n"
+ " Optional file path will be output instead of to console if present.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ return _sip_dbdump(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
+}
+
+/*! \brief Execute sip show peers command */
+static char *_sip_dbdump(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
+{
+ struct sip_peer *peer;
+ struct sip_user *user;
+ struct ao2_iterator i;
+ char fname[1024];
+ const char *id;
+ FILE *f1;
+ char idtext[256] = "";
+ int realtimepeers;
+
+ realtimepeers = ast_check_realtime("sippeers");
+
+ if (s) { /* Manager - get ActionID */
+ id = astman_get_header(m, "ActionID");
+ if (!ast_strlen_zero(id))
+ snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+ }
+
+ switch (argc) {
+ case 3:
+ strncpy(fname, argv[2], sizeof(fname));
+ f1 = fopen(fname, "w");
+ if (!f1) {
+ ast_cli(fd, "Sorry, could not open %s for writing!", fname);
+ return CLI_SHOWUSAGE;
+ }
+ break;
+ case 2:
+ ast_cli(fd, "Sorry, will only generate a file at the moment. Please run again with a file name to write to.\n");
+ default:
+ return CLI_SHOWUSAGE;
+ }
+
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
+
+ user = find_user(peer->name,realtimepeers);
+
+ ao2_lock(peer);
+
+ fprintf(f1, "INSERT INTO sipbuddies (");
+
+ /* print out the populated field names in order */
+ fprintf(f1,"name");
+
+ if (peer->host_dynamic)
+ fprintf(f1,",host");
+ if (ast_test_flag(&peer->flags[0], SIP_NAT))
+ fprintf(f1,",nat");
+ fprintf(f1,",type");
+ if (!ast_strlen_zero(peer->accountcode))
+ fprintf(f1,",accountcode");
+ if (peer->amaflags)
+ fprintf(f1,",amaflags");
+ fprintf(f1,",`call-limit`");
+ if (peer->callgroup)
+ fprintf(f1,",callgroup");
+ if (user && !ast_strlen_zero(user->cid_num))
+ fprintf(f1,",callerid");
+ if (ast_test_flag(&peer->flags[0], SIP_REINVITE))
+ fprintf(f1,",canreinvite");
+ if (!ast_strlen_zero(peer->context))
+ fprintf(f1,",context");
+ if (peer->defaddr.sin_addr.s_addr)
+ fprintf(f1,",defaultip");
+ if (ast_test_flag(&peer->flags[0], SIP_DTMF))
+ fprintf(f1,",dtmfmode");
+ if (!ast_strlen_zero(peer->fromuser))
+ fprintf(f1,",fromuser");
+ if (!ast_strlen_zero(peer->fromdomain))
+ fprintf(f1,",fromdomain");
+ if (ast_test_flag(&peer->flags[0], SIP_INSECURE))
+ fprintf(f1,",insecure");
+ if (!ast_strlen_zero(peer->language))
+ fprintf(f1,",language");
+ if (!AST_LIST_EMPTY(&peer->mailboxes)) {
+ fprintf(f1,",mailbox");
+ }
+ if (!ast_strlen_zero(peer->md5secret))
+ fprintf(f1,",md5secret");
+ if (peer->ha) {
+ if (peer->ha->sense == AST_SENSE_DENY) {
+ fprintf(f1,",deny");
+ }
+ if (peer->ha->next && peer->ha->next->sense == AST_SENSE_ALLOW) {
+ fprintf(f1,",permit");
+ }
+ }
+ if (!ast_strlen_zero(peer->mohinterpret))
+ fprintf(f1,",mohinterpret");
+ if (!ast_strlen_zero(peer->mohsuggest))
+ fprintf(f1,",mohsuggest");
+ if (peer->pickupgroup)
+ fprintf(f1,",pickupgroup");
+ if (peer->maxms)
+ fprintf(f1,",qualify");
+ if (!ast_strlen_zero(peer->regexten))
+ fprintf(f1,",regexten");
+ if (peer->rtptimeout)
+ fprintf(f1,",rtptimeout");
+ if (peer->rtpholdtimeout)
+ fprintf(f1,",rtpholdtimeout");
+ if (!ast_strlen_zero(peer->secret))
+ fprintf(f1,",secret");
+ if (peer->chanvars)
+ fprintf(f1,",setvar");
+ if (ast_codec_pref_index(&peer->prefs, 0)) { /* print the codecs wanted in order */
+ fprintf(f1,",allow");
+ }
+ if (!ast_strlen_zero(peer->fullcontact))
+ fprintf(f1,",fullcontact");
+ if (peer->addr.sin_addr.s_addr)
+ fprintf(f1,",ipaddr");
+ if (peer->addr.sin_port)
+ fprintf(f1,",port");
+ if (!ast_strlen_zero(peer->username))
+ fprintf(f1,",username");
+
+ /* print out the values in order */
+ fprintf(f1, ") VALUES (");
+
+ fprintf(f1,"'%s'", peer->name);
+
+ if (peer->host_dynamic)
+ fprintf(f1,",'dynamic'");
+ if (ast_test_flag(&peer->flags[0], SIP_NAT)) {
+ fprintf(f1,",'%s'",nat2strconfig(ast_test_flag(&peer->flags[0], SIP_NAT)));
+ }
+ if (user)
+ fprintf(f1,",'friend'");
+ else
+ fprintf(f1,",'peer'");
+ if (!ast_strlen_zero(peer->accountcode))
+ fprintf(f1,",'%s'", peer->accountcode);
+ if (peer->amaflags)
+ fprintf(f1,",'%s'", ast_cdr_flags2str(peer->amaflags));
+ fprintf(f1,",%d", peer->call_limit);
+ if (peer->callgroup) {
+ char buf[256];
+
+ fprintf(f1,",'%s'", ast_print_group(buf, sizeof(buf), peer->callgroup));
+ }
+ if (user && !ast_strlen_zero(user->cid_num))
+ fprintf(f1,",\"%s<%s>\"", user->cid_name, user->cid_num);
+ if (ast_test_flag(&peer->flags[0], SIP_REINVITE)) {
+ switch (ast_test_flag(&peer->flags[0], SIP_REINVITE)) {
+ case SIP_REINVITE_NONE:
+ fprintf(f1,",'no'");
+ break;
+ case SIP_CAN_REINVITE:
+ fprintf(f1,",'yes'");
+ break;
+ case SIP_CAN_REINVITE_NAT:
+ fprintf(f1,",'nonat'");
+ break;
+ case SIP_REINVITE_UPDATE:
+ fprintf(f1,",'update'");
+ break;
+ default:
+ fprintf(f1,",'no'");
+ break;
+ }
+ }
+ if (!ast_strlen_zero(peer->context))
+ fprintf(f1,",'%s'",peer->context);
+ if (peer->defaddr.sin_addr.s_addr)
+ fprintf(f1,",'%s'", ast_inet_ntoa(peer->defaddr.sin_addr));
+ if (ast_test_flag(&peer->flags[0], SIP_DTMF)) {
+ fprintf(f1,",'%s'", dtmfmode2str(ast_test_flag(&peer->flags[0], SIP_DTMF)));
+ }
+ if (!ast_strlen_zero(peer->fromuser))
+ fprintf(f1,",'%s'", peer->fromuser);
+ if (!ast_strlen_zero(peer->fromdomain))
+ fprintf(f1,",'%s'", peer->fromdomain);
+ if (ast_test_flag(&peer->flags[0], SIP_INSECURE)) {
+
+ fprintf(f1,",'%s'", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE)));
+ }
+ if (!ast_strlen_zero(peer->language))
+ fprintf(f1,",'%s'", peer->language);
+
+ if (!AST_LIST_EMPTY(&peer->mailboxes)) {
+ struct ast_str *mailbox_str = ast_str_alloca(512);
+ peer_mailboxes_to_str(&mailbox_str, peer);
+ fprintf(f1,",'%s'", mailbox_str->str);
+ }
+
+ if (!ast_strlen_zero(peer->md5secret))
+ fprintf(f1,",'%s'", peer->md5secret);
+ if (peer->ha) {
+ if (peer->ha->sense == AST_SENSE_DENY) {
+ fprintf(f1,",'%s/%s'", ast_inet_ntoa(peer->ha->netaddr), ast_inet_ntoa(peer->ha->netmask));
+ }
+ if (peer->ha->next && peer->ha->next->sense == AST_SENSE_ALLOW) {
+ fprintf(f1,",'%s/%s'", ast_inet_ntoa(peer->ha->next->netaddr), ast_inet_ntoa(peer->ha->next->netmask));
+ }
+ }
+
+ if (!ast_strlen_zero(peer->mohinterpret))
+ fprintf(f1,",'%s'", peer->mohinterpret);
+ if (!ast_strlen_zero(peer->mohsuggest))
+ fprintf(f1,",'%s'", peer->mohsuggest);
+ if (peer->pickupgroup) {
+ char buf[256];
+
+ fprintf(f1,",'%s'", ast_print_group(buf, sizeof(buf), peer->pickupgroup));
+ }
+ if (peer->maxms)
+ fprintf(f1,",'%d'", peer->maxms);
+ if (!ast_strlen_zero(peer->regexten))
+ fprintf(f1,",'%s'", peer->regexten);
+ if (peer->rtptimeout)
+ fprintf(f1,",'%d'", peer->rtptimeout);
+ if (peer->rtpholdtimeout)
+ fprintf(f1,",'%d'", peer->rtpholdtimeout);
+ if (!ast_strlen_zero(peer->secret))
+ fprintf(f1,",'%s'", peer->secret);
+ if (peer->chanvars) {
+ int first=1;
+ struct ast_variable *p1 = peer->chanvars;
+ fprintf(f1,",'");
+ while (p1)
+ {
+ if (!first)
+ fprintf(f1,";");
+ else
+ first = 0;
+
+ fprintf(f1,"%s=%s", p1->name, p1->value);
+ p1 = p1->next;
+ }
+ fprintf(f1,"'");
+ }
+
+ if (ast_codec_pref_index(&peer->prefs, 0)) { /* print the codecs wanted in order */
+ /* this code isn't general, it assumes deny=all; but that's pretty common.
+ people who use this differently will have to modify the results by hand. sorry. */
+ int x, codec;
+ fprintf(f1,",'");
+ for(x = 0; x < 32 ; x++) {
+ codec = ast_codec_pref_index(&peer->prefs, x);
+ if (!codec)
+ break;
+ fprintf(f1, "%s", ast_getformatname(codec));
+ fprintf(f1, ":%d", peer->prefs.framing[x]);
+ if (x < 31 && ast_codec_pref_index(&peer->prefs, x + 1))
+ fprintf(f1, ",");
+ }
+ fprintf(f1,"'");
+ }
+
+ if (!ast_strlen_zero(peer->fullcontact))
+ fprintf(f1,",'%s'", peer->fullcontact);
+ if (peer->addr.sin_addr.s_addr)
+ fprintf(f1,",'%s'", ast_inet_ntoa(peer->addr.sin_addr));
+ if (peer->addr.sin_port)
+ fprintf(f1,",%d", peer->addr.sin_port);
+ if (!ast_strlen_zero(peer->username))
+ fprintf(f1,",'%s'", peer->username);
+
+ fprintf(f1, ");\n");
+ }
+ fclose(f1);
+ return CLI_SUCCESS;
+}
+
/*! \brief Execute sip show peers command */
static char *_sip_show_peers(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
{
regex_t regexbuf;
int havepattern = FALSE;
-
+ struct sip_peer *peer;
+ struct ao2_iterator i;
+
/* the last argument is left-aligned, so we don't need a size anyways */
#define FORMAT2 "%-25.25s %-15.15s %-3.3s %-3.3s %-3.3s %-8s %-10s %s\n"
#define FORMAT "%-25.25s %-15.15s %-3.3s %-3.3s %-3.3s %-8d %-10s %s\n"
@@ -12099,52 +12679,53 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
if (!s) /* Normal list */
ast_cli(fd, FORMAT2, "Name/username", "Host", "Dyn", "Nat", "ACL", "Port", "Status", (realtimepeers ? "Realtime" : ""));
- ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do {
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
char status[20] = "";
char srch[2000];
char pstatus;
- ASTOBJ_RDLOCK(iterator);
-
- if (havepattern && regexec(&regexbuf, iterator->name, 0, NULL, 0)) {
- ASTOBJ_UNLOCK(iterator);
+ ao2_lock(peer);
+ if (havepattern && regexec(&regexbuf, peer->name, 0, NULL, 0)) {
+ ao2_unlock(peer);
+ unref_peer(peer, "toss iterator peer ptr before continue");
continue;
}
- if (!ast_strlen_zero(iterator->username) && !s)
- snprintf(name, sizeof(name), "%s/%s", iterator->name, iterator->username);
+ if (!ast_strlen_zero(peer->username) && !s)
+ snprintf(name, sizeof(name), "%s/%s", peer->name, peer->username);
else
- ast_copy_string(name, iterator->name, sizeof(name));
+ ast_copy_string(name, peer->name, sizeof(name));
- pstatus = peer_status(iterator, status, sizeof(status));
+ pstatus = peer_status(peer, status, sizeof(status));
if (pstatus == 1)
peers_mon_online++;
else if (pstatus == 0)
peers_mon_offline++;
else {
- if (iterator->addr.sin_port == 0)
+ if (peer->addr.sin_port == 0)
peers_unmon_offline++;
else
peers_unmon_online++;
}
snprintf(srch, sizeof(srch), FORMAT, name,
- iterator->addr.sin_addr.s_addr ? ast_inet_ntoa(iterator->addr.sin_addr) : "(Unspecified)",
- iterator->host_dynamic ? " D " : " ", /* Dynamic or not? */
- ast_test_flag(&iterator->flags[0], SIP_NAT_ROUTE) ? " N " : " ", /* NAT=yes? */
- iterator->ha ? " A " : " ", /* permit/deny */
- ntohs(iterator->addr.sin_port), status,
- realtimepeers ? (iterator->is_realtime ? "Cached RT":"") : "");
+ peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
+ peer->host_dynamic ? " D " : " ", /* Dynamic or not? */
+ ast_test_flag(&peer->flags[0], SIP_NAT_ROUTE) ? " N " : " ", /* NAT=yes? */
+ peer->ha ? " A " : " ", /* permit/deny */
+ ntohs(peer->addr.sin_port), status,
+ realtimepeers ? (peer->is_realtime ? "Cached RT":"") : "");
if (!s) {/* Normal CLI list */
ast_cli(fd, FORMAT, name,
- iterator->addr.sin_addr.s_addr ? ast_inet_ntoa(iterator->addr.sin_addr) : "(Unspecified)",
- iterator->host_dynamic ? " D " : " ", /* Dynamic or not? */
- ast_test_flag(&iterator->flags[0], SIP_NAT_ROUTE) ? " N " : " ", /* NAT=yes? */
- iterator->ha ? " A " : " ", /* permit/deny */
+ peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
+ peer->host_dynamic ? " D " : " ", /* Dynamic or not? */
+ ast_test_flag(&peer->flags[0], SIP_NAT_ROUTE) ? " N " : " ", /* NAT=yes? */
+ peer->ha ? " A " : " ", /* permit/deny */
- ntohs(iterator->addr.sin_port), status,
- realtimepeers ? (iterator->is_realtime ? "Cached RT":"") : "");
+ ntohs(peer->addr.sin_port), status,
+ realtimepeers ? (peer->is_realtime ? "Cached RT":"") : "");
} else { /* Manager format */
/* The names here need to be the same as other channels */
astman_append(s,
@@ -12162,22 +12743,22 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
"Status: %s\r\n"
"RealtimeDevice: %s\r\n\r\n",
idtext,
- iterator->name,
- iterator->addr.sin_addr.s_addr ? ast_inet_ntoa(iterator->addr.sin_addr) : "-none-",
- ntohs(iterator->addr.sin_port),
- iterator->host_dynamic ? "yes" : "no", /* Dynamic or not? */
- ast_test_flag(&iterator->flags[0], SIP_NAT_ROUTE) ? "yes" : "no", /* NAT=yes? */
- ast_test_flag(&iterator->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "yes" : "no", /* VIDEOSUPPORT=yes? */
- ast_test_flag(&iterator->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "yes" : "no", /* TEXTSUPPORT=yes? */
- iterator->ha ? "yes" : "no", /* permit/deny */
+ peer->name,
+ peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "-none-",
+ ntohs(peer->addr.sin_port),
+ peer->host_dynamic ? "yes" : "no", /* Dynamic or not? */
+ ast_test_flag(&peer->flags[0], SIP_NAT_ROUTE) ? "yes" : "no", /* NAT=yes? */
+ ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT) ? "yes" : "no", /* VIDEOSUPPORT=yes? */
+ ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT) ? "yes" : "no", /* TEXTSUPPORT=yes? */
+ peer->ha ? "yes" : "no", /* permit/deny */
status,
- realtimepeers ? (iterator->is_realtime ? "yes":"no") : "no");
+ realtimepeers ? (peer->is_realtime ? "yes":"no") : "no");
}
- ASTOBJ_UNLOCK(iterator);
-
total_peers++;
- } while(0) );
+ ao2_unlock(peer);
+ unref_peer(peer, "toss iterator peer ptr");
+ }
if (!s)
ast_cli(fd, "%d sip peers [Monitored: %d online, %d offline Unmonitored: %d online, %d offline]\n",
@@ -12195,6 +12776,39 @@ static char *_sip_show_peers(int fd, int *total, struct mansession *s, const str
#undef FORMAT2
}
+static int user_dump_func(void *userobj, void *arg, int flags)
+{
+ struct sip_user *user = userobj;
+ int refc = ao2_t_ref(userobj, 0, "");
+ int *fd = arg;
+
+ ast_cli(*fd, "name: %s\ntype: user\nobjflags: %d\nrefcount: %d\n\n", user->name, 0, refc);
+ return 0;
+}
+
+static int peer_dump_func(void *userobj, void *arg, int flags)
+{
+ struct sip_peer *peer = userobj;
+ int refc = ao2_t_ref(userobj, 0, "");
+ int *fd = arg;
+
+ ast_cli(*fd, "name: %s\ntype: peer\nobjflags: %d\nrefcount: %d\n\n",
+ peer->name, 0, refc);
+ return 0;
+}
+
+static int dialog_dump_func(void *userobj, void *arg, int flags)
+{
+ struct sip_pvt *pvt = userobj;
+ int refc = ao2_t_ref(userobj, 0, "");
+ int *fd = arg;
+
+ ast_cli(*fd, "name: %s\ntype: dialog\nobjflags: %d\nrefcount: %d\n\n",
+ pvt->callid, 0, refc);
+ return 0;
+}
+
+
/*! \brief List all allocated SIP Objects (realtime or static) */
static char *sip_show_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
@@ -12214,11 +12828,13 @@ static char *sip_show_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_a
if (a->argc != 3)
return CLI_SHOWUSAGE;
ast_cli(a->fd, "-= User objects: %d static, %d realtime =-\n\n", suserobjs, ruserobjs);
- ASTOBJ_CONTAINER_DUMP(a->fd, tmp, sizeof(tmp), &userl);
+ ao2_t_callback(users, OBJ_NODATA, user_dump_func, &a->fd, "initiate ao2_callback to dump users");
ast_cli(a->fd, "-= Peer objects: %d static, %d realtime, %d autocreate =-\n\n", speerobjs, rpeerobjs, apeerobjs);
- ASTOBJ_CONTAINER_DUMP(a->fd, tmp, sizeof(tmp), &peerl);
+ ao2_t_callback(peers, OBJ_NODATA, peer_dump_func, &a->fd, "initiate ao2_callback to dump peers");
ast_cli(a->fd, "-= Registry objects: %d =-\n\n", regobjs);
ASTOBJ_CONTAINER_DUMP(a->fd, tmp, sizeof(tmp), &regl);
+ ast_cli(a->fd, "-= Dialog objects:\n\n");
+ ao2_t_callback(dialogs, OBJ_NODATA, dialog_dump_func, &a->fd, "initiate ao2_callback to dump dialogs");
return CLI_SUCCESS;
}
/*! \brief Print call group and pickup group */
@@ -12290,17 +12906,95 @@ static void cleanup_stale_contexts(char *new, char *old)
}
}
+/* this func is used with ao2_callback to unlink/delete all dialogs that
+ are marked needdestroy. It will return CMP_MATCH for candidates, and they
+ will be unlinked */
+
+/* TODO: Implement a queue and instead of marking a dialog
+ to be destroyed, toss it into the queue. Have a separate
+ thread do the locking and destruction */
+
+static int dialog_needdestroy(void *dialogobj, void *arg, int flags)
+{
+ struct sip_pvt *dialog = dialogobj;
+ time_t *t = arg;
+
+ if (sip_pvt_trylock(dialog)) {
+ /* this path gets executed fairly frequently (3% or so) even in low load
+ situations; the routines we most commonly fight for a lock with:
+ sip_answer (7 out of 9)
+ sip_hangup (2 out of 9)
+ */
+ ao2_unlock(dialogs);
+ usleep(1);
+ ao2_lock(dialogs);
+ /* I had previously returned CMP_STOP here; but changed it to return
+ a zero instead, because there is no need to stop at the first sign
+ of trouble. The old algorithm would restart in such circumstances,
+ but there is no need for this now in astobj2-land. We'll
+ just skip over this element this time, and catch it on the
+ next traversal, which is about once a second or so. See the
+ ao2_callback call in do_monitor. We don't want the number of
+ dialogs to back up too much between runs.
+ */
+ return 0;
+ }
+
+ /* We absolutely cannot destroy the rtp struct while a bridge is active or we WILL crash */
+ if (dialog->rtp && ast_rtp_get_bridged(dialog->rtp)) {
+ ast_verbose("Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text);
+ return 0;
+ }
+
+ if (dialog->vrtp && ast_rtp_get_bridged(dialog->vrtp)) {
+ ast_verbose("Bridge still active. Delaying destroy of SIP dialog '%s' Method: %s\n", dialog->callid, sip_methods[dialog->method].text);
+ return 0;
+ }
+ /* Check RTP timeouts and kill calls if we have a timeout set and do not get RTP */
+ check_rtp_timeout(dialog, *t);
+
+ /* If we have sessions that needs to be destroyed, do it now */
+ /* Check if we have outstanding requests not responsed to or an active call
+ - if that's the case, wait with destruction */
+ if (dialog->needdestroy && !dialog->packets && !dialog->owner) {
+ sip_pvt_unlock(dialog);
+ /* no, the unlink should handle this: dialog_unref(dialog, "needdestroy: one more refcount decrement to allow dialog to be destroyed"); */
+ /* the CMP_MATCH will unlink this dialog from the dialog hash table */
+ dialog_unlink_all(dialog, TRUE, FALSE);
+ return 0; /* the unlink_all should unlink this from the table, so.... no need to return a match */
+ }
+ sip_pvt_unlock(dialog);
+ return 0;
+}
+
+/* this func is used with ao2_callback to unlink/delete all marked
+ peers */
+static int peer_is_marked(void *peerobj, void *arg, int flags)
+{
+ struct sip_peer *peer = peerobj;
+ return peer->the_mark ? CMP_MATCH : 0;
+}
+
+/* this func is used with ao2_callback to unlink/delete all marked
+ users */
+static int user_is_marked(void *userobj, void *arg, int flags)
+{
+ struct sip_user *user = userobj;
+ return user->the_mark ? CMP_MATCH : 0;
+}
+
/*! \brief Remove temporary realtime objects from memory (CLI) */
static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
- struct sip_peer *peer;
- struct sip_user *user;
+ struct sip_peer *peer, *pi;
+ struct sip_user *user, *ui;
int pruneuser = FALSE;
int prunepeer = FALSE;
int multi = FALSE;
char *name = NULL;
regex_t regexbuf;
-
+ struct ao2_iterator i;
+
if (cmd == CLI_INIT) {
e->command = "sip prune realtime [peer|user|all] [all|like]";
e->usage =
@@ -12375,70 +13069,83 @@ static char *sip_prune_realtime(struct ast_cli_entry *e, int cmd, struct ast_cli
if (multi) {
if (prunepeer) {
int pruned = 0;
-
- ASTOBJ_CONTAINER_WRLOCK(&peerl);
- ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do {
- ASTOBJ_RDLOCK(iterator);
- if (name && regexec(&regexbuf, iterator->name, 0, NULL, 0)) {
- ASTOBJ_UNLOCK(iterator);
+
+ i = ao2_iterator_init(peers, 0);
+ while ((pi = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
+ ao2_lock(pi);
+ if (name && regexec(&regexbuf, pi->name, 0, NULL, 0)) {
+ unref_peer(pi, "toss iterator peer ptr before continue");
continue;
};
- if (ast_test_flag(&iterator->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
- ASTOBJ_MARK(iterator);
+ if (ast_test_flag(&pi->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
+ pi->the_mark = 1;
pruned++;
}
- ASTOBJ_UNLOCK(iterator);
- } while (0) );
+ ao2_unlock(pi);
+ unref_peer(pi, "toss iterator peer ptr");
+ }
if (pruned) {
- ASTOBJ_CONTAINER_PRUNE_MARKED(&peerl, sip_destroy_peer);
+ ao2_t_callback(peers, OBJ_NODATA|OBJ_UNLINK, peer_is_marked, 0, "initiating callback to remove marked peers");
ast_cli(a->fd, "%d peers pruned.\n", pruned);
} else
ast_cli(a->fd, "No peers found to prune.\n");
- ASTOBJ_CONTAINER_UNLOCK(&peerl);
}
if (pruneuser) {
int pruned = 0;
- ASTOBJ_CONTAINER_WRLOCK(&userl);
- ASTOBJ_CONTAINER_TRAVERSE(&userl, 1, do {
- ASTOBJ_RDLOCK(iterator);
- if (name && regexec(&regexbuf, iterator->name, 0, NULL, 0)) {
- ASTOBJ_UNLOCK(iterator);
+ i = ao2_iterator_init(users, 0);
+ while ((ui = ao2_t_iterator_next(&i, "iterate thru users table"))) {
+ ao2_lock(ui);
+ if (name && regexec(&regexbuf, ui->name, 0, NULL, 0)) {
+ unref_user(ui, "toss iterator user ptr before continue");
continue;
};
- if (ast_test_flag(&iterator->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
- ASTOBJ_MARK(iterator);
+ if (ast_test_flag(&ui->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
+ ui->the_mark = 1;
pruned++;
}
- ASTOBJ_UNLOCK(iterator);
- } while (0) );
+ ao2_unlock(ui);
+ unref_user(ui, "toss iterator user ptr");
+ }
if (pruned) {
- ASTOBJ_CONTAINER_PRUNE_MARKED(&userl, sip_destroy_user);
+ ao2_t_callback(users, OBJ_NODATA|OBJ_UNLINK, user_is_marked, 0, "callback to remove marked users");
ast_cli(a->fd, "%d users pruned.\n", pruned);
} else
ast_cli(a->fd, "No users found to prune.\n");
- ASTOBJ_CONTAINER_UNLOCK(&userl);
}
} else {
if (prunepeer) {
- if ((peer = ASTOBJ_CONTAINER_FIND_UNLINK(&peerl, name))) {
+ struct sip_peer tmp;
+ ast_copy_string(tmp.name, name, sizeof(tmp.name));
+ if ((peer = ao2_t_find(peers, &tmp, OBJ_POINTER|OBJ_UNLINK, "finding to unlink from peers"))) {
+ if (peer->addr.sin_addr.s_addr) {
+ ao2_t_unlink(peers_by_ip, peer, "unlinking peer from peers_by_ip also");
+ }
if (!ast_test_flag(&peer->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
ast_cli(a->fd, "Peer '%s' is not a Realtime peer, cannot be pruned.\n", name);
- ASTOBJ_CONTAINER_LINK(&peerl, peer);
+ /* put it back! */
+ ao2_t_link(peers, peer, "link peer into peer table");
+ if (peer->addr.sin_addr.s_addr) {
+ ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
+ }
+
} else
ast_cli(a->fd, "Peer '%s' pruned.\n", name);
- unref_peer(peer);
+ unref_peer(peer, "sip_prune_realtime: unref_peer: tossing temp peer ptr");
} else
ast_cli(a->fd, "Peer '%s' not found.\n", name);
}
if (pruneuser) {
- if ((user = ASTOBJ_CONTAINER_FIND_UNLINK(&userl, name))) {
+ struct sip_user tmp;
+ ast_copy_string(tmp.name, name, sizeof(tmp.name));
+ if ((user = ao2_t_find(users, &tmp, OBJ_POINTER|OBJ_UNLINK, "finding to unlink from users table"))) {
if (!ast_test_flag(&user->flags[1], SIP_PAGE2_RTCACHEFRIENDS)) {
ast_cli(a->fd, "User '%s' is not a Realtime user, cannot be pruned.\n", name);
- ASTOBJ_CONTAINER_LINK(&userl, user);
+ /* put it back! */
+ ao2_t_link(users, user, "link unlinked user back into users table");
} else
ast_cli(a->fd, "User '%s' pruned.\n", name);
- unref_user(user);
+ unref_user(user, "unref_user: Tossing temp user ptr");
} else
ast_cli(a->fd, "User '%s' not found.\n", name);
}
@@ -12715,7 +13422,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
ast_cli(fd, " Sess-Expires : %d secs\n", peer->stimer.st_max_se);
ast_cli(fd, " Min-Sess : %d secs\n", peer->stimer.st_min_se);
ast_cli(fd, "\n");
- unref_peer(peer);
+ peer = unref_peer(peer, "sip_show_peer: unref_peer: done with peer ptr");
} else if (peer && type == 1) { /* manager listing */
char buf[256];
struct ast_str *mailbox_str = ast_str_alloca(512);
@@ -12797,7 +13504,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
}
}
- unref_peer(peer);
+ peer = unref_peer(peer, "sip_show_peer: unref_peer: done with peer");
} else {
ast_cli(fd, "Peer %s not found.\n", argv[3]);
@@ -12807,6 +13514,45 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
return CLI_SUCCESS;
}
+static char *sip_show_sched(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ char cbuf[2256];
+ struct ast_cb_names cbnames = {9, { "retrans_pkt",
+ "__sip_autodestruct",
+ "expire_register",
+ "auto_congest",
+ "sip_reg_timeout",
+ "sip_poke_peer_s",
+ "sip_poke_noanswer",
+ "sip_reregister",
+ "sip_reinvite_retry"},
+ { retrans_pkt,
+ __sip_autodestruct,
+ expire_register,
+ auto_congest,
+ sip_reg_timeout,
+ sip_poke_peer_s,
+ sip_poke_noanswer,
+ sip_reregister,
+ sip_reinvite_retry}};
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show sched";
+ e->usage =
+ "Usage: sip show sched\n"
+ " Shows stats on what's in the sched queue at the moment\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+ ast_cli(a->fd, "\n");
+ ast_sched_report(sched, cbuf, sizeof(cbuf), &cbnames);
+ ast_cli(a->fd, "%s", cbuf);
+ return CLI_SUCCESS;
+}
+
+
/*! \brief Show one user in detail */
static char *sip_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
@@ -12871,8 +13617,7 @@ static char *sip_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args
}
ast_cli(a->fd, "\n");
-
- unref_user(user);
+ unref_user(user, "unref_user from sip_show_user, near end");
} else {
ast_cli(a->fd, "User %s not found.\n", a->argv[3]);
ast_cli(a->fd, "\n");
@@ -12905,6 +13650,7 @@ static char *sip_show_registry(struct ast_cli_entry *e, int cmd, struct ast_cli_
if (a->argc != 3)
return CLI_SHOWUSAGE;
ast_cli(a->fd, FORMAT2, "Host", "dnsmgr", "Username", "Refresh", "State", "Reg.Time");
+
ASTOBJ_CONTAINER_TRAVERSE(&regl, 1, do {
ASTOBJ_RDLOCK(iterator);
snprintf(host, sizeof(host), "%s:%d", iterator->hostname, iterator->portno ? iterator->portno : STANDARD_SIP_PORT);
@@ -12953,6 +13699,7 @@ static char *sip_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
} else {
ast_cli(a->fd, "Peer %s not registered\n", a->argv[2]);
}
+ unref_peer(peer, "sip_unregister: unref_peer via sip_unregister: done with peer from find_peer call");
} else {
ast_cli(a->fd, "Peer unknown: \'%s\'. Not unregistered.\n", a->argv[2]);
}
@@ -13195,14 +13942,14 @@ static int show_channels_cb(void *__cur, void *__arg, int flags)
struct sip_pvt *cur = __cur;
struct __show_chan_arg *arg = __arg;
const struct sockaddr_in *dst = sip_real_dst(cur);
-
- /* XXX indentation preserved to reduce diff. Will be fixed later */
- if (cur->subscribed == NONE && !arg->subscriptions) {
- /* set if SIP transfer in progress */
- const char *referstatus = cur->refer ? referstatus2str(cur->refer->status) : "";
- char formatbuf[SIPBUFSIZE/2];
-
- ast_cli(arg->fd, FORMAT, ast_inet_ntoa(dst->sin_addr),
+
+ /* XXX indentation preserved to reduce diff. Will be fixed later */
+ if (cur->subscribed == NONE && !arg->subscriptions) {
+ /* set if SIP transfer in progress */
+ const char *referstatus = cur->refer ? referstatus2str(cur->refer->status) : "";
+ char formatbuf[SIPBUFSIZE/2];
+
+ ast_cli(arg->fd, FORMAT, ast_inet_ntoa(dst->sin_addr),
S_OR(cur->username, S_OR(cur->cid_num, "(None)")),
cur->callid,
ast_getformatname_multiple(formatbuf, sizeof(formatbuf), cur->owner ? cur->owner->nativeformats : 0),
@@ -13211,13 +13958,13 @@ static int show_channels_cb(void *__cur, void *__arg, int flags)
cur->lastmsg ,
referstatus
);
- arg->numchans++;
- }
- if (cur->subscribed != NONE && arg->subscriptions) {
- struct ast_str *mailbox_str = ast_str_alloca(512);
- if (cur->subscribed == MWI_NOTIFICATION && cur->relatedpeer)
- peer_mailboxes_to_str(&mailbox_str, cur->relatedpeer);
- ast_cli(arg->fd, FORMAT4, ast_inet_ntoa(dst->sin_addr),
+ arg->numchans++;
+ }
+ if (cur->subscribed != NONE && arg->subscriptions) {
+ struct ast_str *mailbox_str = ast_str_alloca(512);
+ if (cur->subscribed == MWI_NOTIFICATION && cur->relatedpeer)
+ peer_mailboxes_to_str(&mailbox_str, cur->relatedpeer);
+ ast_cli(arg->fd, FORMAT4, ast_inet_ntoa(dst->sin_addr),
S_OR(cur->username, S_OR(cur->cid_num, "(None)")),
cur->callid,
/* the 'complete' exten/context is hidden in the refer_to field for subscriptions */
@@ -13226,10 +13973,9 @@ static int show_channels_cb(void *__cur, void *__arg, int flags)
subscription_type2str(cur->subscribed),
cur->subscribed == MWI_NOTIFICATION ? S_OR(mailbox_str->str, "<none>") : "<none>",
cur->expiry
-);
- arg->numchans++;
- }
-
+ );
+ arg->numchans++;
+ }
return 0; /* don't care, we scan all channels */
}
@@ -13240,9 +13986,9 @@ static int show_channels_cb(void *__cur, void *__arg, int flags)
*/
static char *sip_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
- struct sip_pvt *cur;
struct __show_chan_arg arg = { .fd = a->fd, .numchans = 0 };
+
if (cmd == CLI_INIT) {
e->command = "sip show {channels|subscriptions}";
e->usage =
@@ -13263,12 +14009,8 @@ static char *sip_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_
ast_cli(arg.fd, FORMAT3, "Peer", "User", "Call ID", "Extension", "Last state", "Type", "Mailbox", "Expiry");
/* iterate on the container and invoke the callback on each item */
- dialoglist_lock();
- for (cur = dialoglist; cur; cur = cur->next) {
- show_channels_cb(cur, &arg, 0);
- }
- dialoglist_unlock();
-
+ ao2_t_callback(dialogs, OBJ_NODATA, show_channels_cb, &arg, "callback to show channels");
+
/* print summary information */
ast_cli(arg.fd, "%d active SIP %s%s\n", arg.numchans,
(arg.subscriptions ? "subscription" : "dialog"),
@@ -13284,38 +14026,53 @@ static char *sip_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_
* given position. As many functions of this kind, each invokation has
* O(state) time complexity so be careful in using it.
*/
+
+
static char *complete_sipch(const char *line, const char *word, int pos, int state)
{
int which=0;
struct sip_pvt *cur;
char *c = NULL;
int wordlen = strlen(word);
+ struct ao2_iterator i;
- dialoglist_lock();
- for (cur = dialoglist; cur; cur = cur->next) {
+ i = ao2_iterator_init(dialogs, 0);
+
+ while ((cur = ao2_t_iterator_next(&i, "iterate thru dialogs"))) {
+ sip_pvt_lock(cur);
if (!strncasecmp(word, cur->callid, wordlen) && ++which > state) {
c = ast_strdup(cur->callid);
+ sip_pvt_unlock(cur);
+ dialog_unref(cur, "drop ref in iterator loop break");
break;
}
+ sip_pvt_unlock(cur);
+ dialog_unref(cur, "drop ref in iterator loop");
}
- dialoglist_unlock();
return c;
}
+
/*! \brief Do completion on peer name */
static char *complete_sip_peer(const char *word, int state, int flags2)
{
char *result = NULL;
int wordlen = strlen(word);
int which = 0;
+ struct ao2_iterator i = ao2_iterator_init(peers, 0);
+ struct sip_peer *peer;
- ASTOBJ_CONTAINER_TRAVERSE(&peerl, !result, do {
+ while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
/* locking of the object is not required because only the name and flags are being compared */
- if (!strncasecmp(word, iterator->name, wordlen) &&
- (!flags2 || ast_test_flag(&iterator->flags[1], flags2)) &&
+ if (!strncasecmp(word, peer->name, wordlen) &&
+ (!flags2 || ast_test_flag(&peer->flags[1], flags2)) &&
++which > state)
- result = ast_strdup(iterator->name);
- } while(0) );
+ result = ast_strdup(peer->name);
+ unref_peer(peer, "toss iterator peer ptr before break");
+ if (result) {
+ break;
+ }
+ }
return result;
}
@@ -13325,15 +14082,21 @@ static char *complete_sip_registered_peer(const char *word, int state, int flags
char *result = NULL;
int wordlen = strlen(word);
int which = 0;
-
- ASTOBJ_CONTAINER_TRAVERSE(&peerl, !result, do {
- ASTOBJ_WRLOCK(iterator);
- if (!strncasecmp(word, iterator->name, wordlen) &&
- (!flags2 || ast_test_flag(&iterator->flags[1], flags2)) &&
- ++which > state && iterator->expire > 0)
- result = ast_strdup(iterator->name);
- ASTOBJ_UNLOCK(iterator);
- } while(0) );
+ struct ao2_iterator i;
+ struct sip_peer *peer;
+
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
+ if (!strncasecmp(word, peer->name, wordlen) &&
+ (!flags2 || ast_test_flag(&peer->flags[1], flags2)) &&
+ ++which > state && peer->expire > 0)
+ result = ast_strdup(peer->name);
+ if (result) {
+ unref_peer(peer, "toss iterator peer ptr before break");
+ break;
+ }
+ unref_peer(peer, "toss iterator peer ptr");
+ }
return result;
}
@@ -13370,17 +14133,28 @@ static char *complete_sip_user(const char *word, int state, int flags2)
char *result = NULL;
int wordlen = strlen(word);
int which = 0;
+ struct ao2_iterator i;
+ struct sip_user *user;
+
+ i = ao2_iterator_init(users, 0);
- ASTOBJ_CONTAINER_TRAVERSE(&userl, !result, do {
+ while ((user = ao2_t_iterator_next(&i, "iterate thru users table"))) {
/* locking of the object is not required because only the name and flags are being compared */
- if (!strncasecmp(word, iterator->name, wordlen)) {
- if (flags2 && !ast_test_flag(&iterator->flags[1], flags2))
+ if (!strncasecmp(word, user->name, wordlen)) {
+ if (flags2 && !ast_test_flag(&user->flags[1], flags2)) {
+ unref_user(user, "toss iterator user ptr before continue");
continue;
+ }
if (++which > state) {
- result = ast_strdup(iterator->name);
+ result = ast_strdup(user->name);
}
}
- } while(0) );
+ if (result) {
+ unref_user(user, "toss iterator user ptr before break");
+ break;
+ }
+ unref_user(user, "toss iterator user ptr");
+ }
return result;
}
@@ -13429,6 +14203,7 @@ static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_a
struct sip_pvt *cur;
size_t len;
int found = 0;
+ struct ao2_iterator i;
switch (cmd) {
case CLI_INIT:
@@ -13444,8 +14219,12 @@ static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_a
if (a->argc != 4)
return CLI_SHOWUSAGE;
len = strlen(a->argv[3]);
- dialoglist_lock();
- for (cur = dialoglist; cur; cur = cur->next) {
+
+ i = ao2_iterator_init(dialogs, 0);
+
+ while ((cur = ao2_t_iterator_next(&i, "iterate thru dialogs"))) {
+ sip_pvt_lock(cur);
+
if (!strncasecmp(cur->callid, a->argv[3], len)) {
char formatbuf[SIPBUFSIZE/2];
ast_cli(a->fd, "\n");
@@ -13517,10 +14296,15 @@ static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_a
found++;
}
+
+ sip_pvt_unlock(cur);
+
+ ao2_t_ref(cur, -1, "toss dialog ptr set by iterator_next");
}
- dialoglist_unlock();
+
if (!found)
ast_cli(a->fd, "No such SIP Call ID starting with '%s'\n", a->argv[3]);
+
return CLI_SUCCESS;
}
@@ -13530,6 +14314,7 @@ static char *sip_show_history(struct ast_cli_entry *e, int cmd, struct ast_cli_a
struct sip_pvt *cur;
size_t len;
int found = 0;
+ struct ao2_iterator i;
switch (cmd) {
case CLI_INIT:
@@ -13544,11 +14329,15 @@ static char *sip_show_history(struct ast_cli_entry *e, int cmd, struct ast_cli_a
if (a->argc != 4)
return CLI_SHOWUSAGE;
+
if (!recordhistory)
ast_cli(a->fd, "\n***Note: History recording is currently DISABLED. Use 'sip set history on' to ENABLE.\n");
+
len = strlen(a->argv[3]);
- dialoglist_lock();
- for (cur = dialoglist; cur; cur = cur->next) {
+
+ i = ao2_iterator_init(dialogs, 0);
+ while ((cur = ao2_t_iterator_next(&i, "iterate thru dialogs"))) {
+ sip_pvt_lock(cur);
if (!strncasecmp(cur->callid, a->argv[3], len)) {
struct sip_history *hist;
int x = 0;
@@ -13565,10 +14354,13 @@ static char *sip_show_history(struct ast_cli_entry *e, int cmd, struct ast_cli_a
ast_cli(a->fd, "Call '%s' has no history\n", cur->callid);
found++;
}
+ sip_pvt_unlock(cur);
+ ao2_t_ref(cur, -1, "toss dialog ptr from iterator_next");
}
- dialoglist_unlock();
+
if (!found)
ast_cli(a->fd, "No such SIP Call ID starting with '%s'\n", a->argv[3]);
+
return CLI_SUCCESS;
}
@@ -13833,7 +14625,7 @@ static char *sip_do_debug_peer(int fd, char *arg)
sipdebug |= sip_debug_console;
}
if (peer)
- unref_peer(peer);
+ unref_peer(peer, "sip_do_debug_peer: unref_peer, from find_peer call");
return CLI_SUCCESS;
}
@@ -13926,7 +14718,9 @@ static char *sip_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
if (create_addr(p, a->argv[i], NULL)) {
/* Maybe they're not registered, etc. */
- sip_destroy(p);
+ dialog_unlink_all(p, TRUE, TRUE);
+ dialog_unref(p, "unref dialog inside for loop" );
+ /* sip_destroy(p); */
ast_cli(a->fd, "Could not create address for '%s'\n", a->argv[i]);
continue;
}
@@ -13942,11 +14736,14 @@ static char *sip_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
/* Recalculate our side, and recalculate Call ID */
ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
build_via(p);
+ ao2_t_unlink(dialogs, p, "About to change the callid -- remove the old name");
build_callid_pvt(p);
+ ao2_t_link(dialogs, p, "Linking in new name");
ast_cli(a->fd, "Sending NOTIFY of type '%s' to '%s'\n", a->argv[2], a->argv[i]);
+ dialog_ref(p, "bump the count of p, which transmit_sip_request will decrement.");
transmit_sip_request(p, &req);
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- dialog_unref(p);
+ dialog_unref(p, "unref pvt at end of for loop in sip_notify");
}
return CLI_SUCCESS;
@@ -14373,7 +15170,7 @@ static int function_sippeer(struct ast_channel *chan, const char *cmd, char *dat
}
}
- unref_peer(peer);
+ unref_peer(peer, "unref_peer from function_sippeer, just before return");
return 0;
}
@@ -14572,6 +15369,7 @@ static int sip_reinvite_retry(const void *data)
ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
p->waitid = -1;
+ dialog_unref(p, "unref the dialog ptr from sip_reinvite_retry, because it held a dialog ptr");
return 0;
}
@@ -14599,7 +15397,7 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru
/* Acknowledge sequence number - This only happens on INVITE from SIP-call */
/* Don't auto congest anymore since we've gotten something useful back */
- AST_SCHED_DEL(sched, p->initid);
+ AST_SCHED_DEL_UNREF(sched, p->initid, dialog_unref(p, "when you delete the initid sched, you should dec the refcount for the stored dialog ptr"));
/* RFC3261 says we must treat every 1xx response (but not 100)
that we don't recognize as if it was 183.
@@ -14902,7 +15700,8 @@ static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, stru
/* Reset the flag after a while
*/
int wait = 3 + ast_random() % 5;
- p->waitid = ast_sched_add(sched, wait, sip_reinvite_retry, p);
+ p->waitid = ast_sched_add(sched, wait, sip_reinvite_retry, dialog_ref(p, "passing dialog ptr into sched structure based on waitid for sip_reinvite_retry."));
+ ast_log(LOG_WARNING, "just did sched_add waitid(%d) for sip_reinvite_retry for dialog %s in handle_response_invite\n", p->waitid, p->callid);
ast_debug(2, "Reinvite race. Waiting %d secs before retry\n", wait);
}
}
@@ -14983,7 +15782,7 @@ static int handle_response_register(struct sip_pvt *p, int resp, char *rest, str
int expires, expires_ms;
struct sip_registry *r;
r=p->registry;
-
+
switch (resp) {
case 401: /* Unauthorized */
if (p->authtries == MAX_AUTHTRIES || do_register_auth(p, req, resp)) {
@@ -15000,7 +15799,8 @@ static int handle_response_register(struct sip_pvt *p, int resp, char *rest, str
case 404: /* Not found */
ast_log(LOG_WARNING, "Got 404 Not found on SIP register to service %s@%s, giving up\n", p->registry->username, p->registry->hostname);
p->needdestroy = 1;
- r->call = NULL;
+ if (r->call)
+ r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 404");
r->regstate = REG_STATE_REJECTED;
AST_SCHED_DEL(sched, r->timeout);
break;
@@ -15014,7 +15814,8 @@ static int handle_response_register(struct sip_pvt *p, int resp, char *rest, str
if (global_regattempts_max)
p->registry->regattempts = global_regattempts_max+1;
p->needdestroy = 1;
- r->call = NULL;
+ if (r->call)
+ r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 408");
AST_SCHED_DEL(sched, r->timeout);
break;
case 423: /* Interval too brief */
@@ -15023,7 +15824,7 @@ static int handle_response_register(struct sip_pvt *p, int resp, char *rest, str
ast_sched_del(sched, r->timeout);
r->timeout = -1;
if (r->call) {
- r->call = NULL;
+ r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 423");
p->needdestroy = 1;
}
if (r->expiry > max_expiry) {
@@ -15039,7 +15840,8 @@ static int handle_response_register(struct sip_pvt *p, int resp, char *rest, str
case 479: /* SER: Not able to process the URI - address is wrong in register*/
ast_log(LOG_WARNING, "Got error 479 on register to %s@%s, giving up (check config)\n", p->registry->username, p->registry->hostname);
p->needdestroy = 1;
- r->call = NULL;
+ if (r->call)
+ r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 479");
r->regstate = REG_STATE_REJECTED;
AST_SCHED_DEL(sched, r->timeout);
break;
@@ -15049,7 +15851,7 @@ static int handle_response_register(struct sip_pvt *p, int resp, char *rest, str
p->needdestroy = 1;
return 0;
}
-
+
r->regstate = REG_STATE_REGISTERED;
r->regtime = ast_tvnow(); /* Reset time of last succesful registration */
manager_event(EVENT_FLAG_SYSTEM, "Registry", "ChannelType: SIP\r\nDomain: %s\r\nStatus: %s\r\n", r->hostname, regstate2str(r->regstate));
@@ -15059,19 +15861,21 @@ static int handle_response_register(struct sip_pvt *p, int resp, char *rest, str
ast_debug(1, "Cancelling timeout %d\n", r->timeout);
}
AST_SCHED_DEL(sched, r->timeout);
- r->call = NULL;
- p->registry = NULL;
+ if (r->call)
+ r->call = dialog_unref(r->call, "unsetting registry->call pointer-- case 200");
+ p->registry = registry_unref(p->registry, "unref registry entry p->registry");
/* Let this one hang around until we have all the responses */
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
/* p->needdestroy = 1; */
-
+
/* set us up for re-registering */
/* figure out how long we got registered for */
AST_SCHED_DEL(sched, r->expire);
+
/* according to section 6.13 of RFC, contact headers override
expires headers, so check those first */
expires = 0;
-
+
/* XXX todo: try to save the extra call */
if (!ast_strlen_zero(get_header(req, "Contact"))) {
const char *contact = NULL;
@@ -15093,13 +15897,13 @@ static int handle_response_register(struct sip_pvt *p, int resp, char *rest, str
if (sscanf(tmptmp + 8, "%d;", &expires) != 1)
expires = 0;
}
-
+
}
if (!expires)
expires=atoi(get_header(req, "expires"));
if (!expires)
expires=default_expiry;
-
+
expires_ms = expires * 1000;
if (expires <= EXPIRY_GUARD_LIMIT)
expires_ms -= MAX((expires_ms * EXPIRY_GUARD_PCT), EXPIRY_GUARD_MIN);
@@ -15107,12 +15911,17 @@ static int handle_response_register(struct sip_pvt *p, int resp, char *rest, str
expires_ms -= EXPIRY_GUARD_SECS * 1000;
if (sipdebug)
ast_log(LOG_NOTICE, "Outbound Registration: Expiry for %s is %d sec (Scheduling reregistration in %d s)\n", r->hostname, expires, expires_ms/1000);
-
+
r->refresh= (int) expires_ms / 1000;
-
+
/* Schedule re-registration before we expire */
- AST_SCHED_REPLACE(r->expire, sched, expires_ms, sip_reregister, r);
- registry_unref(r);
+ AST_SCHED_REPLACE_UNREF(r->expire, sched, expires_ms, sip_reregister, r,
+ registry_unref(_data,"unref in REPLACE del fail"),
+ registry_unref(r,"unref in REPLACE add fail"),
+ registry_addref(r,"The Addition side of REPLACE"));
+ /* it is clear that we would not want to destroy the registry entry if we just
+ scheduled a callback and recorded it in there! */
+ /* since we never bumped the count, we shouldn't decrement it! registry_unref(r, "unref registry ptr r"); if this gets deleted, p->registry will be a bad pointer! */
}
return 1;
}
@@ -15120,7 +15929,7 @@ static int handle_response_register(struct sip_pvt *p, int resp, char *rest, str
/*! \brief Handle qualification responses (OPTIONS) */
static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_request *req)
{
- struct sip_peer *peer = p->relatedpeer;
+ struct sip_peer *peer = /* ref_peer( */ p->relatedpeer /* , "bump refcount on p, as it is being used in this function(handle_response_peerpoke)")*/ ; /* hope this is already refcounted! */
int statechanged, is_reachable, was_reachable;
int pingtime = ast_tvdiff_ms(ast_tvnow(), peer->ps);
@@ -15142,7 +15951,7 @@ static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_req
|| was_reachable != is_reachable;
peer->lastms = pingtime;
- peer->call = dialog_unref(peer->call);
+ peer->call = dialog_unref(peer->call, "unref dialog peer->call");
if (statechanged) {
const char *s = is_reachable ? "Reachable" : "Lagged";
@@ -15162,6 +15971,7 @@ static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_req
AST_SCHED_REPLACE(peer->pokeexpire, sched,
is_reachable ? peer->qualifyfreq : DEFAULT_FREQ_NOTOK,
sip_poke_peer_s, peer);
+ /* unref_peer(peer, "unref relatedpeer ptr var at end of handle_response_peerpoke"); */
}
/*! \brief Immediately stop RTP, VRTP and UDPTL as applicable */
@@ -16192,7 +17002,7 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, in
sip_pvt_unlock(p); /* Unlock SIP structure */
/* The call should be down with no ast_channel, so hang it up */
- c->tech_pvt = dialog_unref(c->tech_pvt);
+ c->tech_pvt = dialog_unref(c->tech_pvt, "unref dialog c->tech_pvt");
ast_hangup(c);
return 0;
}
@@ -16347,7 +17157,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
if (p->refer->refer_call == p) {
ast_log(LOG_NOTICE, "INVITE with replaces into it's own call id (%s == %s)!\n", replace_id, p->callid);
- p->refer->refer_call = dialog_unref(p->refer->refer_call);
+ p->refer->refer_call = dialog_unref(p->refer->refer_call, "unref dialog p->refer->refer_call");
transmit_response(p, "400 Bad request", req); /* The best way to not not accept the transfer */
error = 1;
}
@@ -16899,6 +17709,8 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *
transferer->refer->status = REFER_FAILED;
sip_pvt_unlock(targetcall_pvt);
ast_channel_unlock(current->chan1);
+ if (targetcall_pvt)
+ ao2_t_ref(targetcall_pvt, -1, "Drop targetcall_pvt pointer");
return -1;
}
@@ -16969,6 +17781,8 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *
ast_channel_unlock(targetcall_pvt->owner);
}
}
+ if (targetcall_pvt)
+ ao2_t_ref(targetcall_pvt, -1, "drop targetcall_pvt");
return 1;
}
@@ -17449,8 +18263,9 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
struct ast_channel *bridged_to;
/* If we have an INCOMING invite that we haven't answered, terminate that transaction */
- if (p->pendinginvite && !ast_test_flag(&p->flags[0], SIP_OUTGOING) && !req->ignore && !p->owner)
+ if (p->pendinginvite && !ast_test_flag(&p->flags[0], SIP_OUTGOING) && !req->ignore && !p->owner) {
transmit_response_reliable(p, "487 Request Terminated", &p->initreq);
+ }
p->invitestate = INV_TERMINATED;
@@ -17516,7 +18331,7 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
ast_debug(3, "Received bye, issuing owner hangup\n");
} else {
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
- ast_debug(3, "Received bye, no owner, selfdestruct soon.\n");
+ ast_debug(3, "Received bye, no owner, selfdestruct soon.\n");
}
transmit_response(p, "200 OK", req);
@@ -17558,6 +18373,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
const char *accept = get_header(req, "Accept");
int resubscribe = (p->subscribed != NONE);
char *temp, *event;
+ struct ao2_iterator i;
if (p->initreq.headers) {
/* We already have a dialog */
@@ -17652,7 +18468,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
transmit_response(p, "403 Forbidden (policy)", req);
p->needdestroy = 1;
if (authpeer)
- unref_peer(authpeer);
+ unref_peer(authpeer, "unref_peer, from handle_request_subscribe (authpeer 1)");
return 0;
}
@@ -17667,7 +18483,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
transmit_response(p, "404 Not Found", req);
p->needdestroy = 1;
if (authpeer)
- unref_peer(authpeer);
+ unref_peer(authpeer, "unref_peer, from handle_request_subscribe (authpeer 2)");
return 0;
}
@@ -17677,7 +18493,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
if (!strcmp(event, "presence") || !strcmp(event, "dialog")) { /* Presence, RFC 3842 */
if (authpeer) /* We do not need the authpeer any more */
- unref_peer(authpeer);
+ unref_peer(authpeer, "unref_peer, from handle_request_subscribe (authpeer 2)");
/* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */
/* Polycom phones only handle xpidf+xml, even if they say they can
@@ -17723,7 +18539,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
ast_debug(2, "Received SIP mailbox subscription for unknown format: %s\n", accept);
p->needdestroy = 1;
if (authpeer)
- unref_peer(authpeer);
+ unref_peer(authpeer, "unref_peer, from handle_request_subscribe (authpeer 3)");
return 0;
}
/* Looks like they actually want a mailbox status
@@ -17736,7 +18552,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
p->needdestroy = 1;
ast_log(LOG_NOTICE, "Received SIP subscribe for peer without mailbox: %s\n", authpeer->name);
if (authpeer)
- unref_peer(authpeer);
+ unref_peer(authpeer, "unref_peer, from handle_request_subscribe (authpeer 4)");
return 0;
}
@@ -17744,26 +18560,36 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
if (ast_test_flag(&authpeer->flags[1], SIP_PAGE2_SUBSCRIBEMWIONLY)) {
add_peer_mwi_subs(authpeer);
}
- if (authpeer->mwipvt && authpeer->mwipvt != p) /* Destroy old PVT if this is a new one */
+ if (authpeer->mwipvt && authpeer->mwipvt != p) { /* Destroy old PVT if this is a new one */
/* We only allow one subscription per peer */
- sip_destroy(authpeer->mwipvt);
- authpeer->mwipvt = p; /* Link from peer to pvt */
- p->relatedpeer = authpeer; /* Link from pvt to peer */
+ dialog_unlink_all(authpeer->mwipvt, TRUE, TRUE);
+ authpeer->mwipvt = dialog_unref(authpeer->mwipvt, "unref dialog authpeer->mwipvt");
+ /* sip_destroy(authpeer->mwipvt); */
+ }
+ if (authpeer->mwipvt)
+ dialog_unref(authpeer->mwipvt, "Unref previously stored mwipvt dialog pointer");
+ authpeer->mwipvt = dialog_ref(p, "setting peers' mwipvt to p"); /* Link from peer to pvt UH- should this be dialog_ref()? */
+ if (p->relatedpeer)
+ unref_peer(p->relatedpeer, "Unref previously stored relatedpeer ptr");
+ p->relatedpeer = ref_peer(authpeer, "setting dialog's relatedpeer pointer"); /* already refcounted...Link from pvt to peer UH- should this be dialog_ref()? */
/* Do not release authpeer here */
} else { /* At this point, Asterisk does not understand the specified event */
transmit_response(p, "489 Bad Event", req);
ast_debug(2, "Received SIP subscribe for unknown event package: %s\n", event);
p->needdestroy = 1;
if (authpeer)
- unref_peer(authpeer);
+ unref_peer(authpeer, "unref_peer, from handle_request_subscribe (authpeer 5)");
return 0;
}
/* Add subscription for extension state from the PBX core */
if (p->subscribed != MWI_NOTIFICATION && !resubscribe) {
- if (p->stateid > -1)
+ if (p->stateid > -1) {
ast_extension_state_del(p->stateid, cb_extensionstate);
- p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, p);
+ /* we need to dec the refcount, now that the extensionstate is removed */
+ dialog_unref(p, "the extensionstate containing this dialog ptr was deleted");
+ }
+ p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, dialog_ref(p,"copying dialog ptr into extension state struct"));
}
if (!req->ignore && p)
@@ -17791,9 +18617,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
if (p->subscribed == MWI_NOTIFICATION) {
transmit_response(p, "200 OK", req);
if (p->relatedpeer) { /* Send first notification */
- ASTOBJ_WRLOCK(p->relatedpeer);
+ ao2_lock(p->relatedpeer); /* was WRLOCK */
sip_send_mwi_to_peer(p->relatedpeer, NULL, 0);
- ASTOBJ_UNLOCK(p->relatedpeer);
+ ao2_unlock(p->relatedpeer);
}
} else {
struct sip_pvt *p_old;
@@ -17817,26 +18643,34 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
for it to expire and send NOTIFY messages to the peer only to have them
ignored (or generate errors)
*/
- dialoglist_lock();
- for (p_old = dialoglist; p_old; p_old = p_old->next) {
- if (p_old == p)
+ i = ao2_iterator_init(dialogs, 0);
+
+ while ((p_old = ao2_t_iterator_next(&i, "iterate thru dialogs"))) {
+ if (p_old == p) {
+ ao2_t_ref(p_old, -1, "toss dialog ptr from iterator_next before continue");
continue;
- if (p_old->initreq.method != SIP_SUBSCRIBE)
+ }
+ if (p_old->initreq.method != SIP_SUBSCRIBE) {
+ ao2_t_ref(p_old, -1, "toss dialog ptr from iterator_next before continue");
continue;
- if (p_old->subscribed == NONE)
+ }
+ if (p_old->subscribed == NONE) {
+ ao2_t_ref(p_old, -1, "toss dialog ptr from iterator_next before continue");
continue;
+ }
sip_pvt_lock(p_old);
if (!strcmp(p_old->username, p->username)) {
if (!strcmp(p_old->exten, p->exten) &&
!strcmp(p_old->context, p->context)) {
p_old->needdestroy = 1;
sip_pvt_unlock(p_old);
+ ao2_t_ref(p_old, -1, "toss dialog ptr from iterator_next before break");
break;
}
}
sip_pvt_unlock(p_old);
+ ao2_t_ref(p_old, -1, "toss dialog ptr from iterator_next");
}
- dialoglist_unlock();
}
if (!p->expiry)
p->needdestroy = 1;
@@ -18213,6 +19047,8 @@ static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin)
break; /* locking succeeded */
ast_debug(1, "Failed to grab owner channel lock, trying again. (SIP call %s)\n", p->callid);
sip_pvt_unlock(p);
+ if (lockretry != 1)
+ ao2_t_ref(p, -1, "release p (from find_call) inside lockretry loop"); /* we'll look for it again, but p is dead now */
ast_mutex_unlock(&netlock);
/* Sleep for a very short amount of time */
usleep(1);
@@ -18230,6 +19066,7 @@ static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin)
transmit_response(p, "503 Server error", req); /* We must respond according to RFC 3261 sec 12.2 */
/* XXX We could add retry-after to make sure they come back */
append_history(p, "LockFail", "Owner lock failed, transaction failed.");
+ ao2_t_ref(p, -1, "release p (from find_call) at end of lockretry"); /* p is gone after the return */
return 1;
}
@@ -18246,7 +19083,7 @@ static int handle_request_do(struct sip_request *req, struct sockaddr_in *sin)
ast_channel_unlock(p->owner);
sip_pvt_unlock(p);
ast_mutex_unlock(&netlock);
-
+ ao2_t_ref(p, -1, "throw away dialog ptr from find_call at end of routine"); /* p is gone after the return */
return 1;
}
@@ -18391,38 +19228,44 @@ static int sip_send_mwi_to_peer(struct sip_peer *peer, const struct ast_event *e
if (peer->mwipvt) {
/* Base message on subscription */
- p = dialog_ref(peer->mwipvt);
+ p = dialog_ref(peer->mwipvt, "sip_send_mwi_to_peer: Setting dialog ptr p from peer->mwipvt-- should this be done?");
} else {
/* Build temporary dialog for this message */
if (!(p = sip_alloc(NULL, NULL, 0, SIP_NOTIFY)))
return -1;
if (create_addr_from_peer(p, peer)) {
/* Maybe they're not registered, etc. */
- sip_destroy(p);
+ dialog_unlink_all(p, TRUE, TRUE);
+ dialog_unref(p, "unref dialog p just created via sip_alloc");
+ /* sip_destroy(p); */
return 0;
}
/* Recalculate our side, and recalculate Call ID */
ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
build_via(p);
+ ao2_t_unlink(dialogs, p, "About to change the callid -- remove the old name");
build_callid_pvt(p);
+ ao2_t_link(dialogs, p, "Linking in under new name");
/* Destroy this session after 32 secs */
sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
}
/* Send MWI */
ast_set_flag(&p->flags[0], SIP_OUTGOING);
+ /* the following will decrement the refcount on p as it finishes */
transmit_notify_with_mwi(p, newmsgs, oldmsgs, peer->vmexten);
-
+ dialog_unref(p, "unref dialog ptr p just before it goes out of scope at the end of sip_send_mwi_to_peer.");
return 0;
}
-/*! \brief helper function for the monitoring thread */
+/*! \brief helper function for the monitoring thread -- seems to be called with the assumption that the dialog is locked */
static void check_rtp_timeout(struct sip_pvt *dialog, time_t t)
{
/* If we have no RTP or no active owner, no need to check timers */
if (!dialog->rtp || !dialog->owner)
return;
/* If the call is not in UP state or redirected outside Asterisk, no need to check timers */
+
if (dialog->owner->_state != AST_STATE_UP || dialog->redirip.sin_addr.s_addr)
return;
@@ -18491,14 +19334,13 @@ static void check_rtp_timeout(struct sip_pvt *dialog, time_t t)
static void *do_monitor(void *data)
{
int res;
- struct sip_pvt *dialog;
time_t t;
int reloading;
/* Add an I/O event to our SIP UDP socket */
if (sipsock > -1)
sipsock_read_id = ast_io_add(io, sipsock, sipsock_read, AST_IO_IN, NULL);
-
+
/* From here on out, we die whenever asked */
for(;;) {
/* Check for a reload request */
@@ -18522,36 +19364,17 @@ static void *do_monitor(void *data)
}
}
-restartsearch:
/* Check for dialogs needing to be killed */
- dialoglist_lock();
t = time(NULL);
/* don't scan the dialogs list if it hasn't been a reasonable period
of time since the last time we did it (when MWI is being sent, we can
get back to this point every millisecond or less)
*/
- for (dialog = dialoglist; dialog; dialog = dialog->next) {
- if (sip_pvt_trylock(dialog)) {
- dialoglist_unlock();
- usleep(1);
- goto restartsearch;
- }
+ ao2_t_callback(dialogs, OBJ_UNLINK|OBJ_NODATA, dialog_needdestroy, &t, "callback to remove dialogs w/needdestroy");
- /* Check RTP timeouts and kill calls if we have a timeout set and do not get RTP */
- check_rtp_timeout(dialog, t);
- /* If we have sessions that needs to be destroyed, do it now */
- /* Check if we have outstanding requests not responsed to or an active call
- - if that's the case, wait with destruction */
- if (dialog->needdestroy && !dialog->packets && !dialog->owner) {
- sip_pvt_unlock(dialog);
- __sip_destroy(dialog, TRUE, FALSE);
- dialoglist_unlock();
- usleep(1);
- goto restartsearch;
- }
- sip_pvt_unlock(dialog);
- }
- dialoglist_unlock();
+ /* the old methodology would be to restart the search for dialogs to delete with every
+ dialog that was found and destroyed, probably because the list contents would change,
+ so we'd need to restart. This isn't the best thing to do with callbacks. */
pthread_testcancel();
/* Wait for sched or io */
@@ -18611,10 +19434,7 @@ static void restart_session_timer(struct sip_pvt *p)
}
if (p->stimer->st_active == TRUE) {
- if (ast_sched_del(sched, p->stimer->st_schedid) != 0) {
- ast_log(LOG_WARNING, "ast_sched_del failed: %d - %s\n", p->stimer->st_schedid, p->callid);
- }
-
+ AST_SCHED_DEL(sched, p->stimer->st_schedid);
ast_debug(2, "Session timer stopped: %d - %s\n", p->stimer->st_schedid, p->callid);
start_session_timer(p);
}
@@ -18631,7 +19451,7 @@ static void stop_session_timer(struct sip_pvt *p)
if (p->stimer->st_active == TRUE) {
p->stimer->st_active = FALSE;
- ast_sched_del(sched, p->stimer->st_schedid);
+ AST_SCHED_DEL(sched, p->stimer->st_schedid);
ast_debug(2, "Session timer stopped: %d - %s\n", p->stimer->st_schedid, p->callid);
}
}
@@ -18829,6 +19649,7 @@ int st_get_se(struct sip_pvt *p, int max)
struct sip_user *up = find_user(p->username, 1);
if (up) {
p->stimer->st_cached_max_se = up->stimer.st_max_se;
+ unref_user(up, "unref user pointer from find_user call in st_get_se");
return (p->stimer->st_cached_max_se);
}
}
@@ -18836,6 +19657,7 @@ int st_get_se(struct sip_pvt *p, int max)
struct sip_peer *pp = find_peer(p->peername, NULL, 1);
if (pp) {
p->stimer->st_cached_max_se = pp->stimer.st_max_se;
+ unref_peer(pp, "unref peer pointer from find_peer call in st_get_se");
return (p->stimer->st_cached_max_se);
}
}
@@ -18850,6 +19672,7 @@ int st_get_se(struct sip_pvt *p, int max)
struct sip_user *up = find_user(p->username, 1);
if (up) {
p->stimer->st_cached_min_se = up->stimer.st_min_se;
+ unref_user(up, "unref user pointer from find_user call in st_get_se (2)");
return (p->stimer->st_cached_min_se);
}
}
@@ -18857,6 +19680,7 @@ int st_get_se(struct sip_pvt *p, int max)
struct sip_peer *pp = find_peer(p->peername, NULL, 1);
if (pp) {
p->stimer->st_cached_min_se = pp->stimer.st_min_se;
+ unref_peer(pp, "unref peer pointer from find_peer call in st_get_se (2)");
return (p->stimer->st_cached_min_se);
}
}
@@ -18879,6 +19703,7 @@ enum st_refresher st_get_refresher(struct sip_pvt *p)
struct sip_user *up = find_user(p->username, 1);
if (up) {
p->stimer->st_cached_ref = up->stimer.st_ref;
+ unref_user(up, "unref user pointer from find_user call in st_get_refresher");
return up->stimer.st_ref;
}
}
@@ -18887,6 +19712,7 @@ enum st_refresher st_get_refresher(struct sip_pvt *p)
struct sip_peer *pp = find_peer(p->peername, NULL, 1);
if (pp) {
p->stimer->st_cached_ref = pp->stimer.st_ref;
+ unref_peer(pp, "unref peer pointer from find_peer call in st_get_refresher");
return pp->stimer.st_ref;
}
}
@@ -18911,6 +19737,7 @@ enum st_mode st_get_mode(struct sip_pvt *p)
struct sip_user *up = find_user(p->username, 1);
if (up) {
p->stimer->st_cached_mode = up->stimer.st_mode_oper;
+ unref_user(up, "unref user pointer from find_user call in st_get_mode");
return up->stimer.st_mode_oper;
}
}
@@ -18918,6 +19745,7 @@ enum st_mode st_get_mode(struct sip_pvt *p)
struct sip_peer *pp = find_peer(p->peername, NULL, 1);
if (pp) {
p->stimer->st_cached_mode = pp->stimer.st_mode_oper;
+ unref_peer(pp, "unref peer pointer from find_peer call in st_get_mode");
return pp->stimer.st_mode_oper;
}
}
@@ -18939,8 +19767,12 @@ static int sip_poke_noanswer(const void *data)
if (global_regextenonqualify)
register_peer_exten(peer, FALSE);
}
- if (peer->call)
- peer->call = sip_destroy(peer->call);
+ if (peer->call) {
+ dialog_unlink_all(peer->call, TRUE, TRUE);
+ peer->call = dialog_unref(peer->call, "unref dialog peer->call");
+ /* peer->call = sip_destroy(peer->call);*/
+ }
+
peer->lastms = -1;
ast_device_state_changed("SIP/%s", peer->name);
/* Try again quickly */
@@ -18956,22 +19788,27 @@ static int sip_poke_peer(struct sip_peer *peer)
{
struct sip_pvt *p;
int xmitres = 0;
-
+
if (!peer->maxms || !peer->addr.sin_addr.s_addr) {
/* IF we have no IP, or this isn't to be monitored, return
immediately after clearing things out */
AST_SCHED_DEL(sched, peer->pokeexpire);
+
peer->lastms = 0;
- peer->call = NULL;
+ if (peer->call)
+ peer->call = dialog_unref(peer->call, "unref dialog peer->call");
return 0;
}
if (peer->call) {
if (sipdebug)
ast_log(LOG_NOTICE, "Still have a QUALIFY dialog active, deleting\n");
- peer->call = sip_destroy(peer->call);
+ dialog_unlink_all(peer->call, TRUE, TRUE);
+ peer->call = dialog_unref(peer->call, "unref dialog peer->call");
+ /* peer->call = sip_destroy(peer->call); */
}
- if (!(p = peer->call = sip_alloc(NULL, NULL, 0, SIP_OPTIONS)))
+ if (!(p = sip_alloc(NULL, NULL, 0, SIP_OPTIONS)))
return -1;
+ peer->call = dialog_ref(p, "copy sip alloc from p to peer->call");
p->sa = peer->addr;
p->recv = peer->addr;
@@ -18991,16 +19828,21 @@ static int sip_poke_peer(struct sip_peer *peer)
/* Recalculate our side, and recalculate Call ID */
ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
build_via(p);
+ ao2_t_unlink(dialogs, p, "About to change the callid -- remove the old name");
build_callid_pvt(p);
+ ao2_t_link(dialogs, p, "Linking in under new name");
AST_SCHED_DEL(sched, peer->pokeexpire);
- p->relatedpeer = peer;
+
+ if (p->relatedpeer)
+ p->relatedpeer = unref_peer(p->relatedpeer,"unsetting the relatedpeer field in the dialog, before it is set to something else.");
+ p->relatedpeer = ref_peer(peer, "setting the relatedpeer field in the dialog");
ast_set_flag(&p->flags[0], SIP_OUTGOING);
#ifdef VOCAL_DATA_HACK
ast_copy_string(p->username, "__VOCAL_DATA_SHOULD_READ_THE_SIP_SPEC__", sizeof(p->username));
- xmitres = transmit_invite(p, SIP_INVITE, 0, 2);
+ xmitres = transmit_invite(p, SIP_INVITE, 0, 2); /* sinks the p refcount */
#else
- xmitres = transmit_invite(p, SIP_OPTIONS, 0, 2);
+ xmitres = transmit_invite(p, SIP_OPTIONS, 0, 2); /* sinks the p refcount */
#endif
peer->ps = ast_tvnow();
if (xmitres == XMIT_ERROR)
@@ -19009,7 +19851,7 @@ static int sip_poke_peer(struct sip_peer *peer)
AST_SCHED_REPLACE(peer->pokeexpire, sched,
peer->maxms * 2, sip_poke_noanswer, peer);
}
-
+ dialog_unref(p, "unref dialog at end of sip_poke_peer, obtained from sip_alloc, just before it goes out of scope");
return 0;
}
@@ -19100,7 +19942,7 @@ static int sip_devicestate(void *data)
/* there is no address, it's unavailable */
res = AST_DEVICE_UNAVAILABLE;
}
- unref_peer(p);
+ unref_peer(p, "unref_peer, from sip_devicestate, release ref from find_peer");
} else {
res = AST_DEVICE_UNKNOWN;
}
@@ -19155,7 +19997,9 @@ static struct ast_channel *sip_request_call(const char *type, int format, void *
p->outgoing_call = TRUE;
if (!(p->options = ast_calloc(1, sizeof(*p->options)))) {
- sip_destroy(p);
+ dialog_unlink_all(p, TRUE, TRUE);
+ dialog_unref(p, "unref dialog p from mem fail");
+ /* sip_destroy(p); */
ast_log(LOG_ERROR, "Unable to build option SIP data structure - Out of memory\n");
*cause = AST_CAUSE_SWITCH_CONGESTION;
return NULL;
@@ -19203,7 +20047,9 @@ static struct ast_channel *sip_request_call(const char *type, int format, void *
if (create_addr(p, host, NULL)) {
*cause = AST_CAUSE_UNREGISTERED;
ast_debug(3, "Cant create SIP call - target device not registred\n");
- sip_destroy(p);
+ dialog_unlink_all(p, TRUE, TRUE);
+ dialog_unref(p, "unref dialog p UNREGISTERED");
+ /* sip_destroy(p); */
return NULL;
}
if (ast_strlen_zero(p->peername) && ext)
@@ -19211,7 +20057,9 @@ static struct ast_channel *sip_request_call(const char *type, int format, void *
/* Recalculate our side, and recalculate Call ID */
ast_sip_ouraddrfor(&p->sa.sin_addr, &p->ourip);
build_via(p);
+ ao2_t_unlink(dialogs, p, "About to change the callid -- remove the old name");
build_callid_pvt(p);
+ ao2_t_link(dialogs, p, "Linking in under new name");
/* We have an extension to call, don't use the full contact here */
/* This to enable dialing registered peers with extension dialling,
@@ -19242,8 +20090,11 @@ static struct ast_channel *sip_request_call(const char *type, int format, void *
"Channel: %s\r\nChanneltype: %s\r\nSIPcallid: %s\r\nSIPfullcontact: %s\r\nPeername: %s\r\n",
p->owner? p->owner->name : "", "SIP", p->callid, p->fullcontact, p->peername);
sip_pvt_unlock(p);
- if (!tmpc)
- sip_destroy(p);
+ if (!tmpc) {
+ dialog_unlink_all(p, TRUE, TRUE);
+ /* sip_destroy(p); */
+ }
+ dialog_unref(p, "toss pvt ptr at end of sip_request_call");
ast_update_use_count();
restart_monitor();
return tmpc;
@@ -19566,18 +20417,14 @@ static struct sip_user *build_user(const char *name, struct ast_variable *v, str
{
struct sip_user *user;
int format;
- struct ast_ha *oldha = NULL;
struct ast_flags userflags[2] = {{(0)}};
struct ast_flags mask[2] = {{(0)}};
-
- if (!(user = ast_calloc(1, sizeof(*user))))
+ if (!(user = ao2_t_alloc(sizeof(*user), sip_destroy_user_fn, "allocate a user struct")))
return NULL;
- suserobjs++;
- ASTOBJ_INIT(user);
+ ast_atomic_fetchadd_int(&suserobjs, 1);
ast_copy_string(user->name, name, sizeof(user->name));
- oldha = user->ha;
user->ha = NULL;
ast_copy_flags(&user->flags[0], &global_flags[0], SIP_FLAGS_TO_COPY);
ast_copy_flags(&user->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
@@ -19710,7 +20557,6 @@ static struct sip_user *build_user(const char *name, struct ast_variable *v, str
ast_copy_flags(&user->flags[1], &userflags[1], mask[1].flags);
if (ast_test_flag(&user->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE))
global_allowsubscribe = TRUE; /* No global ban any more */
- ast_free_ha(oldha);
return user;
}
@@ -19772,11 +20618,10 @@ static struct sip_peer *temp_peer(const char *name)
{
struct sip_peer *peer;
- if (!(peer = ast_calloc(1, sizeof(*peer))))
+ if (!(peer = ao2_t_alloc(sizeof(*peer), sip_destroy_peer_fn, "allocate a peer struct")))
return NULL;
- apeerobjs++;
- ASTOBJ_INIT(peer);
+ ast_atomic_fetchadd_int(&apeerobjs, 1);
set_peer_defaults(peer);
ast_copy_string(peer->name, name, sizeof(peer->name));
@@ -19826,34 +20671,36 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
struct ast_flags peerflags[2] = {{(0)}};
struct ast_flags mask[2] = {{(0)}};
char callback[256] = "";
+ struct sip_peer tmp_peer;
const char *srvlookup = NULL;
static int deprecation_warning = 1;
-
- if (!realtime)
+
+ if (!realtime) {
/* Note we do NOT use find_peer here, to avoid realtime recursion */
/* We also use a case-sensitive comparison (unlike find_peer) so
that case changes made to the peer name will be properly handled
during reload
*/
- peer = ASTOBJ_CONTAINER_FIND_UNLINK_FULL(&peerl, name, name, 0, 0, strcmp);
-
+ ast_copy_string(tmp_peer.name, name, sizeof(tmp_peer.name));
+ peer = ao2_t_find(peers, &tmp_peer, OBJ_POINTER|OBJ_UNLINK, "find and unlink peer from peers table");
+ }
+
if (peer) {
/* Already in the list, remove it and it will be added back (or FREE'd) */
found++;
- if (!(peer->objflags & ASTOBJ_FLAG_MARKED))
+ if (!(peer->the_mark))
firstpass = 0;
} else {
- if (!(peer = ast_calloc(1, sizeof(*peer))))
+ if (!(peer = ao2_t_alloc(sizeof(*peer), sip_destroy_peer_fn, "allocate a peer struct")))
return NULL;
if (realtime) {
- rpeerobjs++;
+ ast_atomic_fetchadd_int(&rpeerobjs, 1);
ast_debug(3, "-REALTIME- peer built. Name: %s. Peer objects: %d\n", name, rpeerobjs);
} else
- speerobjs++;
- ASTOBJ_INIT(peer);
+ ast_atomic_fetchadd_int(&speerobjs, 1);
}
- /* Note that our peer HAS had its reference count incrased */
+ /* Note that our peer HAS had its reference count increased */
if (firstpass) {
peer->lastmsgssent = -1;
oldha = peer->ha;
@@ -19952,7 +20799,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
}
} else if (!strcasecmp(v->name, "defaultip")) {
if (ast_get_ip(&peer->defaddr, v->value)) {
- unref_peer(peer);
+ unref_peer(peer, "unref_peer: from build_peer defaultip");
return NULL;
}
} else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
@@ -20118,7 +20965,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
snprintf(transport, sizeof(transport), "_sip._%s", get_transport(peer->socket.type));
if (ast_dnsmgr_lookup(srvlookup, &peer->addr, &peer->dnsmgr, global_srvlookup ? transport : NULL)) {
- unref_peer(peer);
+ unref_peer(peer, "getting rid of a peer pointer");
return NULL;
}
@@ -20157,8 +21004,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
* way, then we will get events when app_voicemail gets loaded. */
sip_send_mwi_to_peer(peer, NULL, 1);
}
-
- ASTOBJ_UNMARK(peer);
+ peer->the_mark = 0;
ast_free_ha(oldha);
if (!ast_strlen_zero(callback)) { /* build string from peer info */
@@ -20173,6 +21019,13 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
return peer;
}
+static int peer_markall_func(void *userobj, void *arg, int flags)
+{
+ struct sip_peer *peer = userobj;
+ peer->the_mark = 1;
+ return 0;
+}
+
/*! \brief Re-read SIP.conf config file
\note This function reloads all config data, except for
active peers (with registrations). They will only
@@ -20192,7 +21045,10 @@ static int reload_config(enum channelreloadreason reason)
int auto_sip_domains = FALSE;
struct sockaddr_in old_bindaddr = bindaddr;
int registry_count = 0, peer_count = 0, user_count = 0;
-
+ time_t run_start, run_end;
+
+ run_start = time(0);
+
cfg = ast_config_load(config, config_flags);
/* We *must* have a config file otherwise stop immediately */
@@ -20229,25 +21085,34 @@ static int reload_config(enum channelreloadreason reason)
/* First, destroy all outstanding registry calls */
/* This is needed, since otherwise active registry entries will not be destroyed */
- ASTOBJ_CONTAINER_TRAVERSE(&regl, 1, do {
- ASTOBJ_RDLOCK(iterator);
- if (iterator->call) {
- ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", iterator->username, iterator->hostname);
- /* This will also remove references to the registry */
- iterator->call = sip_destroy(iterator->call);
- }
- ASTOBJ_UNLOCK(iterator);
-
+ ASTOBJ_CONTAINER_TRAVERSE(&regl, 1, do { /* regl is locked */
+
+ /* avoid a deadlock in the unlink_all call, if iterator->call's (a dialog) registry entry
+ is this registry entry. In other words, if the dialog we are pointing to points back to
+ us, then if we get a lock on this object, and try to UNREF it, we will deadlock, because
+ we already ... NO. This is not the problem. */
+ ASTOBJ_RDLOCK(iterator); /* now regl is locked, and the object is also locked */
+ if (iterator->call) {
+ ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", iterator->username, iterator->hostname);
+ /* This will also remove references to the registry */
+ dialog_unlink_all(iterator->call, TRUE, TRUE);
+ iterator->call = dialog_unref(iterator->call, "remove iterator->call from registry traversal");
+ /* iterator->call = sip_destroy(iterator->call); */
+ }
+ ASTOBJ_UNLOCK(iterator);
+
} while(0));
/* Then, actually destroy users and registry */
- ASTOBJ_CONTAINER_DESTROYALL(&userl, sip_destroy_user);
+ ao2_t_ref(users, -1, "destroy users table");
ast_debug(4, "--------------- Done destroying user list\n");
ASTOBJ_CONTAINER_DESTROYALL(&regl, sip_registry_destroy);
ast_debug(4, "--------------- Done destroying registry list\n");
- ASTOBJ_CONTAINER_MARKALL(&peerl);
+ ao2_t_callback(peers, OBJ_NODATA, peer_markall_func, 0, "callback to mark all peers");
+ /* reinstate the user table */
+ users = ao2_t_container_alloc(hash_user_size, user_hash_cb, user_cmp_cb, "allocate users");
}
-
+
/* Reset certificate handling for TLS sessions */
default_tls_cfg.certfile = ast_strdup(AST_CERTFILE); /*XXX Not sure if this is useful */
default_tls_cfg.cipher = ast_strdup("");
@@ -20371,6 +21236,7 @@ static int reload_config(enum channelreloadreason reason)
ast_clear_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT);
ast_clear_flag(&global_flags[1], SIP_PAGE2_TEXTSUPPORT);
+
/* Read the [general] config section of sip.conf (or from realtime config) */
for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
if (handle_common_options(&global_flags[0], &dummy[0], v))
@@ -20650,6 +21516,27 @@ static int reload_config(enum channelreloadreason reason)
} else {
ast_log(LOG_WARNING, "Invalid port number '%s' at line %d of %s\n", v->value, v->lineno, config);
}
+ } else if (!strcasecmp(v->name, "hash_user")) {
+ int i;
+ if (sscanf(v->value, "%d", &i) == 1 && i > 2) {
+ hash_user_size = i;
+ } else {
+ ast_log(LOG_WARNING, "Invalid hash_user size '%s' at line %d of %s -- should be much larger than 2\n", v->value, v->lineno, config);
+ }
+ } else if (!strcasecmp(v->name, "hash_peer")) {
+ int i;
+ if (sscanf(v->value, "%d", &i) == 1 && i > 2) {
+ hash_peer_size = i;
+ } else {
+ ast_log(LOG_WARNING, "Invalid hash_peer size '%s' at line %d of %s -- should be much larger than 2\n", v->value, v->lineno, config);
+ }
+ } else if (!strcasecmp(v->name, "hash_dialog")) {
+ int i;
+ if (sscanf(v->value, "%d", &i) == 1 && i > 2) {
+ hash_dialog_size = i;
+ } else {
+ ast_log(LOG_WARNING, "Invalid hash_dialog size '%s' at line %d of %s -- should be much larger than 2\n", v->value, v->lineno, config);
+ }
} else if (!strcasecmp(v->name, "qualify")) {
if (!strcasecmp(v->value, "no")) {
default_qualify = 0;
@@ -20738,14 +21625,18 @@ static int reload_config(enum channelreloadreason reason)
if (ast_true(hassip) || (!hassip && genhassip)) {
user = build_user(cat, gen, ast_variable_browse(ucfg, cat), 0);
if (user) {
- ASTOBJ_CONTAINER_LINK(&userl,user);
- ASTOBJ_UNREF(user, sip_destroy_user);
+ ao2_t_link(users, user, "link user into users table");
+ unref_user(user, "Unref the result of build_user. Now, the table link is the only one left.");
user_count++;
}
peer = build_peer(cat, gen, ast_variable_browse(ucfg, cat), 0);
if (peer) {
- ASTOBJ_CONTAINER_LINK(&peerl, peer);
- unref_peer(peer);
+ ao2_t_link(peers, peer, "link peer into peer table");
+ if (peer->addr.sin_addr.s_addr) {
+ ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
+ }
+
+ unref_peer(peer, "unref_peer: from reload_config");
peer_count++;
}
}
@@ -20804,21 +21695,25 @@ static int reload_config(enum channelreloadreason reason)
if (is_user) {
user = build_user(cat, ast_variable_browse(cfg, cat), NULL, 0);
if (user) {
- ASTOBJ_CONTAINER_LINK(&userl, user);
- unref_user(user);
+ ao2_t_link(users, user, "link user into users table");
+ unref_user(user, "Unref the result of build_user. Now, the table link is the only one left.");
user_count++;
}
}
if (is_peer) {
peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0);
if (peer) {
- ASTOBJ_CONTAINER_LINK(&peerl, peer);
- unref_peer(peer);
+ ao2_t_link(peers, peer, "link peer into peers table");
+ if (peer->addr.sin_addr.s_addr) {
+ ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");
+ }
+ unref_peer(peer, "unref the result of the build_peer call. Now, the links from the tables are the only ones left.");
peer_count++;
}
}
}
}
+
bindaddr.sin_family = AF_INET;
internip = bindaddr;
if (ast_find_ourip(&internip.sin_addr, bindaddr)) {
@@ -20917,6 +21812,8 @@ static int reload_config(enum channelreloadreason reason)
/* Done, tell the manager */
manager_event(EVENT_FLAG_SYSTEM, "ChannelReload", "ChannelType: SIP\r\nReloadReason: %s\r\nRegistry_Count: %d\r\nPeer_Count: %d\r\nUser_Count: %d\r\n", channelreloadreason2txt(reason), registry_count, peer_count, user_count);
+ run_end = time(0);
+ ast_log(LOG_NOTICE, "reload_config done...Runtime= %d sec\n", (int)(run_end-run_start));
return 0;
}
@@ -21374,17 +22271,21 @@ static int sip_get_codec(struct ast_channel *chan)
static void sip_poke_all_peers(void)
{
int ms = 0;
+ struct ao2_iterator i;
+ struct sip_peer *peer;
+
+ i = ao2_iterator_init(peers, 0);
if (!speerobjs) /* No peers, just give up */
return;
- ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do {
- ASTOBJ_WRLOCK(iterator);
+ while ((peer = ao2_t_iterator_next(&i, "iterate thru peers table"))) {
+ ao2_lock(peer);
ms += 100;
- AST_SCHED_REPLACE(iterator->pokeexpire, sched, ms, sip_poke_peer_s, iterator);
- ASTOBJ_UNLOCK(iterator);
- } while (0)
- );
+ AST_SCHED_REPLACE(peer->pokeexpire, sched, ms, sip_poke_peer_s, peer);
+ ao2_unlock(peer);
+ unref_peer(peer, "toss iterator peer ptr");
+ }
}
/*! \brief Send all known registrations */
@@ -21401,8 +22302,10 @@ static void sip_send_all_registers(void)
ASTOBJ_CONTAINER_TRAVERSE(&regl, 1, do {
ASTOBJ_WRLOCK(iterator);
ms += regspacing;
- AST_SCHED_REPLACE(iterator->expire,
- sched, ms, sip_reregister, iterator);
+ AST_SCHED_REPLACE_UNREF(iterator->expire, sched, ms, sip_reregister, iterator,
+ registry_unref(_data, "REPLACE sched del decs the refcount"),
+ registry_unref(iterator, "REPLACE sched add failure decs the refcount"),
+ registry_addref(iterator, "REPLACE sched add incs the refcount"));
ASTOBJ_UNLOCK(iterator);
} while (0)
);
@@ -21411,10 +22314,15 @@ static void sip_send_all_registers(void)
/*! \brief Reload module */
static int sip_do_reload(enum channelreloadreason reason)
{
+ time_t start_poke, end_poke;
+
reload_config(reason);
+ ast_sched_dump(sched);
+ start_poke = time(0);
/* Prune peers who still are supposed to be deleted */
- ASTOBJ_CONTAINER_PRUNE_MARKED(&peerl, sip_destroy_peer);
+ ao2_t_callback(peers, OBJ_NODATA|OBJ_UNLINK, peer_is_marked, 0, "callback to remove marked peers");
+
ast_debug(4, "--------------- Done destroying pruned peers\n");
/* Send qualify (OPTIONS) to all peers */
@@ -21422,6 +22330,9 @@ static int sip_do_reload(enum channelreloadreason reason)
/* Register with all services */
sip_send_all_registers();
+ end_poke = time(0);
+
+ ast_log(LOG_NOTICE, "do_reload finished. peer poke/prune reg contact time = %d sec.\n", (int)(end_poke-start_poke));
ast_debug(4, "--------------- SIP reload done\n");
@@ -21472,6 +22383,7 @@ static struct ast_cli_entry cli_sip[] = {
AST_CLI_DEFINE(sip_show_inuse, "List all inuse/limits"),
AST_CLI_DEFINE(sip_show_objects, "List all SIP object allocations"),
AST_CLI_DEFINE(sip_show_peers, "List defined SIP peers"),
+ AST_CLI_DEFINE(sip_dbdump, "dump peer info into realtime db sql format"),
AST_CLI_DEFINE(sip_show_registry, "List SIP registration status"),
AST_CLI_DEFINE(sip_unregister, "Unregister (force expiration) a SIP peer from the registery\n"),
AST_CLI_DEFINE(sip_show_settings, "Show SIP global settings"),
@@ -21481,6 +22393,7 @@ static struct ast_cli_entry cli_sip[] = {
AST_CLI_DEFINE(sip_show_peer, "Show details on specific SIP peer"),
AST_CLI_DEFINE(sip_show_users, "List defined SIP users"),
AST_CLI_DEFINE(sip_show_user, "Show details on specific SIP user"),
+ AST_CLI_DEFINE(sip_show_sched, "Present a report on the status of the sched queue"),
AST_CLI_DEFINE(sip_prune_realtime, "Prune cached Realtime users/peers"),
AST_CLI_DEFINE(sip_do_debug, "Enable/Disable SIP debugging"),
AST_CLI_DEFINE(sip_set_history, "Enable/Disable SIP history", .deprecate_cmd = &cli_sip_do_history_deprecated),
@@ -21492,9 +22405,14 @@ static struct ast_cli_entry cli_sip[] = {
static int load_module(void)
{
ast_verbose("SIP channel loading...\n");
- ASTOBJ_CONTAINER_INIT(&userl); /* User object list */
- ASTOBJ_CONTAINER_INIT(&peerl); /* Peer object list */
- ASTOBJ_CONTAINER_INIT(&regl); /* Registry object list */
+ /* the fact that ao2_containers can't resize automatically is a major worry! */
+ /* if the number of objects gets above MAX_XXX_BUCKETS, things will slow down */
+ users = ao2_t_container_alloc(hash_user_size, user_hash_cb, user_cmp_cb, "allocate users");
+ peers = ao2_t_container_alloc(hash_peer_size, peer_hash_cb, peer_cmp_cb, "allocate peers");
+ peers_by_ip = ao2_t_container_alloc(hash_peer_size, peer_iphash_cb, peer_ipcmp_cb, "allocate peers_by_ip");
+ dialogs = ao2_t_container_alloc(hash_dialog_size, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs");
+
+ ASTOBJ_CONTAINER_INIT(&regl); /* Registry object list -- not searched for anything */
if (!(sched = sched_context_create())) {
ast_log(LOG_ERROR, "Unable to create scheduler context\n");
@@ -21565,9 +22483,12 @@ static int load_module(void)
/*! \brief PBX unload module API */
static int unload_module(void)
{
- struct sip_pvt *p, *pl;
+ struct sip_pvt *p;
struct sip_threadinfo *th;
struct ast_context *con;
+ struct ao2_iterator i;
+
+ ast_sched_dump(sched);
/* First, take us out of the channel type list */
ast_channel_unregister(&sip_tech);
@@ -21595,7 +22516,7 @@ static int unload_module(void)
ast_manager_unregister("SIPpeers");
ast_manager_unregister("SIPshowpeer");
ast_manager_unregister("SIPshowregistry");
-
+
/* Kill TCP/TLS server threads */
if (sip_tcp_desc.master)
ast_tcptls_server_stop(&sip_tcp_desc);
@@ -21615,13 +22536,13 @@ static int unload_module(void)
AST_LIST_TRAVERSE_SAFE_END;
AST_LIST_UNLOCK(&threadl);
- dialoglist_lock();
/* Hangup all dialogs if they have an owner */
- for (p = dialoglist; p ; p = p->next) {
+ i = ao2_iterator_init(dialogs, 0);
+ while ((p = ao2_t_iterator_next(&i, "iterate thru dialogs"))) {
if (p->owner)
ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
+ ao2_t_ref(p, -1, "toss dialog ptr from iterator_next");
}
- dialoglist_unlock();
ast_mutex_lock(&monlock);
if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) {
@@ -21632,27 +22553,19 @@ static int unload_module(void)
monitor_thread = AST_PTHREADT_STOP;
ast_mutex_unlock(&monlock);
-restartdestroy:
- dialoglist_lock();
/* Destroy all the dialogs and free their memory */
- p = dialoglist;
- while (p) {
- pl = p;
- p = p->next;
- if (__sip_destroy(pl, TRUE, TRUE) < 0) {
- /* Something is still bridged, let it react to getting a hangup */
- dialoglist = p;
- dialoglist_unlock();
- usleep(1);
- goto restartdestroy;
- }
+ i = ao2_iterator_init(dialogs, 0);
+ while ((p = ao2_t_iterator_next(&i, "iterate thru dialogs"))) {
+ dialog_unlink_all(p, TRUE, TRUE);
+ ao2_t_ref(p, -1, "throw away iterator result");
}
- dialoglist = NULL;
- dialoglist_unlock();
/* Free memory for local network address mask */
ast_free_ha(localaddr);
+ clear_realm_authentication(authl);
+
+
if (default_tls_cfg.certfile)
ast_free(default_tls_cfg.certfile);
if (default_tls_cfg.cipher)
@@ -21662,14 +22575,14 @@ restartdestroy:
if (default_tls_cfg.capath)
ast_free(default_tls_cfg.capath);
- ASTOBJ_CONTAINER_DESTROYALL(&userl, sip_destroy_user);
- ASTOBJ_CONTAINER_DESTROY(&userl);
- ASTOBJ_CONTAINER_DESTROYALL(&peerl, sip_destroy_peer);
- ASTOBJ_CONTAINER_DESTROY(&peerl);
ASTOBJ_CONTAINER_DESTROYALL(&regl, sip_registry_destroy);
ASTOBJ_CONTAINER_DESTROY(&regl);
- clear_realm_authentication(authl);
+ ao2_t_ref(peers, -1, "unref the peers table");
+ ao2_t_ref(peers_by_ip, -1, "unref the peers_by_ip table");
+ ao2_t_ref(users, -1, "unref the users table");
+ ao2_t_ref(dialogs, -1, "unref the dialogs table");
+
clear_sip_domains();
close(sipsock);
sched_context_destroy(sched);
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index 1080777aa..10293a77c 100644
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -67,7 +67,21 @@ context=default ; Default context for incoming calls
;match_auth_username=yes ; if available, match user entry using the
; 'username' field from the authentication line
; instead of the From: field.
-
+;;
+;; hash table sizes. For maximum efficiency, adjust the following
+;; values to be slightly larger than the maximum number of users/peers.
+;; Too large, and space is wasted. Too small, and things will run slower.
+;; 563 is probably way too big for small (home) applications, but it
+;; should cover most small/medium sites.
+;; it is recommended to make the sizes be a prime number!
+;; This was internally set to 17 for small-memory applications...
+;; All tables default to 563, except when compiled in LOW_MEMORY mode,
+;; in which case, they default to 17. You can override this by uncommenting
+;; the following, and changing the values.
+;hash_users=563
+;hash_peers=563
+;hash_dialogs=563
+
allowoverlap=no ; Disable overlap dialing support. (Default is yes)
;allowtransfer=no ; Disable all transfers (unless enabled in peers or users)
; Default is enabled
@@ -126,7 +140,7 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
; Disabling DNS SRV lookups disables the
; ability to place SIP calls based on domain
; names to some other SIP users on the Internet
-
+
;domain=mydomain.tld ; Set default domain for this host
; If configured, Asterisk will only allow
; INVITE and REFER to non-local domains
diff --git a/doc/chan_sip-perf-testing.txt b/doc/chan_sip-perf-testing.txt
new file mode 100644
index 000000000..85b22bddc
--- /dev/null
+++ b/doc/chan_sip-perf-testing.txt
@@ -0,0 +1,110 @@
+Measuring the SIP channel driver's Performance
+==============================================
+
+This file documents the methods I used to measure
+the performance of the SIP channel driver, in
+terms of maximum simultaneous calls and how quickly
+it could handle incoming calls.
+
+Knowing these limitations can be valuable to those
+implementing PBX's in 'large' environments. Will your
+installation handle expected call volume?
+
+Quoting these numbers can be totally useless for other
+installations. Minor changes like the amount of RAM
+in a system, the speed of the ethernet, the amount of
+cache in the CPU, the CPU clock speed, whether or not
+you log CDR's, etc. can affect the numbers greatly.
+
+In my set up, I had a dedicated test machine running Asterisk,
+and another machine which ran sipp, connected together with
+ethernet.
+
+The version of sipp that I used was sipp-2.0.1; however,
+I have reason to believe that other versions would work
+just as well.
+
+On the asterisk machine, I included the following in my
+extensions.ael file:
+
+context test11
+{
+ s => {
+ Answer();
+ while (1) {
+ Background(demo-instruct);
+ }
+ Hangup();
+ }
+ _X. => {
+ Answer();
+ while (1) {
+ Background(demo-instruct);
+ }
+ Hangup();
+ }
+}
+
+Basically, incoming SIP calls are answered, and
+the demo-instruct sound file is played endlessly
+to the caller. This test depends on the calling
+party to hang up, thus allowing sipp to determine
+the length of a call.
+
+The sip.conf file has this entry:
+
+[asterisk02]
+type=friend
+context=test11
+host=192.168.134.240 ;; the address of the host you will be running sipp on
+user=sipp
+canreinvite=no
+disallow=all
+allow=ulaw
+
+Note that it's pretty simplistic; no authentication beyond the host ip,
+and it uses ulaw, which is pretty efficient, low-cpu-intensive codec.
+
+
+To measure the impact of incoming call traffic on the Asterisk
+machine, I run vmstat. It gives me an idea of the cpu usage by
+Asterisk. The most common failure mode of Asterisk at high call volumes,
+is that the CPU reaches 100% utilization, and then cannot keep up with
+the workload, resulting in timeouts and other failures, which swiftly
+compound and cascade, until gross failure ensues. Watch the CPU Idle %
+numbers.
+
+I learned to split the testing into two modes: one for just call call processing
+power, in the which we had relatively few simultaneous calls in place,
+and another where we allow the the number of simultaneous calls to quickly
+reach a set maximum, and then rerun sipp, looking for the maximum.
+
+Call processing power is measured with extremely short duration calls:
+
+ ./sipp -sn uac 192.168.134.252 -s 12 -d 100 -l 256
+
+The above tells sipp to call your asterisk test machine (192.168.134.252)
+at extension 12, each call lasts just .1 second, with a limit of 256 simultaneous
+calls. The simultaneous calls will be the rate/sec of incoming calls times the call length,
+so 1 simultaneous call at 10 calls/sec, and 45 at 450 calls/sec. Setting the limit
+to 256 implies you do not intend to test above 2560 calls/sec.
+
+Sipp starts at 10 calls/sec, and you can slowly increase the speed by hitting '*' or '+'.
+Watch your cpu utilization on the asterisk server. When you approach 100%, you have found
+your limit.
+
+
+Simultaneous calls can be measured with very long duration calls:
+
+./sipp -sn uac 192.168.134.252 -s 12 -d 100000 -l 270
+
+This will place 100 sec duration calls to Asterisk. The number of simultaneous
+calls will increase until the maximum of 270 is reached. If Asterisk survives
+this number and is not at 100% cpu utilization, you can stop sipp and run it again
+with a higher -l argument.
+
+
+By changing one Asterisk parameter at a time, you can get a feel for how much that change
+will affect performance.
+
+