[asterisk-commits] file: branch file/pimp_sip_location r381511 - in /team/file/pimp_sip_location...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Feb 14 16:24:47 CST 2013


Author: file
Date: Thu Feb 14 16:24:44 2013
New Revision: 381511

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=381511
Log:
Add the ability for a session to have multiple legs. For an outgoing call this means you can dial multiple contacts and once one answers the other legs are ended.

Next up is fetching all contacts for an AOR and adding them as legs, then fixing up todos, then clean up. But yow'sa. It works.

Modified:
    team/file/pimp_sip_location/channels/chan_gulp.c
    team/file/pimp_sip_location/include/asterisk/res_sip_session.h
    team/file/pimp_sip_location/res/res_sip.c
    team/file/pimp_sip_location/res/res_sip_session.c
    team/file/pimp_sip_location/res/res_sip_session.exports.in

Modified: team/file/pimp_sip_location/channels/chan_gulp.c
URL: http://svnview.digium.com/svn/asterisk/team/file/pimp_sip_location/channels/chan_gulp.c?view=diff&rev=381511&r1=381510&r2=381511
==============================================================================
--- team/file/pimp_sip_location/channels/chan_gulp.c (original)
+++ team/file/pimp_sip_location/channels/chan_gulp.c Thu Feb 14 16:24:44 2013
@@ -412,15 +412,10 @@
 static int gulp_call(struct ast_channel *ast, const char *dest, int timeout)
 {
 	struct ast_sip_session *session = ast_channel_tech_pvt(ast);
-	pjsip_tx_data *packet;
 
 	pj_thread_register_check();
 
-	if (pjsip_inv_invite(session->inv_session, &packet) != PJ_SUCCESS) {
-		return -1;
-	}
-
-	ast_sip_session_send_request(session, packet);
+	ast_sip_session_call(session);
 
 	return 0;
 }
@@ -477,26 +472,19 @@
 static int gulp_hangup(struct ast_channel *ast)
 {
 	struct ast_sip_session *session = ast_channel_tech_pvt(ast);
-	pj_status_t status;
-	pjsip_tx_data *packet = NULL;
 	int cause = hangup_cause2sip(ast_channel_hangupcause(session->channel));
 
 	pj_thread_register_check();
 
-	if (((status = pjsip_inv_end_session(session->inv_session, cause ? cause : 603, NULL, &packet)) == PJ_SUCCESS) && packet) {
-		if (packet->msg->type == PJSIP_RESPONSE_MSG) {
-			ast_sip_session_send_response(session, packet);
-		} else {
-			ast_sip_session_send_request(session, packet);
-		}
-	}
+	ast_sip_session_end(session, cause);
 
 	session->channel = NULL;
 	ast_channel_tech_pvt_set(ast, NULL);
 
 	ao2_cleanup(session);
 
-	return (status == PJ_SUCCESS) ? 0 : -1;
+	/* TODO: See if we can actually provide an accurate return value */
+	return 0;
 }
 
 /*! \brief Function called by core to create a new outgoing Gulp session */
@@ -507,7 +495,7 @@
 	struct ast_sip_session *session = NULL;
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(endpoint);
-		AST_APP_ARG(location);
+		AST_APP_ARG(aor);
 	);
 
 	if (ast_strlen_zero(data)) {
@@ -531,14 +519,14 @@
 		*cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
 		return NULL;
 	} else if (!(endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name))) {
-		ast_log(LOG_ERROR, "Unable to create Gulp channel - endpoint '%s' was not found", endpoint_name);
+		ast_log(LOG_ERROR, "Unable to create Gulp channel - endpoint '%s' was not found\n", endpoint_name);
 		*cause = AST_CAUSE_NO_ROUTE_DESTINATION;
 		return NULL;
 	}
 
 	pj_thread_register_check();
 
