Replace res_ais with a new module, res_corosync.

This patch removes res_ais and introduces a new module, res_corosync.
The OpenAIS project is deprecated and is now just a wrapper around
Corosync.  This module provides the same functionality using the same
core infrastructure, but without the use of the deprecated components.

Technically res_ais could have been used with an AIS implementation other
than OpenAIS, but that is the only one I know of that was ever used.

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


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@354046 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Russell Bryant 2012-02-05 10:58:37 +00:00
parent a898eb4d07
commit 055a19e128
13 changed files with 700 additions and 1015 deletions

View File

@ -160,6 +160,15 @@ Text Messaging
instead of simply the uri. This is the format that MessageSend() can use instead of simply the uri. This is the format that MessageSend() can use
in the from parameter for outgoing SIP messages. in the from parameter for outgoing SIP messages.
res_corosync
------------
* A new module, res_corosync, has been introduced. This module uses the
Corosync cluster enginer (http://www.corosync.org) to allow a local cluster
of Asterisk servers to both Message Waiting Indication (MWI) and/or
Device State (presence) information. This module is very similar to, and
is a replacement for the res_ais module that was in previous releases of
Asterisk.
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
--- Functionality changes from Asterisk 1.8 to Asterisk 10 ------------------- --- Functionality changes from Asterisk 1.8 to Asterisk 10 -------------------
------------------------------------------------------------------------------ ------------------------------------------------------------------------------

View File

@ -26,6 +26,12 @@ Parking:
- The comebacktoorigin setting must now be set per parking lot. The setting in - The comebacktoorigin setting must now be set per parking lot. The setting in
the general section will not be applied automatically to each parking lot. the general section will not be applied automatically to each parking lot.
res_ais:
- Users of res_ais in versions of Asterisk prior to Asterisk 11 must change
to use the res_corosync module, instead. OpenAIS is deprecated, but
Corosync is still actively developed and maintained. Corosync came out of
the OpenAIS project.
Dialplan Functions: Dialplan Functions:
- MAILBOX_EXISTS has been deprecated. Use VM_INFO with the 'exists' parameter - MAILBOX_EXISTS has been deprecated. Use VM_INFO with the 'exists' parameter
instead. instead.

View File

@ -1,5 +1,6 @@
ALSA=@PBX_ALSA@ ALSA=@PBX_ALSA@
BLUETOOTH=@PBX_BLUETOOTH@ BLUETOOTH=@PBX_BLUETOOTH@
COROSYNC=@PBX_COROSYNC@
CRYPTO=@PBX_CRYPTO@ CRYPTO=@PBX_CRYPTO@
BFD=@PBX_BFD@ BFD=@PBX_BFD@
BISON=@PBX_BISON@ BISON=@PBX_BISON@
@ -45,7 +46,6 @@ PORTAUDIO=@PBX_PORTAUDIO@
PRI=@PBX_PRI@ PRI=@PBX_PRI@
OPENR2=@PBX_OPENR2@ OPENR2=@PBX_OPENR2@
RESAMPLE=@PBX_RESAMPLE@ RESAMPLE=@PBX_RESAMPLE@
AIS=@PBX_AIS@
RADIUS=@PBX_RADIUS@ RADIUS=@PBX_RADIUS@
LAUNCHD=@PBX_LAUNCHD@ LAUNCHD=@PBX_LAUNCHD@
SPANDSP=@PBX_SPANDSP@ SPANDSP=@PBX_SPANDSP@

View File

@ -1,85 +0,0 @@
;
; Sample configuration file for res_ais
; * SAForum AIS (Application Interface Specification)
;
; More information on the AIS specification is available from the SAForum.
; * http://www.saforum.org/
;
; A nice open source implementation of AIS is available called openais. Visit
; the openais website for downloads and more information.
; * http://www.openais.org/
;
;
; *** NOTE ***
; This document includes some information about using the res_ais module for
; distributed events. However, it is important to note that res_ais is still
; considered experimental, as the module exposes the binary format of events
; over the network between servers. This format is still subject to change
; between 1.6.X releases.
; ************
;
; [general]
; The general section is reserved but not currently used.
;
;
; Event channels are named distributed groups that share events. Each node
; that is the member of the event channel should have an entry in their
; ais.conf file that indicates that they are a member of the event channel.
; Each node's entry for the event channel also indicates which event types
; will be published to other nodes, as well as which event types this node
; will subscribe to from other nodes in the event channel.
;
; The name of the event channel is the name in brackets that begin a section
; in the configuration file.
; [mwi]
;
; To define an event channel, this entry must be in the configuration section:
; type=event_channel
;
; Indicate that a node is capable of publishing events of a certain type by
; using the publish_event directive.
; publish_event=mwi
;
; Indicate that a node is interested in receiving events of a certain type
; from other nodes in the event channel by using the subscribe_event directive.
; subscribe_event=mwi
;
; Supported event types include: mwi, device_state
;
;
; This example is for a node that can provide MWI state information, but should
; also be listening for MWI state changes from other nodes. Examples of when
; this would be used are when this is both a voicemail server and also has
; phones directly registered to it.
;
; [mwi]
; type=event_channel
; publish_event=mwi
; subscribe_event=mwi
;
;
; This example would be used for a node that can provide MWI state to other
; nodes, but does not need to know about MWI state changes that happen on
; any other node. This would most likely be a voicemail server where no
; phones are directly registered.
;
; [mwi]
; type=event_channel
; publish_event=mwi
;
;
; This example would be used for a node that has phones directly registered
; to it, but does not have direct access to voicemail. So, this node wants
; to be informed about MWI state changes on other voicemail server nodes, but
; is not capable of publishing any state changes.
;
; [mwi]
; type=event_channel
; subscribe_event=mwi
;

View File

@ -0,0 +1,31 @@
;
; Sample configuration file for res_corosync.
;
; This module allows events to be shared amongst a local cluster of
; Asterisk servers. Specifically, the types of events that may be
; shared include:
;
; - Device State (for shared presence information)
;
; - Message Waiting Indication, or MWI (to allow Voicemail to live on
; a server that is different from where the phones are registered)
;
; For more information about Corosync, see: http://www.corosync.org/
;
[general]
;
; Publish Message Waiting Indication (MWI) events from this server to the
; cluster.
;publish_event = mwi
;
; Subscribe to MWI events from the cluster.
;subscribe_event = mwi
;
; Publish Device State (presence) events from this server to the cluster.
;publish_event = device_state
;
; Subscribe to Device State (presence) events from the cluster.
;subscribe_event = device_state
;

171
configure vendored
View File

@ -1,5 +1,5 @@
#! /bin/sh #! /bin/sh
# From configure.ac Revision: 350839 . # From configure.ac Revision: 353317 .
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.68 for asterisk trunk. # Generated by GNU Autoconf 2.68 for asterisk trunk.
# #
@ -629,9 +629,6 @@ PBX_MSG_NOSIGNAL
PBX_IXJUSER PBX_IXJUSER
PBX_H323 PBX_H323
CONFIG_GMIME CONFIG_GMIME
AIS_LIB
AIS_INCLUDE
PBX_AIS
OPENH323_BUILD OPENH323_BUILD
OPENH323_SUFFIX OPENH323_SUFFIX
OPENH323_LIBDIR OPENH323_LIBDIR
@ -884,10 +881,6 @@ PBX_OPENR2
OPENR2_DIR OPENR2_DIR
OPENR2_INCLUDE OPENR2_INCLUDE
OPENR2_LIB OPENR2_LIB
PBX_OPENAIS
OPENAIS_DIR
OPENAIS_INCLUDE
OPENAIS_LIB
PBX_OGG PBX_OGG
OGG_DIR OGG_DIR
OGG_INCLUDE OGG_INCLUDE
@ -1016,6 +1009,10 @@ PBX_CURSES
CURSES_DIR CURSES_DIR
CURSES_INCLUDE CURSES_INCLUDE
CURSES_LIB CURSES_LIB
PBX_COROSYNC
COROSYNC_DIR
COROSYNC_INCLUDE
COROSYNC_LIB
PBX_CAP PBX_CAP
CAP_DIR CAP_DIR
CAP_INCLUDE CAP_INCLUDE
@ -1180,6 +1177,7 @@ with_bfd
with_execinfo with_execinfo
with_bluetooth with_bluetooth
with_cap with_cap
with_cpg
with_curses with_curses
with_crypto with_crypto
with_dahdi with_dahdi
@ -1212,7 +1210,6 @@ with_neon29
with_netsnmp with_netsnmp
with_newt with_newt
with_ogg with_ogg
with_openais
with_openr2 with_openr2
with_osptk with_osptk
with_oss with_oss
@ -1895,6 +1892,7 @@ Optional Packages:
--with-execinfo=PATH use Stack Backtrace files in PATH --with-execinfo=PATH use Stack Backtrace files in PATH
--with-bluetooth=PATH use Bluetooth files in PATH --with-bluetooth=PATH use Bluetooth files in PATH
--with-cap=PATH use POSIX 1.e capabilities files in PATH --with-cap=PATH use POSIX 1.e capabilities files in PATH
--with-cpg=PATH use Corosync files in PATH
--with-curses=PATH use curses files in PATH --with-curses=PATH use curses files in PATH
--with-crypto=PATH use OpenSSL Cryptography files in PATH --with-crypto=PATH use OpenSSL Cryptography files in PATH
--with-dahdi=PATH use DAHDI files in PATH --with-dahdi=PATH use DAHDI files in PATH
@ -1928,7 +1926,6 @@ Optional Packages:
--with-netsnmp=PATH use Net-SNMP files in PATH --with-netsnmp=PATH use Net-SNMP files in PATH
--with-newt=PATH use newt files in PATH --with-newt=PATH use newt files in PATH
--with-ogg=PATH use OGG files in PATH --with-ogg=PATH use OGG files in PATH
--with-openais=PATH use OpenAIS files in PATH
--with-openr2=PATH use MFR2 files in PATH --with-openr2=PATH use MFR2 files in PATH
--with-osptk=PATH use OSP Toolkit files in PATH --with-osptk=PATH use OSP Toolkit files in PATH
--with-oss=PATH use Open Sound System files in PATH --with-oss=PATH use Open Sound System files in PATH
@ -8072,6 +8069,38 @@ fi
COROSYNC_DESCRIP="Corosync"
COROSYNC_OPTION="cpg"
PBX_COROSYNC=0
# Check whether --with-cpg was given.
if test "${with_cpg+set}" = set; then :
withval=$with_cpg;
case ${withval} in
n|no)
USE_COROSYNC=no
# -1 is a magic value used by menuselect to know that the package
# was disabled, other than 'not found'
PBX_COROSYNC=-1
;;
y|ye|yes)
ac_mandatory_list="${ac_mandatory_list} COROSYNC"
;;
*)
COROSYNC_DIR="${withval}"
ac_mandatory_list="${ac_mandatory_list} COROSYNC"
;;
esac
fi
CURSES_DESCRIP="curses" CURSES_DESCRIP="curses"
CURSES_OPTION="curses" CURSES_OPTION="curses"
PBX_CURSES=0 PBX_CURSES=0
@ -9441,38 +9470,6 @@ fi
OPENAIS_DESCRIP="OpenAIS"
OPENAIS_OPTION="openais"
PBX_OPENAIS=0
# Check whether --with-openais was given.
if test "${with_openais+set}" = set; then :
withval=$with_openais;
case ${withval} in
n|no)
USE_OPENAIS=no
# -1 is a magic value used by menuselect to know that the package
# was disabled, other than 'not found'
PBX_OPENAIS=-1
;;
y|ye|yes)
ac_mandatory_list="${ac_mandatory_list} OPENAIS"
;;
*)
OPENAIS_DIR="${withval}"
ac_mandatory_list="${ac_mandatory_list} OPENAIS"
;;
esac
fi
OPENR2_DESCRIP="MFR2" OPENR2_DESCRIP="MFR2"
OPENR2_OPTION="openr2" OPENR2_OPTION="openr2"
PBX_OPENR2=0 PBX_OPENR2=0
@ -25818,40 +25815,31 @@ fi
# This is a bit complex... in reality, Asterisk's AIS support is dependent on finding
# *any* implementation of AIS, not just OpenAIS. However, the configure script needs
# to know the specifics of each possible implementation, and then represent the one
# that was found as 'AIS'.
PBX_AIS=0 if test "x${PBX_COROSYNC}" != "x1" -a "${USE_COROSYNC}" != "no"; then
# OpenAIS installs its libraries into /usr/lib/openais by default, so check there
if test "x${PBX_OPENAIS}" != "x1" -a "${USE_OPENAIS}" != "no"; then
pbxlibdir="" pbxlibdir=""
# if --with-OPENAIS=DIR has been specified, use it. # if --with-COROSYNC=DIR has been specified, use it.
if test "x${OPENAIS_DIR}" != "x"; then if test "x${COROSYNC_DIR}" != "x"; then
if test -d ${OPENAIS_DIR}/lib; then if test -d ${COROSYNC_DIR}/lib; then
pbxlibdir="-L${OPENAIS_DIR}/lib" pbxlibdir="-L${COROSYNC_DIR}/lib"
else else
pbxlibdir="-L${OPENAIS_DIR}" pbxlibdir="-L${COROSYNC_DIR}"
fi fi
fi fi
pbxfuncname="saClmInitialize" pbxfuncname="cpg_join"
if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers
AST_OPENAIS_FOUND=yes AST_COROSYNC_FOUND=yes
else else
ast_ext_lib_check_save_CFLAGS="${CFLAGS}" ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
CFLAGS="${CFLAGS} " CFLAGS="${CFLAGS} "
as_ac_Lib=`$as_echo "ac_cv_lib_SaClm_${pbxfuncname}" | $as_tr_sh` as_ac_Lib=`$as_echo "ac_cv_lib_cpg_${pbxfuncname}" | $as_tr_sh`
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lSaClm" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lcpg" >&5
$as_echo_n "checking for ${pbxfuncname} in -lSaClm... " >&6; } $as_echo_n "checking for ${pbxfuncname} in -lcpg... " >&6; }
if eval \${$as_ac_Lib+:} false; then : if eval \${$as_ac_Lib+:} false; then :
$as_echo_n "(cached) " >&6 $as_echo_n "(cached) " >&6
else else
ac_check_lib_save_LIBS=$LIBS ac_check_lib_save_LIBS=$LIBS
LIBS="-lSaClm ${pbxlibdir} -L/usr/lib/openais -L/usr/lib64/openais $LIBS" LIBS="-lcpg ${pbxlibdir} -lcfg $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */ /* end confdefs.h. */
@ -25883,47 +25871,47 @@ eval ac_res=\$$as_ac_Lib
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; } $as_echo "$ac_res" >&6; }
if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
AST_OPENAIS_FOUND=yes AST_COROSYNC_FOUND=yes
else else
AST_OPENAIS_FOUND=no AST_COROSYNC_FOUND=no
fi fi
CFLAGS="${ast_ext_lib_check_save_CFLAGS}" CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
fi fi
# now check for the header. # now check for the header.
if test "${AST_OPENAIS_FOUND}" = "yes"; then if test "${AST_COROSYNC_FOUND}" = "yes"; then
OPENAIS_LIB="${pbxlibdir} -lSaClm -L/usr/lib/openais -L/usr/lib64/openais" COROSYNC_LIB="${pbxlibdir} -lcpg -lcfg"
# if --with-OPENAIS=DIR has been specified, use it. # if --with-COROSYNC=DIR has been specified, use it.
if test "x${OPENAIS_DIR}" != "x"; then if test "x${COROSYNC_DIR}" != "x"; then
OPENAIS_INCLUDE="-I${OPENAIS_DIR}/include" COROSYNC_INCLUDE="-I${COROSYNC_DIR}/include"
fi fi
OPENAIS_INCLUDE="${OPENAIS_INCLUDE} " COROSYNC_INCLUDE="${COROSYNC_INCLUDE} "
if test "xopenais/saClm.h" = "x" ; then # no header, assume found if test "xcorosync/cpg.h" = "x" ; then # no header, assume found
OPENAIS_HEADER_FOUND="1" COROSYNC_HEADER_FOUND="1"
else # check for the header else # check for the header
ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
CPPFLAGS="${CPPFLAGS} ${OPENAIS_INCLUDE}" CPPFLAGS="${CPPFLAGS} ${COROSYNC_INCLUDE}"
ac_fn_c_check_header_mongrel "$LINENO" "openais/saClm.h" "ac_cv_header_openais_saClm_h" "$ac_includes_default" ac_fn_c_check_header_mongrel "$LINENO" "corosync/cpg.h" "ac_cv_header_corosync_cpg_h" "$ac_includes_default"
if test "x$ac_cv_header_openais_saClm_h" = xyes; then : if test "x$ac_cv_header_corosync_cpg_h" = xyes; then :
OPENAIS_HEADER_FOUND=1 COROSYNC_HEADER_FOUND=1
else else
OPENAIS_HEADER_FOUND=0 COROSYNC_HEADER_FOUND=0
fi fi
CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}" CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
fi fi
if test "x${OPENAIS_HEADER_FOUND}" = "x0" ; then if test "x${COROSYNC_HEADER_FOUND}" = "x0" ; then
OPENAIS_LIB="" COROSYNC_LIB=""
OPENAIS_INCLUDE="" COROSYNC_INCLUDE=""
else else
if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library
OPENAIS_LIB="" COROSYNC_LIB=""
fi fi
PBX_OPENAIS=1 PBX_COROSYNC=1
cat >>confdefs.h <<_ACEOF cat >>confdefs.h <<_ACEOF
#define HAVE_OPENAIS 1 #define HAVE_COROSYNC 1
_ACEOF _ACEOF
fi fi
@ -25932,21 +25920,6 @@ fi
if test "${PBX_OPENAIS}" = 1; then
PBX_AIS=1
if test -n "${OPENAIS_DIR}"; then
AIS_INCLUDE="${OPENAIS_INCLUDE}/openais"
AIS_LIB="-lSaEvt ${OPENAIS_LIB}"
else
AIS_INCLUDE="-I/usr/include/openais"
AIS_LIB="-lSaClm -lSaEvt -L/usr/lib/openais -L/usr/lib64/openais"
fi
fi
if test "x${PBX_SPEEX}" != "x1" -a "${USE_SPEEX}" != "no"; then if test "x${PBX_SPEEX}" != "x1" -a "${USE_SPEEX}" != "no"; then
pbxlibdir="" pbxlibdir=""

