diff options
author | Mark Spencer <markster@digium.com> | 2004-12-25 22:15:32 +0000 |
---|---|---|
committer | Mark Spencer <markster@digium.com> | 2004-12-25 22:15:32 +0000 |
commit | 802dbe4677a939012ec6240167c70051b5433ad8 (patch) | |
tree | e324fe1a3413357e19bf765b9abad32bdfafc0c8 | |
parent | a84da8a0b924becf3d65092e7d2668aac1c42f11 (diff) |
Add Asterisk "object model" and update chan_sip to use it for peers/regs/users
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4554 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rwxr-xr-x | channels/chan_sip.c | 486 | ||||
-rwxr-xr-x | include/asterisk/astobj.h | 312 |
2 files changed, 515 insertions, 283 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 55e8a9557..5b586222f 100755 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -43,6 +43,7 @@ #include <asterisk/causes.h> #include <asterisk/utils.h> #include <asterisk/file.h> +#include <asterisk/astobj.h> #ifdef OSP_SUPPORT #include <asterisk/astosp.h> #endif @@ -157,6 +158,14 @@ static int global_progressinband = 0; static int global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT; +/* Object counters */ +static int suserobjs = 0; +static int ruserobjs = 0; +static int speerobjs = 0; +static int rpeerobjs = 0; +static int apeerobjs = 0; +static int regobjs = 0; + #ifdef OSP_SUPPORT static int global_ospauth = 0; /* OSP = Open Settlement Protocol */ #endif @@ -263,12 +272,11 @@ struct sip_history { #define SIP_PROMISCREDIR (1 << 8) /* Promiscuous redirection */ #define SIP_TRUSTRPID (1 << 9) /* Trust RPID headers? */ #define SIP_USEREQPHONE (1 << 10) /* Add user=phone to numeric URI. Default off */ -#define SIP_TEMPONLY (1 << 11) /* Flag for temporary users (realtime) */ +#define SIP_REALTIME (1 << 11) /* Flag for realtime users */ #define SIP_USECLIENTCODE (1 << 12) /* Trust X-ClientCode info message */ #define SIP_OUTGOING (1 << 13) /* Is this an outgoing call? */ -#define SIP_DELME (1 << 14) -#define SIP_SELFDESTRUCT (1 << 15) -#define SIP_DYNAMIC (1 << 16) /* Is this a dynamic peer? */ +#define SIP_SELFDESTRUCT (1 << 14) +#define SIP_DYNAMIC (1 << 15) /* Is this a dynamic peer? */ /* sip_pvt: PVT structures are used for each SIP conversation, ie. a call */ static struct sip_pvt { @@ -393,7 +401,7 @@ struct sip_pkt { /* Structure for SIP user data. User's place calls to us */ struct sip_user { /* Users who can access various contexts */ - char name[80]; /* The name in sip.conf */ + ASTOBJ_COMPONENTS(struct sip_user); char secret[80]; /* Password */ char md5secret[80]; /* Password in md5 */ char context[80]; /* Default context for incoming calls */ @@ -424,12 +432,11 @@ struct sip_user { int progressinband; struct ast_ha *ha; /* ACL setting */ struct ast_variable *vars; - struct sip_user *next; }; /* Structure for SIP peer data, we place calls to peers if registred or fixed IP address (host) */ struct sip_peer { - char name[80]; /* Peer name in sip.conf */ + ASTOBJ_COMPONENTS(struct sip_peer); char secret[80]; /* Password */ char md5secret[80]; /* Password in MD5 */ char context[80]; /* Default context for incoming calls */ @@ -477,7 +484,6 @@ struct sip_peer { struct sockaddr_in defaddr; /* Default IP address, used until registration */ struct ast_ha *ha; /* Access control list */ int lastmsg; - struct sip_peer *next; }; AST_MUTEX_DEFINE_STATIC(sip_reload_lock); @@ -500,6 +506,7 @@ static int sip_reloading = 0; /* sip_registry: Registrations with other SIP proxies */ struct sip_registry { + ASTOBJ_COMPONENTS_FULL(struct sip_registry,1,1); int portno; /* Optional port override */ char username[80]; /* Who we are registering as */ char authuser[80]; /* Who we *authenticate* as */ @@ -526,25 +533,21 @@ struct sip_registry { char qop[80]; /* Quality of Protection. */ char lastmsg[256]; /* Last Message sent/received */ - struct sip_registry *next; }; /*--- The user list: Users and friends ---*/ static struct ast_user_list { - struct sip_user *users; - ast_mutex_t lock; + ASTOBJ_CONTAINER_COMPONENTS(struct sip_user); } userl; /*--- The peer list: Peers and Friends ---*/ static struct ast_peer_list { - struct sip_peer *peers; - ast_mutex_t lock; + ASTOBJ_CONTAINER_COMPONENTS(struct sip_peer); } peerl; /*--- The register list: Other SIP proxys we register with and call ---*/ static struct ast_register_list { - struct sip_registry *registrations; - ast_mutex_t lock; + ASTOBJ_CONTAINER_COMPONENTS(struct sip_registry); int recheck; } regl; @@ -1058,7 +1061,7 @@ static void register_peer_exten(struct sip_peer *peer, int onoff) } } -static void destroy_peer(struct sip_peer *peer) +static void sip_destroy_peer(struct sip_peer *peer) { /* Delete it, it needs to disappear */ if (peer->call) @@ -1069,13 +1072,19 @@ static void destroy_peer(struct sip_peer *peer) ast_sched_del(sched, peer->pokeexpire); register_peer_exten(peer, 0); ast_free_ha(peer->ha); + if (ast_test_flag(peer, SIP_SELFDESTRUCT)) + apeerobjs--; + else if (ast_test_flag(peer, SIP_REALTIME)) + rpeerobjs--; + else + speerobjs--; free(peer); } /*--- update_peer: Update peer data in database (if used) ---*/ static void update_peer(struct sip_peer *p, int expiry) { - if (ast_test_flag(p, SIP_TEMPONLY)) + if (ast_test_flag(p, SIP_REALTIME)) realtime_update_peer(p->name, &p->addr, p->username, expiry); } @@ -1100,14 +1109,14 @@ static struct sip_peer *realtime_peer(const char *peername, struct sockaddr_in * peer = build_peer(peername, var, 1); if (peer) { /* Add some finishing touches, addresses, etc */ - ast_set_flag(peer, SIP_TEMPONLY); + ast_set_flag(peer, SIP_REALTIME); tmp = var; while(tmp) { if (!strcasecmp(tmp->name, "type")) { if (strcasecmp(tmp->value, "friend") && strcasecmp(tmp->value, "peer")) { /* Whoops, we weren't supposed to exist! */ - destroy_peer(peer); + sip_destroy_peer(peer); peer = NULL; break; } @@ -1135,35 +1144,32 @@ static struct sip_peer *realtime_peer(const char *peername, struct sockaddr_in * } ast_destroy_realtime(var); } + if (peer) { + /* Reference and destroy, so when our caller unrefs, we disappear */ + ASTOBJ_REF(peer); + ASTOBJ_DESTROY(peer, sip_destroy_peer); + } return 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) || + (p->insecure && + (p->addr.sin_addr.s_addr == sin->sin_addr.s_addr))); +} + /*--- find_peer: Locate peer by name or ip address */ static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin) { struct sip_peer *p = NULL; - p = peerl.peers; - if (peer) { - /* Find by peer name */ - while(p) { - if (!strcasecmp(p->name, peer)) { - break; - } - p = p->next; - } - } - else { - /* Find by sin */ - while(p) { - if (!inaddrcmp(&p->addr, sin) || - (p->insecure && - (p->addr.sin_addr.s_addr == sin->sin_addr.s_addr))) { - break; - } - p = p->next; - } - } + if (peer) + ASTOBJ_CONTAINER_FIND(&peerl,p,peer); + else + ASTOBJ_CONTAINER_FIND_FULL(&peerl,p,sin,name,sip_addr_hashfunc,1,sip_addrcmp); if (!p) { p = realtime_peer(peer, sin); @@ -1172,13 +1178,17 @@ static struct sip_peer *find_peer(const char *peer, struct sockaddr_in *sin) return(p); } -static void destroy_user(struct sip_user *user) +static void sip_destroy_user(struct sip_user *user) { ast_free_ha(user->ha); if(user->vars) { ast_destroy_realtime(user->vars); user->vars = NULL; } + if (ast_test_flag(user, SIP_REALTIME)) + ruserobjs--; + else + suserobjs--; free(user); } @@ -1193,15 +1203,18 @@ static struct sip_user *realtime_user(const char *username) /* Make sure it's not a user only... */ user = build_user(username, var); if (user) { + /* Move counter from s to r... */ + suserobjs--; + ruserobjs++; /* Add some finishing touches, addresses, etc */ - ast_set_flag(user, SIP_TEMPONLY); + ast_set_flag(user, SIP_REALTIME); tmp = var; while(tmp) { if (!strcasecmp(tmp->name, "type")) { if (strcasecmp(tmp->value, "friend") && strcasecmp(tmp->value, "user")) { /* Whoops, we weren't supposed to exist! */ - destroy_user(user); + sip_destroy_user(user); user = NULL; break; } @@ -1211,21 +1224,19 @@ static struct sip_user *realtime_user(const char *username) } ast_destroy_realtime(var); } + if (user) { + /* Reference and destroy, so when our caller unrefs, we disappear */ + ASTOBJ_REF(user); + ASTOBJ_DESTROY(user, sip_destroy_user); + } return user; } /*--- find_user: Locate user by name */ -static struct sip_user *find_user(char *name) +static struct sip_user *find_user(const char *name) { struct sip_user *u = NULL; - - u = userl.users; - while(u) { - if (!strcasecmp(u->name, name)) { - break; - } - u = u->next; - } + ASTOBJ_CONTAINER_FIND(&userl,u,name); if (!u) { u = realtime_user(name); } @@ -1254,7 +1265,6 @@ static int create_addr(struct sip_pvt *r, char *opeer) port++; } r->sa.sin_family = AF_INET; - ast_mutex_lock(&peerl.lock); p = find_peer(peer, NULL); if (p) { @@ -1316,13 +1326,10 @@ static int create_addr(struct sip_pvt *r, char *opeer) } memcpy(&r->recv, &r->sa, sizeof(r->recv)); } else { - if (ast_test_flag(p, SIP_TEMPONLY)) { - destroy_peer(p); - } + ASTOBJ_UNREF(p,sip_destroy_peer); p = NULL; } } - ast_mutex_unlock(&peerl.lock); if (!p && !found) { hostn = peer; if (port) @@ -1354,9 +1361,7 @@ static int create_addr(struct sip_pvt *r, char *opeer) } else if (!p) return -1; else { - if (ast_test_flag(p, SIP_TEMPONLY)) { - destroy_peer(p); - } + ASTOBJ_UNREF(p,sip_destroy_peer); return 0; } } @@ -1451,6 +1456,24 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout) return res; } +static void sip_registry_destroy(struct sip_registry *reg) +{ + /* Really delete */ + if (reg->call) { + /* Clear registry before destroying to ensure + we don't get reentered trying to grab the registry lock */ + reg->call->registry = NULL; + sip_destroy(reg->call); + } + if (reg->expire > -1) + ast_sched_del(sched, reg->expire); + if (reg->timeout > -1) + ast_sched_del(sched, reg->timeout); + regobjs--; + free(reg); + +} + /*--- __sip_destroy: Execute destrucion of call structure, release memory---*/ static void __sip_destroy(struct sip_pvt *p, int lockowner) { @@ -1478,16 +1501,9 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner) p->route = NULL; } if (p->registry) { - /* Carefully unlink from registry */ - struct sip_registry *reg; - ast_mutex_lock(®l.lock); - reg = regl.registrations; - while(reg) { - if ((reg == p->registry) && (p->registry->call == p)) - p->registry->call=NULL; - reg = reg->next; - } - ast_mutex_unlock(®l.lock); + if (p->registry->call == p) + p->registry->call = NULL; + ASTOBJ_UNREF(p->registry,sip_registry_destroy); } /* Unlink us from the owner if we have one */ if (p->owner) { @@ -1543,11 +1559,9 @@ static int update_user_counter(struct sip_pvt *fup, int event) char name[256] = ""; struct sip_user *u; strncpy(name, fup->username, sizeof(name) - 1); - ast_mutex_lock(&userl.lock); u = find_user(name); if (!u) { ast_log(LOG_DEBUG, "%s is not a local user\n", name); - ast_mutex_unlock(&userl.lock); return 0; } switch(event) { @@ -1569,10 +1583,7 @@ static int update_user_counter(struct sip_pvt *fup, int event) if ( event == INC_OUT_USE ) { u->inUse++; } - ast_mutex_unlock(&userl.lock); - if (ast_test_flag(u, SIP_TEMPONLY)) { - destroy_user(u); - } + ASTOBJ_UNREF(u,sip_destroy_user); return -1; } } @@ -1604,10 +1615,7 @@ static int update_user_counter(struct sip_pvt *fup, int event) default: ast_log(LOG_ERROR, "update_user_counter(%s,%d) called with no event!\n",u->name,event); } - ast_mutex_unlock(&userl.lock); - if (ast_test_flag(u, SIP_TEMPONLY)) { - destroy_user(u); - } + ASTOBJ_UNREF(u,sip_destroy_user); return 0; } @@ -2462,6 +2470,8 @@ static int sip_register(char *value, int lineno) reg = malloc(sizeof(struct sip_registry)); if (reg) { memset(reg, 0, sizeof(struct sip_registry)); + regobjs++; + ASTOBJ_INIT(reg); strncpy(reg->contact, contact, sizeof(reg->contact) - 1); if (username) strncpy(reg->username, username, sizeof(reg->username)-1); @@ -2477,10 +2487,8 @@ static int sip_register(char *value, int lineno) reg->portno = porta ? atoi(porta) : 0; reg->callid_valid = 0; reg->ocseq = 101; - ast_mutex_lock(®l.lock); - reg->next = regl.registrations; - regl.registrations = reg; - ast_mutex_unlock(®l.lock); + ASTOBJ_CONTAINER_LINK(®l, reg); + ASTOBJ_UNREF(reg,sip_registry_destroy); } else { ast_log(LOG_ERROR, "Out of memory\n"); return -1; @@ -4072,13 +4080,15 @@ static int sip_reregister(void *data) /* if we are here, we know that we need to reregister. */ struct sip_registry *r=(struct sip_registry *)data; + /* Since registry's are only added/removed by the the monitor thread, this + may be overkill to reference/dereference at all here */ if (sipdebug) ast_log(LOG_NOTICE, " -- Re-registration for %s@%s\n", r->username, r->hostname); - ast_mutex_lock(®l.lock); + ASTOBJ_REF(r); r->expire = -1; __sip_do_register(r); - ast_mutex_unlock(®l.lock); + ASTOBJ_UNREF(r,sip_registry_destroy); return 0; } @@ -4099,13 +4109,14 @@ static int sip_reg_timeout(void *data) struct sip_pvt *p; int res; - ast_mutex_lock(®l.lock); - + ASTOBJ_REF(r); ast_log(LOG_NOTICE, " -- Registration for '%s@%s' timed out, trying again\n", r->username, r->hostname); if (r->call) { /* Unlink us, destroy old call. Locking is not relevent here because all this happens in the single SIP manager thread. */ p = r->call; + if (p->registry) + ASTOBJ_UNREF(p->registry, sip_registry_destroy); p->registry = NULL; r->call = NULL; ast_set_flag(p, SIP_NEEDDESTROY); @@ -4116,7 +4127,7 @@ static int sip_reg_timeout(void *data) manager_event(EVENT_FLAG_SYSTEM, "Registry", "Channel: SIP\r\nUser: %s\r\nDomain: %s\r\nStatus: %s\r\n", r->username, r->hostname, regstate2str(r->regstate)); r->timeout = -1; res=transmit_register(r, "REGISTER", NULL, NULL); - ast_mutex_unlock(®l.lock); + ASTOBJ_UNREF(r,sip_registry_destroy); return 0; } @@ -4172,6 +4183,7 @@ static int transmit_register(struct sip_registry *r, char *cmd, char *auth, char ast_set_flag(p, SIP_OUTGOING); /* Registration is outgoing call */ r->call=p; /* Save pointer to SIP packet */ p->registry=r; /* Add pointer to registry in packet */ + ASTOBJ_REF(p->registry); /* Reference registry to prevent it from disappearing */ if (!ast_strlen_zero(r->secret)) /* Secret (password) */ strncpy(p->peersecret, r->secret, sizeof(p->peersecret)-1); if (!ast_strlen_zero(r->md5secret)) @@ -4387,7 +4399,7 @@ static int expire_register(void *data) p->expire = -1; ast_device_state_changed("SIP/%s", p->name); if (ast_test_flag(p, SIP_SELFDESTRUCT)) { - ast_set_flag(p, SIP_DELME); + ASTOBJ_MARK(p); prune_peers(); } return 0; @@ -4643,7 +4655,7 @@ static int parse_contact(struct sip_pvt *pvt, struct sip_peer *p, struct sip_req ast_sched_del(sched, p->expire); if ((expiry < 1) || (expiry > max_expiry)) expiry = max_expiry; - if (!ast_test_flag(p, SIP_TEMPONLY)) + if (!ast_test_flag(p, SIP_REALTIME)) p->expire = ast_sched_add(sched, (expiry + 10) * 1000, expire_register, p); else p->expire = -1; @@ -5000,16 +5012,11 @@ static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct si *c = '\0'; strncpy(p->exten, name, sizeof(p->exten) - 1); build_contact(p); - ast_mutex_lock(&peerl.lock); peer = find_peer(name, NULL); if (!(peer && ast_apply_ha(peer->ha, sin))) { - if (peer && ast_test_flag(peer, SIP_TEMPONLY)) { - destroy_peer(peer); - } + ASTOBJ_UNREF(peer,sip_destroy_peer); peer = NULL; } - ast_mutex_unlock(&peerl.lock); - if (peer) { if (!ast_test_flag(peer, SIP_DYNAMIC)) { ast_log(LOG_NOTICE, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name); @@ -5034,10 +5041,7 @@ static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct si /* Create peer if we have autocreate mode enabled */ peer = temp_peer(name); if (peer) { - ast_mutex_lock(&peerl.lock); - peer->next = peerl.peers; - peerl.peers = peer; - ast_mutex_unlock(&peerl.lock); + ASTOBJ_CONTAINER_LINK(&peerl, peer); peer->lastmsgssent = -1; sip_cancel_destroy(p); if (parse_contact(p, peer, req)) { @@ -5056,9 +5060,8 @@ static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct si } if (res < 0) transmit_response(p, "403 Forbidden", &p->initreq); - if (peer && ast_test_flag(peer, SIP_TEMPONLY)) { - destroy_peer(peer); - } + if (peer) + ASTOBJ_UNREF(peer,sip_destroy_peer); return res; } @@ -5520,7 +5523,6 @@ static int check_user_full(struct sip_pvt *p, struct sip_request *req, char *cmd strncpy(p->cid_name, calleridname, sizeof(p->cid_name) - 1); if (ast_strlen_zero(of)) return 0; - ast_mutex_lock(&userl.lock); user = find_user(of); /* Find user based on user name in the from header */ if (user && ast_apply_ha(user->ha, sin)) { @@ -5594,16 +5596,13 @@ static int check_user_full(struct sip_pvt *p, struct sip_request *req, char *cmd if (user) { if (debug) ast_verbose("Found user '%s', but fails host access\n", user->name); - if (ast_test_flag(user, SIP_TEMPONLY)) - destroy_user(user); + ASTOBJ_UNREF(user,sip_destroy_user); } user = NULL; } - /* Temp user gets cleaned up at the end */ - ast_mutex_unlock(&userl.lock); + if (!user) { /* If we didn't find a user match, check for peers */ - ast_mutex_lock(&peerl.lock); /* Look for peer based on the IP address we received data from */ /* If peer is registred from this IP address or have this as a default IP address, this call is from the peer @@ -5680,18 +5679,13 @@ static int check_user_full(struct sip_pvt *p, struct sip_request *req, char *cmd p->noncodeccapability &= ~AST_RTP_DTMF; } } - if (ast_test_flag(peer, SIP_TEMPONLY)) - destroy_peer(peer); - } else - if (debug) - ast_verbose("Found no matching peer or user for '%s:%d'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr), ntohs(p->recv.sin_port)); - ast_mutex_unlock(&peerl.lock); - + ASTOBJ_UNREF(peer,sip_destroy_peer); + } else if (debug) + ast_verbose("Found no matching peer or user for '%s:%d'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr), ntohs(p->recv.sin_port)); } - if (user && ast_test_flag(user, SIP_TEMPONLY)) - destroy_user(user); - + if (user) + ASTOBJ_UNREF(user,sip_destroy_user); return res; } @@ -5760,10 +5754,8 @@ static int sip_show_inuse(int fd, int argc, char *argv[]) { if (argc != 3) return RESULT_SHOWUSAGE; - ast_mutex_lock(&userl.lock); - user = userl.users; ast_cli(fd, FORMAT, "Username", "incoming", "Limit","outgoing","Limit"); - for(user=userl.users;user;user=user->next) { + ASTOBJ_CONTAINER_TRAVERSE(&userl,user, do { if (user->incominglimit) snprintf(ilimits, sizeof(ilimits), "%d", user->incominglimit); else @@ -5775,8 +5767,7 @@ static int sip_show_inuse(int fd, int argc, char *argv[]) { snprintf(iused, sizeof(iused), "%d", user->inUse); snprintf(oused, sizeof(oused), "%d", user->outUse); ast_cli(fd, FORMAT2, user->name, iused, ilimits,oused,olimits); - } - ast_mutex_unlock(&userl.lock); + } while (0) ); return RESULT_SUCCESS; #undef FORMAT #undef FORMAT2 @@ -5805,17 +5796,15 @@ static int sip_show_users(int fd, int argc, char *argv[]) struct sip_user *user; if (argc != 3) return RESULT_SHOWUSAGE; - ast_mutex_lock(&userl.lock); ast_cli(fd, FORMAT, "Username", "Secret", "Accountcode", "Def.Context", "ACL", "NAT"); - for(user=userl.users;user;user=user->next) { + ASTOBJ_CONTAINER_TRAVERSE(&userl,user, ast_cli(fd, FORMAT, user->name, user->secret, user->accountcode, user->context, user->ha ? "Yes" : "No", - nat2str(user->nat)); - } - ast_mutex_unlock(&userl.lock); + nat2str(user->nat)) + ); return RESULT_SUCCESS; #undef FORMAT } @@ -5835,10 +5824,9 @@ static int sip_show_peers(int fd, int argc, char *argv[]) if (argc != 3 && argc != 5) return RESULT_SHOWUSAGE; - ast_mutex_lock(&peerl.lock); ast_cli(fd, FORMAT2, "Name/username", "Host", "Dyn", "Nat", "ACL", "Mask", "Port", "Status"); - for (peer = peerl.peers;peer;peer = peer->next) { + ASTOBJ_CONTAINER_TRAVERSE(&peerl,peer, do { char nm[20] = ""; char status[20] = ""; int print_line = -1; @@ -5871,7 +5859,7 @@ static int sip_show_peers(int fd, int argc, char *argv[]) } else { strncpy(status, "Unmonitored", sizeof(status) - 1); /* Checking if port is 0 */ - if ( ntohs(peer->addr.sin_port) == 0 ) { + if ( ntohs(peer->addr.sin_port) == 0 ) { peers_offline++; } else { peers_online++; @@ -5907,14 +5895,29 @@ static int sip_show_peers(int fd, int argc, char *argv[]) ntohs(peer->addr.sin_port), status); } total_peers++; - } + } while(0) ); ast_cli(fd,"%d sip peers loaded [%d online , %d offline]\n",total_peers,peers_online,peers_offline); - ast_mutex_unlock(&peerl.lock); return RESULT_SUCCESS; #undef FORMAT #undef FORMAT2 } +static int sip_show_objects(int fd, int argc, char *argv[]) +{ + struct sip_user *user; + struct sip_peer *peer; + struct sip_registry *reg; + char tmp[256]; + if (argc != 3) + return RESULT_SHOWUSAGE; + ast_cli(fd, "-= User objects: %d static, %d realtime =-\n\n", suserobjs, ruserobjs); + ASTOBJ_CONTAINER_DUMP(fd, tmp, sizeof(tmp), &userl, user); + ast_cli(fd, "-= Peer objects: %d static, %d realtime, %d autocreate =-\n\n", speerobjs, rpeerobjs, apeerobjs); + ASTOBJ_CONTAINER_DUMP(fd, tmp, sizeof(tmp), &peerl, peer); + ast_cli(fd, "-= Registry objects: %d =-\n\n", regobjs); + ASTOBJ_CONTAINER_DUMP(fd, tmp, sizeof(tmp), ®l, reg); + return RESULT_SUCCESS; +} /*--- print_group: Print call group and pickup group ---*/ static void print_group(int fd, unsigned int group) { @@ -5947,7 +5950,6 @@ static int sip_show_peer(int fd, int argc, char *argv[]) if (argc != 4) return RESULT_SHOWUSAGE; - ast_mutex_lock(&peerl.lock); peer = find_peer(argv[3], NULL); if (peer) { ast_cli(fd,"\n\n"); @@ -6020,16 +6022,12 @@ static int sip_show_peer(int fd, int argc, char *argv[]) ast_cli(fd, " Useragent : %s\n", peer->useragent); ast_cli(fd, " Full Contact : %s\n", peer->fullcontact); ast_cli(fd,"\n"); + ASTOBJ_UNREF(peer,sip_destroy_peer); } else { ast_cli(fd,"Peer %s not found.\n", argv[3]); ast_cli(fd,"\n"); } - ast_mutex_unlock(&peerl.lock); - - if (peer && ast_test_flag(peer, SIP_TEMPONLY)) { - destroy_peer(peer); - } return RESULT_SUCCESS; } @@ -6043,14 +6041,12 @@ static int sip_show_registry(int fd, int argc, char *argv[]) if (argc != 3) return RESULT_SHOWUSAGE; - ast_mutex_lock(®l.lock); ast_cli(fd, FORMAT2, "Host", "Username", "Refresh", "State"); - for (reg = regl.registrations;reg;reg = reg->next) { + ASTOBJ_CONTAINER_TRAVERSE(®l,reg, do { snprintf(host, sizeof(host), "%s:%d", reg->hostname, reg->portno ? reg->portno : DEFAULT_SIP_PORT); ast_cli(fd, FORMAT, host, reg->username, reg->refresh, regstate2str(reg->regstate)); - } - ast_mutex_unlock(®l.lock); + } while(0)); return RESULT_SUCCESS; #undef FORMAT #undef FORMAT2 @@ -6362,13 +6358,7 @@ static int sip_do_debug_peer(int fd, int argc, char *argv[]) char iabuf[INET_ADDRSTRLEN]; if (argc != 4) return RESULT_SHOWUSAGE; - ast_mutex_lock(&peerl.lock); - for (peer = peerl.peers;peer;peer = peer->next) - if (!strcmp(peer->name, argv[3])) - break; - ast_mutex_unlock(&peerl.lock); - if (!peer) - peer = realtime_peer(argv[3], NULL); + peer = find_peer(argv[3], NULL); if (peer) { if (peer->addr.sin_addr.s_addr) { debugaddr.sin_family = AF_INET; @@ -6378,9 +6368,7 @@ static int sip_do_debug_peer(int fd, int argc, char *argv[]) sipdebug = 1; } else ast_cli(fd, "Unable to get IP address of peer '%s'\n", argv[3]); - if (ast_test_flag(peer, SIP_TEMPONLY)) - destroy_peer(peer); - peer = NULL; + ASTOBJ_UNREF(peer,sip_destroy_peer); } else ast_cli(fd, "No such peer '%s'\n", argv[3]); return RESULT_SUCCESS; @@ -6445,12 +6433,12 @@ static int do_register_auth(struct sip_pvt *p, struct sip_request *req, char *he if (reply_digest(p, req, header, "REGISTER", digest, sizeof(digest))) { /* There's nothing to use for authentication */ /* No digest challenge in request */ - if (sip_debug_test_pvt(p)) + if (sip_debug_test_pvt(p) && p->registry) ast_verbose("No authentication challenge, sending blank registration to domain/host name %s\n", p->registry->hostname); /* No old challenge */ return -1; } - if (sip_debug_test_pvt(p)) + if (sip_debug_test_pvt(p) && p->registry) ast_verbose("Responding to challenge, registration to domain/host name %s\n", p->registry->hostname); return transmit_register(p->registry,"REGISTER",digest, respheader); } @@ -6685,11 +6673,17 @@ static char show_subscriptions_usage[] = "Usage: sip show subscriptions\n" " Shows active SIP subscriptions for extension states\n"; +static char show_objects_usage[] = +"Usage: sip show objects\n" +" Shows status of known SIP objects\n"; + +static struct ast_cli_entry cli_show_objects = + { { "sip", "show", "objects", NULL }, sip_show_objects, "Show all SIP object allocations", show_objects_usage }; static struct ast_cli_entry cli_show_users = { { "sip", "show", "users", NULL }, sip_show_users, "Show defined SIP users", show_users_usage }; static struct ast_cli_entry cli_show_subscriptions = - { { "sip", "show", "subscriptions", NULL }, sip_show_subscriptions, "Show active SIP subscriptions", show_subscriptions_usage}; + { { "sip", "show", "subscriptions", NULL }, sip_show_subscriptions, "Show active SIP subscriptions", show_subscriptions_usage}; static struct ast_cli_entry cli_show_channels = { { "sip", "show", "channels", NULL }, sip_show_channels, "Show active SIP channels", show_channels_usage}; static struct ast_cli_entry cli_show_channel = @@ -6705,11 +6699,11 @@ static struct ast_cli_entry cli_show_peer = static struct ast_cli_entry cli_show_peers = { { "sip", "show", "peers", NULL }, sip_show_peers, "Show defined SIP peers", show_peers_usage }; static struct ast_cli_entry cli_show_peers_include = - { { "sip", "show", "peers", "include", NULL }, sip_show_peers, "Show defined SIP peers", show_peers_usage }; + { { "sip", "show", "peers", "include", NULL }, sip_show_peers, "Show defined SIP peers", show_peers_usage }; static struct ast_cli_entry cli_show_peers_exclude = - { { "sip", "show", "peers", "exclude", NULL }, sip_show_peers, "Show defined SIP peers", show_peers_usage }; + { { "sip", "show", "peers", "exclude", NULL }, sip_show_peers, "Show defined SIP peers", show_peers_usage }; static struct ast_cli_entry cli_show_peers_begin = - { { "sip", "show", "peers", "begin", NULL }, sip_show_peers, "Show defined SIP peers", show_peers_usage }; + { { "sip", "show", "peers", "begin", NULL }, sip_show_peers, "Show defined SIP peers", show_peers_usage }; static struct ast_cli_entry cli_inuse_show = { { "sip", "show", "inuse", NULL }, sip_show_inuse, "List all inuse/limit", show_inuse_usage }; static struct ast_cli_entry cli_show_registry = @@ -6994,6 +6988,7 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_ /* Schedule re-registration before we expire */ r->expire=ast_sched_add(sched, expires_ms, sip_reregister, r); + ASTOBJ_UNREF(r, sip_registry_destroy); } else ast_log(LOG_WARNING, "Got 200 OK on REGISTER that isn't a register\n"); @@ -7971,19 +7966,16 @@ static int sip_send_mwi_to_peer(struct sip_peer *peer) /* Return now if it's the same thing we told them last time */ if (((newmsgs << 8) | (oldmsgs)) == peer->lastmsgssent) { - ast_mutex_unlock(&peerl.lock); return 0; } p = sip_alloc(NULL, NULL, 0); if (!p) { ast_log(LOG_WARNING, "Unable to build sip pvt data for MWI\n"); - ast_mutex_unlock(&peerl.lock); return -1; } strncpy(name, peer->name, sizeof(name) - 1); peer->lastmsgssent = ((newmsgs << 8) | (oldmsgs)); - ast_mutex_unlock(&peerl.lock); if (create_addr(p, name)) { /* Maybe they're not registered, etc. */ sip_destroy(p); @@ -8099,26 +8091,25 @@ restartsearch: ast_sched_runq(sched); /* needs work to send mwi to realtime peers */ - ast_mutex_lock(&peerl.lock); - peer = peerl.peers; time(&t); fastrestart = 0; curpeernum = 0; - while(peer) { + ASTOBJ_CONTAINER_TRAVERSE(&peerl,peer, if ((curpeernum > lastpeernum) && !ast_strlen_zero(peer->mailbox) && ((t - peer->lastmsgcheck) > global_mwitime)) { - sip_send_mwi_to_peer(peer); fastrestart = 1; lastpeernum = curpeernum; + ASTOBJ_REF(peer); + ast_mutex_unlock(&peer->lock); break; } - curpeernum++; - peer = peer->next; - } - /* Remember, sip_send_mwi_to_peer releases the lock if we've called it */ - if (!peer) { + curpeernum++ + ); + if (peer) { + sip_send_mwi_to_peer(peer); + ASTOBJ_UNREF(peer,sip_destroy_peer); + } else { /* Reset where we come from */ lastpeernum = -1; - ast_mutex_unlock(&peerl.lock); } ast_mutex_unlock(&monlock); } @@ -8270,7 +8261,6 @@ static int sip_devicestate(void *data) ext = NULL; } - ast_mutex_lock(&peerl.lock); p = find_peer(host, NULL); if (p) { found++; @@ -8281,16 +8271,14 @@ static int sip_devicestate(void *data) res = AST_DEVICE_UNKNOWN; } } - ast_mutex_unlock(&peerl.lock); if (!p && !found) { hp = ast_gethostbyname(host, &ahp); if (hp) res = AST_DEVICE_UNKNOWN; } - if (p && ast_test_flag(p, SIP_TEMPONLY)) { - destroy_peer(p); - } + if (p) + ASTOBJ_UNREF(p,sip_destroy_peer); return res; } @@ -8389,6 +8377,7 @@ static struct sip_user *build_user(const char *name, struct ast_variable *v) user = (struct sip_user *)malloc(sizeof(struct sip_user)); if (user) { + suserobjs++; memset(user, 0, sizeof(struct sip_user)); strncpy(user->name, name, sizeof(user->name)-1); oldha = user->ha; @@ -8532,6 +8521,7 @@ static struct sip_peer *temp_peer(char *name) if (!peer) return NULL; + apeerobjs++; memset(peer, 0, sizeof(struct sip_peer)); peer->expire = -1; peer->pokeexpire = -1; @@ -8572,37 +8562,29 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, int int found=0; prev = NULL; - ast_mutex_lock(&peerl.lock); if (temponly) { peer = NULL; } else { - peer = peerl.peers; - while(peer) { - if (!strcasecmp(peer->name, name)) { - break; - } - prev = peer; - peer = peer->next; - } + /* Note we do NOT use find_peer here, to avoid realtime recursion */ + ASTOBJ_CONTAINER_FIND_UNLINK(&peerl,peer,prev,name); } if (peer) { + /* Already in the list, remove it and it will be added back (or FREE'd) */ found++; - /* Already in the list, remove it and it will be added back (or FREE'd) */ - if (prev) { - prev->next = peer->next; - } else { - peerl.peers = peer->next; - } - ast_mutex_unlock(&peerl.lock); } else { - ast_mutex_unlock(&peerl.lock); peer = malloc(sizeof(struct sip_peer)); if (peer) { memset(peer, 0, sizeof(struct sip_peer)); + if (temponly) + rpeerobjs++; + else + speerobjs++; + ASTOBJ_INIT(peer); peer->expire = -1; peer->pokeexpire = -1; } } + /* Note that our peer has NOT had its reference count incrased */ if (peer) { peer->lastmsgssent = -1; if (!found) { @@ -8702,7 +8684,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, int ast_clear_flag(peer, SIP_DYNAMIC); if (!obproxyfound || !strcasecmp(v->name, "outboundproxy")) { if (ast_get_ip_or_srv(&peer->addr, v->value, "_sip._udp")) { - destroy_peer(peer); + ASTOBJ_DESTROY(peer, sip_destroy_peer); return NULL; } } @@ -8715,7 +8697,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, int inet_aton("255.255.255.255", &peer->mask); } else if (!strcasecmp(v->name, "defaultip")) { if (ast_get_ip(&peer->defaddr, v->value)) { - destroy_peer(peer); + ASTOBJ_DESTROY(peer, sip_destroy_peer); return NULL; } } else if (!strcasecmp(v->name, "permit") || @@ -8801,7 +8783,7 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, int } if (!found && ast_test_flag(peer, SIP_DYNAMIC)) reg_source_db(peer); - ast_clear_flag(peer, SIP_DELME); + ASTOBJ_UNMARK(peer); } ast_free_ha(oldha); return peer; @@ -9068,19 +9050,15 @@ static int reload_config(void) if (!strcasecmp(utype, "user") || !strcasecmp(utype, "friend")) { user = build_user(cat, ast_variable_browse(cfg, cat)); if (user) { - ast_mutex_lock(&userl.lock); - user->next = userl.users; - userl.users = user; - ast_mutex_unlock(&userl.lock); + ASTOBJ_CONTAINER_LINK(&userl,user); + ASTOBJ_UNREF(user, sip_destroy_user); } } if (!strcasecmp(utype, "peer") || !strcasecmp(utype, "friend")) { peer = build_peer(cat, ast_variable_browse(cfg, cat), 0); if (peer) { - ast_mutex_lock(&peerl.lock); - peer->next = peerl.peers; - peerl.peers = peer; - ast_mutex_unlock(&peerl.lock); + ASTOBJ_CONTAINER_LINK(&peerl,peer); + ASTOBJ_UNREF(peer, sip_destroy_peer); } } else if (strcasecmp(utype, "user")) { ast_log(LOG_WARNING, "Unknown type '%s' for '%s' in %s\n", utype, cat, "sip.conf"); @@ -9381,47 +9359,14 @@ static struct ast_rtp_protocol sip_rtp = { /* Also, check registations with other SIP proxies */ static void delete_users(void) { - struct sip_user *user, *userlast; + struct sip_user *user; struct sip_peer *peer; - struct sip_registry *reg, *regn; + struct sip_registry *reg; /* Delete all users */ - ast_mutex_lock(&userl.lock); - for (user=userl.users;user;) { - userlast = user; - user=user->next; - destroy_user(userlast); - } - userl.users=NULL; - ast_mutex_unlock(&userl.lock); - - ast_mutex_lock(®l.lock); - for (reg = regl.registrations;reg;) { - regn = reg->next; - /* Really delete */ - if (reg->call) { - /* Clear registry before destroying to ensure - we don't get reentered trying to grab the registry lock */ - reg->call->registry = NULL; - sip_destroy(reg->call); - } - if (reg->expire > -1) - ast_sched_del(sched, reg->expire); - if (reg->timeout > -1) - ast_sched_del(sched, reg->timeout); - free(reg); - reg = regn; - } - regl.registrations = NULL; - ast_mutex_unlock(®l.lock); - - ast_mutex_lock(&peerl.lock); - for (peer=peerl.peers;peer;) { - /* Assume all will be deleted, and we'll find out for sure later */ - ast_set_flag(peer, SIP_DELME); - peer = peer->next; - } - ast_mutex_unlock(&peerl.lock); + ASTOBJ_CONTAINER_DESTROYALL(&userl,user,sip_destroy_user); + ASTOBJ_CONTAINER_DESTROYALL(®l,reg,sip_registry_destroy); + ASTOBJ_CONTAINER_MARKALL(&peerl,peer); } /*--- prune_peers: Delete all peers marked for deletion ---*/ @@ -9429,21 +9374,7 @@ static void prune_peers(void) { /* Prune peers who still are supposed to be deleted */ struct sip_peer *peer, *peerlast, *peernext; - ast_mutex_lock(&peerl.lock); - peerlast = NULL; - for (peer=peerl.peers;peer;) { - peernext = peer->next; - if (ast_test_flag(peer, SIP_DELME)) { - destroy_peer(peer); - if (peerlast) - peerlast->next = peernext; - else - peerl.peers = peernext; - } else - peerlast = peer; - peer=peernext; - } - ast_mutex_unlock(&peerl.lock); + ASTOBJ_CONTAINER_PRUNE_MARKED(&peerl,peerlast,peernext,peer,sip_destroy_peer); } /*--- sip_do_reload: Reload module */ @@ -9455,14 +9386,8 @@ static int sip_do_reload(void) reload_config(); prune_peers(); /* And start the monitor for the first time */ - ast_mutex_lock(®l.lock); - for (reg = regl.registrations; reg; reg = reg->next) - __sip_do_register(reg); - ast_mutex_unlock(®l.lock); - ast_mutex_lock(&peerl.lock); - for (peer = peerl.peers; peer; peer = peer->next) - sip_poke_peer(peer); - ast_mutex_unlock(&peerl.lock); + ASTOBJ_CONTAINER_TRAVERSE(®l,reg,__sip_do_register(reg)); + ASTOBJ_CONTAINER_TRAVERSE(&peerl,peer,sip_poke_peer(peer)); return 0; } @@ -9496,9 +9421,9 @@ int load_module() struct sip_peer *peer; struct sip_registry *reg; - ast_mutex_init(&userl.lock); - ast_mutex_init(&peerl.lock); - ast_mutex_init(®l.lock); + ASTOBJ_CONTAINER_INIT(&userl); + ASTOBJ_CONTAINER_INIT(&peerl); + ASTOBJ_CONTAINER_INIT(®l); sched = sched_context_create(); if (!sched) { ast_log(LOG_WARNING, "Unable to create schedule context\n"); @@ -9517,6 +9442,7 @@ int load_module() return -1; } ast_cli_register(&cli_show_users); + ast_cli_register(&cli_show_objects); ast_cli_register(&cli_show_subscriptions); ast_cli_register(&cli_show_channels); ast_cli_register(&cli_show_channel); @@ -9540,15 +9466,8 @@ int load_module() ast_register_application(app_dtmfmode, sip_dtmfmode, synopsis_dtmfmode, descrip_dtmfmode); ast_register_application(app_sipaddheader, sip_addheader, synopsis_sipaddheader, descrip_sipaddheader); ast_register_application(app_sipgetheader, sip_getheader, synopsis_sipgetheader, descrip_sipgetheader); - ast_mutex_lock(&peerl.lock); - for (peer = peerl.peers; peer; peer = peer->next) - sip_poke_peer(peer); - ast_mutex_unlock(&peerl.lock); - - ast_mutex_lock(®l.lock); - for (reg = regl.registrations; reg; reg = reg->next) - __sip_do_register(reg); - ast_mutex_unlock(®l.lock); + ASTOBJ_CONTAINER_TRAVERSE(&peerl,peer,sip_poke_peer(peer)); + ASTOBJ_CONTAINER_TRAVERSE(®l,reg,__sip_do_register(reg)); /* And start the monitor for the first time */ restart_monitor(); @@ -9565,6 +9484,7 @@ int unload_module() ast_unregister_application(app_sipaddheader); ast_unregister_application(app_sipgetheader); ast_cli_unregister(&cli_show_users); + ast_cli_unregister(&cli_show_objects); ast_cli_unregister(&cli_show_channels); ast_cli_unregister(&cli_show_channel); ast_cli_unregister(&cli_show_history); @@ -9634,9 +9554,9 @@ int unload_module() } /* Free memory for local network address mask */ ast_free_ha(localaddr); - ast_mutex_destroy(&userl.lock); - ast_mutex_destroy(&peerl.lock); - ast_mutex_destroy(®l.lock); + ASTOBJ_CONTAINER_RELEASE(&userl); + ASTOBJ_CONTAINER_RELEASE(&peerl); + ASTOBJ_CONTAINER_RELEASE(®l); return 0; } diff --git a/include/asterisk/astobj.h b/include/asterisk/astobj.h new file mode 100755 index 000000000..4ce6ed2d0 --- /dev/null +++ b/include/asterisk/astobj.h @@ -0,0 +1,312 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Object Model for Asterisk + * + * Copyright (C) 2004-2005, Digium, Inc. + * + * Mark Spencer <markster@digium.com> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#ifndef _ASTERISK_ASTOBJ_H +#define _ASTERISK_ASTOBJ_H + +#include <string.h> + +/*! + \file astobj.h + \brief A set of macros implementing the asterisk object and container. Macros + are used for maximum performance, to support multiple inheritance, and + to be easily integrated into existing structures without additional + malloc calls, etc. + +*/ + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define ASTOBJ_DEFAULT_NAMELEN 80 +#define ASTOBJ_DEFAULT_BUCKETS 256 +#define ASTOBJ_DEFAULT_HASH ast_strhash + +#define ASTOBJ_FLAG_DELME (1 << 0) /* Object has been deleted, remove on last unref */ +#define ASTOBJ_FLAG_MARKED (1 << 1) /* Object has been marked for possible deletion */ + +/* C++ is simply a syntactic crutch for those who cannot think for themselves + in an object oriented way. */ + +#ifdef ASTOBJ_CONTAINER_HASHMODEL +#define __ASTOBJ_HASH(type,hashes) \ + type *next[hashes] +#else +#define __ASTOBJ_HASH(type,hashes) \ + type *next[1] +#endif + +#define ASTOBJ_COMPONENTS_NOLOCK_FULL(type,namelen,hashes) \ + char name[namelen]; \ + int refcount; \ + int objflags; \ + __ASTOBJ_HASH(type,hashes) + +#define ASTOBJ_COMPONENTS_NOLOCK(type) \ + ASTOBJ_COMPONENTS_NOLOCK_FULL(type,ASTOBJ_DEFAULT_NAMELEN,1) + +#define ASTOBJ_COMPONENTS(type) \ + ast_mutex_t lock; \ + ASTOBJ_COMPONENTS_NOLOCK(type) + +#define ASTOBJ_COMPONENTS_FULL(type,namelen,hashes) \ + ast_mutex_t lock; \ + ASTOBJ_COMPONENTS_NOLOCK_FULL(type,namelen,hashes) + +#define ASTOBJ_REF(object) \ + do { \ + ast_mutex_lock(&(object)->lock); \ + (object)->refcount++; \ + ast_mutex_unlock(&(object)->lock); \ + } while(0) + +#define ASTOBJ_UNREF(object,destructor) \ + do { \ + int destroyme; \ + ast_mutex_lock(&(object)->lock); \ + if ((object)->refcount > 0) \ + (object)->refcount--; \ + else \ + ast_log(LOG_WARNING, "Unreferencing unreferenced (object)!\n"); \ + destroyme = (!(object)->refcount) && ((object)->objflags & ASTOBJ_FLAG_DELME); \ + ast_mutex_unlock(&(object)->lock); \ + if (destroyme) \ + destructor((object)); \ + } while(0) + +#define ASTOBJ_MARK(object) \ + (object)->objflags |= ASTOBJ_FLAG_MARKED; + +#define ASTOBJ_UNMARK(object) \ + (object)->objflags &= ~ASTOBJ_FLAG_MARKED; + +#define ASTOBJ_DESTROY(object,destructor) \ + do { \ + int destroyme; \ + ast_mutex_lock(&(object)->lock); \ + destroyme = (!(object)->refcount); \ + (object)->objflags |= ASTOBJ_FLAG_DELME; \ + ast_mutex_unlock(&(object)->lock); \ + if (destroyme) \ + destructor((object)); \ + } while(0) + +#define ASTOBJ_INIT(object) \ + do { \ + ast_mutex_init(&(object)->lock); \ + object->name[0] = '\0'; \ + } while(0) + +/* Containers for objects -- current implementation is linked lists, but + should be able to be converted to hashes relatively easily */ + +#ifdef ASTOBJ_CONTAINER_HASHMODEL +#error "Hash model for object containers not yet implemented!" +#else +/* Linked lists */ +#define ASTOBJ_CONTAINER_COMPONENTS_NOLOCK_FULL(type,hashes,buckets) \ + type *head + +#define ASTOBJ_CONTAINER_INIT_FULL(container,hashes,buckets) \ + do { \ + ast_mutex_init(&(container)->lock); \ + } while(0) + +#define ASTOBJ_CONTAINER_RELEASE_FULL(container,hashes,buckets) \ + do { \ + ast_mutex_destroy(&(container)->lock); \ + } while(0) + +#define ASTOBJ_CONTAINER_TRAVERSE(container,iterator,eval) \ + do { \ + ast_mutex_lock(&((container)->lock)); \ + (iterator) = (container)->head; \ + while((iterator)) { \ + ast_mutex_lock(&(iterator)->lock); \ + eval; \ + ast_mutex_unlock(&(iterator)->lock); \ + (iterator) = (iterator)->next[0]; \ + } \ + ast_mutex_unlock(&(container)->lock); \ + } while(0) + +#define ASTOBJ_CONTAINER_FIND_FULL(container,iterator,data,field,hashfunc,hashoffset,comparefunc) \ + do { \ + int res; \ + ast_mutex_lock(&((container)->lock)); \ + (iterator) = (container)->head; \ + while((iterator)) { \ + ast_mutex_lock(&(iterator)->lock); \ + res = (comparefunc((iterator)->field,(data))); \ + if (!res) \ + ASTOBJ_REF((iterator)); \ + ast_mutex_unlock(&(iterator)->lock); \ + if (!res) \ + break; \ + (iterator) = (iterator)->next[0]; \ + } \ + ast_mutex_unlock(&(container)->lock); \ + } while(0) + +#define ASTOBJ_CONTAINER_DESTROYALL(container,iterator,destructor) \ + do { \ + ast_mutex_lock(&((container)->lock)); \ + (iterator) = (container)->head; \ + while((iterator)) { \ + (container)->head = (iterator)->next[0]; \ + ASTOBJ_DESTROY(iterator,destructor); \ + (iterator) = (container)->head; \ + } \ + ast_mutex_unlock(&(container)->lock); \ + } while(0) + +#define ASTOBJ_CONTAINER_UNLINK_FULL(container,iterator,data,field,hashfunc,hashoffset,comparefunc) \ + do { \ + int res=-1; \ + ast_mutex_lock(&((container)->lock)); \ + (iterator) = (container)->head; \ + if ((iterator)) { \ + ast_mutex_lock(&(iterator)->lock); \ + res = (comparefunc((iterator)->field,(data))); \ + if (!res && ((iterator)->refcount < 1)) \ + ast_log(LOG_WARNING, "Unlink called with refcount < 1!\n"); \ + ast_mutex_unlock(&(iterator)->lock); \ + if (!res) \ + (container)->head = (iterator)->next[0]; \ + else while((iterator)->next[0]) { \ + ast_mutex_lock(&(iterator)->next[0]->lock); \ + res = (comparefunc((iterator)->next[0]->field,(data))); \ + if (!res && ((iterator)->next[0]->refcount < 1)) \ + ast_log(LOG_WARNING, "Unlink called with refcount < 1!\n"); \ + ast_mutex_unlock(&(iterator)->next[0]->lock); \ + if (!res) { \ + (iterator)->next[0] = (iterator)->next[0]->next[0]; \ + break; \ + } \ + (iterator) = (iterator)->next[0]; \ + } \ + } \ + ast_mutex_unlock(&(container)->lock); \ + } while(0) + +#define ASTOBJ_CONTAINER_FIND_UNLINK_FULL(container,reiterator,iterator,data,field,hashfunc,hashoffset,comparefunc) \ + do { \ + int res=-1; \ + (reiterator) = NULL; \ + ast_mutex_lock(&((container)->lock)); \ + (iterator) = (container)->head; \ + if ((iterator)) { \ + ast_mutex_lock(&(iterator)->lock); \ + res = (comparefunc((iterator)->field,(data))); \ + if (!res && ((iterator)->refcount < 1)) \ + ast_log(LOG_WARNING, "Unlink called with refcount < 1!\n"); \ + ast_mutex_unlock(&(iterator)->lock); \ + if (!res) {\ + (reiterator) = (container)->head; \ + (container)->head = (iterator)->next[0]; \ + } else while((iterator)->next[0]) { \ + ast_mutex_lock(&(iterator)->next[0]->lock); \ + res = (comparefunc((iterator)->next[0]->field,(data))); \ + ast_mutex_unlock(&(iterator)->next[0]->lock); \ + if (!res) { \ + (reiterator) = (iterator)->next[0]; \ + (iterator)->next[0] = (iterator)->next[0]->next[0]; \ + break; \ + } \ + (iterator) = (iterator)->next[0]; \ + } \ + } \ + ast_mutex_unlock(&(container)->lock); \ + } while(0) + +#define ASTOBJ_CONTAINER_PRUNE_MARKED(container,previ,nexti,iterator,destructor) \ + do { \ + (previ) = NULL; \ + ast_mutex_lock(&((container)->lock)); \ + (iterator) = (container)->head; \ + while((iterator)) { \ + ast_mutex_lock(&(iterator)->lock); \ + (nexti) = (iterator)->next[0]; \ + if ((iterator)->objflags & ASTOBJ_FLAG_MARKED) { \ + if ((previ)) \ + (previ)->next[0] = (nexti); \ + else \ + (container)->head = (nexti); \ + ast_mutex_unlock(&(iterator)->lock); \ + ASTOBJ_DESTROY(iterator,destructor); \ + } else { \ + (previ) = (iterator); \ + ast_mutex_unlock(&(iterator)->lock); \ + } \ + (iterator) = (nexti); \ + } \ + ast_mutex_unlock(&(container)->lock); \ + } while(0) + +#define ASTOBJ_CONTAINER_LINK_FULL(container,newobj,data,field,hashfunc,hashoffset,comparefunc) \ + do { \ + ASTOBJ_REF(newobj); \ + ast_mutex_lock(&(container)->lock); \ + (newobj)->next[0] = (container)->head; \ + (container)->head = (newobj); \ + ast_mutex_unlock(&(container)->lock); \ + } while(0) + +#endif /* Hash model */ + +/* Common to hash and linked list models */ +#define ASTOBJ_CONTAINER_COMPONENTS_NOLOCK(type) \ + ASTOBJ_CONTAINER_COMPONENTS_NOLOCK_FULL(type,1,ASTOBJ_DEFAULT_BUCKETS) + +#define ASTOBJ_CONTAINER_COMPONENTS(type) \ + ast_mutex_t lock; \ + ASTOBJ_CONTAINER_COMPONENTS_NOLOCK(type) + +#define ASTOBJ_CONTAINER_INIT(container) \ + ASTOBJ_CONTAINER_INIT_FULL(container,1,ASTOBJ_DEFAULT_BUCKETS) + +#define ASTOBJ_CONTAINER_RELEASE(container) \ + ASTOBJ_CONTAINER_RELEASE_FULL(container,1,ASTOBJ_DEFAULT_BUCKETS) + +#define ASTOBJ_CONTAINER_FIND(container,iterator,namestr) \ + ASTOBJ_CONTAINER_FIND_FULL(container,iterator,namestr,name,ASTOBJ_DEFAULT_HASH,0,strcasecmp) + +#define ASTOBJ_CONTAINER_FIND_UNLINK(container,reiterator,iterator,namestr) \ + ASTOBJ_CONTAINER_FIND_UNLINK_FULL(container,reiterator,iterator,namestr,name,ASTOBJ_DEFAULT_HASH,0,strcasecmp) + +#define ASTOBJ_CONTAINER_UNLINK(container,iterator,namestr) \ + ASTOBJ_CONTAINER_UNLINK_FULL(container,iterator,namestr,name,ASTOBJ_DEFAULT_HASH,0,strcasecmp) + +#define ASTOBJ_CONTAINER_LINK(container,newobj) \ + ASTOBJ_CONTAINER_LINK_FULL(container,newobj,(newobj)->name,name,ASTOBJ_DEFAULT_HASH,0,strcasecmp) + +#define ASTOBJ_CONTAINER_MARKALL(container,iterator) \ + ASTOBJ_CONTAINER_TRAVERSE(container,iterator,(iterator)->objflags |= ASTOBJ_FLAG_MARKED) + +#define ASTOBJ_CONTAINER_UNMARKALL(container,iterator) \ + ASTOBJ_CONTAINER_TRAVERSE(container,iterator,(iterator)->objflags &= ~ASTOBJ_FLAG_MARKED) + +#define ASTOBJ_DUMP(s,slen,obj) \ + snprintf((s),(slen),"name: %s\nobjflags: %d\nrefcount: %d\n\n", (obj)->name, (obj)->objflags, (obj)->refcount); + +#define ASTOBJ_CONTAINER_DUMP(fd,s,slen,container,iterator) \ + ASTOBJ_CONTAINER_TRAVERSE(container,iterator,do { ASTOBJ_DUMP(s,slen,iterator); ast_cli(fd, s); } while(0)) + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + + + +#endif |