Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.797 diff -u -r1.797 chan_sip.c --- channels/chan_sip.c 28 Jul 2005 18:24:04 -0000 1.797 +++ channels/chan_sip.c 31 Jul 2005 19:08:45 -0000 @@ -111,8 +111,9 @@ #define DEFAULT_FREQ_OK 60 * 1000 /* How often to check for the host to be up */ #define DEFAULT_FREQ_NOTOK 10 * 1000 /* How often to check, if the host is down... */ -#define DEFAULT_RETRANS 2000 /* How frequently to retransmit */ -#define MAX_RETRANS 5 /* Try only 5 times for retransmissions */ +#define DEFAULT_RETRANS 1000 /* How frequently to retransmit */ + /* 2 * 500 ms in RFC 3261 */ +#define MAX_RETRANS 7 /* Try only 7 times for retransmissions */ #define DEBUG_READ 0 /* Recieved data */ @@ -360,6 +361,7 @@ static int compactheaders = 0; /* send compact sip headers */ static int recordhistory = 0; /* Record SIP history. Off by default */ +static int dumphistory = 0; /* Dump history to verbose before destroying SIP dialog */ static char global_musicclass[MAX_MUSICCLASS] = ""; /* Global music on hold class */ #define DEFAULT_REALM "asterisk" @@ -501,6 +503,7 @@ ast_group_t pickupgroup; /* Pickup group */ int lastinvite; /* Last Cseq of invite */ unsigned int flags; /* SIP_ flags */ + int timer_t1; /* SIP timer T1, ms rtt */ unsigned int sipoptions; /* Supported SIP sipoptions on the other end */ int capability; /* Special capability (codec) */ int jointcapability; /* Supported capability at both ends (codecs ) */ @@ -605,6 +608,8 @@ unsigned int flags; /* non-zero if this is a response packet (e.g. 200 OK) */ struct sip_pvt *owner; /* Owner call */ int retransid; /* Retransmission ID */ + int timer_a; /* SIP timer A, retransmission timer */ + int timer_t1; /* SIP Timer T1, estimated RTT or 500 ms */ int packetlen; /* Length of packet */ char data[0]; }; @@ -819,6 +824,7 @@ static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, char *realm); /* Find authentication for a specific realm */ static void append_date(struct sip_request *req); /* Append date to SIP packet */ static int determine_firstline_parts(struct sip_request *req); +static void sip_dump_history(struct sip_pvt *dialog); /* Dump history to LOG_DEBUG at end of dialog, before destroying data */ /* Definition of this channel for channel registration */ static const struct ast_channel_tech sip_tech = { @@ -1032,69 +1038,111 @@ /*--- retrans_pkt: Retransmit SIP message if no answer ---*/ static int retrans_pkt(void *data) { - struct sip_pkt *pkt=data, *prev, *cur; - int res = 0; + struct sip_pkt *pkt=data, *prev, *cur = NULL; char iabuf[INET_ADDRSTRLEN]; + int reschedule = DEFAULT_RETRANS; + + /* Lock channel */ ast_mutex_lock(&pkt->owner->lock); + if (pkt->retrans < MAX_RETRANS) { + char buf[80]; + pkt->retrans++; - if (sip_debug_test_pvt(pkt->owner)) { + if (!pkt->timer_t1) { /* Re-schedule using timer_a and timer_t1 */ + if (sipdebug && option_debug > 3) + ast_log(LOG_DEBUG, "SIP TIMER: Not rescheduling id #%d:%s (Method %d) (No timer T1)\n", pkt->retransid, sip_methods[pkt->method].text, pkt->method); + } else { + int siptimer_a; + + if (sipdebug && option_debug > 3) + ast_log(LOG_DEBUG, "SIP TIMER: Rescheduling retransmission #%d (%d) %s - %d\n", pkt->retransid, pkt->retrans, sip_methods[pkt->method].text, pkt->method); + if (!pkt->timer_a) + pkt->timer_a = 2 ; + else + pkt->timer_a = 2 * pkt->timer_a; + + /* For non-invites, a maximum of 4 secs */ + if (pkt->method != SIP_INVITE && pkt->timer_a > 4000) + pkt->timer_a = 4000; + siptimer_a = pkt->timer_t1 * pkt->timer_a; /* Double each time */ + + /* Reschedule re-transmit */ + reschedule = siptimer_a; + if (option_debug > 3) + ast_log(LOG_DEBUG, "** SIP timers: Rescheduling retransmission %d to %d ms (t1 %d ms (Retrans id #%d)) \n", pkt->retrans +1, siptimer_a, pkt->timer_t1, pkt->retransid); + } + + if (pkt->owner && sip_debug_test_pvt(pkt->owner)) { if (ast_test_flag(pkt->owner, SIP_NAT) & SIP_NAT_ROUTE) ast_verbose("Retransmitting #%d (NAT) to %s:%d:\n%s\n---\n", pkt->retrans, ast_inet_ntoa(iabuf, sizeof(iabuf), pkt->owner->recv.sin_addr), ntohs(pkt->owner->recv.sin_port), pkt->data); else ast_verbose("Retransmitting #%d (no NAT) to %s:%d:\n%s\n---\n", pkt->retrans, ast_inet_ntoa(iabuf, sizeof(iabuf), pkt->owner->sa.sin_addr), ntohs(pkt->owner->sa.sin_port), pkt->data); } - append_history(pkt->owner, "ReTx", pkt->data); + snprintf(buf, sizeof(buf), "ReTx %d", reschedule); + + append_history(pkt->owner, buf, pkt->data); __sip_xmit(pkt->owner, pkt->data, pkt->packetlen); - res = 1; - } else { + ast_mutex_unlock(&pkt->owner->lock); + return reschedule; + } + /* Too many retries */ + if (pkt->owner && pkt->method != SIP_OPTIONS) { ast_log(LOG_WARNING, "Maximum retries exceeded on call %s for seqno %d (%s %s)\n", pkt->owner->callid, pkt->seqno, (ast_test_flag(pkt, FLAG_FATAL)) ? "Critical" : "Non-critical", (ast_test_flag(pkt, FLAG_RESPONSE)) ? "Response" : "Request"); - append_history(pkt->owner, "MaxRetries", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)"); - pkt->retransid = -1; - if (ast_test_flag(pkt, FLAG_FATAL)) { - while(pkt->owner->owner && ast_mutex_trylock(&pkt->owner->owner->lock)) { - ast_mutex_unlock(&pkt->owner->lock); - usleep(1); - ast_mutex_lock(&pkt->owner->lock); - } - if (pkt->owner->owner) { - ast_set_flag(pkt->owner, SIP_ALREADYGONE); - ast_queue_hangup(pkt->owner->owner); - ast_mutex_unlock(&pkt->owner->owner->lock); - } else { - /* If no owner, destroy now */ - ast_set_flag(pkt->owner, SIP_NEEDDESTROY); - } + } else { + if (pkt->method == SIP_OPTIONS && sipdebug) + ast_log(LOG_WARNING, "Cancelling retransmit of OPTIONs (call id %s) \n", pkt->owner->callid); + } + append_history(pkt->owner, "MaxRetries", (ast_test_flag(pkt, FLAG_FATAL)) ? "(Critical)" : "(Non-critical)"); + + pkt->retransid = -1; + + if (ast_test_flag(pkt, FLAG_FATAL)) { + while(pkt->owner->owner && ast_mutex_trylock(&pkt->owner->owner->lock)) { + ast_mutex_unlock(&pkt->owner->lock); + usleep(1); + ast_mutex_lock(&pkt->owner->lock); } - /* In any case, go ahead and remove the packet */ - prev = NULL; - cur = pkt->owner->packets; - while(cur) { - if (cur == pkt) - break; - prev = cur; - cur = cur->next; + if (pkt->owner->owner) { + ast_set_flag(pkt->owner, SIP_ALREADYGONE); + ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet.\n", pkt->owner->callid); + ast_queue_hangup(pkt->owner->owner); + ast_mutex_unlock(&pkt->owner->owner->lock); + } else { + /* If no channel owner, destroy now */ + ast_set_flag(pkt->owner, SIP_NEEDDESTROY); } - if (cur) { - if (prev) - prev->next = cur->next; - else - pkt->owner->packets = cur->next; - ast_mutex_unlock(&pkt->owner->lock); - free(cur); - pkt = NULL; - } else - ast_log(LOG_WARNING, "Weird, couldn't find packet owner!\n"); } + /* In any case, go ahead and remove the packet */ + prev = NULL; + cur = pkt->owner->packets; + while(cur) { + if (cur == pkt) + break; + prev = cur; + cur = cur->next; + } + if (cur) { + if (prev) + prev->next = cur->next; + else + pkt->owner->packets = cur->next; + ast_mutex_unlock(&pkt->owner->lock); + free(cur); + pkt = NULL; + } else + ast_log(LOG_WARNING, "Weird, couldn't find packet owner!\n"); if (pkt) ast_mutex_unlock(&pkt->owner->lock); - return res; + return 0; } /*--- __sip_reliable_xmit: transmit packet with retransmits ---*/ static int __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, char *data, int len, int fatal, int sipmethod) { struct sip_pkt *pkt; + int siptimer_a = DEFAULT_RETRANS; + pkt = malloc(sizeof(struct sip_pkt) + len + 1); if (!pkt) return -1; @@ -1107,10 +1155,16 @@ pkt->seqno = seqno; pkt->flags = resp; pkt->data[len] = '\0'; + pkt->timer_t1 = p->timer_t1; /* Set SIP timer T1 */ if (fatal) ast_set_flag(pkt, FLAG_FATAL); + if (pkt->timer_t1) + siptimer_a = pkt->timer_t1 * 2; + /* Schedule retransmission */ - pkt->retransid = ast_sched_add(sched, DEFAULT_RETRANS, retrans_pkt, pkt); + pkt->retransid = ast_sched_add_variable(sched, siptimer_a, retrans_pkt, pkt, 1); + if (option_debug > 3 && sipdebug) + ast_log(LOG_DEBUG, "*** SIP TIMER: Initalizing retransmit timer on packet: Id #%d\n", pkt->retransid); pkt->next = p->packets; p->packets = pkt; @@ -1181,6 +1235,7 @@ if ((cur->seqno == seqno) && ((ast_test_flag(cur, FLAG_RESPONSE)) == resp) && ((ast_test_flag(cur, FLAG_RESPONSE)) || (!strncasecmp(msg, cur->data, strlen(msg)) && (cur->data[strlen(msg)] < 33)))) { + ast_mutex_lock(&p->lock); if (!resp && (seqno == p->pendinginvite)) { ast_log(LOG_DEBUG, "Acked pending invite %d\n", p->pendinginvite); p->pendinginvite = 0; @@ -1191,16 +1246,20 @@ prev->next = cur->next; else p->packets = cur->next; - if (cur->retransid > -1) + if (cur->retransid > -1) { + if (sipdebug && option_debug > 3) + ast_log(LOG_DEBUG, "** SIP TIMER: Cancelling retransmit of packet (reply received) Retransid #%d\n", cur->retransid); ast_sched_del(sched, cur->retransid); + } free(cur); + ast_mutex_unlock(&p->lock); res = 0; break; } prev = cur; cur = cur->next; } - ast_log(LOG_DEBUG, "Stopping retransmission on '%s' of %s %d: %s\n", p->callid, resp ? "Response" : "Request", seqno, res ? "Not Found" : "Found"); + ast_log(LOG_DEBUG, "Stopping retransmission on '%s' of %s %d: Match %s\n", p->callid, resp ? "Response" : "Request", seqno, res ? "Not Found" : "Found"); return res; } @@ -1212,7 +1271,7 @@ char *c; while(p->packets) { if (cur == p->packets) { - ast_log(LOG_WARNING, "Have a packet that doesn't want to give up!\n"); + ast_log(LOG_WARNING, "Have a packet that doesn't want to give up! %s\n", sip_methods[cur->method].text); return -1; } cur = p->packets; @@ -1237,8 +1296,11 @@ ((ast_test_flag(cur, FLAG_RESPONSE)) || (!strncasecmp(msg, cur->data, strlen(msg)) && (cur->data[strlen(msg)] < 33)))) { /* this is our baby */ - if (cur->retransid > -1) + if (cur->retransid > -1) { + if (option_debug > 3 && sipdebug) + ast_log(LOG_DEBUG, "*** SIP TIMER: Cancelling retransmission #%d - %s (got response)\n", cur->retransid, msg); ast_sched_del(sched, cur->retransid); + } cur->retransid = -1; res = 0; break; @@ -1709,6 +1771,9 @@ r->maxtime = peer->maxms; r->callgroup = peer->callgroup; r->pickupgroup = peer->pickupgroup; + /* Set timer T1 to RTT for this peer (if known by qualify=) */ + if (peer->maxms && peer->lastms) + r->timer_t1 = peer->lastms; if (ast_test_flag(r, SIP_DTMF) == SIP_DTMF_RFC2833) r->noncodeccapability |= AST_RTP_DTMF; else @@ -1724,7 +1789,7 @@ /*--- create_addr: create address structure from peer name ---*/ /* Or, if peer not found, find it in the global DNS */ /* returns TRUE (-1) on failure, FALSE on success */ -static int create_addr(struct sip_pvt *r, char *opeer) +static int create_addr(struct sip_pvt *dialog, char *opeer) { struct hostent *hp; struct ast_hostent ahp; @@ -1741,12 +1806,13 @@ *port = '\0'; port++; } - r->sa.sin_family = AF_INET; + dialog->sa.sin_family = AF_INET; + dialog->timer_t1 = 500; /* Default SIP retransmission timer T1 (RFC 3261) */ p = find_peer(peer, NULL, 1); if (p) { found++; - if (create_addr_from_peer(r, p)) + if (create_addr_from_peer(dialog, p)) ASTOBJ_UNREF(p, sip_destroy_peer); } if (!p) { @@ -1771,10 +1837,10 @@ } hp = ast_gethostbyname(hostn, &ahp); if (hp) { - ast_copy_string(r->tohost, peer, sizeof(r->tohost)); - memcpy(&r->sa.sin_addr, hp->h_addr, sizeof(r->sa.sin_addr)); - r->sa.sin_port = htons(portno); - memcpy(&r->recv, &r->sa, sizeof(r->recv)); + ast_copy_string(dialog->tohost, peer, sizeof(dialog->tohost)); + memcpy(&dialog->sa.sin_addr, hp->h_addr, sizeof(dialog->sa.sin_addr)); + dialog->sa.sin_port = htons(portno); + memcpy(&dialog->recv, &dialog->sa, sizeof(dialog->recv)); return 0; } else { ast_log(LOG_WARNING, "No such host: %s\n", peer); @@ -1905,6 +1971,10 @@ if (sip_debug_test_pvt(p)) ast_verbose("Destroying call '%s'\n", p->callid); + + if (dumphistory) + sip_dump_history(p); + if (p->stateid > -1) ast_extension_state_del(p->stateid, NULL); if (p->initid > -1) @@ -1942,6 +2012,7 @@ p->history = p->history->next; free(hist); } + cur = iflist; while(cur) { if (cur == p) { @@ -1960,17 +2031,19 @@ } if (p->initid > -1) ast_sched_del(sched, p->initid); + while((cp = p->packets)) { p->packets = p->packets->next; - if (cp->retransid > -1) + if (cp->retransid > -1) { ast_sched_del(sched, cp->retransid); + } free(cp); } - ast_mutex_destroy(&p->lock); if (p->chanvars) { ast_variables_destroy(p->chanvars); p->chanvars = NULL; } + ast_mutex_destroy(&p->lock); free(p); } @@ -4670,6 +4743,11 @@ if (!r) return 0; + if (recordhistory) { + char tmp[80]; + snprintf(tmp, sizeof(tmp), "Account: %s@%s", r->username, r->hostname); + append_history(r->call, "RegistryRenew", tmp); + } /* Since registry's are only added/removed by the the monitor thread, this may be overkill to reference/dereference at all here */ if (sipdebug) @@ -4712,7 +4790,6 @@ r->call = NULL; ast_set_flag(p, SIP_NEEDDESTROY); /* Pretend to ACK anything just in case */ - /* OEJ: Ack what??? */ __sip_pretend_ack(p); } /* If we have a limit, stop registration and give up */ @@ -4770,6 +4847,11 @@ ast_log(LOG_WARNING, "Unable to allocate registration call\n"); return 0; } + if (recordhistory) { + char tmp[80]; + snprintf(tmp, sizeof(tmp), "Account: %s@%s", r->username, r->hostname); + append_history(p, "RegistryInit", tmp); + } /* Find address to hostname */ if (create_addr(p,r->hostname)) { /* we have what we hope is a temporary network error, @@ -4827,11 +4909,11 @@ /* set up a timeout */ if (auth == NULL) { if (r->timeout > -1) { - ast_log(LOG_WARNING, "Still have a registration timeout, %d\n", r->timeout); + ast_log(LOG_WARNING, "Still have a registration timeout, #%d - deleting it\n", r->timeout); ast_sched_del(sched, r->timeout); } r->timeout = ast_sched_add(sched, global_reg_timeout * 1000, sip_reg_timeout, r); - ast_log(LOG_DEBUG, "Scheduled a registration timeout for %s : %d\n", r->hostname, r->timeout); + ast_log(LOG_DEBUG, "Scheduled a registration timeout for %s id #%d \n", r->hostname, r->timeout); } if (strchr(r->username, '@')) { @@ -7584,7 +7666,7 @@ return RESULT_SUCCESS; } -/*--- sip_show_channel: Show details of one call ---*/ +/*--- sip_show_history: Show history details of one call ---*/ static int sip_show_history(int fd, int argc, char *argv[]) { struct sip_pvt *cur; @@ -7601,7 +7683,7 @@ ast_mutex_lock(&iflock); cur = iflist; while(cur) { - if (!strncasecmp(cur->callid, argv[3],len)) { + if (!strncasecmp(cur->callid, argv[3], len)) { ast_cli(fd,"\n"); if (cur->subscribed) ast_cli(fd, " * Subscription\n"); @@ -7627,6 +7709,35 @@ } +/*--- dump_history: Dump SIP history to debug log file at end of + lifespan for SIP dialog */ +void sip_dump_history(struct sip_pvt *dialog) +{ + int x; + struct sip_history *hist; + + if (!dialog) + return; + + ast_log(LOG_DEBUG, "\n---------- SIP HISTORY for '%s' \n", dialog->callid); + if (dialog->subscribed) + ast_log(LOG_DEBUG, " * Subscription\n"); + else + ast_log(LOG_DEBUG, " * SIP Call\n"); + x = 0; + hist = dialog->history; + while(hist) { + x++; + ast_log(LOG_DEBUG, " %d. %s\n", x, hist->event); + hist = hist->next; + } + if (!x) + ast_log(LOG_DEBUG, "Call '%s' has no history\n", dialog->callid); + ast_log(LOG_DEBUG, "\n---------- END SIP HISTORY for '%s' \n", dialog->callid); + +} + + /*--- receive_info: Receive SIP INFO Message ---*/ /* Doesn't read the duration of the DTMF signal */ static void receive_info(struct sip_pvt *p, struct sip_request *req) @@ -7891,6 +8002,11 @@ /* No old challenge */ return -1; } + if (recordhistory) { + char tmp[80]; + snprintf(tmp, sizeof(tmp), "Try: %d", p->authtries); + append_history(p, "RegistryAuth", tmp); + } 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, SIP_REGISTER, digest, respheader); @@ -8434,7 +8550,7 @@ else expires_ms -= EXPIRY_GUARD_SECS * 1000; if (sipdebug) - ast_log(LOG_NOTICE, "Outbound Registration: Expiry for %s is %d sec (Scheduling reregistration in %d ms)\n", r->hostname, expires, expires_ms); + 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; @@ -8451,6 +8567,7 @@ struct sip_peer *peer; int pingtime; struct timeval tv; + if (resp != 100) { int statechanged = 0; int newstate = 0; @@ -10030,12 +10147,13 @@ return 0; } if (peer->call > 0) { - ast_log(LOG_NOTICE, "Still have a call...\n"); + if (sipdebug) + ast_log(LOG_NOTICE, "Still have a QUALIFY dialog active, deleting\n"); sip_destroy(peer->call); } p = peer->call = sip_alloc(NULL, NULL, 0, SIP_OPTIONS); if (!peer->call) { - ast_log(LOG_WARNING, "Unable to allocate call for poking peer '%s'\n", peer->name); + ast_log(LOG_WARNING, "Unable to allocate dialog for poking peer '%s'\n", peer->name); return -1; } memcpy(&p->sa, &peer->addr, sizeof(p->sa)); @@ -10855,6 +10973,8 @@ outboundproxyip.sin_family = AF_INET; /* Type of address: IPv4 */ videosupport = 0; compactheaders = 0; + dumphistory = 0; + recordhistory = 0; relaxdtmf = 0; callevents = 0; ourport = DEFAULT_SIP_PORT; @@ -10970,13 +11090,17 @@ default_expiry = atoi(v->value); if (default_expiry < 1) default_expiry = DEFAULT_DEFAULT_EXPIRY; - } else if (!strcasecmp(v->name, "sipdebug")){ + } else if (!strcasecmp(v->name, "sipdebug")) { sipdebug = ast_true(v->value); - } else if (!strcasecmp(v->name, "registertimeout")){ + } else if (!strcasecmp(v->name, "dumphistory")) { + dumphistory = ast_true(v->value); + } else if (!strcasecmp(v->name, "recordhistory")) { + recordhistory = ast_true(v->value); + } else if (!strcasecmp(v->name, "registertimeout")) { global_reg_timeout = atoi(v->value); if (global_reg_timeout < 1) global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT; - } else if (!strcasecmp(v->name, "registerattempts")){ + } else if (!strcasecmp(v->name, "registerattempts")) { global_regattempts_max = atoi(v->value); } else if (!strcasecmp(v->name, "bindaddr")) { if (!(hp = ast_gethostbyname(v->value, &ahp))) { @@ -11016,8 +11140,6 @@ ast_parse_allow_disallow(&prefs, &global_capability, v->value, 0); } else if (!strcasecmp(v->name, "register")) { sip_register(v->value, v->lineno); - } else if (!strcasecmp(v->name, "recordhistory")) { - recordhistory = ast_true(v->value); } else if (!strcasecmp(v->name, "tos")) { if (sscanf(v->value, "%d", &format) == 1) tos = format & 0xff; Index: sched.c =================================================================== RCS file: /usr/cvsroot/asterisk/sched.c,v retrieving revision 1.22 diff -u -r1.22 sched.c --- sched.c 15 Jul 2005 23:00:46 -0000 1.22 +++ sched.c 31 Jul 2005 19:08:45 -0000 @@ -42,6 +42,7 @@ int id; /* ID number of event */ struct timeval when; /* Absolute time event should take place */ int resched; /* When to reschedule */ + int variable; /* Use return value from callback to reschedule */ void *data; /* Data */ ast_sched_cb callback; /* Callback */ }; @@ -209,7 +210,8 @@ return 0; } -int ast_sched_add(struct sched_context *con, int when, ast_sched_cb callback, void *data) + +int ast_sched_add_variable(struct sched_context *con, int when, ast_sched_cb callback, void *data, int variable) { /* * Schedule callback(data) to happen when ms into the future @@ -227,6 +229,7 @@ tmp->callback = callback; tmp->data = data; tmp->resched = when; + tmp->variable = variable; tmp->when = ast_tv(0, 0); if (sched_settime(&tmp->when, when)) { sched_release(con, tmp); @@ -243,6 +246,11 @@ return res; } +int ast_sched_add(struct sched_context *con, int when, ast_sched_cb callback, void *data) +{ + return ast_sched_add_variable(con, when, callback, data, 0); +} + int ast_sched_del(struct sched_context *con, int id) { /* @@ -359,7 +367,7 @@ * If they return non-zero, we should schedule them to be * run again. */ - if (sched_settime(¤t->when, current->resched)) { + if (sched_settime(¤t->when, current->variable? res : current->resched)) { sched_release(con, current); } else schedule(con, current); Index: include/asterisk/sched.h =================================================================== RCS file: /usr/cvsroot/asterisk/include/asterisk/sched.h,v retrieving revision 1.8 diff -u -r1.8 sched.h --- include/asterisk/sched.h 15 Jul 2005 23:00:47 -0000 1.8 +++ include/asterisk/sched.h 31 Jul 2005 19:08:45 -0000 @@ -62,9 +62,26 @@ * Schedule an event to take place at some point in the future. callback * will be called with data as the argument, when milliseconds into the * future (approximately) + * If callback returns 0, no further events will be re-scheduled * Returns a schedule item ID on success, -1 on failure */ extern int ast_sched_add(struct sched_context *con, int when, ast_sched_cb callback, void *data); + +/*!Adds a scheduled event */ +/*! + * \param con Schduler context to add + * \param when how many milliseconds to wait for event to occur + * \param callback function to call when the amount of time expires + * \param data data to pass to the callback + * \param variable If true, the result value of callback function will be + * used for rescheduling + * Schedule an event to take place at some point in the future. callback + * will be called with data as the argument, when milliseconds into the + * future (approximately) + * If callback returns 0, no further events will be re-scheduled + * Returns a schedule item ID on success, -1 on failure + */ +extern int ast_sched_add_variable(struct sched_context *con, int when, ast_sched_cb callback, void *data, int variable); /*! Deletes a scheduled event */ /*!