View File

@ -377,6 +377,7 @@ AST_EXT_LIB_SETUP([BFD], [Debug symbol decoding], [bfd])
AST_EXT_LIB_SETUP([BKTR], [Stack Backtrace], [execinfo]) AST_EXT_LIB_SETUP([BKTR], [Stack Backtrace], [execinfo])
AST_EXT_LIB_SETUP([BLUETOOTH], [Bluetooth], [bluetooth]) AST_EXT_LIB_SETUP([BLUETOOTH], [Bluetooth], [bluetooth])
AST_EXT_LIB_SETUP([CAP], [POSIX 1.e capabilities], [cap]) AST_EXT_LIB_SETUP([CAP], [POSIX 1.e capabilities], [cap])
AST_EXT_LIB_SETUP([COROSYNC], [Corosync], [cpg])
AST_EXT_LIB_SETUP([CURSES], [curses], [curses]) AST_EXT_LIB_SETUP([CURSES], [curses], [curses])
AST_EXT_LIB_SETUP([CRYPTO], [OpenSSL Cryptography], [crypto]) AST_EXT_LIB_SETUP([CRYPTO], [OpenSSL Cryptography], [crypto])
AST_EXT_LIB_SETUP([DAHDI], [DAHDI], [dahdi]) AST_EXT_LIB_SETUP([DAHDI], [DAHDI], [dahdi])
@ -409,7 +410,6 @@ AST_EXT_LIB_SETUP([NEON29], [neon29], [neon29])
AST_EXT_LIB_SETUP([NETSNMP], [Net-SNMP], [netsnmp]) AST_EXT_LIB_SETUP([NETSNMP], [Net-SNMP], [netsnmp])
AST_EXT_LIB_SETUP([NEWT], [newt], [newt]) AST_EXT_LIB_SETUP([NEWT], [newt], [newt])
AST_EXT_LIB_SETUP([OGG], [OGG], [ogg]) AST_EXT_LIB_SETUP([OGG], [OGG], [ogg])
AST_EXT_LIB_SETUP([OPENAIS], [OpenAIS], [openais])
AST_EXT_LIB_SETUP([OPENR2], [MFR2], [openr2]) AST_EXT_LIB_SETUP([OPENR2], [MFR2], [openr2])
AST_EXT_LIB_SETUP([OSPTK], [OSP Toolkit], [osptk]) AST_EXT_LIB_SETUP([OSPTK], [OSP Toolkit], [osptk])
AST_EXT_LIB_SETUP([OSS], [Open Sound System], [oss]) AST_EXT_LIB_SETUP([OSS], [Open Sound System], [oss])
@ -1985,31 +1985,7 @@ AST_EXT_LIB_CHECK([LUA], [lua], [luaL_register], [lua.h], [-lm])
AST_EXT_LIB_CHECK([RADIUS], [radiusclient-ng], [rc_read_config], [radiusclient-ng.h]) AST_EXT_LIB_CHECK([RADIUS], [radiusclient-ng], [rc_read_config], [radiusclient-ng.h])
# This is a bit complex... in reality, Asterisk's AIS support is dependent on finding AST_EXT_LIB_CHECK([COROSYNC], [cpg], [cpg_join], [corosync/cpg.h], [-lcfg])
# *any* implementation of AIS, not just OpenAIS. However, the configure script needs
# to know the specifics of each possible implementation, and then represent the one
# that was found as 'AIS'.
PBX_AIS=0
# OpenAIS installs its libraries into /usr/lib/openais by default, so check there
AST_EXT_LIB_CHECK([OPENAIS], [SaClm], [saClmInitialize], [openais/saClm.h], [-L/usr/lib/openais -L/usr/lib64/openais])
if test "${PBX_OPENAIS}" = 1; then
PBX_AIS=1
if test -n "${OPENAIS_DIR}"; then
AIS_INCLUDE="${OPENAIS_INCLUDE}/openais"
AIS_LIB="-lSaEvt ${OPENAIS_LIB}"
else
AIS_INCLUDE="-I/usr/include/openais"
AIS_LIB="-lSaClm -lSaEvt -L/usr/lib/openais -L/usr/lib64/openais"
fi
fi
AC_SUBST(PBX_AIS)
AC_SUBST(AIS_INCLUDE)
AC_SUBST(AIS_LIB)
AST_EXT_LIB_CHECK([SPEEX], [speex], [speex_encode], [speex/speex.h], [-lm]) AST_EXT_LIB_CHECK([SPEEX], [speex], [speex_encode], [speex/speex.h], [-lm])

