]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Merged revisions 318734 via svnmerge from
authorRichard Mudgett <rmudgett@digium.com>
Fri, 13 May 2011 01:14:28 +0000 (01:14 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Fri, 13 May 2011 01:14:28 +0000 (01:14 +0000)
https://origsvn.digium.com/svn/asterisk/branches/1.4

................
  r318734 | rmudgett | 2011-05-12 20:09:40 -0500 (Thu, 12 May 2011) | 43 lines

  Merged revisions 318671 via svnmerge from
  https://origsvn.digium.com/svn/asterisk/branches/1.8

  * The applicable fixes for v1.4 are the SIP deadlock and the in progress
  masquerade check for multiple parties trying to pickup the same call.
        issue18654_v1.4.patch uploaded by rmudgett (license 664)

  * Backported to v1.6.2.
        issue18654_v1.6.2.patch uploaded by rmudgett (license 664)

  ........
    r318671 | alecdavis | 2011-05-13 10:52:08 +1200 (Fri, 13 May 2011) | 30 lines

    Fix directed group pickup feature code *8 with pickupsounds enabled

    Since 1.6.2, the new pickupsound and pickupfailsound in features.conf cause many issues.

    1). chan_sip:handle_request_invite() shouldn't be playing out the fail/success audio, as it has 'netlock' locked.
    2). dialplan applications for directed_pickups shouldn't beep.
    3). feature code for directed pickup should beep on success/failure if configured.

    Created a sip_pickup() thread to handle the pickup and playout the audio, spawned from handle_request_invite.

    Moved app_directed:pickup_do() to features:ast_do_pickup().

    Functions below, all now use the new ast_do_pickup()
    app_directed_pickup.c:
       pickup_by_channel()
       pickup_by_exten()
       pickup_by_mark()
       pickup_by_part()
    features.c:
       ast_pickup_call()

    (closes issue #18654)
    Reported by: Docent
    Patches:
          ast_do_pickup_1.8_trunk.diff.txt uploaded by alecdavis (license 585)
    Tested by: lmadsen, francesco_r, amilcar, isis242, alecdavis, irroot, rymkus, loloski, rmudgett

    Review: https://reviewboard.asterisk.org/r/1185/
  ........
................

git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.6.2@318735 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_directed_pickup.c
channels/chan_sip.c
include/asterisk/features.h
main/features.c

index 0b70da587f2a8f8838c3183054e2ab784003c8fa..ce8c075550d9ce00cd9e27f07a55a5242f871199 100644 (file)
@@ -87,38 +87,17 @@ static const char *app = "Pickup";
 static const char *app2 = "PickupChan";
 /*! \todo This application should return a result code, like PICKUPRESULT */
 
-/* Perform actual pickup between two channels */
-static int pickup_do(struct ast_channel *chan, struct ast_channel *target)
-{
-       int res = 0;
-
-       ast_debug(1, "Call pickup on '%s' by '%s'\n", target->name, chan->name);
-
-       if ((res = ast_answer(chan))) {
-               ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
-               return -1;
-       }
-
-       if ((res = ast_queue_control(chan, AST_CONTROL_ANSWER))) {
-               ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
-               return -1;
-       }
-
-       if ((res = ast_channel_masquerade(target, chan))) {
-               ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, target->name);
-               return -1;
-       }
-
-       return res;
-}
-
 /* Helper function that determines whether a channel is capable of being picked up */
 static int can_pickup(struct ast_channel *chan)
 {
-       if (!chan->pbx && (chan->_state == AST_STATE_RINGING || chan->_state == AST_STATE_RING || chan->_state == AST_STATE_DOWN))
+       if (!chan->pbx && !chan->masq &&
+               !ast_test_flag(chan, AST_FLAG_ZOMBIE) &&
+               (chan->_state == AST_STATE_RINGING ||
+                chan->_state == AST_STATE_RING ||
+                chan->_state == AST_STATE_DOWN)) {
                return 1;
-       else
-               return 0;
+       }
+       return 0;
 }
 
 /*! \brief Helper Function to walk through ALL channels checking NAME and STATE */
@@ -169,8 +148,8 @@ static int pickup_by_channel(struct ast_channel *chan, char *pickup)
                return -1;
 
        /* Just check that we are not picking up the SAME as target */