-	if (!(session = ast_sip_session_create_outgoing(endpoint, args.location, request_user))) {
+	if (!(session = ast_sip_session_create_outgoing(endpoint, args.aor, request_user))) {
 		ao2_cleanup(endpoint);
 		return NULL;
 	}

Modified: team/file/pimp_sip_location/include/asterisk/res_sip_session.h
URL: http://svnview.digium.com/svn/asterisk/team/file/pimp_sip_location/include/asterisk/res_sip_session.h?view=diff&rev=381511&r1=381510&r2=381511
==============================================================================
--- team/file/pimp_sip_location/include/asterisk/res_sip_session.h (original)
+++ team/file/pimp_sip_location/include/asterisk/res_sip_session.h Thu Feb 14 16:24:44 2013
@@ -68,6 +68,8 @@
 	struct ao2_container *datastores;
 	/* Media information */
 	struct ast_sip_session_media media;
+	/* Active invite session legs */
+	struct ao2_container *legs;
 	/* Workspace for tasks relating to this SIP session */
 	struct ast_sip_work *work;
 };
@@ -156,6 +158,21 @@
 struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint *endpoint, const char *location, const char *request_user);
 
 /*!
+ * \brief Send the initial INVITE for all outgoing legs of a SIP session
+ *
+ * \param session A pointer to the SIP session
+ */
+void ast_sip_session_call(struct ast_sip_session *session);
+
+/*!
+ * \brief End a SIP session, this may in turn end all outgoing legs
+ *
+ * \param session A pointer to the SIP session
+ * \param reason Specific reason code to end with if possible
+ */
+void ast_sip_session_end(struct ast_sip_session *session, int reason);
+
+/*!
  * \brief Register an SDP handler
  *
  * An SDP handler is responsible for parsing incoming SDP streams and ensuring that
@@ -303,8 +320,9 @@
  * call into any registered supplements' outgoing_request callback.
  *
  * \param session The session to which to send the request
+ * \param inv_session Optional specific invite session to send request on, if not specified the active session will be used
  * \param tdata The request to send
  */
-void ast_sip_session_send_request(struct ast_sip_session *session, pjsip_tx_data *tdata);
+void ast_sip_session_send_request(struct ast_sip_session *session, pjsip_inv_session *inv_session, pjsip_tx_data *tdata);
 
 #endif /* _RES_SIP_SESSION_H */

Modified: team/file/pimp_sip_location/res/res_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/file/pimp_sip_location/res/res_sip.c?view=diff&rev=381511&r1=381510&r2=381511
==============================================================================
--- team/file/pimp_sip_location/res/res_sip.c (original)
+++ team/file/pimp_sip_location/res/res_sip.c Thu Feb 14 16:24:44 2013
@@ -289,31 +289,15 @@
 	return 0;
 }
 
-/*! \brief Callback function which simply returns immediately so we get the first contact in the list */
-static int grab_first_contact(void *obj, void *arg, int flags)
-{
-	return CMP_MATCH | CMP_STOP;
-}
-
-pjsip_dialog *ast_sip_create_dialog(const struct ast_sip_endpoint *endpoint, const char *aor_name, const char *request_user)
+pjsip_dialog *ast_sip_create_dialog(const struct ast_sip_endpoint *endpoint, const char *uri, const char *request_user)
 {
 	pj_str_t local_uri = pj_str("sip:temp at localhost"), remote_uri;
-	RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
-	RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup);
-	RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
 	pjsip_dialog *dlg = NULL;
 	const char *transport_name = endpoint->transport, *outbound_proxy = endpoint->outbound_proxy;
 	pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
 	const pj_str_t HCONTACT = { "Contact", 7 };
 