View File

@ -137,6 +137,9 @@
/* Define to 1 if you have the `closefrom' function. */ /* Define to 1 if you have the `closefrom' function. */
#undef HAVE_CLOSEFROM #undef HAVE_CLOSEFROM
/* Define to 1 if you have the Corosync library. */
#undef HAVE_COROSYNC
/* Define to 1 if you have the `cos' function. */ /* Define to 1 if you have the `cos' function. */
#undef HAVE_COS #undef HAVE_COS
@ -506,9 +509,6 @@
/* Define to 1 if you have the OGG library. */ /* Define to 1 if you have the OGG library. */
#undef HAVE_OGG #undef HAVE_OGG
/* Define to 1 if you have the OpenAIS library. */
#undef HAVE_OPENAIS
/* Define if your system has the OpenH323 libraries. */ /* Define if your system has the OpenH323 libraries. */
#undef HAVE_OPENH323 #undef HAVE_OPENH323

View File

@ -222,8 +222,8 @@ OPENR2_LIB=@OPENR2_LIB@
PWLIB_INCLUDE=@PWLIB_INCLUDE@ PWLIB_INCLUDE=@PWLIB_INCLUDE@
PWLIB_LIB=@PWLIB_LIB@ PWLIB_LIB=@PWLIB_LIB@
AIS_INCLUDE=@AIS_INCLUDE@ COROSYNC_INCLUDE=@COROSYNC_INCLUDE@
AIS_LIB=@AIS_LIB@ COROSYNC_LIB=@COROSYNC_LIB@
RADIUS_INCLUDE=@RADIUS_INCLUDE@ RADIUS_INCLUDE=@RADIUS_INCLUDE@
RADIUS_LIB=@RADIUS_LIB@ RADIUS_LIB=@RADIUS_LIB@

