summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.
+
+