Version 0.3.0 from FTP
authorMark Spencer <markster@digium.com>
Fri, 6 Dec 2002 16:22:19 +0000 (16:22 +0000)
committerMark Spencer <markster@digium.com>
Fri, 6 Dec 2002 16:22:19 +0000 (16:22 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@564 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_agent.c

index d9eef1c..61f5bb2 100755 (executable)
@@ -52,11 +52,13 @@ static char *app = "AgentLogin";
 static char *synopsis = "Call agent login";
 
 static char *descrip =
-"  AgentLogin():\n"
+"  AgentLogin([AgentNo][|options]):\n"
 "Asks the agent to login to the system.  Always returns -1.  While\n"
 "logged in, the agent can receive calls and will hear a 'beep'\n"
 "when a new call comes in.  The agent can dump the call by pressing\n"
-"the star key.\n";
+"the star key.\n"
+"The option string may contain zero or more of the following characters:\n"
+"      's' -- silent login - do not announce the login ok segment\n";
 
 static char moh[80] = "default";
 
@@ -77,6 +79,9 @@ static struct agent_pvt {
        char agent[AST_MAX_AGENT];                      /* Agent ID */
        char password[AST_MAX_AGENT];           /* Password for Agent login */
        char name[AST_MAX_AGENT];
+       pthread_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 */
        struct ast_channel *chan;                       /* Channel we use */
        struct agent_pvt *next;                         /* Agent */
@@ -119,6 +124,10 @@ static int add_agent(struct ast_variable *var)
                if (p) {
                        memset(p, 0, sizeof(struct agent_pvt));
                        strncpy(p->agent, tmp, sizeof(p->agent) -1);
+                       ast_pthread_mutex_init( &p->lock );
+                       ast_pthread_mutex_init( &p->app_lock );
+                       p->owning_app = -1;
+                       p->app_sleep_cond = 1;
                        p->next = agents;
                        agents = p;
                        
@@ -179,6 +188,7 @@ static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
        ast_pthread_mutex_lock(&p->lock);
        if (p->owner != oldchan) {
                ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
+               ast_pthread_mutex_unlock(&p->lock);
                return -1;
        }
        p->owner = newchan;
@@ -228,6 +238,7 @@ static int agent_call(struct ast_channel *ast, char *dest, int timeout)
        }
        /* Call is immediately up */
        ast_setstate(ast, AST_STATE_UP);
+       CLEANUP(ast,p);
        ast_pthread_mutex_unlock(&p->lock);
        return res;
 }
@@ -238,21 +249,43 @@ static int agent_hangup(struct ast_channel *ast)
        ast_pthread_mutex_lock(&p->lock);
        p->owner = NULL;
        ast->pvt->pvt = NULL;
+       p->app_sleep_cond = 1;
        ast_pthread_mutex_unlock(&p->lock);
+       /* Release ownership of the agent to other threads (presumably running the login app). */
+       ast_pthread_mutex_unlock(&p->app_lock);
        if (p->chan) {
                /* If they're dead, go ahead and hang up on the agent now */
+               ast_pthread_mutex_lock(&p->chan->lock);
                if (p->dead)
                        ast_softhangup(p->chan, AST_SOFTHANGUP_EXPLICIT);
                ast_moh_start(p->chan, p->moh);
-       } else if (p->dead) 
+               ast_pthread_mutex_unlock(&p->chan->lock);
+       } else if (p->dead)
                /* Go ahead and lose it */
                free(p);
+
        return 0;
 }
 
+static int agent_cont_sleep( void *data )
+{
+       struct agent_pvt *p;
+       int res;
+
+       p = (struct agent_pvt *)data;
+
+       ast_pthread_mutex_lock(&p->lock);
+       res = p->app_sleep_cond;
+       ast_pthread_mutex_unlock(&p->lock);
+       if( !res )
+               ast_log( LOG_DEBUG, "agent_cont_sleep() returning %d\n", res );
+       return res;
+}
+
 static struct ast_channel *agent_new(struct agent_pvt *p, int state)
 {
        struct ast_channel *tmp;
+       struct ast_frame null_frame = { AST_FRAME_NULL };
        if (!p->chan) {
                ast_log(LOG_WARNING, "No channel? :(\n");
                return NULL;
@@ -286,12 +319,26 @@ static struct ast_channel *agent_new(struct agent_pvt *p, int state)
                strncpy(tmp->context, p->chan->context, sizeof(tmp->context)-1);
                strncpy(tmp->exten, p->chan->exten, sizeof(tmp->exten)-1);
                tmp->priority = 1;
-               /* Wake up any waiting blockers (by definition the login app) */
+               /* 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( pthread_mutex_trylock(&p->app_lock) )
+               {
+                       ast_queue_frame(p->chan, &null_frame, 1);
+                       ast_pthread_mutex_unlock(&p->lock);     /* For other thread to read the condition. */
+                       ast_pthread_mutex_lock(&p->app_lock);
+                       ast_pthread_mutex_lock(&p->lock);
+               }
+               p->owning_app = pthread_self();
+               /* After the above step, there should not be any blockers. */
                if (p->chan->blocking) {
-                       pthread_kill(p->chan->blocker, SIGURG);
-                       /* Wait until the blocker releases it */
-                       while(p->chan->blocking)
-                               usleep(1000);
+                       ast_log( LOG_ERROR, "A blocker exists after agent channel ownership acquired\n" );
+                       CRASH;
                }
                ast_moh_stop(p->chan);
        } else