View File

@ -1,48 +0,0 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2007, Digium, Inc.
*
* Russell Bryant <russell@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \author Russell Bryant <russell@digium.com>
*
* \brief Usage of the SAForum AIS (Application Interface Specification)
*
* \arg http://www.openais.org/
*/
#ifndef RES_AIS_AIS_H
#define RES_AIS_AIS_H
#include <saAis.h>
#include <saClm.h>
#include <saEvt.h>
extern SaVersionT ais_version;
extern SaClmHandleT clm_handle;
extern SaEvtHandleT evt_handle;
int ast_ais_clm_load_module(void);
int ast_ais_clm_unload_module(void);
int ast_ais_evt_load_module(void);
int ast_ais_evt_unload_module(void);
const char *ais_err2str(SaAisErrorT error);
#endif /* RES_AIS_AIS_H */

View File

@ -1,168 +0,0 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2007, Digium, Inc.
*
* Russell Bryant <russell@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \author Russell Bryant <russell@digium.com>
*
* \brief Usage of the SAForum AIS (Application Interface Specification)
*
* \arg http://www.openais.org/
*
* This file contains the code specific to the use of the CLM
* (Cluster Membership) Service.
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "ais.h"
#include "asterisk/module.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
#include "asterisk/logger.h"
SaClmHandleT clm_handle;
static SaAisErrorT clm_init_res;
static void clm_node_get_cb(SaInvocationT invocation,
const SaClmClusterNodeT *cluster_node, SaAisErrorT error);
static void clm_track_cb(const SaClmClusterNotificationBufferT *notif_buffer,
SaUint32T num_members, SaAisErrorT error);
static const SaClmCallbacksT clm_callbacks = {
.saClmClusterNodeGetCallback = clm_node_get_cb,
.saClmClusterTrackCallback = clm_track_cb,
};
static void clm_node_get_cb(SaInvocationT invocation,
const SaClmClusterNodeT *cluster_node, SaAisErrorT error)
{
}
static void clm_track_cb(const SaClmClusterNotificationBufferT *notif_buffer,
SaUint32T num_members, SaAisErrorT error)
{
}
static char *ais_clm_show_members(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
int i;
SaClmClusterNotificationBufferT buf;
SaClmClusterNotificationT notif[64];
SaAisErrorT ais_res;
switch (cmd) {
case CLI_INIT:
e->command = "ais clm show members";
e->usage =
"Usage: ais clm show members\n"
" List members of the cluster using the CLM (Cluster Membership) service.\n";
return NULL;
case CLI_GENERATE:
return NULL; /* no completion */
}
if (a->argc != e->args)
return CLI_SHOWUSAGE;
buf.notification = notif;
buf.numberOfItems = ARRAY_LEN(notif);
ais_res = saClmClusterTrack(clm_handle, SA_TRACK_CURRENT, &buf);
if (ais_res != SA_AIS_OK) {
ast_cli(a->fd, "Error retrieving current cluster members.\n");
return CLI_FAILURE;
}
ast_cli(a->fd, "\n"
"=============================================================\n"
"=== Cluster Members =========================================\n"
"=============================================================\n"
"===\n");
for (i = 0; i < buf.numberOfItems; i++) {
SaClmClusterNodeT *node = &buf.notification[i].clusterNode;
ast_cli(a->fd, "=== ---------------------------------------------------------\n"
"=== Node Name: %s\n"
"=== ==> ID: 0x%x\n"
"=== ==> Address: %s\n"
"=== ==> Member: %s\n",
(char *) node->nodeName.value, (int) node->nodeId,
(char *) node->nodeAddress.value,
node->member ? "Yes" : "No");
ast_cli(a->fd, "=== ---------------------------------------------------------\n"
"===\n");
}
ast_cli(a->fd, "=============================================================\n"
"\n");
return CLI_SUCCESS;
}
static struct ast_cli_entry ais_cli[] = {
AST_CLI_DEFINE(ais_clm_show_members, "List current members of the cluster"),
};
int ast_ais_clm_load_module(void)
{
clm_init_res = saClmInitialize(&clm_handle, &clm_callbacks, &ais_version);
if (clm_init_res != SA_AIS_OK) {
ast_log(LOG_ERROR, "Could not initialize cluster membership service: %s\n",
ais_err2str(clm_init_res));
return -1;
}
ast_cli_register_multiple(ais_cli, ARRAY_LEN(ais_cli));
return 0;
}
int ast_ais_clm_unload_module(void)
{
SaAisErrorT ais_res;
if (clm_init_res != SA_AIS_OK) {
return 0;
}
ast_cli_unregister_multiple(ais_cli, ARRAY_LEN(ais_cli));
ais_res = saClmFinalize(clm_handle);
if (ais_res != SA_AIS_OK) {
ast_log(LOG_ERROR, "Problem stopping cluster membership service: %s\n",
ais_err2str(ais_res));
return -1;
}
return 0;
}

View File

