2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
22 * \brief Implementation of Agents (proxy channel)
24 * \author Mark Spencer <markster@digium.com>
26 * This file is the implementation of Agents modules.
27 * It is a dynamic module that is loaded by Asterisk.
29 * \arg \ref Config_agent
31 * \ingroup channel_drivers
36 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
42 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 #include <sys/signal.h>
50 #include "asterisk/lock.h"
51 #include "asterisk/channel.h"
52 #include "asterisk/config.h"
53 #include "asterisk/logger.h"
54 #include "asterisk/module.h"
55 #include "asterisk/pbx.h"
56 #include "asterisk/options.h"
57 #include "asterisk/lock.h"
58 #include "asterisk/sched.h"
59 #include "asterisk/io.h"
60 #include "asterisk/rtp.h"
61 #include "asterisk/acl.h"
62 #include "asterisk/callerid.h"
63 #include "asterisk/file.h"
64 #include "asterisk/cli.h"
65 #include "asterisk/app.h"
66 #include "asterisk/musiconhold.h"
67 #include "asterisk/manager.h"
68 #include "asterisk/features.h"
69 #include "asterisk/utils.h"
70 #include "asterisk/causes.h"
71 #include "asterisk/astdb.h"
72 #include "asterisk/devicestate.h"
73 #include "asterisk/monitor.h"
74 #include "asterisk/stringfields.h"
76 static const char tdesc
[] = "Call Agent Proxy Channel";
77 static const char config
[] = "agents.conf";
79 static const char app
[] = "AgentLogin";
80 static const char app2
[] = "AgentCallbackLogin";
81 static const char app3
[] = "AgentMonitorOutgoing";
83 static const char synopsis
[] = "Call agent login";
84 static const char synopsis2
[] = "Call agent callback login";
85 static const char synopsis3
[] = "Record agent's outgoing call";
87 static const char descrip
[] =
88 " AgentLogin([AgentNo][|options]):\n"
89 "Asks the agent to login to the system. Always returns -1. While\n"
90 "logged in, the agent can receive calls and will hear a 'beep'\n"
91 "when a new call comes in. The agent can dump the call by pressing\n"
93 "The option string may contain zero or more of the following characters:\n"
94 " 's' -- silent login - do not announce the login ok segment after agent logged in/off\n";
96 static const char descrip2
[] =
97 " AgentCallbackLogin([AgentNo][|[options][|[exten]@context]]):\n"
98 "Asks the agent to login to the system with callback.\n"
99 "The agent's callback extension is called (optionally with the specified\n"
101 "The option string may contain zero or more of the following characters:\n"
102 " 's' -- silent login - do not announce the login ok segment agent logged in/off\n";
104 static const char descrip3
[] =
105 " AgentMonitorOutgoing([options]):\n"
106 "Tries to figure out the id of the agent who is placing outgoing call based on\n"
107 "comparison of the callerid of the current interface and the global variable \n"
108 "placed by the AgentCallbackLogin application. That's why it should be used only\n"
109 "with the AgentCallbackLogin app. Uses the monitoring functions in chan_agent \n"
110 "instead of Monitor application. That have to be configured in the agents.conf file.\n"
112 "Normally the app returns 0 unless the options are passed. Also if the callerid or\n"
113 "the agentid are not specified it'll look for n+101 priority.\n"
115 " 'd' - make the app return -1 if there is an error condition and there is\n"
116 " no extension n+101\n"
117 " 'c' - change the CDR so that the source of the call is 'Agent/agent_id'\n"
118 " 'n' - don't generate the warnings when there is no callerid or the\n"
119 " agentid is not known.\n"
120 " It's handy if you want to have one context for agent and non-agent calls.\n";
122 static const char mandescr_agents
[] =
123 "Description: Will list info about all possible agents.\n"
126 static const char mandescr_agent_logoff
[] =
127 "Description: Sets an agent as no longer logged in.\n"
128 "Variables: (Names marked with * are required)\n"
129 " *Agent: Agent ID of the agent to log off\n"
130 " Soft: Set to 'true' to not hangup existing calls\n";
132 static const char mandescr_agent_callback_login
[] =
133 "Description: Sets an agent as logged in with callback.\n"
134 "Variables: (Names marked with * are required)\n"
135 " *Agent: Agent ID of the agent to login\n"
136 " *Exten: Extension to use for callback\n"
137 " Context: Context to use for callback\n"
138 " AckCall: Set to 'true' to require an acknowledgement by '#' when agent is called back\n"
139 " WrapupTime: the minimum amount of time after disconnecting before the caller can receive a new call\n";
141 static char moh
[80] = "default";
143 #define AST_MAX_AGENT 80 /*!< Agent ID or Password max length */
144 #define AST_MAX_BUF 256
145 #define AST_MAX_FILENAME_LEN 256
147 static const char pa_family
[] = "/Agents"; /*!< Persistent Agents astdb family */
148 #define PA_MAX_LEN 2048 /*!< The maximum length of each persistent member agent database entry */
150 static int persistent_agents
= 0; /*!< queues.conf [general] option */
151 static void dump_agents(void);
153 static ast_group_t group
;
154 static int autologoff
;
155 static int wrapuptime
;
158 static int multiplelogin
= 1;
159 static int autologoffunavail
= 0;
161 static int maxlogintries
= 3;
162 static char agentgoodbye
[AST_MAX_FILENAME_LEN
] = "vm-goodbye";
164 static int recordagentcalls
= 0;
165 static char recordformat
[AST_MAX_BUF
] = "";
166 static char recordformatext
[AST_MAX_BUF
] = "";
167 static char urlprefix
[AST_MAX_BUF
] = "";
168 static char savecallsin
[AST_MAX_BUF
] = "";
169 static int updatecdr
= 0;
170 static char beep
[AST_MAX_BUF
] = "beep";
172 #define GETAGENTBYCALLERID "AGENTBYCALLERID"
174 /*! \brief Structure representing an agent. */
176 ast_mutex_t lock
; /*!< Channel private lock */
177 int dead
; /*!< Poised for destruction? */
178 int pending
; /*!< Not a real agent -- just pending a match */
179 int abouttograb
; /*!< About to grab */
180 int autologoff
; /*!< Auto timeout time */
181 int ackcall
; /*!< ackcall */
182 int deferlogoff
; /*!< Defer logoff to hangup */
183 time_t loginstart
; /*!< When agent first logged in (0 when logged off) */
184 time_t start
; /*!< When call started */
185 struct timeval lastdisc
; /*!< When last disconnected */
186 int wrapuptime
; /*!< Wrapup time in ms */
187 ast_group_t group
; /*!< Group memberships */
188 int acknowledged
; /*!< Acknowledged */
189 char moh
[80]; /*!< Which music on hold */
190 char agent
[AST_MAX_AGENT
]; /*!< Agent ID */
191 char password
[AST_MAX_AGENT
]; /*!< Password for Agent login */
192 char name
[AST_MAX_AGENT
];
193 ast_mutex_t app_lock
; /**< Synchronization between owning applications */
194 volatile pthread_t owning_app
; /**< Owning application thread id */
195 volatile int app_sleep_cond
; /**< Sleep condition for the login app */
196 struct ast_channel
*owner
; /**< Agent */
197 char loginchan
[80]; /**< channel they logged in from */
198 char logincallerid
[80]; /**< Caller ID they had when they logged in */
199 struct ast_channel
*chan
; /**< Channel we use */
200 AST_LIST_ENTRY(agent_pvt
) list
; /**< Next Agent in the linked list. */
203 static AST_LIST_HEAD_STATIC(agents
, agent_pvt
); /*!< Holds the list of agents (loaded form agents.conf). */
205 #define CHECK_FORMATS(ast, p) do { \
207 if (ast->nativeformats != p->chan->nativeformats) { \
208 ast_log(LOG_DEBUG, "Native formats changing from %d to %d\n", ast->nativeformats, p->chan->nativeformats); \
209 /* Native formats changed, reset things */ \
210 ast->nativeformats = p->chan->nativeformats; \
211 ast_log(LOG_DEBUG, "Resetting read to %d and write to %d\n", ast->readformat, ast->writeformat);\
212 ast_set_read_format(ast, ast->readformat); \
213 ast_set_write_format(ast, ast->writeformat); \
215 if (p->chan->readformat != ast->rawreadformat) \
216 ast_set_read_format(p->chan, ast->rawreadformat); \
217 if (p->chan->writeformat != ast->rawwriteformat) \
218 ast_set_write_format(p->chan, ast->rawwriteformat); \
222 /*! \brief Cleanup moves all the relevant FD's from the 2nd to the first, but retains things
223 properly for a timingfd XXX This might need more work if agents were logged in as agents or other
224 totally impractical combinations XXX */
226 #define CLEANUP(ast, p) do { \
229 for (x=0;x<AST_MAX_FDS;x++) {\
230 if (x != AST_TIMING_FD) \
231 ast->fds[x] = p->chan->fds[x]; \
233 ast->fds[AST_AGENT_FD] = p->chan->fds[AST_TIMING_FD]; \
237 /*--- Forward declarations */
238 static struct ast_channel
*agent_request(const char *type
, int format
, void *data
, int *cause
);
239 static int agent_devicestate(void *data
);
240 static void agent_logoff_maintenance(struct agent_pvt
*p
, char *loginchan
, long logintime
, const char *uniqueid
, char *logcommand
);
241 static int agent_digit_begin(struct ast_channel
*ast
, char digit
);
242 static int agent_digit_end(struct ast_channel
*ast
, char digit
, unsigned int duration
);
243 static int agent_call(struct ast_channel
*ast
, char *dest
, int timeout
);
244 static int agent_hangup(struct ast_channel
*ast
);
245 static int agent_answer(struct ast_channel
*ast
);
246 static struct ast_frame
*agent_read(struct ast_channel
*ast
);
247 static int agent_write(struct ast_channel
*ast
, struct ast_frame
*f
);
248 static int agent_sendhtml(struct ast_channel
*ast
, int subclass
, const char *data
, int datalen
);
249 static int agent_sendtext(struct ast_channel
*ast
, const char *text
);
250 static int agent_indicate(struct ast_channel
*ast
, int condition
, const void *data
, size_t datalen
);
251 static int agent_fixup(struct ast_channel
*oldchan
, struct ast_channel
*newchan
);
252 static struct ast_channel
*agent_bridgedchannel(struct ast_channel
*chan
, struct ast_channel
*bridge
);
253 static void set_agentbycallerid(const char *callerid
, const char *agent
);
255 /*! \brief Channel interface description for PBX integration */
256 static const struct ast_channel_tech agent_tech
= {
258 .description
= tdesc
,
260 .requester
= agent_request
,
261 .devicestate
= agent_devicestate
,
262 .send_digit_begin
= agent_digit_begin
,
263 .send_digit_end
= agent_digit_end
,
265 .hangup
= agent_hangup
,
266 .answer
= agent_answer
,
268 .write
= agent_write
,
269 .write_video
= agent_write
,
270 .send_html
= agent_sendhtml
,
271 .send_text
= agent_sendtext
,
272 .exception
= agent_read
,
273 .indicate
= agent_indicate
,
274 .fixup
= agent_fixup
,
275 .bridged_channel
= agent_bridgedchannel
,
279 * Adds an agent to the global list of agents.
281 * \param agent A string with the username, password and real name of an agent. As defined in agents.conf. Example: "13,169,John Smith"
282 * \param pending If it is pending or not.
283 * @return The just created agent.
284 * \sa agent_pvt, agents.
286 static struct agent_pvt
*add_agent(char *agent
, int pending
)
289 AST_DECLARE_APP_ARGS(args
,
291 AST_APP_ARG(password
);
294 char *password
= NULL
;
299 parse
= ast_strdupa(agent
);
301 /* Extract username (agt), password and name from agent (args). */
302 AST_NONSTANDARD_APP_ARGS(args
, parse
, ',');
305 ast_log(LOG_WARNING
, "A blank agent line!\n");
309 if(ast_strlen_zero(args
.agt
) ) {
310 ast_log(LOG_WARNING
, "An agent line with no agentid!\n");
315 if(!ast_strlen_zero(args
.password
)) {
316 password
= args
.password
;
317 while (*password
&& *password
< 33) password
++;
319 if(!ast_strlen_zero(args
.name
)) {
321 while (*name
&& *name
< 33) name
++;
324 /* Are we searching for the agent here ? To see if it exists already ? */
325 AST_LIST_TRAVERSE(&agents
, p
, list
) {
326 if (!pending
&& !strcmp(p
->agent
, agt
))
331 if (!(p
= ast_calloc(1, sizeof(*p
))))
333 ast_copy_string(p
->agent
, agt
, sizeof(p
->agent
));
334 ast_mutex_init(&p
->lock
);
335 ast_mutex_init(&p
->app_lock
);
336 p
->owning_app
= (pthread_t
) -1;
337 p
->app_sleep_cond
= 1;
339 p
->pending
= pending
;
340 AST_LIST_INSERT_TAIL(&agents
, p
, list
);
343 ast_copy_string(p
->password
, password
? password
: "", sizeof(p
->password
));
344 ast_copy_string(p
->name
, name
? name
: "", sizeof(p
->name
));
345 ast_copy_string(p
->moh
, moh
, sizeof(p
->moh
));
346 p
->ackcall
= ackcall
;
347 p
->autologoff
= autologoff
;
349 /* If someone reduces the wrapuptime and reloads, we want it
350 * to change the wrapuptime immediately on all calls */
351 if (p
->wrapuptime
> wrapuptime
) {
352 struct timeval now
= ast_tvnow();
353 /* XXX check what is this exactly */
355 /* We won't be pedantic and check the tv_usec val */
356 if (p
->lastdisc
.tv_sec
> (now
.tv_sec
+ wrapuptime
/1000)) {
357 p
->lastdisc
.tv_sec
= now
.tv_sec
+ wrapuptime
/1000;
358 p
->lastdisc
.tv_usec
= now
.tv_usec
;
361 p
->wrapuptime
= wrapuptime
;
371 * Deletes an agent after doing some clean up.
372 * Further documentation: How safe is this function ? What state should the agent be to be cleaned.
373 * \param p Agent to be deleted.
376 static int agent_cleanup(struct agent_pvt
*p
)
378 struct ast_channel
*chan
= p
->owner
;
380 chan
->tech_pvt
= NULL
;
381 p
->app_sleep_cond
= 1;
382 /* Release ownership of the agent to other threads (presumably running the login app). */
383 ast_mutex_unlock(&p
->app_lock
);
385 ast_channel_free(chan
);
387 ast_mutex_destroy(&p
->lock
);
388 ast_mutex_destroy(&p
->app_lock
);
394 static int check_availability(struct agent_pvt
*newlyavailable
, int needlock
);
396 static int agent_answer(struct ast_channel
*ast
)
398 ast_log(LOG_WARNING
, "Huh? Agent is being asked to answer?\n");
402 static int __agent_start_monitoring(struct ast_channel
*ast
, struct agent_pvt
*p
, int needlock
)
404 char tmp
[AST_MAX_BUF
],tmp2
[AST_MAX_BUF
], *pointer
;
405 char filename
[AST_MAX_BUF
];
410 snprintf(filename
, sizeof(filename
), "agent-%s-%s",p
->agent
, ast
->uniqueid
);
411 /* substitute . for - */
412 if ((pointer
= strchr(filename
, '.')))
414 snprintf(tmp
, sizeof(tmp
), "%s%s",savecallsin
? savecallsin
: "", filename
);
415 ast_monitor_start(ast
, recordformat
, tmp
, needlock
);
416 ast_monitor_setjoinfiles(ast
, 1);
417 snprintf(tmp2
, sizeof(tmp2
), "%s%s.%s", urlprefix
? urlprefix
: "", filename
, recordformatext
);
419 ast_verbose("name is %s, link is %s\n",tmp
, tmp2
);
422 ast
->cdr
= ast_cdr_alloc();
423 ast_cdr_setuserfield(ast
, tmp2
);
426 ast_log(LOG_ERROR
, "Recording already started on that call.\n");
430 static int agent_start_monitoring(struct ast_channel
*ast
, int needlock
)
432 return __agent_start_monitoring(ast
, ast
->tech_pvt
, needlock
);
435 static struct ast_frame
*agent_read(struct ast_channel
*ast
)
437 struct agent_pvt
*p
= ast
->tech_pvt
;
438 struct ast_frame
*f
= NULL
;
439 static struct ast_frame answer_frame
= { AST_FRAME_CONTROL
, AST_CONTROL_ANSWER
};
441 ast_mutex_lock(&p
->lock
);
442 CHECK_FORMATS(ast
, p
);
444 ast_copy_flags(p
->chan
, ast
, AST_FLAG_EXCEPTION
);
445 p
->chan
->fdno
= (ast
->fdno
== AST_AGENT_FD
) ? AST_TIMING_FD
: ast
->fdno
;
446 f
= ast_read(p
->chan
);
450 /* If there's a channel, hang it up (if it's on a callback) make it NULL */
452 p
->chan
->_bridge
= NULL
;
453 /* Note that we don't hangup if it's not a callback because Asterisk will do it
454 for us when the PBX instance that called login finishes */
455 if (!ast_strlen_zero(p
->loginchan
)) {
457 ast_log(LOG_DEBUG
, "Bridge on '%s' being cleared (2)\n", p
->chan
->name
);
459 status
= pbx_builtin_getvar_helper(p
->chan
, "CHANLOCALSTATUS");
460 if (autologoffunavail
&& status
&& !strcasecmp(status
, "CHANUNAVAIL")) {
461 long logintime
= time(NULL
) - p
->loginstart
;
463 ast_log(LOG_NOTICE
, "Agent read: '%s' is not available now, auto logoff\n", p
->name
);
464 agent_logoff_maintenance(p
, p
->loginchan
, logintime
, ast
->uniqueid
, "Chanunavail");
467 if (p
->wrapuptime
&& p
->acknowledged
)
468 p
->lastdisc
= ast_tvadd(ast_tvnow(), ast_samp2tv(p
->wrapuptime
, 1000));
474 /* if acknowledgement is not required, and the channel is up, we may have missed
475 an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */
476 if (!p
->ackcall
&& !p
->acknowledged
&& p
->chan
&& (p
->chan
->_state
== AST_STATE_UP
))
478 switch (f
->frametype
) {
479 case AST_FRAME_CONTROL
:
480 if (f
->subclass
== AST_CONTROL_ANSWER
) {
482 if (option_verbose
> 2)
483 ast_verbose(VERBOSE_PREFIX_3
"%s answered, waiting for '#' to acknowledge\n", p
->chan
->name
);
484 /* Don't pass answer along */
489 /* Use the builtin answer frame for the
490 recording start check below. */
496 case AST_FRAME_DTMF_BEGIN
:
497 case AST_FRAME_DTMF_END
:
498 if (!p
->acknowledged
&& (f
->subclass
== '#')) {
499 if (option_verbose
> 2)
500 ast_verbose(VERBOSE_PREFIX_3
"%s acknowledged\n", p
->chan
->name
);
504 } else if (f
->subclass
== '*' && endcall
) {
505 /* terminates call */
510 case AST_FRAME_VOICE
:
511 case AST_FRAME_VIDEO
:
512 /* don't pass voice or video until the call is acknowledged */
513 if (!p
->acknowledged
) {
518 /* pass everything else on through */
524 if (p
->chan
&& !p
->chan
->_bridge
) {
525 if (strcasecmp(p
->chan
->tech
->type
, "Local")) {
526 p
->chan
->_bridge
= ast
;
528 ast_log(LOG_DEBUG
, "Bridge on '%s' being set to '%s' (3)\n", p
->chan
->name
, p
->chan
->_bridge
->name
);
531 ast_mutex_unlock(&p
->lock
);
532 if (recordagentcalls
&& f
== &answer_frame
)
533 agent_start_monitoring(ast
,0);
537 static int agent_sendhtml(struct ast_channel
*ast
, int subclass
, const char *data
, int datalen
)
539 struct agent_pvt
*p
= ast
->tech_pvt
;
541 ast_mutex_lock(&p
->lock
);
543 res
= ast_channel_sendhtml(p
->chan
, subclass
, data
, datalen
);
544 ast_mutex_unlock(&p
->lock
);
548 static int agent_sendtext(struct ast_channel
*ast
, const char *text
)
550 struct agent_pvt
*p
= ast
->tech_pvt
;
552 ast_mutex_lock(&p
->lock
);
554 res
= ast_sendtext(p
->chan
, text
);
555 ast_mutex_unlock(&p
->lock
);
559 static int agent_write(struct ast_channel
*ast
, struct ast_frame
*f
)
561 struct agent_pvt
*p
= ast
->tech_pvt
;
563 CHECK_FORMATS(ast
, p
);
564 ast_mutex_lock(&p
->lock
);
568 if ((f
->frametype
!= AST_FRAME_VOICE
) ||
569 (f
->frametype
!= AST_FRAME_VIDEO
) ||
570 (f
->subclass
== p
->chan
->writeformat
)) {
571 res
= ast_write(p
->chan
, f
);
573 ast_log(LOG_DEBUG
, "Dropping one incompatible %s frame on '%s' to '%s'\n",
574 f
->frametype
== AST_FRAME_VOICE
? "audio" : "video",
575 ast
->name
, p
->chan
->name
);
580 ast_mutex_unlock(&p
->lock
);
584 static int agent_fixup(struct ast_channel
*oldchan
, struct ast_channel
*newchan
)
586 struct agent_pvt
*p
= newchan
->tech_pvt
;
587 ast_mutex_lock(&p
->lock
);
588 if (p
->owner
!= oldchan
) {
589 ast_log(LOG_WARNING
, "old channel wasn't %p but was %p\n", oldchan
, p
->owner
);
590 ast_mutex_unlock(&p
->lock
);
594 ast_mutex_unlock(&p
->lock
);
598 static int agent_indicate(struct ast_channel
*ast
, int condition
, const void *data
, size_t datalen
)
600 struct agent_pvt
*p
= ast
->tech_pvt
;
602 ast_mutex_lock(&p
->lock
);
604 res
= p
->chan
->tech
->indicate
? p
->chan
->tech
->indicate(p
->chan
, condition
, data
, datalen
) : -1;
607 ast_mutex_unlock(&p
->lock
);
611 static int agent_digit_begin(struct ast_channel
*ast
, char digit
)
613 struct agent_pvt
*p
= ast
->tech_pvt
;
615 ast_mutex_lock(&p
->lock
);
616 ast_senddigit_begin(p
->chan
, digit
);
617 ast_mutex_unlock(&p
->lock
);
621 static int agent_digit_end(struct ast_channel
*ast
, char digit
, unsigned int duration
)
623 struct agent_pvt
*p
= ast
->tech_pvt
;
625 ast_mutex_lock(&p
->lock
);
626 ast_senddigit_end(p
->chan
, digit
, duration
);
627 ast_mutex_unlock(&p
->lock
);
631 static int agent_call(struct ast_channel
*ast
, char *dest
, int timeout
)
633 struct agent_pvt
*p
= ast
->tech_pvt
;
636 ast_mutex_lock(&p
->lock
);
640 ast_log(LOG_DEBUG
, "Pretending to dial on pending agent\n");
641 newstate
= AST_STATE_DIALING
;
644 ast_log(LOG_NOTICE
, "Whoa, they hung up between alloc and call... what are the odds of that?\n");
647 ast_mutex_unlock(&p
->lock
);
649 ast_setstate(ast
, newstate
);
651 } else if (!ast_strlen_zero(p
->loginchan
)) {
653 /* Call on this agent */
654 if (option_verbose
> 2)
655 ast_verbose(VERBOSE_PREFIX_3
"outgoing agentcall, to agent '%s', on '%s'\n", p
->agent
, p
->chan
->name
);
656 ast_set_callerid(p
->chan
,
657 ast
->cid
.cid_num
, ast
->cid
.cid_name
, NULL
);
658 ast_channel_inherit_variables(ast
, p
->chan
);
659 res
= ast_call(p
->chan
, p
->loginchan
, 0);
661 ast_mutex_unlock(&p
->lock
);
664 ast_verbose( VERBOSE_PREFIX_3
"agent_call, call to agent '%s' call on '%s'\n", p
->agent
, p
->chan
->name
);
665 if (option_debug
> 2)
666 ast_log(LOG_DEBUG
, "Playing beep, lang '%s'\n", p
->chan
->language
);
667 res
= ast_streamfile(p
->chan
, beep
, p
->chan
->language
);
668 if (option_debug
> 2)
669 ast_log(LOG_DEBUG
, "Played beep, result '%d'\n", res
);
671 res
= ast_waitstream(p
->chan
, "");
672 if (option_debug
> 2)
673 ast_log(LOG_DEBUG
, "Waited for stream, result '%d'\n", res
);
676 res
= ast_set_read_format(p
->chan
, ast_best_codec(p
->chan
->nativeformats
));
677 if (option_debug
> 2)
678 ast_log(LOG_DEBUG
, "Set read format, result '%d'\n", res
);
680 ast_log(LOG_WARNING
, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p
->chan
->nativeformats
)));
687 res
= ast_set_write_format(p
->chan
, ast_best_codec(p
->chan
->nativeformats
));
688 if (option_debug
> 2)
689 ast_log(LOG_DEBUG
, "Set write format, result '%d'\n", res
);
691 ast_log(LOG_WARNING
, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p
->chan
->nativeformats
)));
694 /* Call is immediately up, or might need ack */
696 newstate
= AST_STATE_RINGING
;
698 newstate
= AST_STATE_UP
;
699 if (recordagentcalls
)
700 agent_start_monitoring(ast
, 0);
706 ast_mutex_unlock(&p
->lock
);
708 ast_setstate(ast
, newstate
);
712 /*! \brief store/clear the global variable that stores agentid based on the callerid */
713 static void set_agentbycallerid(const char *callerid
, const char *agent
)
715 char buf
[AST_MAX_BUF
];
717 /* if there is no Caller ID, nothing to do */
718 if (ast_strlen_zero(callerid
))
721 snprintf(buf
, sizeof(buf
), "%s_%s", GETAGENTBYCALLERID
, callerid
);
722 pbx_builtin_setvar_helper(NULL
, buf
, agent
);
725 static int agent_hangup(struct ast_channel
*ast
)
727 struct agent_pvt
*p
= ast
->tech_pvt
;
730 ast_mutex_lock(&p
->lock
);
732 ast
->tech_pvt
= NULL
;
733 p
->app_sleep_cond
= 1;
736 /* if they really are hung up then set start to 0 so the test
737 * later if we're called on an already downed channel
738 * doesn't cause an agent to be logged out like when
739 * agent_request() is followed immediately by agent_hangup()
740 * as in apps/app_chanisavail.c:chanavail_exec()
744 ast_log(LOG_DEBUG
, "Hangup called for state %s\n", ast_state2str(ast
->_state
));
745 if (p
->start
&& (ast
->_state
!= AST_STATE_UP
)) {
746 howlong
= time(NULL
) - p
->start
;
748 } else if (ast
->_state
== AST_STATE_RESERVED
)
753 p
->chan
->_bridge
= NULL
;
754 /* If they're dead, go ahead and hang up on the agent now */
755 if (!ast_strlen_zero(p
->loginchan
)) {
756 /* Store last disconnect time */
758 p
->lastdisc
= ast_tvadd(ast_tvnow(), ast_samp2tv(p
->wrapuptime
, 1000));
760 p
->lastdisc
= ast_tv(0,0);
762 status
= pbx_builtin_getvar_helper(p
->chan
, "CHANLOCALSTATUS");
763 if (autologoffunavail
&& status
&& !strcasecmp(status
, "CHANUNAVAIL")) {
764 long logintime
= time(NULL
) - p
->loginstart
;
766 ast_log(LOG_NOTICE
, "Agent hangup: '%s' is not available now, auto logoff\n", p
->name
);
767 agent_logoff_maintenance(p
, p
->loginchan
, logintime
, ast
->uniqueid
, "Chanunavail");
769 /* Recognize the hangup and pass it along immediately */
773 ast_log(LOG_DEBUG
, "Hungup, howlong is %d, autologoff is %d\n", howlong
, p
->autologoff
);
774 if ((p
->deferlogoff
) || (howlong
&& p
->autologoff
&& (howlong
> p
->autologoff
))) {
775 long logintime
= time(NULL
) - p
->loginstart
;
778 ast_log(LOG_NOTICE
, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p
->name
, p
->autologoff
, howlong
);
780 agent_logoff_maintenance(p
, p
->loginchan
, logintime
, ast
->uniqueid
, "Autologoff");
781 if (persistent_agents
)
784 } else if (p
->dead
) {
785 ast_channel_lock(p
->chan
);
786 ast_softhangup(p
->chan
, AST_SOFTHANGUP_EXPLICIT
);
787 ast_channel_unlock(p
->chan
);
788 } else if (p
->loginstart
) {
789 ast_channel_lock(p
->chan
);
790 ast_indicate_data(p
->chan
, AST_CONTROL_HOLD
,
792 !ast_strlen_zero(p
->moh
) ? strlen(p
->moh
) + 1 : 0);
793 ast_channel_unlock(p
->chan
);
796 ast_mutex_unlock(&p
->lock
);
798 /* Only register a device state change if the agent is still logged in */
799 if (!p
->loginstart
) {
800 p
->loginchan
[0] = '\0';
801 p
->logincallerid
[0] = '\0';
802 if (persistent_agents
)
805 ast_device_state_changed("Agent/%s", p
->agent
);
809 AST_LIST_LOCK(&agents
);
810 AST_LIST_REMOVE(&agents
, p
, list
);
811 AST_LIST_UNLOCK(&agents
);
813 if (p
->abouttograb
) {
814 /* Let the "about to grab" thread know this isn't valid anymore, and let it
817 } else if (p
->dead
) {
818 ast_mutex_destroy(&p
->lock
);
819 ast_mutex_destroy(&p
->app_lock
);
823 /* Not dead -- check availability now */
824 ast_mutex_lock(&p
->lock
);
825 /* Store last disconnect time */
826 p
->lastdisc
= ast_tvnow();
827 ast_mutex_unlock(&p
->lock
);
829 /* Release ownership of the agent to other threads (presumably running the login app). */
830 if (ast_strlen_zero(p
->loginchan
))
831 ast_mutex_unlock(&p
->app_lock
);
836 static int agent_cont_sleep( void *data
)
841 p
= (struct agent_pvt
*)data
;
843 ast_mutex_lock(&p
->lock
);
844 res
= p
->app_sleep_cond
;
845 if (p
->lastdisc
.tv_sec
) {
846 if (ast_tvdiff_ms(ast_tvnow(), p
->lastdisc
) > p
->wrapuptime
)
849 ast_mutex_unlock(&p
->lock
);
851 if(option_debug
> 4 && !res
)
852 ast_log(LOG_DEBUG
, "agent_cont_sleep() returning %d\n", res
);
857 static int agent_ack_sleep(void *data
)
864 /* Wait a second and look for something */
866 p
= (struct agent_pvt
*) data
;
871 to
= ast_waitfor(p
->chan
, to
);
876 f
= ast_read(p
->chan
);
879 if (f
->frametype
== AST_FRAME_DTMF
)
884 ast_mutex_lock(&p
->lock
);
885 if (!p
->app_sleep_cond
) {
886 ast_mutex_unlock(&p
->lock
);
888 } else if (res
== '#') {
889 ast_mutex_unlock(&p
->lock
);
892 ast_mutex_unlock(&p
->lock
);
898 static struct ast_channel
*agent_bridgedchannel(struct ast_channel
*chan
, struct ast_channel
*bridge
)
900 struct agent_pvt
*p
= bridge
->tech_pvt
;
901 struct ast_channel
*ret
= NULL
;
905 ret
= bridge
->_bridge
;
906 else if (chan
== bridge
->_bridge
)
911 ast_log(LOG_DEBUG
, "Asked for bridged channel on '%s'/'%s', returning '%s'\n", chan
->name
, bridge
->name
, ret
? ret
->name
: "<none>");
915 /*! \brief Create new agent channel */
916 static struct ast_channel
*agent_new(struct agent_pvt
*p
, int state
)
918 struct ast_channel
*tmp
;
921 ast_log(LOG_WARNING
, "No channel? :(\n");
926 tmp
= ast_channel_alloc(0, state
, 0, 0, "Agent/P%s-%d", p
->agent
, ast_random() & 0xffff);
928 tmp
= ast_channel_alloc(0, state
, 0, 0, "Agent/%s", p
->agent
);
930 ast_log(LOG_WARNING
, "Unable to allocate agent channel structure\n");
934 tmp
->tech
= &agent_tech
;
936 tmp
->nativeformats
= p
->chan
->nativeformats
;
937 tmp
->writeformat
= p
->chan
->writeformat
;
938 tmp
->rawwriteformat
= p
->chan
->writeformat
;
939 tmp
->readformat
= p
->chan
->readformat
;
940 tmp
->rawreadformat
= p
->chan
->readformat
;
941 ast_string_field_set(tmp
, language
, p
->chan
->language
);
942 ast_copy_string(tmp
->context
, p
->chan
->context
, sizeof(tmp
->context
));
943 ast_copy_string(tmp
->exten
, p
->chan
->exten
, sizeof(tmp
->exten
));
944 /* XXX Is this really all we copy form the originating channel?? */
946 tmp
->nativeformats
= AST_FORMAT_SLINEAR
;
947 tmp
->writeformat
= AST_FORMAT_SLINEAR
;
948 tmp
->rawwriteformat
= AST_FORMAT_SLINEAR
;
949 tmp
->readformat
= AST_FORMAT_SLINEAR
;
950 tmp
->rawreadformat
= AST_FORMAT_SLINEAR
;
952 /* Safe, agentlock already held */
955 /* XXX: this needs fixing */
957 ast_atomic_fetchadd_int(&__mod_desc
->usecnt
, +1);
959 ast_update_use_count();
961 /* Wake up and wait for other applications (by definition the login app)
962 * to release this channel). Takes ownership of the agent channel
963 * to this thread only.
964 * For signalling the other thread, ast_queue_frame is used until we
965 * can safely use signals for this purpose. The pselect() needs to be
966 * implemented in the kernel for this.
968 p
->app_sleep_cond
= 0;
969 if(ast_strlen_zero(p
->loginchan
) && ast_mutex_trylock(&p
->app_lock
)) {
971 ast_queue_frame(p
->chan
, &ast_null_frame
);
972 ast_mutex_unlock(&p
->lock
); /* For other thread to read the condition. */
973 ast_mutex_lock(&p
->app_lock
);
974 ast_mutex_lock(&p
->lock
);
976 ast_log(LOG_WARNING
, "Agent disconnected while we were connecting the call\n");
978 tmp
->tech_pvt
= NULL
;
979 p
->app_sleep_cond
= 1;
980 ast_channel_free( tmp
);
981 ast_mutex_unlock(&p
->lock
); /* For other thread to read the condition. */
982 ast_mutex_unlock(&p
->app_lock
);
985 } else if (!ast_strlen_zero(p
->loginchan
)) {
987 ast_queue_frame(p
->chan
, &ast_null_frame
);
989 ast_log(LOG_WARNING
, "Agent disconnected while we were connecting the call\n");
991 tmp
->tech_pvt
= NULL
;
992 p
->app_sleep_cond
= 1;
993 ast_channel_free( tmp
);
994 ast_mutex_unlock(&p
->lock
); /* For other thread to read the condition. */
998 ast_indicate(p
->chan
, AST_CONTROL_UNHOLD
);
999 p
->owning_app
= pthread_self();
1000 /* After the above step, there should not be any blockers. */
1002 if (ast_test_flag(p
->chan
, AST_FLAG_BLOCKING
)) {
1003 ast_log( LOG_ERROR
, "A blocker exists after agent channel ownership acquired\n" );
1012 * Read configuration data. The file named agents.conf.
1014 * \returns Always 0, or so it seems.
1016 static int read_agent_config(void)
1018 struct ast_config
*cfg
;
1019 struct ast_config
*ucfg
;
1020 struct ast_variable
*v
;
1021 struct agent_pvt
*p
;
1022 const char *general_val
;
1023 const char *catname
;
1024 const char *hasagent
;
1032 cfg
= ast_config_load(config
);
1034 ast_log(LOG_NOTICE
, "No agent configuration found -- agent support disabled\n");
1037 AST_LIST_LOCK(&agents
);
1038 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1041 strcpy(moh
, "default");
1042 /* set the default recording values */
1043 recordagentcalls
= 0;
1044 strcpy(recordformat
, "wav");
1045 strcpy(recordformatext
, "wav");
1046 urlprefix
[0] = '\0';
1047 savecallsin
[0] = '\0';
1049 /* Read in [general] section for persistence */
1050 if ((general_val
= ast_variable_retrieve(cfg
, "general", "persistentagents")))
1051 persistent_agents
= ast_true(general_val
);
1052 multiplelogin
= ast_true(ast_variable_retrieve(cfg
, "general", "multiplelogin"));
1054 /* Read in the [agents] section */
1055 v
= ast_variable_browse(cfg
, "agents");
1057 /* Create the interface list */
1058 if (!strcasecmp(v
->name
, "agent")) {
1059 add_agent(v
->value
, 0);
1060 } else if (!strcasecmp(v
->name
, "group")) {
1061 group
= ast_get_group(v
->value
);
1062 } else if (!strcasecmp(v
->name
, "autologoff")) {
1063 autologoff
= atoi(v
->value
);
1066 } else if (!strcasecmp(v
->name
, "ackcall")) {
1067 if (!strcasecmp(v
->value
, "always"))
1069 else if (ast_true(v
->value
))
1073 } else if (!strcasecmp(v
->name
, "endcall")) {
1074 endcall
= ast_true(v
->value
);
1075 } else if (!strcasecmp(v
->name
, "wrapuptime")) {
1076 wrapuptime
= atoi(v
->value
);
1079 } else if (!strcasecmp(v
->name
, "maxlogintries") && !ast_strlen_zero(v
->value
)) {
1080 maxlogintries
= atoi(v
->value
);
1081 if (maxlogintries
< 0)
1083 } else if (!strcasecmp(v
->name
, "goodbye") && !ast_strlen_zero(v
->value
)) {
1084 strcpy(agentgoodbye
,v
->value
);
1085 } else if (!strcasecmp(v
->name
, "musiconhold")) {
1086 ast_copy_string(moh
, v
->value
, sizeof(moh
));
1087 } else if (!strcasecmp(v
->name
, "updatecdr")) {
1088 if (ast_true(v
->value
))
1092 } else if (!strcasecmp(v
->name
, "autologoffunavail")) {
1093 if (ast_true(v
->value
))
1094 autologoffunavail
= 1;
1096 autologoffunavail
= 0;
1097 } else if (!strcasecmp(v
->name
, "recordagentcalls")) {
1098 recordagentcalls
= ast_true(v
->value
);
1099 } else if (!strcasecmp(v
->name
, "recordformat")) {
1100 ast_copy_string(recordformat
, v
->value
, sizeof(recordformat
));
1101 if (!strcasecmp(v
->value
, "wav49"))
1102 strcpy(recordformatext
, "WAV");
1104 ast_copy_string(recordformatext
, v
->value
, sizeof(recordformatext
));
1105 } else if (!strcasecmp(v
->name
, "urlprefix")) {
1106 ast_copy_string(urlprefix
, v
->value
, sizeof(urlprefix
));
1107 if (urlprefix
[strlen(urlprefix
) - 1] != '/')
1108 strncat(urlprefix
, "/", sizeof(urlprefix
) - strlen(urlprefix
) - 1);
1109 } else if (!strcasecmp(v
->name
, "savecallsin")) {
1110 if (v
->value
[0] == '/')
1111 ast_copy_string(savecallsin
, v
->value
, sizeof(savecallsin
));
1113 snprintf(savecallsin
, sizeof(savecallsin
) - 2, "/%s", v
->value
);
1114 if (savecallsin
[strlen(savecallsin
) - 1] != '/')
1115 strncat(savecallsin
, "/", sizeof(savecallsin
) - strlen(savecallsin
) - 1);
1116 } else if (!strcasecmp(v
->name
, "custom_beep")) {
1117 ast_copy_string(beep
, v
->value
, sizeof(beep
));
1121 if ((ucfg
= ast_config_load("users.conf"))) {
1122 genhasagent
= ast_true(ast_variable_retrieve(ucfg
, "general", "hasagent"));
1123 catname
= ast_category_browse(ucfg
, NULL
);
1125 if (strcasecmp(catname
, "general")) {
1126 hasagent
= ast_variable_retrieve(ucfg
, catname
, "hasagent");
1127 if (ast_true(hasagent
) || (!hasagent
&& genhasagent
)) {
1129 const char *fullname
= ast_variable_retrieve(ucfg
, catname
, "fullname");
1130 const char *secret
= ast_variable_retrieve(ucfg
, catname
, "secret");
1135 snprintf(tmp
, sizeof(tmp
), "%s,%s,%s", catname
, secret
,fullname
);
1139 catname
= ast_category_browse(ucfg
, catname
);
1141 ast_config_destroy(ucfg
);
1143 AST_LIST_TRAVERSE_SAFE_BEGIN(&agents
, p
, list
) {
1145 AST_LIST_REMOVE_CURRENT(&agents
, list
);
1146 /* Destroy if appropriate */
1149 ast_mutex_destroy(&p
->lock
);
1150 ast_mutex_destroy(&p
->app_lock
);
1153 /* Cause them to hang up */
1154 ast_softhangup(p
->chan
, AST_SOFTHANGUP_EXPLICIT
);
1159 AST_LIST_TRAVERSE_SAFE_END
1160 AST_LIST_UNLOCK(&agents
);
1161 ast_config_destroy(cfg
);
1165 static int check_availability(struct agent_pvt
*newlyavailable
, int needlock
)
1167 struct ast_channel
*chan
=NULL
, *parent
=NULL
;
1168 struct agent_pvt
*p
;
1172 ast_log(LOG_DEBUG
, "Checking availability of '%s'\n", newlyavailable
->agent
);
1174 AST_LIST_LOCK(&agents
);
1175 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1176 if (p
== newlyavailable
) {
1179 ast_mutex_lock(&p
->lock
);
1180 if (!p
->abouttograb
&& p
->pending
&& ((p
->group
&& (newlyavailable
->group
& p
->group
)) || !strcmp(p
->agent
, newlyavailable
->agent
))) {
1182 ast_log(LOG_DEBUG
, "Call '%s' looks like a winner for agent '%s'\n", p
->owner
->name
, newlyavailable
->agent
);
1183 /* We found a pending call, time to merge */
1184 chan
= agent_new(newlyavailable
, AST_STATE_DOWN
);
1187 ast_mutex_unlock(&p
->lock
);
1190 ast_mutex_unlock(&p
->lock
);
1193 AST_LIST_UNLOCK(&agents
);
1194 if (parent
&& chan
) {
1195 if (newlyavailable
->ackcall
> 1) {
1196 /* Don't do beep here */
1199 if (option_debug
> 2)
1200 ast_log( LOG_DEBUG
, "Playing beep, lang '%s'\n", newlyavailable
->chan
->language
);
1201 res
= ast_streamfile(newlyavailable
->chan
, beep
, newlyavailable
->chan
->language
);
1202 if (option_debug
> 2)
1203 ast_log( LOG_DEBUG
, "Played beep, result '%d'\n", res
);
1205 res
= ast_waitstream(newlyavailable
->chan
, "");
1206 ast_log( LOG_DEBUG
, "Waited for stream, result '%d'\n", res
);
1210 /* Note -- parent may have disappeared */
1211 if (p
->abouttograb
) {
1212 newlyavailable
->acknowledged
= 1;
1213 /* Safe -- agent lock already held */
1214 ast_setstate(parent
, AST_STATE_UP
);
1215 ast_setstate(chan
, AST_STATE_UP
);
1216 ast_copy_string(parent
->context
, chan
->context
, sizeof(parent
->context
));
1217 /* Go ahead and mark the channel as a zombie so that masquerade will
1218 destroy it for us, and we need not call ast_hangup */
1219 ast_mutex_lock(&parent
->lock
);
1220 ast_set_flag(chan
, AST_FLAG_ZOMBIE
);
1221 ast_channel_masquerade(parent
, chan
);
1222 ast_mutex_unlock(&parent
->lock
);
1226 ast_log(LOG_DEBUG
, "Sneaky, parent disappeared in the mean time...\n");
1227 agent_cleanup(newlyavailable
);
1231 ast_log(LOG_DEBUG
, "Ugh... Agent hung up at exactly the wrong time\n");
1232 agent_cleanup(newlyavailable
);
1238 static int check_beep(struct agent_pvt
*newlyavailable
, int needlock
)
1240 struct agent_pvt
*p
;
1243 ast_log(LOG_DEBUG
, "Checking beep availability of '%s'\n", newlyavailable
->agent
);
1245 AST_LIST_LOCK(&agents
);
1246 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1247 if (p
== newlyavailable
) {
1250 ast_mutex_lock(&p
->lock
);
1251 if (!p
->abouttograb
&& p
->pending
&& ((p
->group
&& (newlyavailable
->group
& p
->group
)) || !strcmp(p
->agent
, newlyavailable
->agent
))) {
1253 ast_log(LOG_DEBUG
, "Call '%s' looks like a would-be winner for agent '%s'\n", p
->owner
->name
, newlyavailable
->agent
);
1254 ast_mutex_unlock(&p
->lock
);
1257 ast_mutex_unlock(&p
->lock
);
1260 AST_LIST_UNLOCK(&agents
);
1262 ast_mutex_unlock(&newlyavailable
->lock
);
1263 if (option_debug
> 2)
1264 ast_log( LOG_DEBUG
, "Playing beep, lang '%s'\n", newlyavailable
->chan
->language
);
1265 res
= ast_streamfile(newlyavailable
->chan
, beep
, newlyavailable
->chan
->language
);
1266 if (option_debug
> 2)
1267 ast_log( LOG_DEBUG
, "Played beep, result '%d'\n", res
);
1269 res
= ast_waitstream(newlyavailable
->chan
, "");
1271 ast_log( LOG_DEBUG
, "Waited for stream, result '%d'\n", res
);
1273 ast_mutex_lock(&newlyavailable
->lock
);
1278 /* return 1 if multiple login is fine, 0 if it is not and we find a match, -1 if multiplelogin is not allowed and we don't find a match. */
1279 static int allow_multiple_login(char *chan
, char *context
)
1281 struct agent_pvt
*p
;
1289 snprintf(loginchan
, sizeof(loginchan
), "%s@%s", chan
, S_OR(context
, "default"));
1291 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1292 if(!strcasecmp(chan
, p
->loginchan
))
1298 /*! \brief Part of the Asterisk PBX interface */
1299 static struct ast_channel
*agent_request(const char *type
, int format
, void *data
, int *cause
)
1301 struct agent_pvt
*p
;
1302 struct ast_channel
*chan
= NULL
;
1304 ast_group_t groupmatch
;
1311 if ((s
[0] == '@') && (sscanf(s
+ 1, "%d", &groupoff
) == 1)) {
1312 groupmatch
= (1 << groupoff
);
1313 } else if ((s
[0] == ':') && (sscanf(s
+ 1, "%d", &groupoff
) == 1)) {
1314 groupmatch
= (1 << groupoff
);
1319 /* Check actual logged in agents first */
1320 AST_LIST_LOCK(&agents
);
1321 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1322 ast_mutex_lock(&p
->lock
);
1323 if (!p
->pending
&& ((groupmatch
&& (p
->group
& groupmatch
)) || !strcmp(data
, p
->agent
)) &&
1324 ast_strlen_zero(p
->loginchan
)) {
1327 if (!p
->lastdisc
.tv_sec
) {
1328 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1329 if (!p
->owner
&& p
->chan
) {
1331 chan
= agent_new(p
, AST_STATE_DOWN
);
1334 ast_mutex_unlock(&p
->lock
);
1339 ast_mutex_unlock(&p
->lock
);
1342 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1343 ast_mutex_lock(&p
->lock
);
1344 if (!p
->pending
&& ((groupmatch
&& (p
->group
& groupmatch
)) || !strcmp(data
, p
->agent
))) {
1345 if (p
->chan
|| !ast_strlen_zero(p
->loginchan
))
1349 ast_log(LOG_NOTICE
, "Time now: %ld, Time of lastdisc: %ld\n", tv
.tv_sec
, p
->lastdisc
.tv_sec
);
1351 if (!p
->lastdisc
.tv_sec
|| (tv
.tv_sec
> p
->lastdisc
.tv_sec
)) {
1352 p
->lastdisc
= ast_tv(0, 0);
1353 /* Agent must be registered, but not have any active call, and not be in a waiting state */
1354 if (!p
->owner
&& p
->chan
) {
1355 /* Could still get a fixed agent */
1356 chan
= agent_new(p
, AST_STATE_DOWN
);
1357 } else if (!p
->owner
&& !ast_strlen_zero(p
->loginchan
)) {
1358 /* Adjustable agent */
1359 p
->chan
= ast_request("Local", format
, p
->loginchan
, cause
);
1361 chan
= agent_new(p
, AST_STATE_DOWN
);
1364 ast_mutex_unlock(&p
->lock
);
1369 ast_mutex_unlock(&p
->lock
);
1373 if (!chan
&& waitforagent
) {
1374 /* No agent available -- but we're requesting to wait for one.
1375 Allocate a place holder */
1378 ast_log(LOG_DEBUG
, "Creating place holder for '%s'\n", s
);
1379 p
= add_agent(data
, 1);
1380 p
->group
= groupmatch
;
1381 chan
= agent_new(p
, AST_STATE_DOWN
);
1383 ast_log(LOG_WARNING
, "Weird... Fix this to drop the unused pending agent\n");
1385 ast_log(LOG_DEBUG
, "Not creating place holder for '%s' since nobody logged in\n", s
);
1387 *cause
= hasagent
? AST_CAUSE_BUSY
: AST_CAUSE_UNREGISTERED
;
1388 AST_LIST_UNLOCK(&agents
);
1392 static force_inline
int powerof(unsigned int d
)
1403 * Lists agents and their status to the Manager API.
1404 * It is registered on load_module() and it gets called by the manager backend.
1408 * \sa action_agent_logoff(), action_agent_callback_login(), load_module().
1410 static int action_agents(struct mansession
*s
, const struct message
*m
)
1412 const char *id
= astman_get_header(m
,"ActionID");
1413 char idText
[256] = "";
1415 struct agent_pvt
*p
;
1416 char *username
= NULL
;
1417 char *loginChan
= NULL
;
1418 char *talkingtoChan
= NULL
;
1419 char *status
= NULL
;
1421 if (!ast_strlen_zero(id
))
1422 snprintf(idText
, sizeof(idText
) ,"ActionID: %s\r\n", id
);
1423 astman_send_ack(s
, m
, "Agents will follow");
1424 AST_LIST_LOCK(&agents
);
1425 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1426 ast_mutex_lock(&p
->lock
);
1429 AGENT_LOGGEDOFF - Agent isn't logged in
1430 AGENT_IDLE - Agent is logged in, and waiting for call
1431 AGENT_ONCALL - Agent is logged in, and on a call
1432 AGENT_UNKNOWN - Don't know anything about agent. Shouldn't ever get this. */
1434 username
= S_OR(p
->name
, "None");
1436 /* Set a default status. It 'should' get changed. */
1437 status
= "AGENT_UNKNOWN";
1439 if (!ast_strlen_zero(p
->loginchan
) && !p
->chan
) {
1440 loginChan
= p
->loginchan
;
1441 talkingtoChan
= "n/a";
1442 status
= "AGENT_IDLE";
1443 if (p
->acknowledged
) {
1444 snprintf(chanbuf
, sizeof(chanbuf
), " %s (Confirmed)", p
->loginchan
);
1445 loginChan
= chanbuf
;
1447 } else if (p
->chan
) {
1448 loginChan
= ast_strdupa(p
->chan
->name
);
1449 if (p
->owner
&& p
->owner
->_bridge
) {
1450 talkingtoChan
= p
->chan
->cid
.cid_num
;
1451 status
= "AGENT_ONCALL";
1453 talkingtoChan
= "n/a";
1454 status
= "AGENT_IDLE";
1458 talkingtoChan
= "n/a";
1459 status
= "AGENT_LOGGEDOFF";
1462 astman_append(s
, "Event: Agents\r\n"
1466 "LoggedInChan: %s\r\n"
1467 "LoggedInTime: %d\r\n"
1471 p
->agent
, username
, status
, loginChan
, (int)p
->loginstart
, talkingtoChan
, idText
);
1472 ast_mutex_unlock(&p
->lock
);
1474 AST_LIST_UNLOCK(&agents
);
1475 astman_append(s
, "Event: AgentsComplete\r\n"
1481 static void agent_logoff_maintenance(struct agent_pvt
*p
, char *loginchan
, long logintime
, const char *uniqueid
, char *logcommand
)
1484 char agent
[AST_MAX_AGENT
];
1486 if (!ast_strlen_zero(logcommand
))
1489 tmp
= ast_strdupa("");
1491 snprintf(agent
, sizeof(agent
), "Agent/%s", p
->agent
);
1493 if (!ast_strlen_zero(uniqueid
)) {
1494 manager_event(EVENT_FLAG_AGENT
, "Agentcallbacklogoff",
1498 "Logintime: %ld\r\n"
1500 p
->agent
, tmp
, loginchan
, logintime
, uniqueid
);
1502 manager_event(EVENT_FLAG_AGENT
, "Agentcallbacklogoff",
1506 "Logintime: %ld\r\n",
1507 p
->agent
, tmp
, loginchan
, logintime
);
1510 ast_queue_log("NONE", ast_strlen_zero(uniqueid
) ? "NONE" : uniqueid
, agent
, "AGENTCALLBACKLOGOFF", "%s|%ld|%s", loginchan
, logintime
, tmp
);
1511 set_agentbycallerid(p
->logincallerid
, NULL
);
1512 p
->loginchan
[0] ='\0';
1513 p
->logincallerid
[0] = '\0';
1514 ast_device_state_changed("Agent/%s", p
->agent
);
1515 if (persistent_agents
)
1520 static int agent_logoff(const char *agent
, int soft
)
1522 struct agent_pvt
*p
;
1524 int ret
= -1; /* Return -1 if no agent if found */
1526 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1527 if (!strcasecmp(p
->agent
, agent
)) {
1529 if (p
->owner
|| p
->chan
) {
1533 ast_softhangup(p
->owner
, AST_SOFTHANGUP_EXPLICIT
);
1535 ast_softhangup(p
->chan
, AST_SOFTHANGUP_EXPLICIT
);
1538 logintime
= time(NULL
) - p
->loginstart
;
1540 agent_logoff_maintenance(p
, p
->loginchan
, logintime
, NULL
, "CommandLogoff");
1549 static int agent_logoff_cmd(int fd
, int argc
, char **argv
)
1554 if (argc
< 3 || argc
> 4)
1555 return RESULT_SHOWUSAGE
;
1556 if (argc
== 4 && strcasecmp(argv
[3], "soft"))
1557 return RESULT_SHOWUSAGE
;
1559 agent
= argv
[2] + 6;
1560 ret
= agent_logoff(agent
, argc
== 4);
1562 ast_cli(fd
, "Logging out %s\n", agent
);
1564 return RESULT_SUCCESS
;
1568 * Sets an agent as no longer logged in in the Manager API.
1569 * It is registered on load_module() and it gets called by the manager backend.
1573 * \sa action_agents(), action_agent_callback_login(), load_module().
1575 static int action_agent_logoff(struct mansession
*s
, const struct message
*m
)
1577 const char *agent
= astman_get_header(m
, "Agent");
1578 const char *soft_s
= astman_get_header(m
, "Soft"); /* "true" is don't hangup */
1580 int ret
; /* return value of agent_logoff */
1582 if (ast_strlen_zero(agent
)) {
1583 astman_send_error(s
, m
, "No agent specified");
1587 soft
= ast_true(soft_s
) ? 1 : 0;
1588 ret
= agent_logoff(agent
, soft
);
1590 astman_send_ack(s
, m
, "Agent logged out");
1592 astman_send_error(s
, m
, "No such agent");
1597 static char *complete_agent_logoff_cmd(const char *line
, const char *word
, int pos
, int state
)
1600 struct agent_pvt
*p
;
1601 char name
[AST_MAX_AGENT
];
1602 int which
= 0, len
= strlen(word
);
1604 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1605 snprintf(name
, sizeof(name
), "Agent/%s", p
->agent
);
1606 if (!strncasecmp(word
, name
, len
) && ++which
> state
)
1607 return ast_strdup(name
);
1609 } else if (pos
== 3 && state
== 0)
1610 return ast_strdup("soft");
1616 * Show agents in cli.
1618 static int agents_show(int fd
, int argc
, char **argv
)
1620 struct agent_pvt
*p
;
1621 char username
[AST_MAX_BUF
];
1622 char location
[AST_MAX_BUF
] = "";
1623 char talkingto
[AST_MAX_BUF
] = "";
1624 char moh
[AST_MAX_BUF
];
1625 int count_agents
= 0; /*!< Number of agents configured */
1626 int online_agents
= 0; /*!< Number of online agents */
1627 int offline_agents
= 0; /*!< Number of offline agents */
1629 return RESULT_SHOWUSAGE
;
1630 AST_LIST_LOCK(&agents
);
1631 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1632 ast_mutex_lock(&p
->lock
);
1635 ast_cli(fd
, "-- Pending call to group %d\n", powerof(p
->group
));
1637 ast_cli(fd
, "-- Pending call to agent %s\n", p
->agent
);
1639 if (!ast_strlen_zero(p
->name
))
1640 snprintf(username
, sizeof(username
), "(%s) ", p
->name
);
1644 snprintf(location
, sizeof(location
), "logged in on %s", p
->chan
->name
);
1645 if (p
->owner
&& ast_bridged_channel(p
->owner
))
1646 snprintf(talkingto
, sizeof(talkingto
), " talking to %s", ast_bridged_channel(p
->owner
)->name
);
1648 strcpy(talkingto
, " is idle");
1650 } else if (!ast_strlen_zero(p
->loginchan
)) {
1651 if (ast_tvdiff_ms(ast_tvnow(), p
->lastdisc
) > 0 || !(p
->lastdisc
.tv_sec
))
1652 snprintf(location
, sizeof(location
) - 20, "available at '%s'", p
->loginchan
);
1654 snprintf(location
, sizeof(location
) - 20, "wrapping up at '%s'", p
->loginchan
);
1655 talkingto
[0] = '\0';
1657 if (p
->acknowledged
)
1658 strncat(location
, " (Confirmed)", sizeof(location
) - strlen(location
) - 1);
1660 strcpy(location
, "not logged in");
1661 talkingto
[0] = '\0';
1664 if (!ast_strlen_zero(p
->moh
))
1665 snprintf(moh
, sizeof(moh
), " (musiconhold is '%s')", p
->moh
);
1666 ast_cli(fd
, "%-12.12s %s%s%s%s\n", p
->agent
,
1667 username
, location
, talkingto
, moh
);
1670 ast_mutex_unlock(&p
->lock
);
1672 AST_LIST_UNLOCK(&agents
);
1673 if ( !count_agents
)
1674 ast_cli(fd
, "No Agents are configured in %s\n",config
);
1676 ast_cli(fd
, "%d agents configured [%d online , %d offline]\n",count_agents
, online_agents
, offline_agents
);
1679 return RESULT_SUCCESS
;
1683 static int agents_show_online(int fd
, int argc
, char **argv
)
1685 struct agent_pvt
*p
;
1686 char username
[AST_MAX_BUF
];
1687 char location
[AST_MAX_BUF
] = "";
1688 char talkingto
[AST_MAX_BUF
] = "";
1689 char moh
[AST_MAX_BUF
];
1690 int count_agents
= 0; /* Number of agents configured */
1691 int online_agents
= 0; /* Number of online agents */
1692 int agent_status
= 0; /* 0 means offline, 1 means online */
1694 return RESULT_SHOWUSAGE
;
1695 AST_LIST_LOCK(&agents
);
1696 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1697 agent_status
= 0; /* reset it to offline */
1698 ast_mutex_lock(&p
->lock
);
1699 if (!ast_strlen_zero(p
->name
))
1700 snprintf(username
, sizeof(username
), "(%s) ", p
->name
);
1704 snprintf(location
, sizeof(location
), "logged in on %s", p
->chan
->name
);
1705 if (p
->owner
&& ast_bridged_channel(p
->owner
))
1706 snprintf(talkingto
, sizeof(talkingto
), " talking to %s", ast_bridged_channel(p
->owner
)->name
);
1708 strcpy(talkingto
, " is idle");
1711 } else if (!ast_strlen_zero(p
->loginchan
)) {
1712 snprintf(location
, sizeof(location
) - 20, "available at '%s'", p
->loginchan
);
1713 talkingto
[0] = '\0';
1716 if (p
->acknowledged
)
1717 strncat(location
, " (Confirmed)", sizeof(location
) - strlen(location
) - 1);
1719 if (!ast_strlen_zero(p
->moh
))
1720 snprintf(moh
, sizeof(moh
), " (musiconhold is '%s')", p
->moh
);
1722 ast_cli(fd
, "%-12.12s %s%s%s%s\n", p
->agent
, username
, location
, talkingto
, moh
);
1724 ast_mutex_unlock(&p
->lock
);
1726 AST_LIST_UNLOCK(&agents
);
1728 ast_cli(fd
, "No Agents are configured in %s\n", config
);
1730 ast_cli(fd
, "%d agents online\n", online_agents
);
1732 return RESULT_SUCCESS
;
1737 static char show_agents_usage
[] =
1738 "Usage: agent show\n"
1739 " Provides summary information on agents.\n";
1741 static char show_agents_online_usage
[] =
1742 "Usage: agent show online\n"
1743 " Provides a list of all online agents.\n";
1745 static char agent_logoff_usage
[] =
1746 "Usage: agent logoff <channel> [soft]\n"
1747 " Sets an agent as no longer logged in.\n"
1748 " If 'soft' is specified, do not hangup existing calls.\n";
1750 static struct ast_cli_entry cli_show_agents_deprecated
= {
1751 { "show", "agents", NULL
},
1755 static struct ast_cli_entry cli_show_agents_online_deprecated
= {
1756 { "show", "agents", "online" },
1757 agents_show_online
, NULL
,
1760 static struct ast_cli_entry cli_agents
[] = {
1761 { { "agent", "show", NULL
},
1762 agents_show
, "Show status of agents",
1763 show_agents_usage
, NULL
, &cli_show_agents_deprecated
},
1765 { { "agent", "show", "online" },
1766 agents_show_online
, "Show all online agents",
1767 show_agents_online_usage
, NULL
, &cli_show_agents_online_deprecated
},
1769 { { "agent", "logoff", NULL
},
1770 agent_logoff_cmd
, "Sets an agent offline",
1771 agent_logoff_usage
, complete_agent_logoff_cmd
},
1775 * \brief Log in agent application.
1779 * \param callbackmode non-zero for AgentCallbackLogin
1781 static int __login_exec(struct ast_channel
*chan
, void *data
, int callbackmode
)
1785 int max_login_tries
= maxlogintries
;
1786 struct agent_pvt
*p
;
1787 struct ast_module_user
*u
;
1788 int login_state
= 0;
1789 char user
[AST_MAX_AGENT
] = "";
1790 char pass
[AST_MAX_AGENT
];
1791 char agent
[AST_MAX_AGENT
] = "";
1792 char xpass
[AST_MAX_AGENT
] = "";
1795 AST_DECLARE_APP_ARGS(args
,
1796 AST_APP_ARG(agent_id
);
1797 AST_APP_ARG(options
);
1798 AST_APP_ARG(extension
);
1800 const char *tmpoptions
= NULL
;
1801 char *context
= NULL
;
1802 int play_announcement
= 1;
1803 char agent_goodbye
[AST_MAX_FILENAME_LEN
];
1804 int update_cdr
= updatecdr
;
1805 char *filename
= "agent-loginok";
1806 char tmpchan
[AST_MAX_BUF
] = "";
1808 u
= ast_module_user_add(chan
);
1810 parse
= ast_strdupa(data
);
1812 AST_STANDARD_APP_ARGS(args
, parse
);
1814 ast_copy_string(agent_goodbye
, agentgoodbye
, sizeof(agent_goodbye
));
1816 /* Set Channel Specific Login Overrides */
1817 if (pbx_builtin_getvar_helper(chan
, "AGENTLMAXLOGINTRIES") && strlen(pbx_builtin_getvar_helper(chan
, "AGENTLMAXLOGINTRIES"))) {
1818 max_login_tries
= atoi(pbx_builtin_getvar_helper(chan
, "AGENTMAXLOGINTRIES"));
1819 if (max_login_tries
< 0)
1820 max_login_tries
= 0;
1821 tmpoptions
=pbx_builtin_getvar_helper(chan
, "AGENTMAXLOGINTRIES");
1822 if (option_verbose
> 2)
1823 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
);
1825 if (pbx_builtin_getvar_helper(chan
, "AGENTUPDATECDR") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan
, "AGENTUPDATECDR"))) {
1826 if (ast_true(pbx_builtin_getvar_helper(chan
, "AGENTUPDATECDR")))
1830 tmpoptions
=pbx_builtin_getvar_helper(chan
, "AGENTUPDATECDR");
1831 if (option_verbose
> 2)
1832 ast_verbose(VERBOSE_PREFIX_3
"Saw variable AGENTUPDATECDR=%s, setting update_cdr to: %d on Channel '%s'.\n",tmpoptions
,update_cdr
,chan
->name
);
1834 if (pbx_builtin_getvar_helper(chan
, "AGENTGOODBYE") && !ast_strlen_zero(pbx_builtin_getvar_helper(chan
, "AGENTGOODBYE"))) {
1835 strcpy(agent_goodbye
, pbx_builtin_getvar_helper(chan
, "AGENTGOODBYE"));
1836 tmpoptions
=pbx_builtin_getvar_helper(chan
, "AGENTGOODBYE");
1837 if (option_verbose
> 2)
1838 ast_verbose(VERBOSE_PREFIX_3
"Saw variable AGENTGOODBYE=%s, setting agent_goodbye to: %s on Channel '%s'.\n",tmpoptions
,agent_goodbye
,chan
->name
);
1840 /* End Channel Specific Login Overrides */
1842 if (callbackmode
&& args
.extension
) {
1843 parse
= args
.extension
;
1844 args
.extension
= strsep(&parse
, "@");
1848 if (!ast_strlen_zero(args
.options
)) {
1849 if (strchr(args
.options
, 's')) {
1850 play_announcement
= 0;
1854 if (chan
->_state
!= AST_STATE_UP
)
1855 res
= ast_answer(chan
);
1857 if (!ast_strlen_zero(args
.agent_id
))
1858 ast_copy_string(user
, args
.agent_id
, AST_MAX_AGENT
);
1860 res
= ast_app_getdata(chan
, "agent-user", user
, sizeof(user
) - 1, 0);
1862 while (!res
&& (max_login_tries
==0 || tries
< max_login_tries
)) {
1864 /* Check for password */
1865 AST_LIST_LOCK(&agents
);
1866 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1867 if (!strcmp(p
->agent
, user
) && !p
->pending
)
1868 ast_copy_string(xpass
, p
->password
, sizeof(xpass
));
1870 AST_LIST_UNLOCK(&agents
);
1872 if (!ast_strlen_zero(xpass
))
1873 res
= ast_app_getdata(chan
, "agent-pass", pass
, sizeof(pass
) - 1, 0);
1877 errmsg
= "agent-incorrect";
1880 ast_log(LOG_NOTICE
, "user: %s, pass: %s\n", user
, pass
);
1883 /* Check again for accuracy */
1884 AST_LIST_LOCK(&agents
);
1885 AST_LIST_TRAVERSE(&agents
, p
, list
) {
1886 ast_mutex_lock(&p
->lock
);
1887 if (!strcmp(p
->agent
, user
) &&
1888 !strcmp(p
->password
, pass
) && !p
->pending
) {
1889 login_state
= 1; /* Successful Login */
1891 /* Ensure we can't be gotten until we're done */
1892 gettimeofday(&p
->lastdisc
, NULL
);
1893 p
->lastdisc
.tv_sec
++;
1895 /* Set Channel Specific Agent Overrides */
1896 if (pbx_builtin_getvar_helper(chan
, "AGENTACKCALL") && strlen(pbx_builtin_getvar_helper(chan
, "AGENTACKCALL"))) {
1897 if (!strcasecmp(pbx_builtin_getvar_helper(chan
, "AGENTACKCALL"), "always"))
1899 else if (ast_true(pbx_builtin_getvar_helper(chan
, "AGENTACKCALL")))
1903 tmpoptions
=pbx_builtin_getvar_helper(chan
, "AGENTACKCALL");
1904 if (option_verbose
> 2)
1905 ast_verbose(VERBOSE_PREFIX_3
"Saw variable AGENTACKCALL=%s, setting ackcall to: %d for Agent '%s'.\n",tmpoptions
,p
->ackcall
,p
->agent
);
1907 if (pbx_builtin_getvar_helper(chan
, "AGENTAUTOLOGOFF") && strlen(pbx_builtin_getvar_helper(chan
, "AGENTAUTOLOGOFF"))) {
1908 p
->autologoff
= atoi(pbx_builtin_getvar_helper(chan
, "AGENTAUTOLOGOFF"));
1909 if (p
->autologoff
< 0)
1911 tmpoptions
=pbx_builtin_getvar_helper(chan
, "AGENTAUTOLOGOFF");
1912 if (option_verbose
> 2)
1913 ast_verbose(VERBOSE_PREFIX_3
"Saw variable AGENTAUTOLOGOFF=%s, setting autologff to: %d for Agent '%s'.\n",tmpoptions
,p
->autologoff
,p
->agent
);
1915 if (pbx_builtin_getvar_helper(chan
, "AGENTWRAPUPTIME") && strlen(pbx_builtin_getvar_helper(chan
, "AGENTWRAPUPTIME"))) {
1916 p
->wrapuptime
= atoi(pbx_builtin_getvar_helper(chan
, "AGENTWRAPUPTIME"));
1917 if (p
->wrapuptime
< 0)
1919 tmpoptions
=pbx_builtin_getvar_helper(chan
, "AGENTWRAPUPTIME");
1920 if (option_verbose
> 2)
1921 ast_verbose(VERBOSE_PREFIX_3
"Saw variable AGENTWRAPUPTIME=%s, setting wrapuptime to: %d for Agent '%s'.\n",tmpoptions
,p
->wrapuptime
,p
->agent
);
1923 /* End Channel Specific Agent Overrides */
1925 char last_loginchan
[80] = "";
1927 snprintf(agent
, sizeof(agent
), "Agent/%s", p
->agent
);
1931 /* Retrieve login chan */
1933 if (!ast_strlen_zero(args
.extension
)) {
1934 ast_copy_string(tmpchan
, args
.extension
, sizeof(tmpchan
));
1937 res
= ast_app_getdata(chan
, "agent-newlocation", tmpchan
+pos
, sizeof(tmpchan
) - 2, 0);
1938 if (ast_strlen_zero(tmpchan
) )
1940 if(ast_exists_extension(chan
, S_OR(context
,"default"), tmpchan
,1, NULL
) ) {
1941 if(!allow_multiple_login(tmpchan
,context
) ) {
1942 args
.extension
= NULL
;
1947 if (args
.extension
) {
1948 ast_log(LOG_WARNING
, "Extension '%s' is not valid for automatic login of agent '%s'\n", args
.extension
, p
->agent
);
1949 args
.extension
= NULL
;
1952 ast_log(LOG_WARNING
, "Extension '%s@%s' is not valid for automatic login of agent '%s'\n", tmpchan
, S_OR(context
, "default"), p
->agent
);
1953 res
= ast_streamfile(chan
, "invalid", chan
->language
);
1955 res
= ast_waitstream(chan
, AST_DIGIT_ANY
);
1966 args
.extension
= tmpchan
;
1968 set_agentbycallerid(p
->logincallerid
, NULL
);
1969 if (!ast_strlen_zero(context
) && !ast_strlen_zero(tmpchan
))
1970 snprintf(p
->loginchan
, sizeof(p
->loginchan
), "%s@%s", tmpchan
, context
);
1972 ast_copy_string(last_loginchan
, p
->loginchan
, sizeof(last_loginchan
));
1973 ast_copy_string(p
->loginchan
, tmpchan
, sizeof(p
->loginchan
));
1975 p
->acknowledged
= 0;
1976 if (ast_strlen_zero(p
->loginchan
)) {
1978 filename
= "agent-loggedoff";
1980 if (chan
->cid
.cid_num
) {
1981 ast_copy_string(p
->logincallerid
, chan
->cid
.cid_num
, sizeof(p
->logincallerid
));
1982 set_agentbycallerid(p
->logincallerid
, p
->agent
);
1984 p
->logincallerid
[0] = '\0';
1987 if(update_cdr
&& chan
->cdr
)
1988 snprintf(chan
->cdr
->channel
, sizeof(chan
->cdr
->channel
), "Agent/%s", p
->agent
);
1992 p
->loginchan
[0] = '\0';
1993 p
->logincallerid
[0] = '\0';
1994 p
->acknowledged
= 0;
1996 ast_mutex_unlock(&p
->lock
);
1997 AST_LIST_UNLOCK(&agents
);
1998 if( !res
&& play_announcement
==1 )
1999 res
= ast_streamfile(chan
, filename
, chan
->language
);
2001 ast_waitstream(chan
, "");
2002 AST_LIST_LOCK(&agents
);
2003 ast_mutex_lock(&p
->lock
);
2005 res
= ast_set_read_format(chan
, ast_best_codec(chan
->nativeformats
));
2007 ast_log(LOG_WARNING
, "Unable to set read format to %d\n", ast_best_codec(chan
->nativeformats
));
2010 res
= ast_set_write_format(chan
, ast_best_codec(chan
->nativeformats
));
2012 ast_log(LOG_WARNING
, "Unable to set write format to %d\n", ast_best_codec(chan
->nativeformats
));
2014 /* Check once more just in case */
2017 if (callbackmode
&& !res
) {
2018 /* Just say goodbye and be done with it */
2019 if (!ast_strlen_zero(p
->loginchan
)) {
2020 if (p
->loginstart
== 0)
2021 time(&p
->loginstart
);
2022 manager_event(EVENT_FLAG_AGENT
, "Agentcallbacklogin",
2026 p
->agent
, p
->loginchan
, chan
->uniqueid
);
2027 ast_queue_log("NONE", chan
->uniqueid
, agent
, "AGENTCALLBACKLOGIN", "%s", p
->loginchan
);
2028 if (option_verbose
> 1)
2029 ast_verbose(VERBOSE_PREFIX_2
"Callback Agent '%s' logged in on %s\n", p
->agent
, p
->loginchan
);
2030 ast_device_state_changed("Agent/%s", p
->agent
);
2031 if (persistent_agents
)
2034 logintime
= time(NULL
) - p
->loginstart
;
2037 agent_logoff_maintenance(p
, last_loginchan
, logintime
, chan
->uniqueid
, NULL
);
2038 if (option_verbose
> 1)
2039 ast_verbose(VERBOSE_PREFIX_2
"Callback Agent '%s' logged out\n", p
->agent
);
2041 AST_LIST_UNLOCK(&agents
);
2043 res
= ast_safe_sleep(chan
, 500);
2044 ast_mutex_unlock(&p
->lock
);
2046 ast_indicate_data(chan
, AST_CONTROL_HOLD
,
2048 !ast_strlen_zero(p
->moh
) ? strlen(p
->moh
) + 1 : 0);
2049 if (p
->loginstart
== 0)
2050 time(&p
->loginstart
);
2051 manager_event(EVENT_FLAG_AGENT
, "Agentlogin",
2055 p
->agent
, chan
->name
, chan
->uniqueid
);
2056 if (update_cdr
&& chan
->cdr
)
2057 snprintf(chan
->cdr
->channel
, sizeof(chan
->cdr
->channel
), "Agent/%s", p
->agent
);
2058 ast_queue_log("NONE", chan
->uniqueid
, agent
, "AGENTLOGIN", "%s", chan
->name
);
2059 if (option_verbose
> 1)
2060 ast_verbose(VERBOSE_PREFIX_2
"Agent '%s' logged in (format %s/%s)\n", p
->agent
,
2061 ast_getformatname(chan
->readformat
), ast_getformatname(chan
->writeformat
));
2062 /* Login this channel and wait for it to go away */
2067 check_availability(p
, 0);
2068 ast_mutex_unlock(&p
->lock
);
2069 AST_LIST_UNLOCK(&agents
);
2070 ast_device_state_changed("Agent/%s", p
->agent
);
2072 ast_mutex_lock(&p
->lock
);
2073 if (p
->chan
!= chan
)
2075 ast_mutex_unlock(&p
->lock
);
2076 /* Yield here so other interested threads can kick in. */
2081 AST_LIST_LOCK(&agents
);
2082 ast_mutex_lock(&p
->lock
);
2083 if (p
->lastdisc
.tv_sec
) {
2084 if (ast_tvdiff_ms(ast_tvnow(), p
->lastdisc
) > p
->wrapuptime
) {
2086 ast_log(LOG_DEBUG
, "Wrapup time for %s expired!\n", p
->agent
);
2087 p
->lastdisc
= ast_tv(0, 0);
2091 check_availability(p
, 0);
2094 ast_mutex_unlock(&p
->lock
);
2095 AST_LIST_UNLOCK(&agents
);
2096 /* Synchronize channel ownership between call to agent and itself. */
2097 ast_mutex_lock( &p
->app_lock
);
2098 ast_mutex_lock(&p
->lock
);
2099 p
->owning_app
= pthread_self();
2100 ast_mutex_unlock(&p
->lock
);
2102 res
= agent_ack_sleep(p
);
2104 res
= ast_safe_sleep_conditional( chan
, 1000, agent_cont_sleep
, p
);
2105 ast_mutex_unlock( &p
->app_lock
);
2106 if ((p
->ackcall
> 1) && (res
== 1)) {
2107 AST_LIST_LOCK(&agents
);
2108 ast_mutex_lock(&p
->lock
);
2109 check_availability(p
, 0);
2110 ast_mutex_unlock(&p
->lock
);
2111 AST_LIST_UNLOCK(&agents
);
2116 ast_mutex_lock(&p
->lock
);
2117 if (res
&& p
->owner
)
2118 ast_log(LOG_WARNING
, "Huh? We broke out when there was still an owner?\n");
2119 /* Log us off if appropriate */
2120 if (p
->chan
== chan
)
2122 p
->acknowledged
= 0;
2123 logintime
= time(NULL
) - p
->loginstart
;
2125 ast_mutex_unlock(&p
->lock
);
2126 manager_event(EVENT_FLAG_AGENT
, "Agentlogoff",
2128 "Logintime: %ld\r\n"
2130 p
->agent
, logintime
, chan
->uniqueid
);
2131 ast_queue_log("NONE", chan
->uniqueid
, agent
, "AGENTLOGOFF", "%s|%ld", chan
->name
, logintime
);
2132 if (option_verbose
> 1)
2133 ast_verbose(VERBOSE_PREFIX_2
"Agent '%s' logged out\n", p
->agent
);
2134 /* If there is no owner, go ahead and kill it now */
2135 ast_device_state_changed("Agent/%s", p
->agent
);
2136 if (p
->dead
&& !p
->owner
) {
2137 ast_mutex_destroy(&p
->lock
);
2138 ast_mutex_destroy(&p
->app_lock
);
2143 ast_mutex_unlock(&p
->lock
);
2148 ast_mutex_unlock(&p
->lock
);
2149 errmsg
= "agent-alreadyon";
2154 ast_mutex_unlock(&p
->lock
);
2157 AST_LIST_UNLOCK(&agents
);
2159 if (!res
&& (max_login_tries
==0 || tries
< max_login_tries
))
2160 res
= ast_app_getdata(chan
, errmsg
, user
, sizeof(user
) - 1, 0);
2164 res
= ast_safe_sleep(chan
, 500);
2166 /* AgentLogin() exit */
2167 if (!callbackmode
) {
2168 ast_module_user_remove(u
);
2170 } else { /* AgentCallbackLogin() exit*/
2172 if (login_state
> 0) {
2173 pbx_builtin_setvar_helper(chan
, "AGENTNUMBER", user
);
2174 if (login_state
==1) {
2175 pbx_builtin_setvar_helper(chan
, "AGENTSTATUS", "on");
2176 pbx_builtin_setvar_helper(chan
, "AGENTEXTEN", args
.extension
);
2178 pbx_builtin_setvar_helper(chan
, "AGENTSTATUS", "off");
2180 pbx_builtin_setvar_helper(chan
, "AGENTSTATUS", "fail");
2182 if (ast_exists_extension(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 1, chan
->cid
.cid_num
)) {
2183 ast_module_user_remove(u
);
2186 /* Do we need to play agent-goodbye now that we will be hanging up? */
2187 if (play_announcement
) {
2189 res
= ast_safe_sleep(chan
, 1000);
2190 res
= ast_streamfile(chan
, agent_goodbye
, chan
->language
);
2192 res
= ast_waitstream(chan
, "");
2194 res
= ast_safe_sleep(chan
, 1000);
2198 ast_module_user_remove(u
);
2200 /* We should never get here if next priority exists when in callbackmode */
2205 * Called by the AgentLogin application (from the dial plan).
2210 * \sa callback_login_exec(), agentmonitoroutgoing_exec(), load_module().
2212 static int login_exec(struct ast_channel
*chan
, void *data
)
2214 return __login_exec(chan
, data
, 0);
2217 static void callback_deprecated(void)
2219 static int depwarning
= 0;
2224 ast_log(LOG_WARNING
, "AgentCallbackLogin is deprecated and will be removed in a future release.\n");
2225 ast_log(LOG_WARNING
, "See doc/queues-with-callback-members.txt for an example of how to achieve\n");
2226 ast_log(LOG_WARNING
, "the same functionality using only dialplan logic.\n");
2231 * Called by the AgentCallbackLogin application (from the dial plan).
2236 * \sa login_exec(), agentmonitoroutgoing_exec(), load_module().
2238 static int callback_exec(struct ast_channel
*chan
, void *data
)
2240 callback_deprecated();
2242 return __login_exec(chan
, data
, 1);
2246 * Sets an agent as logged in by callback in the Manager API.
2247 * It is registered on load_module() and it gets called by the manager backend.
2251 * \sa action_agents(), action_agent_logoff(), load_module().
2253 static int action_agent_callback_login(struct mansession
*s
, const struct message
*m
)
2255 const char *agent
= astman_get_header(m
, "Agent");
2256 const char *exten
= astman_get_header(m
, "Exten");
2257 const char *context
= astman_get_header(m
, "Context");
2258 const char *wrapuptime_s
= astman_get_header(m
, "WrapupTime");
2259 const char *ackcall_s
= astman_get_header(m
, "AckCall");
2260 struct agent_pvt
*p
;
2261 int login_state
= 0;
2263 callback_deprecated();
2265 if (ast_strlen_zero(agent
)) {
2266 astman_send_error(s
, m
, "No agent specified");
2270 if (ast_strlen_zero(exten
)) {
2271 astman_send_error(s
, m
, "No extension specified");
2275 AST_LIST_LOCK(&agents
);
2276 AST_LIST_TRAVERSE(&agents
, p
, list
) {
2277 if (strcmp(p
->agent
, agent
) || p
->pending
)
2280 login_state
= 2; /* already logged in (and on the phone)*/
2283 ast_mutex_lock(&p
->lock
);
2284 login_state
= 1; /* Successful Login */
2286 if (ast_strlen_zero(context
))
2287 ast_copy_string(p
->loginchan
, exten
, sizeof(p
->loginchan
));
2289 snprintf(p
->loginchan
, sizeof(p
->loginchan
), "%s@%s", exten
, context
);
2291 if (!ast_strlen_zero(wrapuptime_s
)) {
2292 p
->wrapuptime
= atoi(wrapuptime_s
);
2293 if (p
->wrapuptime
< 0)
2297 if (ast_true(ackcall_s
))
2302 if (p
->loginstart
== 0)
2303 time(&p
->loginstart
);
2304 manager_event(EVENT_FLAG_AGENT
, "Agentcallbacklogin",
2306 "Loginchan: %s\r\n",
2307 p
->agent
, p
->loginchan
);
2308 ast_queue_log("NONE", "NONE", agent
, "AGENTCALLBACKLOGIN", "%s", p
->loginchan
);
2309 if (option_verbose
> 1)
2310 ast_verbose(VERBOSE_PREFIX_2
"Callback Agent '%s' logged in on %s\n", p
->agent
, p
->loginchan
);
2311 ast_device_state_changed("Agent/%s", p
->agent
);
2312 ast_mutex_unlock(&p
->lock
);
2313 if (persistent_agents
)
2316 AST_LIST_UNLOCK(&agents
);
2318 if (login_state
== 1)
2319 astman_send_ack(s
, m
, "Agent logged in");
2320 else if (login_state
== 0)
2321 astman_send_error(s
, m
, "No such agent");
2322 else if (login_state
== 2)
2323 astman_send_error(s
, m
, "Agent already logged in");
2329 * \brief Called by the AgentMonitorOutgoing application (from the dial plan).
2334 * \sa login_exec(), callback_login_exec(), load_module().
2336 static int agentmonitoroutgoing_exec(struct ast_channel
*chan
, void *data
)
2338 int exitifnoagentid
= 0;
2340 int changeoutgoing
= 0;
2342 char agent
[AST_MAX_AGENT
];
2345 if (strchr(data
, 'd'))
2346 exitifnoagentid
= 1;
2347 if (strchr(data
, 'n'))
2349 if (strchr(data
, 'c'))
2352 if (chan
->cid
.cid_num
) {
2354 char agentvar
[AST_MAX_BUF
];
2355 snprintf(agentvar
, sizeof(agentvar
), "%s_%s", GETAGENTBYCALLERID
, chan
->cid
.cid_num
);
2356 if ((tmp
= pbx_builtin_getvar_helper(NULL
, agentvar
))) {
2357 struct agent_pvt
*p
;
2358 ast_copy_string(agent
, tmp
, sizeof(agent
));
2359 AST_LIST_LOCK(&agents
);
2360 AST_LIST_TRAVERSE(&agents
, p
, list
) {
2361 if (!strcasecmp(p
->agent
, tmp
)) {
2362 if (changeoutgoing
) snprintf(chan
->cdr
->channel
, sizeof(chan
->cdr
->channel
), "Agent/%s", p
->agent
);
2363 __agent_start_monitoring(chan
, p
, 1);
2367 AST_LIST_UNLOCK(&agents
);
2372 ast_log(LOG_WARNING
, "Couldn't find the global variable %s, so I can't figure out which agent (if it's an agent) is placing outgoing call.\n", agentvar
);
2377 ast_log(LOG_WARNING
, "There is no callerid on that call, so I can't figure out which agent (if it's an agent) is placing outgoing call.\n");
2379 /* check if there is n + 101 priority */
2380 /*! \todo XXX Needs to check option priorityjump etc etc */
2382 if (ast_exists_extension(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101, chan
->cid
.cid_num
)) {
2383 chan
->priority
+=100;
2384 if (option_verbose
> 2)
2385 ast_verbose(VERBOSE_PREFIX_3
"Going to %d priority because there is no callerid or the agentid cannot be found.\n",chan
->priority
);
2386 } else if (exitifnoagentid
)
2393 * \brief Dump AgentCallbackLogin agents to the ASTdb database for persistence
2395 static void dump_agents(void)
2397 struct agent_pvt
*cur_agent
= NULL
;
2400 AST_LIST_TRAVERSE(&agents
, cur_agent
, list
) {
2401 if (cur_agent
->chan
)
2404 if (!ast_strlen_zero(cur_agent
->loginchan
)) {
2405 snprintf(buf
, sizeof(buf
), "%s;%s", cur_agent
->loginchan
, cur_agent
->logincallerid
);
2406 if (ast_db_put(pa_family
, cur_agent
->agent
, buf
))
2407 ast_log(LOG_WARNING
, "failed to create persistent entry in ASTdb for %s!\n", buf
);
2408 else if (option_debug
)
2409 ast_log(LOG_DEBUG
, "Saved Agent: %s on %s\n", cur_agent
->agent
, cur_agent
->loginchan
);
2411 /* Delete - no agent or there is an error */
2412 ast_db_del(pa_family
, cur_agent
->agent
);
2418 * \brief Reload the persistent agents from astdb.
2420 static void reload_agents(void)
2423 struct ast_db_entry
*db_tree
;
2424 struct ast_db_entry
*entry
;
2425 struct agent_pvt
*cur_agent
;
2426 char agent_data
[256];
2429 char *agent_callerid
;
2431 db_tree
= ast_db_gettree(pa_family
, NULL
);
2433 AST_LIST_LOCK(&agents
);
2434 for (entry
= db_tree
; entry
; entry
= entry
->next
) {
2435 agent_num
= entry
->key
+ strlen(pa_family
) + 2;
2436 AST_LIST_TRAVERSE(&agents
, cur_agent
, list
) {
2437 ast_mutex_lock(&cur_agent
->lock
);
2438 if (strcmp(agent_num
, cur_agent
->agent
) == 0)
2440 ast_mutex_unlock(&cur_agent
->lock
);
2443 ast_db_del(pa_family
, agent_num
);
2446 ast_mutex_unlock(&cur_agent
->lock
);
2447 if (!ast_db_get(pa_family
, agent_num
, agent_data
, sizeof(agent_data
)-1)) {
2449 ast_log(LOG_DEBUG
, "Reload Agent from AstDB: %s on %s\n", cur_agent
->agent
, agent_data
);
2451 agent_chan
= strsep(&parse
, ";");
2452 agent_callerid
= strsep(&parse
, ";");
2453 ast_copy_string(cur_agent
->loginchan
, agent_chan
, sizeof(cur_agent
->loginchan
));
2454 if (agent_callerid
) {
2455 ast_copy_string(cur_agent
->logincallerid
, agent_callerid
, sizeof(cur_agent
->logincallerid
));
2456 set_agentbycallerid(cur_agent
->logincallerid
, cur_agent
->agent
);
2458 cur_agent
->logincallerid
[0] = '\0';
2459 if (cur_agent
->loginstart
== 0)
2460 time(&cur_agent
->loginstart
);
2461 ast_device_state_changed("Agent/%s", cur_agent
->agent
);
2464 AST_LIST_UNLOCK(&agents
);
2466 ast_log(LOG_NOTICE
, "Agents successfully reloaded from database.\n");
2467 ast_db_freetree(db_tree
);
2471 /*! \brief Part of PBX channel interface */
2472 static int agent_devicestate(void *data
)
2474 struct agent_pvt
*p
;
2476 ast_group_t groupmatch
;
2479 int res
= AST_DEVICE_INVALID
;
2482 if ((s
[0] == '@') && (sscanf(s
+ 1, "%d", &groupoff
) == 1))
2483 groupmatch
= (1 << groupoff
);
2484 else if ((s
[0] == ':') && (sscanf(s
+ 1, "%d", &groupoff
) == 1)) {
2485 groupmatch
= (1 << groupoff
);
2490 /* Check actual logged in agents first */
2491 AST_LIST_LOCK(&agents
);
2492 AST_LIST_TRAVERSE(&agents
, p
, list
) {
2493 ast_mutex_lock(&p
->lock
);
2494 if (!p
->pending
&& ((groupmatch
&& (p
->group
& groupmatch
)) || !strcmp(data
, p
->agent
))) {
2496 if (res
!= AST_DEVICE_INUSE
)
2497 res
= AST_DEVICE_BUSY
;
2499 if (res
== AST_DEVICE_BUSY
)
2500 res
= AST_DEVICE_INUSE
;
2501 if (p
->chan
|| !ast_strlen_zero(p
->loginchan
)) {
2502 if (res
== AST_DEVICE_INVALID
)
2503 res
= AST_DEVICE_UNKNOWN
;
2504 } else if (res
== AST_DEVICE_INVALID
)
2505 res
= AST_DEVICE_UNAVAILABLE
;
2507 if (!strcmp(data
, p
->agent
)) {
2508 ast_mutex_unlock(&p
->lock
);
2512 ast_mutex_unlock(&p
->lock
);
2514 AST_LIST_UNLOCK(&agents
);
2518 static struct agent_pvt
*find_agent(char *agentid
)
2520 struct agent_pvt
*cur
;
2522 AST_LIST_TRAVERSE(&agents
, cur
, list
) {
2523 if (!strcmp(cur
->agent
, agentid
))
2530 static int function_agent(struct ast_channel
*chan
, char *cmd
, char *data
, char *buf
, size_t len
)
2533 AST_DECLARE_APP_ARGS(args
,
2534 AST_APP_ARG(agentid
);
2538 struct agent_pvt
*agent
;
2542 if (ast_strlen_zero(data
)) {
2543 ast_log(LOG_WARNING
, "The AGENT function requires an argument - agentid!\n");
2547 parse
= ast_strdupa(data
);
2549 AST_NONSTANDARD_APP_ARGS(args
, parse
, ':');
2551 args
.item
= "status";
2553 if (!(agent
= find_agent(args
.agentid
))) {
2554 ast_log(LOG_WARNING
, "Agent '%s' not found!\n", args
.agentid
);
2558 if (!strcasecmp(args
.item
, "status")) {
2559 char *status
= "LOGGEDOUT";
2560 if (agent
->chan
|| !ast_strlen_zero(agent
->loginchan
))
2561 status
= "LOGGEDIN";
2562 ast_copy_string(buf
, status
, len
);
2563 } else if (!strcasecmp(args
.item
, "password"))
2564 ast_copy_string(buf
, agent
->password
, len
);
2565 else if (!strcasecmp(args
.item
, "name"))
2566 ast_copy_string(buf
, agent
->name
, len
);
2567 else if (!strcasecmp(args
.item
, "mohclass"))
2568 ast_copy_string(buf
, agent
->moh
, len
);
2569 else if (!strcasecmp(args
.item
, "channel")) {
2571 ast_copy_string(buf
, agent
->chan
->name
, len
);
2572 tmp
= strrchr(buf
, '-');
2576 } else if (!strcasecmp(args
.item
, "exten"))
2577 ast_copy_string(buf
, agent
->loginchan
, len
);
2582 struct ast_custom_function agent_function
= {
2584 .synopsis
= "Gets information about an Agent",
2585 .syntax
= "AGENT(<agentid>[:item])",
2586 .read
= function_agent
,
2587 .desc
= "The valid items to retrieve are:\n"
2588 "- status (default) The status of the agent\n"
2589 " LOGGEDIN | LOGGEDOUT\n"
2590 "- password The password of the agent\n"
2591 "- name The name of the agent\n"
2592 "- mohclass MusicOnHold class\n"
2593 "- exten The callback extension for the Agent (AgentCallbackLogin)\n"
2594 "- channel The name of the active channel for the Agent (AgentLogin)\n"
2599 * \brief Initialize the Agents module.
2600 * This function is being called by Asterisk when loading the module.
2601 * Among other things it registers applications, cli commands and reads the cofiguration file.
2603 * \returns int Always 0.
2605 static int load_module(void)
2607 /* Make sure we can register our agent channel type */
2608 if (ast_channel_register(&agent_tech
)) {
2609 ast_log(LOG_ERROR
, "Unable to register channel class 'Agent'\n");
2612 /* Read in the config */
2613 if (!read_agent_config())
2614 return AST_MODULE_LOAD_DECLINE
;
2615 if (persistent_agents
)
2617 /* Dialplan applications */
2618 ast_register_application(app
, login_exec
, synopsis
, descrip
);
2619 ast_register_application(app2
, callback_exec
, synopsis2
, descrip2
);
2620 ast_register_application(app3
, agentmonitoroutgoing_exec
, synopsis3
, descrip3
);
2622 /* Manager commands */
2623 ast_manager_register2("Agents", EVENT_FLAG_AGENT
, action_agents
, "Lists agents and their status", mandescr_agents
);
2624 ast_manager_register2("AgentLogoff", EVENT_FLAG_AGENT
, action_agent_logoff
, "Sets an agent as no longer logged in", mandescr_agent_logoff
);
2625 ast_manager_register2("AgentCallbackLogin", EVENT_FLAG_AGENT
, action_agent_callback_login
, "Sets an agent as logged in by callback", mandescr_agent_callback_login
);
2628 ast_cli_register_multiple(cli_agents
, sizeof(cli_agents
) / sizeof(struct ast_cli_entry
));
2630 /* Dialplan Functions */
2631 ast_custom_function_register(&agent_function
);
2636 static int reload(void)
2638 read_agent_config();
2639 if (persistent_agents
)
2644 static int unload_module(void)
2646 struct agent_pvt
*p
;
2647 /* First, take us out of the channel loop */
2648 ast_channel_unregister(&agent_tech
);
2649 /* Unregister dialplan functions */
2650 ast_custom_function_unregister(&agent_function
);
2651 /* Unregister CLI commands */
2652 ast_cli_unregister_multiple(cli_agents
, sizeof(cli_agents
) / sizeof(struct ast_cli_entry
));
2653 /* Unregister dialplan applications */
2654 ast_unregister_application(app
);
2655 ast_unregister_application(app2
);
2656 ast_unregister_application(app3
);
2657 /* Unregister manager command */
2658 ast_manager_unregister("Agents");
2659 ast_manager_unregister("AgentLogoff");
2660 ast_manager_unregister("AgentCallbackLogin");
2661 /* Unregister channel */
2662 AST_LIST_LOCK(&agents
);
2663 /* Hangup all interfaces if they have an owner */
2664 while ((p
= AST_LIST_REMOVE_HEAD(&agents
, list
))) {
2666 ast_softhangup(p
->owner
, AST_SOFTHANGUP_APPUNLOAD
);
2669 AST_LIST_UNLOCK(&agents
);
2670 AST_LIST_HEAD_DESTROY(&agents
);
2674 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "Agent Proxy Channel",
2675 .load
= load_module
,
2676 .unload
= unload_module
,