diff --git a/CHANGES b/CHANGES
index 2f52855d7db89569a9d4a552ace9dac66a51a37e..6c26121dcb6d3d0140ab29e65a7c214e1fca8cd9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -160,6 +160,15 @@ Text Messaging
    instead of simply the uri.  This is the format that MessageSend() can use
    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 -------------------
 ------------------------------------------------------------------------------
diff --git a/UPGRADE.txt b/UPGRADE.txt
index 6b53de30e68358399ff0eb1c23f2b258402961e5..a3930f028597b0076309459a71b1cbb95d3531f6 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -26,6 +26,12 @@ Parking:
  - 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.
 
+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:
  - MAILBOX_EXISTS has been deprecated. Use VM_INFO with the 'exists' parameter
    instead.
diff --git a/build_tools/menuselect-deps.in b/build_tools/menuselect-deps.in
index 35573c3bf17b5af945f22814ab0034212c741bfc..e97edc605aaa6b3d41f77d8076fb7939086e8ee0 100644
--- a/build_tools/menuselect-deps.in
+++ b/build_tools/menuselect-deps.in
@@ -1,5 +1,6 @@
 ALSA=@PBX_ALSA@
 BLUETOOTH=@PBX_BLUETOOTH@
+COROSYNC=@PBX_COROSYNC@
 CRYPTO=@PBX_CRYPTO@
 BFD=@PBX_BFD@
 BISON=@PBX_BISON@
@@ -45,7 +46,6 @@ PORTAUDIO=@PBX_PORTAUDIO@
 PRI=@PBX_PRI@
 OPENR2=@PBX_OPENR2@
 RESAMPLE=@PBX_RESAMPLE@
-AIS=@PBX_AIS@
 RADIUS=@PBX_RADIUS@
 LAUNCHD=@PBX_LAUNCHD@
 SPANDSP=@PBX_SPANDSP@
diff --git a/configs/ais.conf.sample b/configs/ais.conf.sample
deleted file mode 100644
index a4428891f96142e0d53c0948fd8dddda6c813cd8..0000000000000000000000000000000000000000
--- a/configs/ais.conf.sample
+++ /dev/null
@@ -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
-;
diff --git a/configs/res_corosync.conf.sample b/configs/res_corosync.conf.sample
new file mode 100644
index 0000000000000000000000000000000000000000..9a72c1ccd2ba8c32f587fbf0f1b8c2a8bcc7a6e0
--- /dev/null
+++ b/configs/res_corosync.conf.sample
@@ -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
+;
diff --git a/configure b/configure
index 748ccfbc091508ca263ebab6cf7aed5cea5b9020..2a67196c17c883983cd195a6139af6cb65a9eb65 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.ac Revision: 350839 .
+# From configure.ac Revision: 353317 .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.68 for asterisk trunk.
 #
@@ -629,9 +629,6 @@ PBX_MSG_NOSIGNAL
 PBX_IXJUSER
 PBX_H323
 CONFIG_GMIME
-AIS_LIB
-AIS_INCLUDE
-PBX_AIS
 OPENH323_BUILD
 OPENH323_SUFFIX
 OPENH323_LIBDIR
@@ -884,10 +881,6 @@ PBX_OPENR2
 OPENR2_DIR
 OPENR2_INCLUDE
 OPENR2_LIB
-PBX_OPENAIS
-OPENAIS_DIR
-OPENAIS_INCLUDE
-OPENAIS_LIB
 PBX_OGG
 OGG_DIR
 OGG_INCLUDE
@@ -1016,6 +1009,10 @@ PBX_CURSES
 CURSES_DIR
 CURSES_INCLUDE
 CURSES_LIB
+PBX_COROSYNC
+COROSYNC_DIR
+COROSYNC_INCLUDE
+COROSYNC_LIB
 PBX_CAP
 CAP_DIR
 CAP_INCLUDE
@@ -1180,6 +1177,7 @@ with_bfd
 with_execinfo
 with_bluetooth
 with_cap
+with_cpg
 with_curses
 with_crypto
 with_dahdi
@@ -1212,7 +1210,6 @@ with_neon29
 with_netsnmp
 with_newt
 with_ogg
-with_openais
 with_openr2
 with_osptk
 with_oss
@@ -1895,6 +1892,7 @@ Optional Packages:
   --with-execinfo=PATH    use Stack Backtrace files in PATH
   --with-bluetooth=PATH   use Bluetooth 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-crypto=PATH      use OpenSSL Cryptography 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-newt=PATH        use newt 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-osptk=PATH       use OSP Toolkit 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_OPTION="curses"
     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_OPTION="openr2"
     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