-	if ((aor = ast_sip_location_retrieve_aor(aor_name)) &&
-		(contacts = ast_sip_location_retrieve_aor_contacts(aor)) &&
-		(contact = ao2_callback(contacts, OBJ_NOLOCK, grab_first_contact, NULL))) {
-		pj_cstr(&remote_uri, contact->uri);
-	} else {
-		/* Treat the provided AOR as a SIP URI since none was found */
-		pj_cstr(&remote_uri, aor_name);
-	}
+	pj_cstr(&remote_uri, uri);
 
 	if (pjsip_dlg_create_uac(pjsip_ua_instance(), &local_uri, NULL, &remote_uri, NULL, &dlg) != PJ_SUCCESS) {
 		return NULL;

Modified: team/file/pimp_sip_location/res/res_sip_session.c
URL: http://svnview.digium.com/svn/asterisk/team/file/pimp_sip_location/res/res_sip_session.c?view=diff&rev=381511&r1=381510&r2=381511
==============================================================================
--- team/file/pimp_sip_location/res/res_sip_session.c (original)
+++ team/file/pimp_sip_location/res/res_sip_session.c Thu Feb 14 16:24:44 2013
@@ -43,8 +43,8 @@
 
 /* Some forward declarations */
 static void handle_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata);
-static void handle_incoming_response(struct ast_sip_session *session, pjsip_rx_data *rdata);
-static void handle_incoming(struct ast_sip_session *session, pjsip_rx_data *rdata);
+static void handle_incoming_response(struct ast_sip_session *session, pjsip_inv_session *inv_session, pjsip_rx_data *rdata);
+static void handle_incoming(struct ast_sip_session *session, pjsip_inv_session *inv_session, pjsip_rx_data *rdata);
 static void handle_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata);
 static void handle_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata);
 static void handle_outgoing(struct ast_sip_session *session, pjsip_tx_data *tdata);
@@ -66,6 +66,14 @@
 	AST_LIST_HEAD_NOLOCK(, ast_sip_session_sdp_handler) list;
 	/* The handlers in this list handle streams of this type */
 	char stream_type[1];
+};
+
+/*!
+ * \brief Wrapper structure for invite session
+ */
+struct sip_session_leg {
+	/* The invite session for the leg */
+	pjsip_inv_session *inv_session;
 };
 
 static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, struct ast_sip_session *session, const pjmedia_sdp_session *offer);
@@ -326,10 +334,10 @@
 	return;
 }
 
-void ast_sip_session_send_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
+void ast_sip_session_send_request(struct ast_sip_session *session, pjsip_inv_session *inv_session, pjsip_tx_data *tdata)
 {
 	handle_outgoing_request(session, tdata);
-	pjsip_inv_send_msg(session->inv_session, tdata);
+	pjsip_inv_send_msg(inv_session ? inv_session : session->inv_session, tdata);
 	return;
 }
 
@@ -430,6 +438,7 @@
 		ast_free(supplement);
 	}
 	ast_sip_destroy_work(session->work);
+	ao2_cleanup(session->legs);
 	ao2_cleanup(session->datastores);
 	AST_LIST_HEAD_DESTROY(&session->supplements);
 	ao2_cleanup(session->endpoint);
@@ -450,6 +459,27 @@
 	return 0;
 }
 
+/*! \brief Internal function which adds an invite session leg to a session */
+static int sip_session_add_leg(struct ast_sip_session *session, pjsip_inv_session *inv_session)
+{
+	struct sip_session_leg *leg = ao2_alloc(sizeof(*leg), NULL);
+
+	if (!leg) {
+		return -1;
+	}
+
+	leg->inv_session = inv_session;
+	ao2_link_flags(session->legs, leg, OBJ_NOLOCK);
+	inv_session->mod_data[session_module.id] = session;
+
+	/* If no main invite session yet exists be optimistic and use this one */
+	if (!session->inv_session) {
+		session->inv_session = inv_session;
+	}
+
+	return 0;
+}
+
 struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint, pjsip_inv_session *inv_session)
 {
 	RAII_VAR(struct ast_sip_session *, session, ao2_alloc(sizeof(*session), session_destructor), ao2_cleanup);
@@ -464,7 +494,13 @@
 	}
 	session->work = ast_sip_create_work();
 	session->endpoint = endpoint;
-	session->inv_session = inv_session;
+	session->legs = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL);
+	if (!session->legs) {
+		return NULL;
+	}
+	if (inv_session && sip_session_add_leg(session, inv_session)) {
+		return NULL;
+	}
 	if (add_supplements(session)) {
 		return NULL;
 	}