@ -1,583 +0,0 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2007, Digium, Inc.
*
* Russell Bryant <russell@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \author Russell Bryant <russell@digium.com>
*
* \brief Usage of the SAForum AIS (Application Interface Specification)
*
* \arg http://www.openais.org/
*
* This file contains the code specific to the use of the EVT
* (Event) Service.
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "ais.h"
#include "asterisk/module.h"
#include "asterisk/utils.h"
#include "asterisk/cli.h"
#include "asterisk/logger.h"
#include "asterisk/event.h"
#include "asterisk/config.h"
#include "asterisk/linkedlists.h"
#include "asterisk/devicestate.h"
#ifndef AST_MODULE
/* XXX HACK */
#define AST_MODULE "res_ais"
#endif
SaEvtHandleT evt_handle;
static SaAisErrorT evt_init_res;
void evt_channel_open_cb(SaInvocationT invocation, SaEvtChannelHandleT channel_handle,
SaAisErrorT error);
void evt_event_deliver_cb(SaEvtSubscriptionIdT subscription_id,
const SaEvtEventHandleT event_handle, const SaSizeT event_datalen);
static const SaEvtCallbacksT evt_callbacks = {
.saEvtChannelOpenCallback = evt_channel_open_cb,
.saEvtEventDeliverCallback = evt_event_deliver_cb,
};
static const struct {
const char *str;
enum ast_event_type type;
} supported_event_types[] = {
{ "mwi", AST_EVENT_MWI },
{ "device_state", AST_EVENT_DEVICE_STATE_CHANGE },
};
/*! Used to provide unique id's to egress subscriptions */
static int unique_id;
struct subscribe_event {
AST_LIST_ENTRY(subscribe_event) entry;
/*! This is a unique identifier to identify this subscription in the event
* channel through the different API calls, subscribe, unsubscribe, and
* the event deliver callback. */
SaEvtSubscriptionIdT id;
enum ast_event_type type;
};
struct publish_event {
AST_LIST_ENTRY(publish_event) entry;
/*! We subscribe to events internally so that we can publish them
* on this event channel. */
struct ast_event_sub *sub;
enum ast_event_type type;
};
struct event_channel {
AST_RWLIST_ENTRY(event_channel) entry;
AST_LIST_HEAD_NOLOCK(, subscribe_event) subscribe_events;
AST_LIST_HEAD_NOLOCK(, publish_event) publish_events;
SaEvtChannelHandleT handle;
char name[1];
};
static AST_RWLIST_HEAD_STATIC(event_channels, event_channel);
void evt_channel_open_cb(SaInvocationT invocation, SaEvtChannelHandleT channel_handle,
SaAisErrorT error)
{
}
static void queue_event(struct ast_event *ast_event)
{
ast_event_queue_and_cache(ast_event);
}
void evt_event_deliver_cb(SaEvtSubscriptionIdT sub_id,
const SaEvtEventHandleT event_handle, const SaSizeT event_datalen)
{
/* It is important to note that this works because we *know* that this
* function will only be called by a single thread, the dispatch_thread.
* If this module gets changed such that this is no longer the case, this
* should get changed to a thread-local buffer, instead. */
static unsigned char buf[4096];
struct ast_event *event_dup, *event = (void *) buf;
SaAisErrorT ais_res;
SaSizeT len = sizeof(buf);
if (event_datalen > len) {
ast_log(LOG_ERROR, "Event received with size %u, which is too big\n"
"for the allocated size %u. Change the code to increase the size.\n",
(unsigned int) event_datalen, (unsigned int) len);
return;
}
if (event_datalen < ast_event_minimum_length()) {
ast_debug(1, "Ignoring event that's too small. %u < %u\n",
(unsigned int) event_datalen,
(unsigned int) ast_event_minimum_length());
return;
}
ais_res = saEvtEventDataGet(event_handle, event, &len);
if (ais_res != SA_AIS_OK) {
ast_log(LOG_ERROR, "Error retrieving event payload: %s\n",
ais_err2str(ais_res));
return;
}
if (!ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(event, AST_EVENT_IE_EID))) {
/* Don't feed events back in that originated locally. */
return;
}
if (!(event_dup = ast_malloc(len)))
return;
memcpy(event_dup, event, len);
queue_event(event_dup);
}
static const char *type_to_filter_str(enum ast_event_type type)
{
const char *filter_str = NULL;
int i;
for (i = 0; i < ARRAY_LEN(supported_event_types); i++) {
if (supported_event_types[i].type == type) {
filter_str = supported_event_types[i].str;
break;
}
}
return filter_str;
}
static void ast_event_cb(const struct ast_event *ast_event, void *data)
{
SaEvtEventHandleT event_handle;
SaAisErrorT ais_res;
struct event_channel *event_channel = data;
SaClmClusterNodeT local_node;
SaEvtEventPatternArrayT pattern_array;
SaEvtEventPatternT pattern;
SaSizeT len;
const char *filter_str;
SaEvtEventIdT event_id;
ast_debug(1, "Got an event to forward\n");
if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID))) {
/* If the event didn't originate from this server, don't send it back out. */
ast_debug(1, "Returning here\n");
return;
}
ais_res = saEvtEventAllocate(event_channel->handle, &event_handle);
if (ais_res != SA_AIS_OK) {
ast_log(LOG_ERROR, "Error allocating event: %s\n", ais_err2str(ais_res));
ast_debug(1, "Returning here\n");
return;
}
ais_res = saClmClusterNodeGet(clm_handle, SA_CLM_LOCAL_NODE_ID,
SA_TIME_ONE_SECOND, &local_node);
if (ais_res != SA_AIS_OK) {
ast_log(LOG_ERROR, "Error getting local node name: %s\n", ais_err2str(ais_res));
goto return_event_free;
}
filter_str = type_to_filter_str(ast_event_get_type(ast_event));
len = strlen(filter_str) + 1;
pattern.pattern = (SaUint8T *) filter_str;
pattern.patternSize = len;
pattern.allocatedSize = len;
pattern_array.allocatedNumber = 1;
pattern_array.patternsNumber = 1;
pattern_array.patterns = &pattern;
/*!
* /todo Make retention time configurable
* /todo Make event priorities configurable
*/
ais_res = saEvtEventAttributesSet(event_handle, &pattern_array,
SA_EVT_LOWEST_PRIORITY, SA_TIME_ONE_MINUTE, &local_node.nodeName);
if (ais_res != SA_AIS_OK) {
ast_log(LOG_ERROR, "Error setting event attributes: %s\n", ais_err2str(ais_res));
goto return_event_free;
}
ais_res = saEvtEventPublish(event_handle,
ast_event, ast_event_get_size(ast_event), &event_id);
if (ais_res != SA_AIS_OK) {
ast_log(LOG_ERROR, "Error publishing event: %s\n", ais_err2str(ais_res));
goto return_event_free;
}
return_event_free:
ais_res = saEvtEventFree(event_handle);
if (ais_res != SA_AIS_OK) {
ast_log(LOG_ERROR, "Error freeing allocated event: %s\n", ais_err2str(ais_res));
}
ast_debug(1, "Returning here (event_free)\n");
}
static char *ais_evt_show_event_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct event_channel *event_channel;
switch (cmd) {
case CLI_INIT:
e->command = "ais evt show event channels";
e->usage =
"Usage: ais evt show event channels\n"
" List configured event channels for the (EVT) Eventing service.\n";
return NULL;
case CLI_GENERATE:
return NULL; /* no completion */
}
if (a->argc != e->args)
return CLI_SHOWUSAGE;
ast_cli(a->fd, "\n"
"=============================================================\n"
"=== Event Channels ==========================================\n"
"=============================================================\n"
"===\n");
AST_RWLIST_RDLOCK(&event_channels);
AST_RWLIST_TRAVERSE(&event_channels, event_channel, entry) {
struct publish_event *publish_event;
struct subscribe_event *subscribe_event;
ast_cli(a->fd, "=== ---------------------------------------------------------\n"
"=== Event Channel Name: %s\n", event_channel->name);
AST_LIST_TRAVERSE(&event_channel->publish_events, publish_event, entry) {
ast_cli(a->fd, "=== ==> Publishing Event Type: %s\n",
type_to_filter_str(publish_event->type));
}
AST_LIST_TRAVERSE(&event_channel->subscribe_events, subscribe_event, entry) {
ast_cli(a->fd, "=== ==> Subscribing to Event Type: %s\n",
type_to_filter_str(subscribe_event->type));
}
ast_cli(a->fd, "=== ---------------------------------------------------------\n"
"===\n");
}
AST_RWLIST_UNLOCK(&event_channels);
ast_cli(a->fd, "=============================================================\n"
"\n");
return CLI_SUCCESS;
}
static struct ast_cli_entry ais_cli[] = {
AST_CLI_DEFINE(ais_evt_show_event_channels, "Show configured event channels"),
};
static void add_publish_event(struct event_channel *event_channel, const char *event_type)
{
int i;
enum ast_event_type type = -1;
struct publish_event *publish_event;
for (i = 0; i < ARRAY_LEN(supported_event_types); i++) {
if (!strcasecmp(event_type, supported_event_types[i].str)) {
type = supported_event_types[i].type;
break;
}
}
if (type == -1) {
ast_log(LOG_WARNING, "publish_event option given with invalid value '%s'\n", event_type);
return;
}
if (type == AST_EVENT_DEVICE_STATE_CHANGE && ast_enable_distributed_devstate()) {
return;
}
if (!(publish_event = ast_calloc(1, sizeof(*publish_event)))) {
return;
}
publish_event->type = type;
ast_debug(1, "Subscribing to event type %d\n", type);
publish_event->sub = ast_event_subscribe(type, ast_event_cb, "AIS", event_channel,
AST_EVENT_IE_END);
ast_event_dump_cache(publish_event->sub);
AST_LIST_INSERT_TAIL(&event_channel->publish_events, publish_event, entry);
}
static SaAisErrorT set_egress_subscription(struct event_channel *event_channel,
struct subscribe_event *subscribe_event)
{
SaAisErrorT ais_res;
SaEvtEventFilterArrayT filter_array;
SaEvtEventFilterT filter;
const char *filter_str = NULL;
SaSizeT len;
/* We know it's going to be valid. It was checked earlier. */
filter_str = type_to_filter_str(subscribe_event->type);
filter.filterType = SA_EVT_EXACT_FILTER;
len = strlen(filter_str) + 1;
filter.filter.allocatedSize = len;
filter.filter.patternSize = len;
filter.filter.pattern = (SaUint8T *) filter_str;
filter_array.filtersNumber = 1;
filter_array.filters = &filter;
ais_res = saEvtEventSubscribe(event_channel->handle, &filter_array,
subscribe_event->id);
return ais_res;
}
static void add_subscribe_event(struct event_channel *event_channel, const char *event_type)
{
int i;
enum ast_event_type type = -1;
struct subscribe_event *subscribe_event;
SaAisErrorT ais_res;
for (i = 0; i < ARRAY_LEN(supported_event_types); i++) {
if (!strcasecmp(event_type, supported_event_types[i].str)) {
type = supported_event_types[i].type;
break;
}
}
if (type == -1) {
ast_log(LOG_WARNING, "subscribe_event option given with invalid value '%s'\n", event_type);
return;
}
if (type == AST_EVENT_DEVICE_STATE_CHANGE && ast_enable_distributed_devstate()) {
return;
}
if (!(subscribe_event = ast_calloc(1, sizeof(*subscribe_event)))) {
return;
}
subscribe_event->type = type;
subscribe_event->id = ast_atomic_fetchadd_int(&unique_id, +1);
ais_res = set_egress_subscription(event_channel, subscribe_event);
if (ais_res != SA_AIS_OK) {
ast_log(LOG_ERROR, "Error setting up egress subscription: %s\n",
ais_err2str(ais_res));
free(subscribe_event);
return;
}
AST_LIST_INSERT_TAIL(&event_channel->subscribe_events, subscribe_event, entry);
}
static void build_event_channel(struct ast_config *cfg, const char *cat)
{
struct ast_variable *var;
struct event_channel *event_channel;
SaAisErrorT ais_res;
SaNameT sa_name = { 0, };
AST_RWLIST_WRLOCK(&event_channels);
AST_RWLIST_TRAVERSE(&event_channels, event_channel, entry) {
if (!strcasecmp(event_channel->name, cat))
break;
}
AST_RWLIST_UNLOCK(&event_channels);
if (event_channel) {
ast_log(LOG_WARNING, "Event channel '%s' was specified twice in "
"configuration. Second instance ignored.\n", cat);
return;
}
if (!(event_channel = ast_calloc(1, sizeof(*event_channel) + strlen(cat))))
return;
strcpy(event_channel->name, cat);
ast_copy_string((char *) sa_name.value, cat, sizeof(sa_name.value));
sa_name.length = strlen((char *) sa_name.value);
ais_res = saEvtChannelOpen(evt_handle, &sa_name,
SA_EVT_CHANNEL_PUBLISHER | SA_EVT_CHANNEL_SUBSCRIBER | SA_EVT_CHANNEL_CREATE,
SA_TIME_MAX, &event_channel->handle);
if (ais_res != SA_AIS_OK) {
ast_log(LOG_ERROR, "Error opening event channel: %s\n", ais_err2str(ais_res));
free(event_channel);
return;
}
for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
if (!strcasecmp(var->name, "type")) {
continue;
} else if (!strcasecmp(var->name, "publish_event")) {
add_publish_event(event_channel, var->value);
} else if (!strcasecmp(var->name, "subscribe_event")) {
add_subscribe_event(event_channel, var->value);
} else {
ast_log(LOG_WARNING, "Event channel '%s' contains invalid option '%s'\n",
event_channel->name, var->name);
}
}
AST_RWLIST_WRLOCK(&event_channels);
AST_RWLIST_INSERT_TAIL(&event_channels, event_channel, entry);
AST_RWLIST_UNLOCK(&event_channels);
}
static void load_config(void)
{
static const char filename[] = "ais.conf";
struct ast_config *cfg;
const char *cat = NULL;
struct ast_flags config_flags = { 0 };
if (!(cfg = ast_config_load(filename, config_flags)) || cfg == CONFIG_STATUS_FILEINVALID)
return;
while ((cat = ast_category_browse(cfg, cat))) {
const char *type;
if (!strcasecmp(cat, "general"))
continue;
if (!(type = ast_variable_retrieve(cfg, cat, "type"))) {
ast_log(LOG_WARNING, "Invalid entry in %s defined with no type!\n",
filename);
continue;
}
if (!strcasecmp(type, "event_channel")) {
build_event_channel(cfg, cat);
} else {
ast_log(LOG_WARNING, "Entry in %s defined with invalid type '%s'\n",
filename, type);
}
}
ast_config_destroy(cfg);
}
static void publish_event_destroy(struct publish_event *publish_event)
{
ast_event_unsubscribe(publish_event->sub);
free(publish_event);
}
static void subscribe_event_destroy(const struct event_channel *event_channel,
struct subscribe_event *subscribe_event)
{
SaAisErrorT ais_res;
/* saEvtChannelClose() will actually do this automatically, but it just
* feels cleaner to go ahead and do it manually ... */
ais_res = saEvtEventUnsubscribe(event_channel->handle, subscribe_event->id);
if (ais_res != SA_AIS_OK) {
ast_log(LOG_ERROR, "Error unsubscribing: %s\n", ais_err2str(ais_res));
}
free(subscribe_event);
}
static void event_channel_destroy(struct event_channel *event_channel)
{
struct publish_event *publish_event;
struct subscribe_event *subscribe_event;
SaAisErrorT ais_res;
while ((publish_event = AST_LIST_REMOVE_HEAD(&event_channel->publish_events, entry)))
publish_event_destroy(publish_event);
while ((subscribe_event = AST_LIST_REMOVE_HEAD(&event_channel->subscribe_events, entry)))
subscribe_event_destroy(event_channel, subscribe_event);
ais_res = saEvtChannelClose(event_channel->handle);
if (ais_res != SA_AIS_OK) {
ast_log(LOG_ERROR, "Error closing event channel '%s': %s\n",
event_channel->name, ais_err2str(ais_res));
}
free(event_channel);
}
static void destroy_event_channels(void)
{
struct event_channel *event_channel;
AST_RWLIST_WRLOCK(&event_channels);
while ((event_channel = AST_RWLIST_REMOVE_HEAD(&event_channels, entry))) {
event_channel_destroy(event_channel);
}
AST_RWLIST_UNLOCK(&event_channels);
}
int ast_ais_evt_load_module(void)
{
evt_init_res = saEvtInitialize(&evt_handle, &evt_callbacks, &ais_version);
if (evt_init_res != SA_AIS_OK) {
ast_log(LOG_ERROR, "Could not initialize eventing service: %s\n",
ais_err2str(evt_init_res));
return -1;
}
load_config();
ast_cli_register_multiple(ais_cli, ARRAY_LEN(ais_cli));
return 0;
}
int ast_ais_evt_unload_module(void)
{
SaAisErrorT ais_res;
if (evt_init_res != SA_AIS_OK) {
return 0;
}
destroy_event_channels();
ais_res = saEvtFinalize(evt_handle);
if (ais_res != SA_AIS_OK) {
ast_log(LOG_ERROR, "Problem stopping eventing service: %s\n",
ais_err2str(ais_res));
return -1;
}
return 0;
}