-       if (chan->name != target->name && chan != target) {
-               res = pickup_do(chan, target);
+       if (chan != target) {
+               res = ast_do_pickup(chan, target);
        }
        ast_channel_unlock(target);
 
@@ -205,9 +184,8 @@ static int pickup_by_exten(struct ast_channel *chan, const char *exten, const ch
        target = ast_channel_search_locked(find_by_exten, &search);
 
        if (target) {
-               int res = pickup_do(chan, target);
+               int res = ast_do_pickup(chan, target);
                ast_channel_unlock(target);
-               target = NULL;
                return res;
        }
 
@@ -230,9 +208,31 @@ static int pickup_by_mark(struct ast_channel *chan, const char *mark)
        struct ast_channel *target = ast_channel_search_locked(find_by_mark, (char *) mark);
 
        if (target) {
-               int res = pickup_do(chan, target);
+               int res = ast_do_pickup(chan, target);
+               ast_channel_unlock(target);
+               return res;
+       }
+
+       return -1;
+}
+
+static int find_by_group(struct ast_channel *c, void *data)
+{
+       struct ast_channel *chan = data;
+
+       return (c != chan) && (chan->pickupgroup & c->callgroup) && can_pickup(c);
+}
+
+static int pickup_by_group(struct ast_channel *chan)
+{
+       struct ast_channel *target = ast_channel_search_locked(find_by_group, chan);
+
+       if (target) {
+               int res;
+
+               ast_log(LOG_NOTICE, "%s, pickup attempt by %s\n", target->name, chan->name);
+               res = ast_do_pickup(chan, target);
                ast_channel_unlock(target);
-               target = NULL;
                return res;
        }
 
@@ -247,10 +247,10 @@ static int pickup_exec(struct ast_channel *chan, void *data)
        char *exten = NULL, *context = NULL;
 
        if (ast_strlen_zero(data)) {
-               res = ast_pickup_call(chan);
+               res = pickup_by_group(chan);
                return res;
        }
-       
+
        /* Parse extension (and context if there) */
        while (!ast_strlen_zero(tmp) && (exten = strsep(&tmp, "&"))) {
                if ((context = strchr(exten, '@')))
index 703a7aca439ca70a832f9ef94a4d93fa2455ca5e..85957187ccfd8232bb8d2eaeecffef1036f9aa20 100644 (file)
@@ -2339,6 +2339,10 @@ static struct sip_pvt *get_sip_pvt_byid_locked(const char *callid, const char *t
 static void check_pendings(struct sip_pvt *p);
 static void *sip_park_thread(void *stuff);
 static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct sip_request *req, int seqno);
+
+static void *sip_pickup_thread(void *stuff);
+static int sip_pickup(struct ast_channel *chan);
+
 static int sip_sipredirect(struct sip_pvt *p, const char *dest);
 
 /*--- Codec handling / SDP */
@@ -19526,6 +19530,41 @@ static int sip_park(struct ast_channel *chan1, struct ast_channel *chan2, struct
        return 0;
 }
 
+
+/*! \brief SIP pickup support function
+ *     Starts in a new thread, then pickup the call
+ */
+static void *sip_pickup_thread(void *stuff)
+{
+       struct ast_channel *chan;
+       chan = stuff;
+
+       if (ast_pickup_call(chan)) {
+               chan->hangupcause = AST_CAUSE_CALL_REJECTED;
+       } else {
+               chan->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+       }
+       ast_hangup(chan);
+       chan = NULL;
+       return NULL;
+}
+
+/*! \brief Pickup a call using the subsystem in features.c
+ *     This is executed in a separate thread
+ */
+static int sip_pickup(struct ast_channel *chan)
+{
+       pthread_t threadid;
+
+       if (ast_pthread_create_detached_background(&threadid, NULL, sip_pickup_thread, chan)) {
+               ast_debug(1, "Unable to start Group pickup thread on channel %s\n", chan->name);
+               return -1;
+       }
+       ast_debug(1, "Started Group pickup thread on channel %s\n", chan->name);
+       return 0;
+}
+
+
 /*! \brief Turn off generator data 
        XXX Does this function belong in the SIP channel?
 */
@@ -20239,7 +20278,7 @@ static int sip_uri_cmp(const char *input1, const char *input2)
        return sip_uri_params_cmp(params1, params2);
 }
 
-/* \note No channel or pvt locks should be held while calling this function. */
+/*! \note No channel or pvt locks should be held while calling this function. */
 static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context)
 {
        struct ast_str *str = ast_str_alloca(AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2);
@@ -20980,30 +21019,29 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
 
                                        /* Unlock locks so ast_hangup can do its magic */
                                        ast_channel_unlock(c);
+                                       *nounlock = 1;
                                        sip_pvt_unlock(p);
                                        ast_hangup(c);
                                        sip_pvt_lock(p);
                                        c = NULL;
                                }
                        } else {        /* Pickup call in call group */
-                               ast_channel_unlock(c);
-                               *nounlock = 1;
-                               if (ast_pickup_call(c)) {
-                                       ast_log(LOG_NOTICE, "Nothing to pick up for %s\n", p->callid);
-                                       transmit_response_reliable(p, "503 Unavailable", req);
+                               if (sip_pickup(c)) {
+                                       ast_log(LOG_WARNING, "Failed to start Group pickup by %s\n", c->name);
+                                       transmit_response_reliable(p, "480 Temporarily Unavailable", req);
                                        sip_alreadygone(p);
+                                       c->hangupcause = AST_CAUSE_FAILURE;
+
                                        /* Unlock locks so ast_hangup can do its magic */
+                                       ast_channel_unlock(c);
+                                       *nounlock = 1;
+
+                                       p->invitestate = INV_COMPLETED;
                                        sip_pvt_unlock(p);
-                                       c->hangupcause = AST_CAUSE_CALL_REJECTED;
-                               } else {
-                                       sip_pvt_unlock(p);
-                                       ast_setstate(c, AST_STATE_DOWN);
-                                       c->hangupcause = AST_CAUSE_NORMAL_CLEARING;
+                                       ast_hangup(c);
+                                       sip_pvt_lock(p);
+                                       c = NULL;
                                }
-                               p->invitestate = INV_COMPLETED;
-                               ast_hangup(c);
-                               sip_pvt_lock(p);
-                               c = NULL;
                        }
                        break;
                case AST_STATE_RING:
index 9a666c7438f317ecb4a8ba41232326d7b272226f..72f536d8c1a17f27431528d550878e3092fc39eb 100644 (file)
@@ -120,6 +120,14 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct as
 /*! \brief Pickup a call */
 int ast_pickup_call(struct ast_channel *chan);
 
+/*!
+ * \brief Pickup a call target
+ * \note This function assumes that target is locked
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target);
+
 /*! \brief register new feature into feature_set 
    \param feature an ast_call_feature object which contains a keysequence
    and a callback function which is called when this keysequence is pressed
index d6ee9b1a2e5b230abaa11b6708fe310b4ec2c818..d77384d54855935f6e7ebe4d89715c714458e4de 100644 (file)
@@ -5044,17 +5044,19 @@ static int manager_park(struct mansession *s, const struct message *m)
        return 0;
 }
 
-static int find_channel_by_group(struct ast_channel *c, void *data) {
+static int find_channel_by_group(struct ast_channel *c, void *data)
+{
        struct ast_channel *chan = data;
 
        return !c->pbx &&
                /* Accessing 'chan' here is safe without locking, because there is no way for
-                  the channel do disappear from under us at this point.  pickupgroup *could*
+                  the channel to disappear from under us at this point.  pickupgroup *could*
                   change while we're here, but that isn't a problem. */
                (c != chan) &&
                (chan->pickupgroup & c->callgroup) &&
                ((c->_state == AST_STATE_RINGING) || (c->_state == AST_STATE_RING)) &&
-               !c->masq;
+               !c->masq &&
+               !ast_test_flag(c, AST_FLAG_ZOMBIE);
 }
 
 /*!
@@ -5064,35 +5066,62 @@ static int find_channel_by_group(struct ast_channel *c, void *data) {
  * Walk list of channels, checking it is not itself, channel is pbx one,
  * check that the callgroup for both channels are the same and the channel is ringing.
  * Answer calling channel, flag channel as answered on queue, masq channels together.
-*/
+ */
 int ast_pickup_call(struct ast_channel *chan)
 {
-       struct ast_channel *cur = ast_channel_search_locked(find_channel_by_group, chan);
-
-       if (cur) {
-               int res = -1;
-               ast_debug(1, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
-               res = ast_answer(chan);
-               if (res)
-                       ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
-               res = ast_queue_control(chan, AST_CONTROL_ANSWER);
-               if (res)
-                       ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
-               res = ast_channel_masquerade(cur, chan);
-               if (res)
-                       ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name);           /* Done */
-               if (!ast_strlen_zero(pickupsound)) {
-                       ast_stream_and_wait(cur, pickupsound, "");
+       struct ast_channel *target = ast_channel_search_locked(find_channel_by_group, chan);
+       int res = -1;
+       ast_debug(1, "pickup attempt by %s\n", chan->name);
+
+       if (target) {
+               if (!(res = ast_do_pickup(chan, target))) {
+                       if (!ast_strlen_zero(pickupsound)) {
+                               ast_stream_and_wait(target, pickupsound, "");
+                       }
+               } else {
+                       ast_log(LOG_WARNING, "pickup %s failed by %s\n", target->name, chan->name);
                }
-               ast_channel_unlock(cur);
-               return res;
-       } else  {
-               ast_debug(1, "No call pickup possible...\n");
+               ast_channel_unlock(target);
+       }
+
+       if (res < 0) {
+               ast_debug(1, "No call pickup possible... for %s\n", chan->name);
                if (!ast_strlen_zero(pickupfailsound)) {
+                       ast_answer(chan);
                        ast_stream_and_wait(chan, pickupfailsound, "");
                }
        }
-       return -1;
+
+       return res;
+}
+
+/*!
+ * \brief Pickup a call target, Common Code.
+ * \param chan channel that initiated pickup.
+ * \param target channel.
+ *
+ * Answer calling channel, flag channel as answered on queue, masq channels together.
+ */
+int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target)
+{
+       ast_debug(1, "Call pickup on '%s' by '%s'\n", target->name, chan->name);
+
+       if (ast_answer(chan)) {
+               ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
+               return -1;
+       }
+
+       if (ast_queue_control(chan, AST_CONTROL_ANSWER)) {
+               ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
+               return -1;
+       }
+
+       if (ast_channel_masquerade(target, chan)) {
+               ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, target->name);
+               return -1;
+       }
+
+       return 0;
 }
 
 static char *app_bridge = "Bridge";