@@ -473,49 +509,140 @@
 			iter->session_begin(session);
 		}
 	}
-	inv_session->mod_data[session_module.id] = session;
 	ao2_ref(session, +1);
 	return session;
 }
 
-struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint *endpoint, const char *location, const char *request_user)
-{
-	pjsip_dialog *dlg = NULL;
-	pjsip_inv_session *inv_session = NULL;
-	struct ast_sip_session *session = NULL;
-	pjmedia_sdp_session *offer = NULL;
-	pjsip_timer_setting timer;
-
-	if (!(dlg = ast_sip_create_dialog(endpoint, location, request_user))) {
-		return NULL;
+static int sip_session_create_leg(const struct ast_sip_endpoint *endpoint, struct ast_sip_session *session, const char *uri, pjsip_timer_setting *timer)
+{
+	pjsip_dialog *dlg;
+	struct pjsip_inv_session *inv_session;
+	pjmedia_sdp_session *offer;
+	RAII_VAR(struct sip_session_leg *, leg, NULL, ao2_cleanup);
+
+	if (!(dlg = ast_sip_create_dialog(endpoint, uri, NULL))) {
+		return -1;
 	}
 
 	if (pjsip_inv_create_uac(dlg, NULL, endpoint->extensions, &inv_session) != PJ_SUCCESS) {
 		pjsip_dlg_terminate(dlg);
-		return NULL;
-	}
+		return -1;
+	}
+
+	pjsip_timer_init_session(inv_session, timer);
+
+	if (pjsip_dlg_add_usage(dlg, &session_module, NULL) != PJ_SUCCESS) {
+		pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
+		return -1;
+	}
+
+	/* TODO: Fix SDP stuff so it gets passed a pool */
+	session->inv_session = inv_session;
+	if ((offer = create_local_sdp(inv_session, session, NULL))) {
+		session->inv_session = NULL;
+		pjsip_inv_set_local_sdp(inv_session, offer);
+	} else {
+		session->inv_session = NULL;
+		pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
+		return -1;
+	}
+
+	if (sip_session_add_leg(session, inv_session)) {
+		pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
+		return -1;
+	}
+
+	return 0;
+}
+
+struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint *endpoint, const char *location, const char *request_user)
+{
+	struct ast_sip_session *session;
+	pjsip_timer_setting timer;
+
+	/* Create an outgoing session with no default inv_session, multiple may get added later */
+	if (!(session = ast_sip_session_alloc(endpoint, NULL))) {
+		return NULL;
+	}
+
+	/* TODO: Fix up endpoint refcounting */
+	ao2_ref(endpoint, +1);
 
 	pjsip_timer_setting_default(&timer);
 	timer.min_se = endpoint->min_se;
 	timer.sess_expires = endpoint->sess_expires;
-	pjsip_timer_init_session(inv_session, &timer);
-
-	if (pjsip_dlg_add_usage(dlg, &session_module, NULL) != PJ_SUCCESS) {
-		pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
-		return NULL;
-	}
-
-	if (!(session = ast_sip_session_alloc(endpoint, inv_session))) {
-		pjsip_inv_terminate(inv_session, 500, PJ_FALSE);
-		return NULL;
-	}
-
-	if ((offer = create_local_sdp(inv_session, session, NULL))) {
-		/* TODO: If this fails the session is useless, we need to stop it */
-		pjsip_inv_set_local_sdp(inv_session, offer);
+
+	sip_session_create_leg(endpoint, session, location, &timer);
+	sip_session_create_leg(endpoint, session, "sip:meh at 172.16.1.13", &timer);
+	sip_session_create_leg(endpoint, session, "sip:2000 at labs.asterisk.org", &timer);
+
+	/* If the end result of this attempt is no outgoing legs then kill the session */
+	if (!ao2_container_count(session->legs)) {
+		ao2_ref(session, -1);
+		return NULL;
 	}
 
 	return session;
+}
+
+/*! \brief Internal callback function which creates an initial INVITE and sends it for a call leg */
+static int sip_session_leg_call(void *obj, void *arg, int flags)
+{
+	struct sip_session_leg *leg = obj;
+	struct ast_sip_session *session = arg;
+	pjsip_tx_data *packet;
+
+	if (pjsip_inv_invite(leg->inv_session, &packet) == PJ_SUCCESS) {
+		ast_sip_session_send_request(session, leg->inv_session, packet);
+	}
+
+	return 0;
+}
+
+void ast_sip_session_call(struct ast_sip_session *session)
+{
+	ao2_callback(session->legs, OBJ_NOLOCK | OBJ_NODATA, sip_session_leg_call, session);
+}
+
+
+/*! \brief Internal structure used for ending all session legs */
+struct sip_session_leg_end_details {
+	/*! \brief Session the leg belongs to */
+	struct ast_sip_session *session;
+	/*! \brief Reason for ending */
+	int reason;
+	/*! \brief Optional invite session to ignore */
+	pjsip_inv_session *inv_session;
+};
+
+/*! \brief Internal callback function which ends an invite session with a given reason */
+static int sip_session_leg_end(void *obj, void *arg, int flags)
+{
+	struct sip_session_leg *leg = obj;
+	struct sip_session_leg_end_details *details = arg;
+	pj_status_t status;
+	pjsip_tx_data *packet = NULL;
+
+	if ((leg->inv_session != details->inv_session) &&
+		((status = pjsip_inv_end_session(leg->inv_session, details->reason ? details->reason : 603, NULL, &packet)) == PJ_SUCCESS) && packet) {
+		if (packet->msg->type == PJSIP_RESPONSE_MSG) {
+			ast_sip_session_send_response(details->session, packet);
+		} else {
+			ast_sip_session_send_request(details->session, leg->inv_session, packet);
+		}
+	}
+
+	return 0;
+}
+
+void ast_sip_session_end(struct ast_sip_session *session, int reason)
+{
+	struct sip_session_leg_end_details details = {
+		.session = session,
+		.reason = reason,
+	};
+
+	ao2_callback(session->legs, OBJ_NOLOCK | OBJ_NODATA, sip_session_leg_end, &details);
 }
 
 enum sip_get_destination_result {
@@ -786,7 +913,20 @@
 	}
 }
 