574
res/res_corosync.c Normal file
View File

@ -0,0 +1,574 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2007, Digium, Inc.
* Copyright (C) 2012, Russell Bryant
*
* Russell Bryant <russell@russellbryant.net>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \author Russell Bryant <russell@russellbryant.net>
*
* This module is based on and replaces the previous res_ais module.
*/
/*** MODULEINFO
<depend>corosync</depend>
<support_level>extended</support_level>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
#include <corosync/cpg.h>
#include <corosync/cfg.h>
#include "asterisk/module.h"
#include "asterisk/logger.h"
#include "asterisk/poll-compat.h"
#include "asterisk/config.h"
#include "asterisk/event.h"
#include "asterisk/cli.h"
#include "asterisk/devicestate.h"
AST_RWLOCK_DEFINE_STATIC(event_types_lock);
static struct {
const char *name;
struct ast_event_sub *sub;
unsigned char publish;
unsigned char subscribe;
} event_types[] = {
[AST_EVENT_MWI] = { .name = "mwi", },
[AST_EVENT_DEVICE_STATE_CHANGE] = { .name = "device_state", },
};
static struct {
pthread_t id;
int alert_pipe[2];
unsigned int stop:1;
} dispatch_thread = {
.id = AST_PTHREADT_NULL,
.alert_pipe = { -1, -1 },
};
static cpg_handle_t cpg_handle;
static corosync_cfg_handle_t cfg_handle;
static void cfg_state_track_cb(
corosync_cfg_state_notification_buffer_t *notification_buffer,
cs_error_t error);
static void cfg_shutdown_cb(corosync_cfg_handle_t cfg_handle,
corosync_cfg_shutdown_flags_t flags);
static corosync_cfg_callbacks_t cfg_callbacks = {
.corosync_cfg_state_track_callback = cfg_state_track_cb,
.corosync_cfg_shutdown_callback = cfg_shutdown_cb,
};
static void cpg_deliver_cb(cpg_handle_t handle, const struct cpg_name *group_name,
uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len);
static void cpg_confchg_cb(cpg_handle_t handle, const struct cpg_name *group_name,
const struct cpg_address *member_list, size_t member_list_entries,
const struct cpg_address *left_list, size_t left_list_entries,
const struct cpg_address *joined_list, size_t joined_list_entries);
static cpg_callbacks_t cpg_callbacks = {
.cpg_deliver_fn = cpg_deliver_cb,
.cpg_confchg_fn = cpg_confchg_cb,
};
static void ast_event_cb(const struct ast_event *event, void *data);
static void cfg_state_track_cb(
corosync_cfg_state_notification_buffer_t *notification_buffer,
cs_error_t error)
{
}
static void cfg_shutdown_cb(corosync_cfg_handle_t cfg_handle,
corosync_cfg_shutdown_flags_t flags)
{
}
static void cpg_deliver_cb(cpg_handle_t handle, const struct cpg_name *group_name,
uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
{
struct ast_event *event;
if (msg_len < ast_event_minimum_length()) {
ast_debug(1, "Ignoring event that's too small. %u < %u\n",
(unsigned int) msg_len,
(unsigned int) ast_event_minimum_length());
return;
}
if (!ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(msg, AST_EVENT_IE_EID))) {
/* Don't feed events back in that originated locally. */
return;
}
ast_rwlock_rdlock(&event_types_lock);
if (!event_types[ast_event_get_type(msg)].subscribe) {
/* We are not configured to subscribe to these events. */
ast_rwlock_unlock(&event_types_lock);
return;
}
ast_rwlock_unlock(&event_types_lock);
if (!(event = ast_malloc(msg_len))) {
return;
}
memcpy(event, msg, msg_len);
ast_event_queue_and_cache(event);
}
static void cpg_confchg_cb(cpg_handle_t handle, const struct cpg_name *group_name,
const struct cpg_address *member_list, size_t member_list_entries,
const struct cpg_address *left_list, size_t left_list_entries,
const struct cpg_address *joined_list, size_t joined_list_entries)
{
unsigned int i;
/* If any new nodes have joined, dump our cache of events we are publishing
* that originated from this server. */
if (!joined_list_entries) {
return;
}
for (i = 0; i < ARRAY_LEN(event_types); i++) {
struct ast_event_sub *event_sub;
ast_rwlock_rdlock(&event_types_lock);
if (!event_types[i].publish) {
ast_rwlock_unlock(&event_types_lock);
continue;
}
ast_rwlock_unlock(&event_types_lock);
event_sub = ast_event_subscribe_new(i, ast_event_cb, NULL);
ast_event_sub_append_ie_raw(event_sub, AST_EVENT_IE_EID,
&ast_eid_default, sizeof(ast_eid_default));
ast_event_dump_cache(event_sub);
ast_event_sub_destroy(event_sub);
}
}
static void *dispatch_thread_handler(void *data)
{
cs_error_t cs_err;
struct pollfd pfd[3] = {
{ .events = POLLIN, },
{ .events = POLLIN, },
{ .events = POLLIN, },
};
if ((cs_err = cpg_fd_get(cpg_handle, &pfd[0].fd)) != CS_OK) {
ast_log(LOG_ERROR, "Failed to get CPG fd. This module is now broken.\n");
return NULL;
}
if ((cs_err = corosync_cfg_fd_get(cfg_handle, &pfd[1].fd)) != CS_OK) {
ast_log(LOG_ERROR, "Failed to get CFG fd. This module is now broken.\n");
return NULL;
}
pfd[2].fd = dispatch_thread.alert_pipe[0];
while (!dispatch_thread.stop) {
int res;
pfd[0].revents = 0;
pfd[1].revents = 0;
pfd[2].revents = 0;
res = ast_poll(pfd, ARRAY_LEN(pfd), -1);
if (res == -1 && errno != EINTR && errno != EAGAIN) {
ast_log(LOG_ERROR, "poll() error: %s (%d)\n", strerror(errno), errno);
continue;
}
if (pfd[0].revents & POLLIN) {
if ((cs_err = cpg_dispatch(cpg_handle, CS_DISPATCH_ALL)) != CS_OK) {
ast_log(LOG_WARNING, "Failed CPG dispatch: %d\n", cs_err);
}
}
if (pfd[1].revents & POLLIN) {
if ((cs_err = corosync_cfg_dispatch(cfg_handle, CS_DISPATCH_ALL)) != CS_OK) {
ast_log(LOG_WARNING, "Failed CFG dispatch: %d\n", cs_err);
}
}
}
return NULL;
}
static void ast_event_cb(const struct ast_event *event, void *data)
{
cs_error_t cs_err;
struct iovec iov = {
.iov_base = (void *) event,
.iov_len = ast_event_get_size(event),
};
if (ast_eid_cmp(&ast_eid_default,
ast_event_get_ie_raw(event, AST_EVENT_IE_EID))) {
/* If the event didn't originate from this server, don't send it back out. */
return;
}
/* The ast_event subscription will only exist if we are configured to publish
* these events, so just send away. */
if ((cs_err = cpg_mcast_joined(cpg_handle, CPG_TYPE_FIFO, &iov, 1)) != CS_OK) {
ast_log(LOG_WARNING, "CPG mcast failed (%d)\n", cs_err);
}
}
static char *corosync_show_members(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
cs_error_t cs_err;
struct cpg_name name;
struct cpg_address member_list[CPG_MEMBERS_MAX] = { { 0, }, };
int num_members = CPG_MEMBERS_MAX;
unsigned int i;
switch (cmd) {
case CLI_INIT:
e->command = "corosync show members";
e->usage =
"Usage: corosync show members\n"
" Show corosync cluster members\n";
return NULL;
case CLI_GENERATE:
return NULL; /* no completion */
}
if (a->argc != e->args) {
return CLI_SHOWUSAGE;
}
ast_copy_string(name.value, "asterisk", sizeof(name.value));
name.length = strlen(name.value);
cs_err = cpg_membership_get(cpg_handle, &name, member_list, &num_members);
if (cs_err != CS_OK) {
ast_cli(a->fd, "Failed to get membership list\n");
return CLI_FAILURE;
}
ast_cli(a->fd, "\n"
"=============================================================\n"
"=== Cluster members =========================================\n"
"=============================================================\n"
"===\n"
"=== Number of members: %d\n"
"===\n", num_members);
for (i = 0; i < num_members; i++) {
corosync_cfg_node_address_t addrs[8];
int num_addrs = 0;
unsigned int j;
cs_err = corosync_cfg_get_node_addrs(cfg_handle, member_list[i].nodeid,
ARRAY_LEN(addrs), &num_addrs, addrs);
if (cs_err != CS_OK) {
ast_log(LOG_WARNING, "Failed to get node addresses\n");
continue;
}
ast_cli(a->fd, "=== Node %d\n", i + 1);
for (j = 0; j < num_addrs; j++) {
struct sockaddr *sa = (struct sockaddr *) addrs[j].address;
size_t sa_len = (size_t) addrs[j].address_length;
char buf[128];
getnameinfo(sa, sa_len, buf, sizeof(buf), NULL, 0, NI_NUMERICHOST);
ast_cli(a->fd, "=== --> Address %d: %s\n", j + 1, buf);
}
}
ast_cli(a->fd, "===\n"
"=============================================================\n"
"\n");
return CLI_SUCCESS;
}
static char *corosync_show_config(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
unsigned int i;
switch (cmd) {
case CLI_INIT:
e->command = "corosync show config";
e->usage =
"Usage: corosync show config\n"
" Show configuration loaded from res_corosync.conf\n";
return NULL;
case CLI_GENERATE:
return NULL; /* no completion */
}
if (a->argc != e->args) {
return CLI_SHOWUSAGE;
}
ast_cli(a->fd, "\n"
"=============================================================\n"
"=== res_corosync config =====================================\n"
"=============================================================\n"
"===\n");
ast_rwlock_rdlock(&event_types_lock);
for (i = 0; i < ARRAY_LEN(event_types); i++) {
if (event_types[i].publish) {
ast_cli(a->fd, "=== ==> Publishing Event Type: %s\n",
event_types[i].name);
}
if (event_types[i].subscribe) {
ast_cli(a->fd, "=== ==> Subscribing to Event Type: %s\n",
event_types[i].name);
}
}
ast_rwlock_unlock(&event_types_lock);
ast_cli(a->fd, "===\n"
"=============================================================\n"
"\n");
return CLI_SUCCESS;
}
static struct ast_cli_entry corosync_cli[] = {
AST_CLI_DEFINE(corosync_show_config, "Show configuration"),
AST_CLI_DEFINE(corosync_show_members, "Show cluster members"),
};
enum {
PUBLISH,
SUBSCRIBE,
};
static int set_event(const char *event_type, int pubsub)
{
unsigned int i;
for (i = 0; i < ARRAY_LEN(event_types); i++) {
if (!event_types[i].name || strcasecmp(event_type, event_types[i].name)) {
continue;
}
switch (pubsub) {
case PUBLISH:
event_types[i].publish = 1;
break;
case SUBSCRIBE:
event_types[i].subscribe = 1;
break;
}
break;
}
return (i == ARRAY_LEN(event_types)) ? -1 : 0;
}
static int load_general_config(struct ast_config *cfg)
{
struct ast_variable *v;
int res = 0;
unsigned int i;
ast_rwlock_wrlock(&event_types_lock);
for (i = 0; i < ARRAY_LEN(event_types); i++) {
event_types[i].publish = 0;
event_types[i].subscribe = 0;
}
for (v = ast_variable_browse(cfg, "general"); v && !res; v = v->next) {
if (!strcasecmp(v->name, "publish_event")) {
res = set_event(v->value, PUBLISH);
} else if (!strcasecmp(v->name, "subscribe_event")) {
res = set_event(v->value, SUBSCRIBE);
} else {
ast_log(LOG_WARNING, "Unknown option '%s'\n", v->name);
}
}
for (i = 0; i < ARRAY_LEN(event_types); i++) {
if (event_types[i].publish && !event_types[i].sub) {
event_types[i].sub = ast_event_subscribe(i,
ast_event_cb, "Corosync", NULL,
AST_EVENT_IE_END);
} else if (!event_types[i].publish && event_types[i].sub) {
event_types[i].sub = ast_event_unsubscribe(event_types[i].sub);
}
}
ast_rwlock_unlock(&event_types_lock);
return res;
}
static int load_config(unsigned int reload)
{
static const char filename[] = "res_corosync.conf";
struct ast_config *cfg;
const char *cat = NULL;
struct ast_flags config_flags = { 0 };
int res = 0;
cfg = ast_config_load(filename, config_flags);
if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
return -1;
}
while ((cat = ast_category_browse(cfg, cat))) {
if (!strcasecmp(cat, "general")) {
res = load_general_config(cfg);
} else {
ast_log(LOG_WARNING, "Unknown configuration section '%s'\n", cat);
}
}
ast_config_destroy(cfg);
return res;
}
static void cleanup_module(void)
{
cs_error_t cs_err;
unsigned int i;
for (i = 0; i < ARRAY_LEN(event_types); i++) {
if (event_types[i].sub) {
event_types[i].sub = ast_event_unsubscribe(event_types[i].sub);
}
event_types[i].publish = 0;
event_types[i].subscribe = 0;
}
if (dispatch_thread.id != AST_PTHREADT_NULL) {
char meepmeep = 'x';
dispatch_thread.stop = 1;
if (ast_carefulwrite(dispatch_thread.alert_pipe[1], &meepmeep, 1,
5000) == -1) {
ast_log(LOG_ERROR, "Failed to write to pipe: %s (%d)\n",
strerror(errno), errno);
}
pthread_join(dispatch_thread.id, NULL);
}
if (dispatch_thread.alert_pipe[0] != -1) {
close(dispatch_thread.alert_pipe[0]);
dispatch_thread.alert_pipe[0] = -1;
}
if (dispatch_thread.alert_pipe[1] != -1) {
close(dispatch_thread.alert_pipe[1]);
dispatch_thread.alert_pipe[1] = -1;
}
if (cpg_handle && (cs_err = cpg_finalize(cpg_handle) != CS_OK)) {
ast_log(LOG_ERROR, "Failed to finalize cpg (%d)\n", (int) cs_err);
}
cpg_handle = 0;
if (cfg_handle && (cs_err = corosync_cfg_finalize(cfg_handle) != CS_OK)) {
ast_log(LOG_ERROR, "Failed to finalize cfg (%d)\n", (int) cs_err);
}
cfg_handle = 0;
}
static int load_module(void)
{
cs_error_t cs_err;
enum ast_module_load_result res = AST_MODULE_LOAD_FAILURE;
struct cpg_name name;
if ((cs_err = corosync_cfg_initialize(&cfg_handle, &cfg_callbacks) != CS_OK)) {
ast_log(LOG_ERROR, "Failed to initialize cfg (%d)\n", (int) cs_err);
return AST_MODULE_LOAD_DECLINE;
}
if ((cs_err = cpg_initialize(&cpg_handle, &cpg_callbacks) != CS_OK)) {
ast_log(LOG_ERROR, "Failed to initialize cpg (%d)\n", (int) cs_err);
goto failed;
}
ast_copy_string(name.value, "asterisk", sizeof(name.value));
name.length = strlen(name.value);
if ((cs_err = cpg_join(cpg_handle, &name)) != CS_OK) {
ast_log(LOG_ERROR, "Failed to join (%d)\n", (int) cs_err);
goto failed;
}
if (pipe(dispatch_thread.alert_pipe) == -1) {
ast_log(LOG_ERROR, "Failed to create alert pipe: %s (%d)\n",
strerror(errno), errno);
goto failed;
}
if (ast_pthread_create_background(&dispatch_thread.id, NULL,
dispatch_thread_handler, NULL)) {
ast_log(LOG_ERROR, "Error starting CPG dispatch thread.\n");
goto failed;
}
if (load_config(0)) {
/* simply not configured is not a fatal error */
res = AST_MODULE_LOAD_DECLINE;
goto failed;
}
ast_cli_register_multiple(corosync_cli, ARRAY_LEN(corosync_cli));
ast_enable_distributed_devstate();
return AST_MODULE_LOAD_SUCCESS;
failed:
cleanup_module();
return res;
}
static int unload_module(void)
{
ast_cli_unregister_multiple(corosync_cli, ARRAY_LEN(corosync_cli));
cleanup_module();
return 0;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Corosync");