@@ -435,11 +482,35 @@ static int login_exec(struct ast_channel *chan, void *data)
        char pass[AST_MAX_AGENT];
        char xpass[AST_MAX_AGENT] = "";
        char *errmsg;
+       char info[512];
+       char *opt_user = NULL;
+       char *options = NULL;
+       int play_announcement;
+       struct timespec required;
+       struct timespec remaining;
+       int delay;
+       
        LOCAL_USER_ADD(u);
+
+       /* Parse the arguments XXX Check for failure XXX */
+       strncpy(info, (char *)data, strlen((char *)data) + AST_MAX_EXTENSION-1);
+       opt_user = info;
+       if( opt_user ) {
+               options = strchr(opt_user, '|');
+               if (options) {
+                       *options = '\0';
+                       options++;
+               }
+       }
+
        if (chan->_state != AST_STATE_UP)
                res = ast_answer(chan);
-       if (!res)
-               res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
+       if (!res) {
+               if( opt_user )
+                       strncpy( user, opt_user, AST_MAX_AGENT );
+               else
+                       res = ast_app_getdata(chan, "agent-user", user, sizeof(user) - 1, 0);
+       }
        while (!res && (tries < 3)) {
                /* Check for password */
                ast_pthread_mutex_lock(&agentlock);
@@ -451,7 +522,7 @@ static int login_exec(struct ast_channel *chan, void *data)
                }
                ast_pthread_mutex_unlock(&agentlock);
                if (!res) {
-                       if (strlen(xpass) || !p)
+                       if (strlen(xpass))
                                res = ast_app_getdata(chan, "agent-pass", pass, sizeof(pass) - 1, 0);
                        else
                                strcpy(pass, "");
@@ -470,7 +541,12 @@ static int login_exec(struct ast_channel *chan, void *data)
                        if (!strcmp(p->agent, user) &&
                                !strcmp(p->password, pass)) {
                                        if (!p->chan) {
-                                               res = ast_streamfile(chan, "agent-loginok", chan->language);
+                                               play_announcement = 1;
+                                               if( options )
+                                                       if( strchr( options, 's' ) )
+                                                               play_announcement = 0;
+                                               if( play_announcement )
+                                                       res = ast_streamfile(chan, "agent-loginok", chan->language);
                                                if (!res)
                                                        ast_waitstream(chan, "");
                                                if (!res) {
@@ -500,13 +576,33 @@ static int login_exec(struct ast_channel *chan, void *data)
                                                        p->chan = chan;
                                                        ast_pthread_mutex_unlock(&p->lock);
                                                        ast_pthread_mutex_unlock(&agentlock);
-                                                       while ((res >= 0) && (p->chan == chan)) {
-                                                               /* True sleep here, since we're being monitored
-                                                                  elsewhere instead */
+                                                       while (res >= 0) {
+                                                               /* If we are not the owner, delay here for a while
+                                                                * so other interested threads can kick in. */
+                                                               delay = 0;
+                                                               ast_pthread_mutex_lock(&p->lock);
+                                                               if (p->chan != chan)
+                                                                       res = -1;
                                                                if (p->owner) 
-                                                                       sleep(1);
-                                                               else
-                                                                       res = ast_safe_sleep(chan, 1000);
+                                                                       delay = 1;
+                                                               ast_pthread_mutex_unlock(&p->lock);
+                                                               if (delay) {
+                                                                       sched_yield();
+                                                                       required.tv_sec = 0;
+                                                                       required.tv_nsec = 20 * 1000 * 1000;
+                                                                       nanosleep( &required, &remaining );
+                                                               }
+                                                               if (res)
+                                                                       break;
+
+                                                               /*      Synchronize channel ownership between call to agent and itself. */
+                                                               pthread_mutex_lock( &p->app_lock );
+                                                               ast_pthread_mutex_lock(&p->lock);
+                                                               p->owning_app = pthread_self();
+                                                               ast_pthread_mutex_unlock(&p->lock);
+                                                               res = ast_safe_sleep_conditional( chan, 1000,
+                                                                                                               agent_cont_sleep, p );
+                                                               pthread_mutex_unlock( &p->app_lock );
                                                        }
                                                        if (res && p->owner) 
                                                                ast_log(LOG_WARNING, "Huh?  We broke out when there was still an owner?\n");
@@ -522,6 +618,10 @@ static int login_exec(struct ast_channel *chan, void *data)
                                                        if (p->dead && !p->owner)
                                                                free(p);
                                                }
+                                               else {
+                                                       ast_pthread_mutex_unlock(&p->lock);
+                                                       p = NULL;
+                                               }
                                                res = -1;
                                        } else {
                                                ast_pthread_mutex_unlock(&p->lock);