-static void handle_incoming_response(struct ast_sip_session *session, pjsip_rx_data *rdata)
+/*! \brief Internal function which cancels all outgoing legs except the one that has answered */
+static void sip_session_leg_answered(struct ast_sip_session *session, pjsip_inv_session *answered)
+{
+	struct sip_session_leg_end_details details = {
+		.session = session,
+		.reason = 603,
+		.inv_session = answered,
+	};
+
+	ao2_callback(session->legs, OBJ_NOLOCK | OBJ_NODATA, sip_session_leg_end, &details);
+	session->inv_session = answered;
+}
+
+static void handle_incoming_response(struct ast_sip_session *session, pjsip_inv_session *inv_session, pjsip_rx_data *rdata)
 {
 	struct ast_sip_session_supplement *supplement;
 	struct pjsip_status_line status = rdata->msg_info.msg->line.status;
@@ -797,6 +937,11 @@
 	ast_debug(3, "Response is %d %.*s\n", status.code, (int) pj_strlen(&status.reason),
 			pj_strbuf(&status.reason));
 
+	/* If the remote side has answered end all other legs and treat this as the main invite session */
+	if (status.code == 200) {
+		sip_session_leg_answered(session, inv_session);
+	}
+
 	AST_LIST_TRAVERSE(&session->supplements, supplement, next) {
 		if (supplement->incoming_response && (
 				!supplement->method || !strcmp(supplement->method, method))) {
@@ -805,14 +950,14 @@
 	}
 }
 
-static void handle_incoming(struct ast_sip_session *session, pjsip_rx_data *rdata)
+static void handle_incoming(struct ast_sip_session *session, pjsip_inv_session *inv_session, pjsip_rx_data *rdata)
 {
 	ast_debug(3, "Received %s\n", rdata->msg_info.msg->type == PJSIP_REQUEST_MSG ?
 			"request" : "response");
 	if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
 		handle_incoming_request(session, rdata);
 	} else {
-		handle_incoming_response(session, rdata);
+		handle_incoming_response(session, inv_session, rdata);
 	}
 }
 
