Index: channels/chan_agent.c =================================================================== --- channels/chan_agent.c (revision 7727) +++ channels/chan_agent.c (working copy) @@ -18,7 +18,6 @@ /*! \file - * * \brief Implementation of Agents (proxy channel) * * \author Mark Spencer @@ -139,17 +138,21 @@ " AckCall: Set to 'true' to require an acknowledgement by '#' when agent is called back\n" " WrapupTime: the minimum amount of time after disconnecting before the caller can receive a new call\n"; -static char moh[80] = "default"; +static char moh[MAX_MUSICCLASS] = "default"; -#define AST_MAX_AGENT 80 /**< Agent ID or Password max length */ +#define AST_MAX_AGENT 80 /*!< Agent ID or Password max length */ #define AST_MAX_BUF 256 +#ifndef AST_MAX_FILENAME_LEN #define AST_MAX_FILENAME_LEN 256 +#endif -/** Persistent Agents astdb family */ +/*! \brief Persistent Agents astdb family */ static const char pa_family[] = "/Agents"; -/** The maximum lengh of each persistent member agent database entry */ + +/*! \brief The maximum lengh of each persistent member agent database entry */ #define PA_MAX_LEN 2048 -/** queues.conf [general] option */ + +/*! \brief agents.conf [general] option */ static int persistent_agents = 0; static void dump_agents(void); @@ -178,45 +181,47 @@ #define GETAGENTBYCALLERID "AGENTBYCALLERID" -/** - * Structure representing an agent. +/*! + * \brief Structure representing an agent. */ struct agent_pvt { - ast_mutex_t lock; /**< Channel private lock */ - int dead; /**< Poised for destruction? */ - int pending; /**< Not a real agent -- just pending a match */ - int abouttograb; /**< About to grab */ - int autologoff; /**< Auto timeout time */ - int ackcall; /**< ackcall */ - time_t loginstart; /**< When agent first logged in (0 when logged off) */ - time_t start; /**< When call started */ - struct timeval lastdisc; /**< When last disconnected */ - int wrapuptime; /**< Wrapup time in ms */ - ast_group_t group; /**< Group memberships */ - int acknowledged; /**< Acknowledged */ - char moh[80]; /**< Which music on hold */ - char agent[AST_MAX_AGENT]; /**< Agent ID */ - char password[AST_MAX_AGENT]; /**< Password for Agent login */ - char name[AST_MAX_AGENT]; - ast_mutex_t app_lock; /**< Synchronization between owning applications */ - volatile pthread_t owning_app; /**< Owning application thread id */ - volatile int app_sleep_cond; /**< Sleep condition for the login app */ - struct ast_channel *owner; /**< Agent */ - char loginchan[80]; /**< channel they logged in from */ - char logincallerid[80]; /**< Caller ID they had when they logged in */ - struct ast_channel *chan; /**< Channel we use */ - struct agent_pvt *next; /**< Next Agent in the linked list. */ + ast_mutex_t lock; /*!< Channel private lock */ + int dead; /*!< Poised for destruction? */ + int pending; /*!< Not a real agent -- just pending a match */ + int abouttograb; /*!< About to grab */ + int autologoff; /*!< Auto timeout time */ + int ackcall; /*!< Acknowledge incoming call: 2:always/1:yes/0:no */ + time_t loginstart; /*!< When agent first logged in (0 when logged off) */ + time_t start; /*!< When call started */ + struct timeval lastdisc; /*!< When last disconnected */ + int wrapuptime; /*!< Wrapup time in ms */ + ast_group_t group; /*!< Group memberships */ + int acknowledged; /*!< Acknowledged */ + char moh[MAX_MUSICCLASS]; /*!< Which music on hold class */ + char agent[AST_MAX_AGENT]; /*!< Agent ID */ + char password[AST_MAX_AGENT]; /*!< Password for Agent login */ + char name[AST_MAX_AGENT]; /*!< Agent name */ + ast_mutex_t app_lock; /*!< Synchronization between owning applications */ + volatile pthread_t owning_app; /*!< Owning application thread id */ + volatile int app_sleep_cond; /*!< Sleep condition for the login app */ + struct ast_channel *owner; /*!< AST channel */ + char loginchan[80]; /*!< channel they logged in from */ + char logincallerid[80]; /*!< Caller ID they had when they logged in */ + struct ast_channel *chan; /*!< Channel we use */ + struct agent_pvt *next; /*!< Next Agent call in the linked list. */ }; -static struct agent_pvt *agents = NULL; /**< Holds the list of agents (loaded form agents.conf). */ +static struct agent_pvt *agents = NULL; /*!< Holds the list of agents (loaded form agents.conf). */ #define CHECK_FORMATS(ast, p) do { \ if (p->chan) {\ if (ast->nativeformats != p->chan->nativeformats) { \ - ast_log(LOG_DEBUG, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \ + if (option_debug) \ + ast_log(LOG_DEBUG, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \ /* Native formats changed, reset things */ \ ast->nativeformats = p->chan->nativeformats; \ - ast_log(LOG_DEBUG, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\ + if (option_debug) \ + ast_log(LOG_DEBUG, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\ ast_set_read_format(ast, ast->readformat); \ ast_set_write_format(ast, ast->writeformat); \ } \ @@ -242,6 +247,7 @@ } \ } while(0) +/* Forward declarations */ static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause); static int agent_devicestate(void *data); static int agent_digit(struct ast_channel *ast, char digit); @@ -256,6 +262,7 @@ static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static struct ast_channel *agent_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge); +/*! \brief The Agent channel PBX interface */ static const struct ast_channel_tech agent_tech = { .type = channeltype, .description = tdesc, @@ -276,27 +283,27 @@ .bridged_channel = agent_bridgedchannel, }; -/** - * Unlink (that is, take outside of the linked list) an agent. +/*! + * \brief Unlink (that is, take outside of the linked list) an agent. * - * @param agent Agent to be unlinked. + * \param agent Agent to be unlinked. */ static void agent_unlink(struct agent_pvt *agent) { struct agent_pvt *p, *prev; prev = NULL; p = agents; - // Iterate over all agents looking for the one. + /* Iterate over all agents looking for the one. */ while(p) { if (p == agent) { - // Once it wal found, check if it is the first one. + /* Once it wal found, check if it is the first one. */ if (prev) - // If it is not, tell the previous agent that the next one is the next one of the current (jumping the current). + /* If it is not, tell the previous agent that the next one is the next one of the current (jumping the current). */ prev->next = agent->next; else - // If it is the first one, just change the general pointer to point to the second one. + /* If it is the first one, just change the general pointer to point to the second one. */ agents = agent->next; - // We are done. + /* We are done. */ break; } prev = p; @@ -304,13 +311,13 @@ } } -/** - * Adds an agent to the global list of agents. +/*! + * \brief Adds an agent to the global list of agents. * - * @param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith" - * @param pending If it is pending or not. - * @return The just created agent. - * @sa agent_pvt, agents. + * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith" + * \param pending If it is pending or not. + * \return The just created agent. + * \sa agent_pvt, agents. */ static struct agent_pvt *add_agent(char *agent, int pending) { @@ -324,7 +331,7 @@ args = ast_strdupa(agent); - // Extract username (agt), password and name from agent (args). + /* Extract username (agt), password and name from agent (args). */ if ((argc = ast_app_separate_args(args, ',', argv, sizeof(argv) / sizeof(argv[0])))) { agt = argv[0]; if (argc > 1) { @@ -339,7 +346,7 @@ ast_log(LOG_WARNING, "A blank agent line!\n"); } - // Are we searching for the agent here ? to see if it exists already ? + /* Are we searching for the agent here ? to see if it exists already ? */ prev=NULL; p = agents; while(p) { @@ -348,27 +355,27 @@ prev = p; p = p->next; } + if (!p) { - // Build the agent. + /* Build the agent. */ p = malloc(sizeof(struct agent_pvt)); - if (p) { - memset(p, 0, sizeof(struct agent_pvt)); - ast_copy_string(p->agent, agt, sizeof(p->agent)); - ast_mutex_init(&p->lock); - ast_mutex_init(&p->app_lock); - p->owning_app = (pthread_t) -1; - p->app_sleep_cond = 1; - p->group = group; - p->pending = pending; - p->next = NULL; - if (prev) - prev->next = p; - else - agents = p; - - } else { + if (!p) { + ast_log(LOG_WARNING, "Memory allocation error, out of memory. Can't allocate agent %s\n", agt); return NULL; } + memset(p, 0, sizeof(struct agent_pvt)); + ast_copy_string(p->agent, agt, sizeof(p->agent)); + ast_mutex_init(&p->lock); + ast_mutex_init(&p->app_lock); + p->owning_app = (pthread_t) -1; + p->app_sleep_cond = 1; + p->group = group; + p->pending = pending; + p->next = NULL; + if (prev) + prev->next = p; + else + agents = p; } ast_copy_string(p->password, password ? password : "", sizeof(p->password)); @@ -398,11 +405,11 @@ return p; } -/** - * Deletes an agent after doing some clean up. +/*! + * \brief Deletes an agent after doing some clean up. * Further documentation: How safe is this function ? What state should the agent be to be cleaned. - * @param p Agent to be deleted. - * @returns Always 0. + * \param p Agent to be deleted. + * \returns Always 0. */ static int agent_cleanup(struct agent_pvt *p) { @@ -432,23 +439,22 @@ static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock) { - char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer; - char filename[AST_MAX_BUF]; + char tmp[AST_MAX_FILENAME_LEN], tmp2[AST_MAX_BUF], *pointer; + char filename[AST_MAX_FILENAME_LEN]; int res = -1; if (!p) return -1; if (!ast->monitor) { - snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid); + snprintf(filename, sizeof(filename), "agent-%s-%s", p->agent, ast->uniqueid); /* substitute . for - */ if ((pointer = strchr(filename, '.'))) *pointer = '-'; - snprintf(tmp, sizeof(tmp), "%s%s",savecallsin ? savecallsin : "", filename); + snprintf(tmp, sizeof(tmp), "%s%s", savecallsin ? savecallsin : "", filename); ast_monitor_start(ast, recordformat, tmp, needlock); ast_monitor_setjoinfiles(ast, 1); snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix ? urlprefix : "", filename, recordformatext); -#if 0 - ast_verbose("name is %s, link is %s\n",tmp, tmp2); -#endif + if (option_debug > 3) + ast_log(LOG_DEBUG, "Agent monitor filename is %s, link is %s\n",tmp, tmp2); if (!ast->cdr) ast->cdr = ast_cdr_alloc(); ast_cdr_setuserfield(ast, tmp2); @@ -469,6 +475,7 @@ struct ast_frame *f = NULL; static struct ast_frame null_frame = { AST_FRAME_NULL, }; static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER }; + ast_mutex_lock(&p->lock); CHECK_FORMATS(ast, p); if (p->chan) { @@ -487,7 +494,7 @@ /* Note that we don't hangup if it's not a callback because Asterisk will do it for us when the PBX instance that called login finishes */ if (!ast_strlen_zero(p->loginchan)) { - if (p->chan) + if (option_debug && p->chan) ast_log(LOG_DEBUG, "Bridge on '%s' being cleared (2)\n", p->chan->name); ast_hangup(p->chan); if (p->wrapuptime && p->acknowledged) @@ -546,7 +553,7 @@ if (p->chan && !p->chan->_bridge) { if (strcasecmp(p->chan->type, "Local")) { p->chan->_bridge = ast; - if (p->chan) + if (option_debug && p->chan) ast_log(LOG_DEBUG, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name); } } @@ -556,32 +563,41 @@ return f; } +/*! \brief Send HTML forward to channel, part of PBX interface */ static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen) { struct agent_pvt *p = ast->tech_pvt; - int res = -1; + int res; + + if (!p->chan) + return 0; + ast_mutex_lock(&p->lock); - if (p->chan) - res = ast_channel_sendhtml(p->chan, subclass, data, datalen); + res = ast_channel_sendhtml(p->chan, subclass, data, datalen); ast_mutex_unlock(&p->lock); return res; } +/*! \brief Send text forward to channel, part of PBX interface */ static int agent_sendtext(struct ast_channel *ast, const char *text) { struct agent_pvt *p = ast->tech_pvt; int res = -1; + + if (!p->chan) + return res; ast_mutex_lock(&p->lock); - if (p->chan) - res = ast_sendtext(p->chan, text); + res = ast_sendtext(p->chan, text); ast_mutex_unlock(&p->lock); return res; } +/*! \brief Write frame, part of PBX interface */ static int agent_write(struct ast_channel *ast, struct ast_frame *f) { struct agent_pvt *p = ast->tech_pvt; - int res = -1; + int res = 0; + CHECK_FORMATS(ast, p); ast_mutex_lock(&p->lock); if (p->chan) { @@ -589,19 +605,20 @@ (f->subclass == p->chan->writeformat)) { res = ast_write(p->chan, f); } else { - ast_log(LOG_DEBUG, "Dropping one incompatible voice frame on '%s' to '%s'\n", ast->name, p->chan->name); - res = 0; + if (option_debug) + ast_log(LOG_DEBUG, "Dropping one incompatible voice frame on '%s' to '%s'\n", ast->name, p->chan->name); } - } else - res = 0; + } CLEANUP(ast, p); ast_mutex_unlock(&p->lock); return res; } +/*! \brief Fixup channels during masquerading, part of pbx interface */ static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) { struct agent_pvt *p = newchan->tech_pvt; + ast_mutex_lock(&p->lock); if (p->owner != oldchan) { ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner); @@ -613,15 +630,17 @@ return 0; } +/*! \brief Indicate, part of PBX interface */ static int agent_indicate(struct ast_channel *ast, int condition) { struct agent_pvt *p = ast->tech_pvt; - int res = -1; + int res = 0; + + if (!p->chan) + return res; + ast_mutex_lock(&p->lock); - if (p->chan) - res = ast_indicate(p->chan, condition); - else - res = 0; + res = ast_indicate(p->chan, condition); ast_mutex_unlock(&p->lock); return res; } @@ -629,16 +648,17 @@ static int agent_digit(struct ast_channel *ast, char digit) { struct agent_pvt *p = ast->tech_pvt; - int res = -1; + int res = 0; + + if (!p->chan) + return res; ast_mutex_lock(&p->lock); - if (p->chan) - res = p->chan->tech->send_digit(p->chan, digit); - else - res = 0; + res = p->chan->tech->send_digit(p->chan, digit); ast_mutex_unlock(&p->lock); return res; } +/*! \brief Call, part of PBX interface */ static int agent_call(struct ast_channel *ast, char *dest, int timeout) { struct agent_pvt *p = ast->tech_pvt; @@ -648,7 +668,8 @@ p->acknowledged = 0; if (!p->chan) { if (p->pending) { - ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n"); + if (option_debug) + ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n"); newstate = AST_STATE_DIALING; res = 0; } else { @@ -663,7 +684,7 @@ time(&p->start); /* Call on this agent */ if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name); + ast_verbose(VERBOSE_PREFIX_3 "Placing outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name); if (p->chan->cid.cid_num) free(p->chan->cid.cid_num); if (ast->cid.cid_num) @@ -682,17 +703,22 @@ ast_mutex_unlock(&p->lock); return res; } - ast_verbose( VERBOSE_PREFIX_3 "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name); - ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", p->chan->language); + if (option_verbose > 2) + ast_verbose( VERBOSE_PREFIX_3 "Agent_call: call to agent '%s' call on '%s'\n", p->agent, p->chan->name); + if (option_debug > 2) + ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", p->chan->language); res = ast_streamfile(p->chan, beep, p->chan->language); - ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res); + if (option_debug > 2) + ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res); if (!res) { res = ast_waitstream(p->chan, ""); - ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res); + if (option_debug > 2) + ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res); } if (!res) { res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats)); - ast_log( LOG_DEBUG, "Set read format, result '%d'\n", res); + if (option_debug > 1) + ast_log( LOG_DEBUG, "Set read format, result '%d'\n", res); if (res) ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats))); } else { @@ -701,13 +727,13 @@ } if (!res) { - ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats)); - ast_log( LOG_DEBUG, "Set write format, result '%d'\n", res); + res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats)); + if (option_debug > 1) + ast_log( LOG_DEBUG, "Set write format, result '%d'\n", res); if (res) ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats))); } - if( !res ) - { + if (!res) { /* Call is immediately up, or might need ack */ if (p->ackcall > 1) newstate = AST_STATE_RINGING; @@ -726,7 +752,7 @@ return res; } -/* store/clear the global variable that stores agentid based on the callerid */ +/*! \brief store/clear the global variable that stores agentid based on the callerid */ static void set_agentbycallerid(const char *callerid, const char *agent) { char buf[AST_MAX_BUF]; @@ -735,14 +761,16 @@ if (ast_strlen_zero(callerid)) return; - snprintf(buf, sizeof(buf), "%s_%s",GETAGENTBYCALLERID, callerid); + snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid); pbx_builtin_setvar_helper(NULL, buf, agent); } +/*! \brief Hangup support, part of PBX interface */ static int agent_hangup(struct ast_channel *ast) { struct agent_pvt *p = ast->tech_pvt; int howlong = 0; + ast_mutex_lock(&p->lock); p->owner = NULL; ast->tech_pvt = NULL; @@ -760,7 +788,8 @@ usecnt--; ast_mutex_unlock(&usecnt_lock); - ast_log(LOG_DEBUG, "Hangup called for state %s\n", ast_state2str(ast->_state)); + if (option_debug) + ast_log(LOG_DEBUG, "Agent hangup called for state %s\n", ast_state2str(ast->_state)); if (p->start && (ast->_state != AST_STATE_UP)) { howlong = time(NULL) - p->start; p->start = 0; @@ -782,7 +811,8 @@ ast_hangup(p->chan); p->chan = NULL; } - ast_log(LOG_DEBUG, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff); + if (option_debug) + ast_log(LOG_DEBUG, "Hungup, howlong is %d, autologoff is %d\n", howlong, p->autologoff); if (howlong && p->autologoff && (howlong > p->autologoff)) { char agent[AST_MAX_AGENT] = ""; long logintime = time(NULL) - p->loginstart; @@ -856,7 +886,7 @@ } ast_mutex_unlock(&p->lock); #if 0 - if( !res ) + if( !res && option_debug > 2) ast_log( LOG_DEBUG, "agent_cont_sleep() returning %d\n", res ); #endif return res; @@ -865,49 +895,49 @@ static int agent_ack_sleep( void *data ) { struct agent_pvt *p; - int res=0; + int res = 0; int to = 1000; struct ast_frame *f; /* Wait a second and look for something */ p = (struct agent_pvt *)data; - if (p->chan) { - for(;;) { - to = ast_waitfor(p->chan, to); - if (to < 0) { - res = -1; - break; - } - if (!to) { - res = 0; - break; - } - f = ast_read(p->chan); - if (!f) { - res = -1; - break; - } - if (f->frametype == AST_FRAME_DTMF) - res = f->subclass; - else - res = 0; - ast_frfree(f); - ast_mutex_lock(&p->lock); - if (!p->app_sleep_cond) { - ast_mutex_unlock(&p->lock); - res = 0; - break; - } else if (res == '#') { - ast_mutex_unlock(&p->lock); - res = 1; - break; - } + if (!p->chan) + return -1; + + for(;;) { + to = ast_waitfor(p->chan, to); + if (to < 0) { + res = -1; + break; + } + if (!to) { + res = 0; + break; + } + f = ast_read(p->chan); + if (!f) { + res = -1; + break; + } + if (f->frametype == AST_FRAME_DTMF) + res = f->subclass; + else + res = 0; + ast_frfree(f); + ast_mutex_lock(&p->lock); + if (!p->app_sleep_cond) { ast_mutex_unlock(&p->lock); res = 0; + break; + } else if (res == '#') { + ast_mutex_unlock(&p->lock); + res = 1; + break; } - } else - res = -1; + ast_mutex_unlock(&p->lock); + res = 0; + } return res; } @@ -928,7 +958,7 @@ return ret; } -/*--- agent_new: Create new agent channel ---*/ +/*! \brief Create new agent channel */ static struct ast_channel *agent_new(struct agent_pvt *p, int state) { struct ast_channel *tmp; @@ -940,85 +970,84 @@ } #endif tmp = ast_channel_alloc(0); - if (tmp) { - tmp->tech = &agent_tech; + if (!tmp) { + ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n"); + return tmp; + } + tmp->tech = &agent_tech; + if (p->chan) { + tmp->nativeformats = p->chan->nativeformats; + tmp->writeformat = p->chan->writeformat; + tmp->rawwriteformat = p->chan->writeformat; + tmp->readformat = p->chan->readformat; + tmp->rawreadformat = p->chan->readformat; + ast_copy_string(tmp->language, p->chan->language, sizeof(tmp->language)); + ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context)); + ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten)); + } else { + tmp->nativeformats = AST_FORMAT_SLINEAR; + tmp->writeformat = AST_FORMAT_SLINEAR; + tmp->rawwriteformat = AST_FORMAT_SLINEAR; + tmp->readformat = AST_FORMAT_SLINEAR; + tmp->rawreadformat = AST_FORMAT_SLINEAR; + } + if (p->pending) + snprintf(tmp->name, sizeof(tmp->name), "Agent/P%s-%d", p->agent, rand() & 0xffff); + else + snprintf(tmp->name, sizeof(tmp->name), "Agent/%s", p->agent); + tmp->type = channeltype; + /* Safe, agentlock already held */ + ast_setstate(tmp, state); + tmp->tech_pvt = p; + p->owner = tmp; + ast_mutex_lock(&usecnt_lock); + usecnt++; + ast_mutex_unlock(&usecnt_lock); + ast_update_use_count(); + tmp->priority = 1; + /* Wake up and wait for other applications (by definition the login app) + * to release this channel). Takes ownership of the agent channel + * to this thread only. + * For signalling the other thread, ast_queue_frame is used until we + * can safely use signals for this purpose. The pselect() needs to be + * implemented in the kernel for this. + */ + p->app_sleep_cond = 0; + if( ast_mutex_trylock(&p->app_lock) ) { if (p->chan) { - tmp->nativeformats = p->chan->nativeformats; - tmp->writeformat = p->chan->writeformat; - tmp->rawwriteformat = p->chan->writeformat; - tmp->readformat = p->chan->readformat; - tmp->rawreadformat = p->chan->readformat; - ast_copy_string(tmp->language, p->chan->language, sizeof(tmp->language)); - ast_copy_string(tmp->context, p->chan->context, sizeof(tmp->context)); - ast_copy_string(tmp->exten, p->chan->exten, sizeof(tmp->exten)); - } else { - tmp->nativeformats = AST_FORMAT_SLINEAR; - tmp->writeformat = AST_FORMAT_SLINEAR; - tmp->rawwriteformat = AST_FORMAT_SLINEAR; - tmp->readformat = AST_FORMAT_SLINEAR; - tmp->rawreadformat = AST_FORMAT_SLINEAR; + ast_queue_frame(p->chan, &null_frame); + ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ + ast_mutex_lock(&p->app_lock); + ast_mutex_lock(&p->lock); } - if (p->pending) - snprintf(tmp->name, sizeof(tmp->name), "Agent/P%s-%d", p->agent, rand() & 0xffff); - else - snprintf(tmp->name, sizeof(tmp->name), "Agent/%s", p->agent); - tmp->type = channeltype; - /* Safe, agentlock already held */ - ast_setstate(tmp, state); - tmp->tech_pvt = p; - p->owner = tmp; - ast_mutex_lock(&usecnt_lock); - usecnt++; - ast_mutex_unlock(&usecnt_lock); - ast_update_use_count(); - tmp->priority = 1; - /* Wake up and wait for other applications (by definition the login app) - * to release this channel). Takes ownership of the agent channel - * to this thread only. - * For signalling the other thread, ast_queue_frame is used until we - * can safely use signals for this purpose. The pselect() needs to be - * implemented in the kernel for this. - */ - p->app_sleep_cond = 0; - if( ast_mutex_trylock(&p->app_lock) ) - { - if (p->chan) { - ast_queue_frame(p->chan, &null_frame); - ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ - ast_mutex_lock(&p->app_lock); - ast_mutex_lock(&p->lock); - } - if( !p->chan ) - { - ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n"); - p->owner = NULL; - tmp->tech_pvt = NULL; - p->app_sleep_cond = 1; - ast_channel_free( tmp ); - ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ - ast_mutex_unlock(&p->app_lock); - return NULL; - } + if( !p->chan ) { + ast_log(LOG_WARNING, "Agent disconnected while we were connecting the call\n"); + p->owner = NULL; + tmp->tech_pvt = NULL; + p->app_sleep_cond = 1; + ast_channel_free( tmp ); + ast_mutex_unlock(&p->lock); /* For other thread to read the condition. */ + ast_mutex_unlock(&p->app_lock); + return NULL; } - p->owning_app = pthread_self(); - /* After the above step, there should not be any blockers. */ - if (p->chan) { - if (ast_test_flag(p->chan, AST_FLAG_BLOCKING)) { - ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" ); - CRASH; - } - ast_moh_stop(p->chan); + } + p->owning_app = pthread_self(); + /* After the above step, there should not be any blockers. */ + if (p->chan) { + if (ast_test_flag(p->chan, AST_FLAG_BLOCKING)) { + ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" ); + CRASH; } - } else - ast_log(LOG_WARNING, "Unable to allocate agent channel structure\n"); + ast_moh_stop(p->chan); + } return tmp; } -/** - * Read configuration data. The file named agents.conf. +/*! + * \brief Read configuration data. The file named agents.conf. * - * @returns Always 0, or so it seems. + * \returns -1 on failure, otherwise 0 */ static int read_agent_config(void) { @@ -1034,7 +1063,7 @@ cfg = ast_config_load(config); if (!cfg) { ast_log(LOG_NOTICE, "No agent configuration found -- agent support disabled\n"); - return 0; + return -1; } ast_mutex_lock(&agentlock); p = agents; @@ -1185,13 +1214,14 @@ res = 0; } else { if (option_debug > 2) - ast_log( LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language); + ast_log(LOG_DEBUG, "Playing beep, lang '%s'\n", newlyavailable->chan->language); res = ast_streamfile(newlyavailable->chan, beep, newlyavailable->chan->language); if (option_debug > 2) - ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res); + ast_log(LOG_DEBUG, "Played beep, result '%d'\n", res); if (!res) { res = ast_waitstream(newlyavailable->chan, ""); - ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res); + if (option_debug > 2) + ast_log(LOG_DEBUG, "Waited for stream, result '%d'\n", res); } } if (!res) { @@ -1228,7 +1258,8 @@ struct agent_pvt *p; int res=0; - ast_log(LOG_DEBUG, "Checking beep availability of '%s'\n", newlyavailable->agent); + if (option_debug) + ast_log(LOG_DEBUG, "Checking beep availability of '%s'\n", newlyavailable->agent); if (needlock) ast_mutex_lock(&agentlock); p = agents; @@ -1258,7 +1289,7 @@ ast_log( LOG_DEBUG, "Played beep, result '%d'\n", res); if (!res) { res = ast_waitstream(newlyavailable->chan, ""); - if (option_debug) + if (option_debug > 2) ast_log( LOG_DEBUG, "Waited for stream, result '%d'\n", res); } ast_mutex_lock(&newlyavailable->lock); @@ -1266,7 +1297,7 @@ return res; } -/*--- agent_request: Part of the Asterisk PBX interface ---*/ +/*! \brief agent_request: Part of the Asterisk PBX interface ---*/ static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause) { struct agent_pvt *p; @@ -1350,7 +1381,7 @@ /* No agent available -- but we're requesting to wait for one. Allocate a place holder */ if (hasagent) { - if (option_debug) + if (option_debug > 1) ast_log(LOG_DEBUG, "Creating place holder for '%s'\n", s); p = add_agent(data, 1); p->group = groupmatch; @@ -1359,7 +1390,8 @@ ast_log(LOG_WARNING, "Weird... Fix this to drop the unused pending agent\n"); } } else - ast_log(LOG_DEBUG, "Not creating place holder for '%s' since nobody logged in\n", s); + if (option_debug > 1) + ast_log(LOG_DEBUG, "Not creating place holder for '%s' since nobody logged in\n", s); } if (hasagent) *cause = AST_CAUSE_BUSY; @@ -1378,13 +1410,13 @@ return 0; } -/** - * Lists agents and their status to the Manager API. +/*! + * \brief Lists agents and their status to the Manager API. * It is registered on load_module() and it gets called by the manager backend. - * @param s - * @param m - * @returns - * @sa action_agent_logoff(), action_agent_callback_login(), load_module(). + * \param s Manager session + * \param m Manager message (including action ID) + * \returns + * \sa action_agent_logoff(), action_agent_callback_login(), load_module(). */ static int action_agents(struct mansession *s, struct message *m) { @@ -1420,7 +1452,7 @@ /* Set a default status. It 'should' get changed. */ status = "AGENT_UNKNOWN"; - if (!ast_strlen_zero(p->loginchan) && !p->chan) { + if (!ast_strlen_zero(p->loginchan)) { loginChan = p->loginchan; talkingtoChan = "n/a"; status = "AGENT_IDLE"; @@ -1520,13 +1552,13 @@ return RESULT_SUCCESS; } -/** - * Sets an agent as no longer logged in in the Manager API. +/*! + * \brief Sets an agent as no longer logged in in the Manager API. * It is registered on load_module() and it gets called by the manager backend. - * @param s - * @param m - * @returns - * @sa action_agents(), action_agent_callback_login(), load_module(). + * \param s + * \param m + * \returns + * \sa action_agents(), action_agent_callback_login(), load_module(). */ static int action_agent_logoff(struct mansession *s, struct message *m) { @@ -1546,11 +1578,7 @@ soft = 0; ret = agent_logoff(agent, soft); - if (ret == 0) - astman_send_ack(s, m, "Agent logged out"); - else - astman_send_error(s, m, "No such agent"); - + astman_send_ack(s, m, ret ? "No such agent" : "Agent logged out"); return 0; } @@ -1575,8 +1603,8 @@ return NULL; } -/** - * Show agents in cli. +/*! + * \brief Show agents in cli. */ static int agents_show(int fd, int argc, char **argv) { @@ -1596,7 +1624,7 @@ ast_mutex_lock(&p->lock); if (p->pending) { if (p->group) - ast_cli(fd, "-- Pending call to group %d\n", powerof(p->group)); + ast_cli(fd, "-- Pending call to agent group %d\n", powerof(p->group)); else ast_cli(fd, "-- Pending call to agent %s\n", p->agent); } else { @@ -1633,10 +1661,10 @@ p = p->next; } ast_mutex_unlock(&agentlock); - if ( !count_agents ) { + if (!count_agents) { ast_cli(fd, "No Agents are configured in %s\n",config); } else { - ast_cli(fd, "%d agents configured [%d online , %d offline]\n",count_agents, online_agents, offline_agents); + ast_cli(fd, "%d agents configured (%d online , %d offline)\n",count_agents, online_agents, offline_agents); } ast_cli(fd, "\n"); @@ -1715,7 +1743,7 @@ max_login_tries = 0; tmpoptions=pbx_builtin_getvar_helper(chan, "AGENTMAXLOGINTRIES"); if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions,max_login_tries,chan->name); + ast_verbose(VERBOSE_PREFIX_3 "Saw variable AGENTMAXLOGINTRIES=%s, setting max_login_tries to: %d on Channel '%s'.\n",tmpoptions, max_login_tries, chan->name); } if (pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) { if (ast_true(pbx_builtin_getvar_helper(chan, "AGENTUPDATECDR"))) @@ -2111,39 +2139,39 @@ return -1; } -/** - * Called by the AgentLogin application (from the dial plan). +/*! + * \brief Called by the AgentLogin application (from the dial plan). * - * @param chan - * @param data - * @returns - * @sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module(). + * \param chan + * \param data + * \returns + * \sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module(). */ static int login_exec(struct ast_channel *chan, void *data) { return __login_exec(chan, data, 0); } -/** - * Called by the AgentCallbackLogin application (from the dial plan). +/*! + * \brief Called by the AgentCallbackLogin application (from the dial plan). * - * @param chan - * @param data - * @returns - * @sa login_exec(), agentmonitoroutgoing_exec(), load_module(). + * \param chan + * \param data + * \returns + * \sa login_exec(), agentmonitoroutgoing_exec(), load_module(). */ static int callback_exec(struct ast_channel *chan, void *data) { return __login_exec(chan, data, 1); } -/** - * Sets an agent as logged in by callback in the Manager API. +/*! + * \brief Sets an agent as logged in by callback in the Manager API. * It is registered on load_module() and it gets called by the manager backend. - * @param s - * @param m - * @returns - * @sa action_agents(), action_agent_logoff(), load_module(). + * \param s + * \param m + * \returns + * \sa action_agents(), action_agent_logoff(), load_module(). */ static int action_agent_callback_login(struct mansession *s, struct message *m) { @@ -2220,13 +2248,13 @@ return 0; } -/** - * Called by the AgentMonitorOutgoing application (from the dial plan). +/*! + * \brief Called by the AgentMonitorOutgoing application (from the dial plan). * - * @param chan - * @param data - * @returns - * @sa login_exec(), callback_login_exec(), load_module(). + * \param chan + * \param data + * \returns + * \sa login_exec(), callback_login_exec(), load_module(). */ static int agentmonitoroutgoing_exec(struct ast_channel *chan, void *data) { @@ -2235,6 +2263,7 @@ int changeoutgoing = 0; int res = 0; char agent[AST_MAX_AGENT]; + const char *tmp; if (data) { if (strchr(data, 'd')) @@ -2245,7 +2274,6 @@ changeoutgoing = 1; } if (chan->cid.cid_num) { - const char *tmp; char agentvar[AST_MAX_BUF]; snprintf(agentvar, sizeof(agentvar), "%s_%s", GETAGENTBYCALLERID, chan->cid.cid_num); if ((tmp = pbx_builtin_getvar_helper(NULL, agentvar))) { @@ -2285,8 +2313,8 @@ return 0; } -/** - * Dump AgentCallbackLogin agents to the database for persistence +/*! + * \brief Dump AgentCallbackLogin agents to the database for persistence */ static void dump_agents(void) { @@ -2300,9 +2328,9 @@ if (!ast_strlen_zero(cur_agent->loginchan)) { snprintf(buf, sizeof(buf), "%s;%s", cur_agent->loginchan, cur_agent->logincallerid); if (ast_db_put(pa_family, cur_agent->agent, buf)) - ast_log(LOG_WARNING, "failed to create persistent entry!\n"); + ast_log(LOG_WARNING, "failed to create persistant entry!\n"); else if (option_debug) - ast_log(LOG_DEBUG, "Saved Agent: %s on %s\n", cur_agent->agent, cur_agent->loginchan); + ast_log(LOG_DEBUG, "Saved persistant agent in astDB: %s on %s\n", cur_agent->agent, cur_agent->loginchan); } else { /* Delete - no agent or there is an error */ ast_db_del(pa_family, cur_agent->agent); @@ -2310,8 +2338,8 @@ } } -/** - * Reload the persistent agents from astdb. +/*! + * \brief Reload the persistant agents from astdb. */ static void reload_agents(void) { @@ -2344,7 +2372,7 @@ ast_mutex_unlock(&cur_agent->lock); if (!ast_db_get(pa_family, agent_num, agent_data, sizeof(agent_data)-1)) { if (option_debug) - ast_log(LOG_DEBUG, "Reload Agent: %s on %s\n", cur_agent->agent, agent_data); + ast_log(LOG_DEBUG, "Reload agent from astDB: %s on %s\n", cur_agent->agent, agent_data); parse = agent_data; agent_chan = strsep(&parse, ";"); agent_callerid = strsep(&parse, ";"); @@ -2361,12 +2389,12 @@ } ast_mutex_unlock(&agentlock); if (db_tree) { - ast_log(LOG_NOTICE, "Agents sucessfully reloaded from database.\n"); + ast_log(LOG_NOTICE, "Agents sucessfully reloaded from astDB database.\n"); ast_db_freetree(db_tree); } } -/*--- agent_devicestate: Part of PBX channel interface ---*/ +/*! \brief Part of PBX channel interface ---*/ static int agent_devicestate(void *data) { struct agent_pvt *p; @@ -2416,95 +2444,11 @@ return res; } -struct agent_pvt *find_agent(char *agentid) -{ - struct agent_pvt *cur = agents; - - for (; cur; cur = cur->next) { - if (!strcmp(cur->agent, agentid)) - break; - } - - return cur; -} - -static char *function_agent(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) -{ - char *agentid; - char *item; - char *tmp; - struct agent_pvt *agent; - - buf[0] = '\0'; - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "The AGENT function requires an argument - agentid!\n"); - return buf; - } - - item = ast_strdupa(data); - if (!item) { - ast_log(LOG_ERROR, "Out of memory!\n"); - return buf; - } - - agentid = strsep(&item, ":"); - if (!item) - item = "status"; - - agent = find_agent(agentid); - if (!agent) { - ast_log(LOG_WARNING, "Agent '%s' not found!\n", agentid); - return buf; - } - - if (!strcasecmp(item, "status")) { - if (agent->chan || !ast_strlen_zero(agent->loginchan)) { - ast_copy_string(buf, "LOGGEDIN", len); - } else { - ast_copy_string(buf, "LOGGEDOUT", len); - } - } else if (!strcasecmp(item, "password")) { - ast_copy_string(buf, agent->password, len); - } else if (!strcasecmp(item, "name")) { - ast_copy_string(buf, agent->name, len); - } else if (!strcasecmp(item, "mohclass")) { - ast_copy_string(buf, agent->moh, len); - } else if (!strcasecmp(item, "channel")) { - if (agent->chan) { - ast_copy_string(buf, agent->chan->name, len); - tmp = strrchr(buf, '-'); - if (tmp) - *tmp = '\0'; - } - } else if (!strcasecmp(item, "exten")) { - ast_copy_string(buf, agent->loginchan, len); - } - - return buf; -} - -struct ast_custom_function agent_function = { - .name = "AGENT", - .synopsis = "Gets information about an Agent", - .syntax = "AGENT([:item])", - .read = function_agent, - .desc = "The valid items to retrieve are:\n" - "- status (default) The status of the agent\n" - " LOGGEDIN | LOGGEDOUT\n" - "- password The password of the agent\n" - "- name The name of the agent\n" - "- mohclass MusicOnHold class\n" - "- exten The callback extension for the Agent (AgentCallbackLogin)\n" - "- channel The name of the active channel for the Agent (AgentLogin)\n" -}; - - -/** - * Initialize the Agents module. +/*! + * \brief Initialize the Agents module. * This funcion is being called by Asterisk when loading the module. Among other thing it registers applications, cli commands and reads the cofiguration file. * - * @returns int Always 0. + * \returns int Always 0. */ int load_module() { @@ -2513,6 +2457,13 @@ ast_log(LOG_ERROR, "Unable to register channel class %s\n", channeltype); return -1; } + + /* Read the configuration file */ + if (read_agent_config() == -1) { + ast_log(LOG_ERROR, "Unable to read agent configuration file %s\n", config); + return -1; + } + /* Dialplan applications */ ast_register_application(app, login_exec, synopsis, descrip); ast_register_application(app2, callback_exec, synopsis2, descrip2); @@ -2521,13 +2472,11 @@ ast_manager_register2("Agents", EVENT_FLAG_AGENT, action_agents, "Lists agents and their status", mandescr_agents); ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff, "Sets an agent as no longer logged in", mandescr_agent_logoff); ast_manager_register2("AgentCallbackLogin", EVENT_FLAG_AGENT, action_agent_callback_login, "Sets an agent as logged in by callback", mandescr_agent_callback_login); - /* CLI Commands */ + /* CLI Application */ ast_cli_register(&cli_show_agents); ast_cli_register(&cli_agent_logoff); - /* Dialplan Functions */ - ast_custom_function_register(&agent_function); - /* Read in the config */ - read_agent_config(); + + /* Reload saved agent states */ if (persistent_agents) reload_agents(); return 0; @@ -2544,24 +2493,27 @@ int unload_module() { struct agent_pvt *p; + /* First, take us out of the channel loop */ - /* Unregister dialplan functions */ - ast_custom_function_unregister(&agent_function); - /* Unregister CLI commands */ + /* Unregister CLI application */ ast_cli_unregister(&cli_show_agents); ast_cli_unregister(&cli_agent_logoff); + /* Unregister dialplan applications */ ast_unregister_application(app); ast_unregister_application(app2); ast_unregister_application(app3); + /* Unregister manager command */ ast_manager_unregister("Agents"); ast_manager_unregister("AgentLogoff"); ast_manager_unregister("AgentCallbackLogin"); + /* Unregister channel */ ast_channel_unregister(&agent_tech); + + /* Hangup all interfaces if they have an owner */ if (!ast_mutex_lock(&agentlock)) { - /* Hangup all interfaces if they have an owner */ p = agents; while(p) { if (p->owner)