-
-# OpenAIS installs its libraries into /usr/lib/openais by default, so check there
 
-
-if test "x${PBX_OPENAIS}" != "x1" -a "${USE_OPENAIS}" != "no"; then
+if test "x${PBX_COROSYNC}" != "x1" -a "${USE_COROSYNC}" != "no"; then
    pbxlibdir=""
-   # if --with-OPENAIS=DIR has been specified, use it.
-   if test "x${OPENAIS_DIR}" != "x"; then
-      if test -d ${OPENAIS_DIR}/lib; then
-      	 pbxlibdir="-L${OPENAIS_DIR}/lib"
+   # if --with-COROSYNC=DIR has been specified, use it.
+   if test "x${COROSYNC_DIR}" != "x"; then
+      if test -d ${COROSYNC_DIR}/lib; then
+      	 pbxlibdir="-L${COROSYNC_DIR}/lib"
       else
-      	 pbxlibdir="-L${OPENAIS_DIR}"
+      	 pbxlibdir="-L${COROSYNC_DIR}"
       fi
    fi
-   pbxfuncname="saClmInitialize"
+   pbxfuncname="cpg_join"
    if test "x${pbxfuncname}" = "x" ; then   # empty lib, assume only headers
-      AST_OPENAIS_FOUND=yes
+      AST_COROSYNC_FOUND=yes
    else
       ast_ext_lib_check_save_CFLAGS="${CFLAGS}"
       CFLAGS="${CFLAGS} "
-      as_ac_Lib=`$as_echo "ac_cv_lib_SaClm_${pbxfuncname}" | $as_tr_sh`
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lSaClm" >&5
-$as_echo_n "checking for ${pbxfuncname} in -lSaClm... " >&6; }
+      as_ac_Lib=`$as_echo "ac_cv_lib_cpg_${pbxfuncname}" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lcpg" >&5
+$as_echo_n "checking for ${pbxfuncname} in -lcpg... " >&6; }
 if eval \${$as_ac_Lib+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   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
 /* 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 "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
-  AST_OPENAIS_FOUND=yes
+  AST_COROSYNC_FOUND=yes
 else
-  AST_OPENAIS_FOUND=no
+  AST_COROSYNC_FOUND=no
 fi
 
       CFLAGS="${ast_ext_lib_check_save_CFLAGS}"
    fi
 
    # now check for the header.
-   if test "${AST_OPENAIS_FOUND}" = "yes"; then
-      OPENAIS_LIB="${pbxlibdir} -lSaClm -L/usr/lib/openais -L/usr/lib64/openais"
-      # if --with-OPENAIS=DIR has been specified, use it.
-      if test "x${OPENAIS_DIR}" != "x"; then
-         OPENAIS_INCLUDE="-I${OPENAIS_DIR}/include"
-      fi
-      OPENAIS_INCLUDE="${OPENAIS_INCLUDE} "
-      if test "xopenais/saClm.h" = "x" ; then	# no header, assume found
-         OPENAIS_HEADER_FOUND="1"
+   if test "${AST_COROSYNC_FOUND}" = "yes"; then
+      COROSYNC_LIB="${pbxlibdir} -lcpg -lcfg"
+      # if --with-COROSYNC=DIR has been specified, use it.
+      if test "x${COROSYNC_DIR}" != "x"; then
+         COROSYNC_INCLUDE="-I${COROSYNC_DIR}/include"
+      fi
+      COROSYNC_INCLUDE="${COROSYNC_INCLUDE} "
+      if test "xcorosync/cpg.h" = "x" ; then	# no header, assume found
+         COROSYNC_HEADER_FOUND="1"
       else				# check for the header
          ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}"
-         CPPFLAGS="${CPPFLAGS} ${OPENAIS_INCLUDE}"
-         ac_fn_c_check_header_mongrel "$LINENO" "openais/saClm.h" "ac_cv_header_openais_saClm_h" "$ac_includes_default"
-if test "x$ac_cv_header_openais_saClm_h" = xyes; then :
-  OPENAIS_HEADER_FOUND=1
+         CPPFLAGS="${CPPFLAGS} ${COROSYNC_INCLUDE}"
+         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_corosync_cpg_h" = xyes; then :
+  COROSYNC_HEADER_FOUND=1
 else
-  OPENAIS_HEADER_FOUND=0
+  COROSYNC_HEADER_FOUND=0
 fi
 
 
          CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}"
       fi
-      if test "x${OPENAIS_HEADER_FOUND}" = "x0" ; then
-         OPENAIS_LIB=""
-         OPENAIS_INCLUDE=""
+      if test "x${COROSYNC_HEADER_FOUND}" = "x0" ; then
+         COROSYNC_LIB=""
+         COROSYNC_INCLUDE=""
       else
          if test "x${pbxfuncname}" = "x" ; then		# only checking headers -> no library
-            OPENAIS_LIB=""
+            COROSYNC_LIB=""
          fi
-         PBX_OPENAIS=1
+         PBX_COROSYNC=1
          cat >>confdefs.h <<_ACEOF
-#define HAVE_OPENAIS 1
+#define HAVE_COROSYNC 1
 _ACEOF
 
       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
    pbxlibdir=""
diff --git a/configure.ac b/configure.ac
index ddf14a1147956727ae17cd7b1332317d5ca6a357..04d6fbe5fa8bddd10588ae37d6cd67e643d4e271 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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([BLUETOOTH], [Bluetooth], [bluetooth])
 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([CRYPTO], [OpenSSL Cryptography], [crypto])
 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([NEWT], [newt], [newt])
 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([OSPTK], [OSP Toolkit], [osptk])
 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])
 
-# 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
-
-# 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([COROSYNC], [cpg], [cpg_join], [corosync/cpg.h], [-lcfg])
 
 AST_EXT_LIB_CHECK([SPEEX], [speex], [speex_encode], [speex/speex.h], [-lm])
 
diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index 5f3e7d5357119932243a704f39b549ea6d91b8d8..0ba8ebaf7b938dca3389a09c2e802872b185f3ea 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -137,6 +137,9 @@
 /* Define to 1 if you have the `closefrom' function. */
 #undef HAVE_CLOSEFROM
 