@@ -861,6 +1006,13 @@
 	} else {
 		handle_outgoing_response(session, tdata);
 	}
+}
+
+/*! \brief Internal callback function which removes an invite session call leg if present */
+static int sip_session_leg_match(void *obj, void *arg, int flags)
+{
+	struct sip_session_leg *leg = obj;
+	return (leg->inv_session == arg) ? CMP_MATCH | CMP_STOP : 0;
 }
 
 static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
@@ -873,7 +1025,7 @@
 		handle_outgoing(session, e->body.tx_msg.tdata);
 		break;
 	case PJSIP_EVENT_RX_MSG:
-		handle_incoming(session, e->body.rx_msg.rdata);
+		handle_incoming(session, inv, e->body.rx_msg.rdata);
 		break;
 	case PJSIP_EVENT_TSX_STATE:
 		ast_debug(3, "Source of transaction state change is %s\n", pjsip_event_str(e->body.tsx_state.type));
@@ -883,7 +1035,7 @@
 			handle_outgoing(session, e->body.tsx_state.src.tdata);
 			break;
 		case PJSIP_EVENT_RX_MSG:
-			handle_incoming(session, e->body.tsx_state.src.rdata);
+			handle_incoming(session, inv, e->body.tsx_state.src.rdata);
 			break;
 		case PJSIP_EVENT_TRANSPORT_ERROR:
 		case PJSIP_EVENT_TIMER:
@@ -903,17 +1055,23 @@
 	}
 
 	if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
-		struct ast_sip_session_supplement *iter;
-
-		/* Session is dead. Let's get rid of the reference to the session */
-		AST_LIST_TRAVERSE(&session->supplements, iter, next) {
-			if (iter->session_end) {
-				iter->session_end(session);
+		ao2_callback(session->legs, OBJ_NOLOCK | OBJ_NODATA | OBJ_UNLINK, sip_session_leg_match, inv);
+
+		/* If no more call legs exist this session is really dead */
+		if (!ao2_container_count(session->legs)) {
+			struct ast_sip_session_supplement *iter;
+
+			/* Session is dead. Let's get rid of the reference to the session */
+			AST_LIST_TRAVERSE(&session->supplements, iter, next) {
+				if (iter->session_end) {
+					iter->session_end(session);
+				}
 			}
+
+			ao2_cleanup(session);
 		}
 
 		inv->mod_data[session_module.id] = NULL;
-		ao2_cleanup(session);
 	}
 }
 

Modified: team/file/pimp_sip_location/res/res_sip_session.exports.in
URL: http://svnview.digium.com/svn/asterisk/team/file/pimp_sip_location/res/res_sip_session.exports.in?view=diff&rev=381511&r1=381510&r2=381511
==============================================================================
--- team/file/pimp_sip_location/res/res_sip_session.exports.in (original)
+++ team/file/pimp_sip_location/res/res_sip_session.exports.in Thu Feb 14 16:24:44 2013
@@ -12,6 +12,8 @@
 		LINKER_SYMBOL_PREFIXast_sip_session_send_response;
 		LINKER_SYMBOL_PREFIXast_sip_session_send_request;
 		LINKER_SYMBOL_PREFIXast_sip_session_create_outgoing;
+		LINKER_SYMBOL_PREFIXast_sip_session_call;
+		LINKER_SYMBOL_PREFIXast_sip_session_end;
 		LINKER_SYMBOL_PREFIXpjsip_inv_*;
 	local:
 		*;




More information about the asterisk-commits mailing list