+/* Define to 1 if you have the Corosync library. */
+#undef HAVE_COROSYNC
+
 /* Define to 1 if you have the `cos' function. */
 #undef HAVE_COS
 
@@ -506,9 +509,6 @@
 /* Define to 1 if you have the OGG library. */
 #undef HAVE_OGG
 
-/* Define to 1 if you have the OpenAIS library. */
-#undef HAVE_OPENAIS
-
 /* Define if your system has the OpenH323 libraries. */
 #undef HAVE_OPENH323
 
diff --git a/makeopts.in b/makeopts.in
index 250998db01036adf60550d6900df7696d1ff530e..63c84e627d96816d2a28052ff98d0fb9cbad533d 100644
--- a/makeopts.in
+++ b/makeopts.in
@@ -222,8 +222,8 @@ OPENR2_LIB=@OPENR2_LIB@
 PWLIB_INCLUDE=@PWLIB_INCLUDE@
 PWLIB_LIB=@PWLIB_LIB@
 
-AIS_INCLUDE=@AIS_INCLUDE@
-AIS_LIB=@AIS_LIB@
+COROSYNC_INCLUDE=@COROSYNC_INCLUDE@
+COROSYNC_LIB=@COROSYNC_LIB@
 
 RADIUS_INCLUDE=@RADIUS_INCLUDE@
 RADIUS_LIB=@RADIUS_LIB@
diff --git a/res/ais/ais.h b/res/ais/ais.h
deleted file mode 100644
index 6aaeadf15f92341711180d22f2b698a1ec6ec408..0000000000000000000000000000000000000000
--- a/res/ais/ais.h
+++ /dev/null
@@ -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 */
diff --git a/res/ais/clm.c b/res/ais/clm.c
deleted file mode 100644
index d290ee2cd3738d08152dbd12c35c9a506c6eaa3a..0000000000000000000000000000000000000000
--- a/res/ais/clm.c
+++ /dev/null
@@ -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;
-}
diff --git a/res/ais/evt.c b/res/ais/evt.c
deleted file mode 100644
index 8d11c647312c81c4cc2dc835cd2eea05a0628e24..0000000000000000000000000000000000000000
--- a/res/ais/evt.c
+++ /dev/null
@@ -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;
-}
diff --git a/res/res_corosync.c b/res/res_corosync.c
new file mode 100644
index 0000000000000000000000000000000000000000..9ce44103d7c5deee56d7fa9125d578f3c778cfd0
--- /dev/null
+++ b/res/res_corosync.c
@@ -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");