This is to send dbus events on major cluster events:
 - membership changes
 - application connect/dissconnet from corosync
 - quorum changes

dbus events can then be converted into snmp traps by foghorn or
corosync-notifyd can be run to directly send snmp traps.

Signed-off-by: Angus Salkeld <[email protected]>
---
 Makefile.am                |    2 +-
 TODO                       |   53 +++-
 conf/COROSYNC-MIB.txt      |  186 ++++++++
 conf/Makefile.am           |   44 ++
 conf/corosync-signals.conf |   26 ++
 configure.ac               |   74 +++-
 corosync.spec.in           |   23 +
 cts/.gitignore             |    1 +
 init/.gitignore            |    1 +
 init/Makefile.am           |    7 +-
 init/notifyd.in            |  147 +++++++
 man/Makefile.am            |    1 +
 man/corosync-notifyd.8     |  145 ++++++
 tools/.gitignore           |    1 +
 tools/Makefile.am          |   12 +-
 tools/corosync-notifyd.c   | 1035 ++++++++++++++++++++++++++++++++++++++++++++
 16 files changed, 1751 insertions(+), 7 deletions(-)
 create mode 100644 conf/COROSYNC-MIB.txt
 create mode 100644 conf/Makefile.am
 create mode 100644 conf/corosync-signals.conf
 create mode 100755 init/notifyd.in
 create mode 100644 man/corosync-notifyd.8
 create mode 100644 tools/corosync-notifyd.c

diff --git a/Makefile.am b/Makefile.am
index f448eaa..8d8666f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -65,7 +65,7 @@ corolenstest_DATA     = conf/lenses/tests/test_corosync.aug
 endif
 
 SUBDIRS                        = include lcr lib exec services tools test cts 
pkgconfig \
-                         man init
+                         man init conf
 
 install-exec-local:
        $(INSTALL) -d $(DESTDIR)/${COROSYSCONFDIR}/service.d
diff --git a/TODO b/TODO
index 21a7a51..c78f766 100644
--- a/TODO
+++ b/TODO
@@ -46,13 +46,64 @@ automatically reenable a redundant ring when it has been 
back in service.
 ------------------------------------------------------------------------------
 topic-snmp
 ------------------------------------------------------------------------------
-Main Developer: Steven Dake
+Main Developer: Steven Dake & Angus Salkeld
 Started: Not Started
 Finished: 0%
 target: needle
 Description:
 This topic involves investigation of adding SNMP support into Corosync.
 
+Initial requirements
+--------------------
+1) send dbus signals for the following initial events:
+   1.1) node joine/leave
+   1.2) application connect/dissconnect from corosync
+   1.3) quorum gain/lost
+
+2) foghorn (http://git.fedorahosted.org/git/foghorn.git) then converts
+   dbus signals into snmp traps
+
+3) if you don't want to use dbus (embedded target...) then you should be
+   able to use snmp-agentx.
+
+4) these events should be logged as well.
+
+Still to do:
+-----------
+a) add mib definitions for 1.2
+   and add code to send the traps
+b) chat to Yuki Sato about merging his link-status work more in.
+
+How to run/test (snmp only mode)
+--------------------------------
+$ ./configure --enable-snmp
+$ make && sudo make install
+$ /etc/init.d/corosync start
+$ vim /etc/snmp/snmptrapd.conf
+
+Add the following:
+authCommunity   log,execute,net public
+$ /etc/init.d/snmptrapd start
+$ echo "OPTIONS=\"-d\"" > /etc/sysconfig/corosync-notifyd
+$ /etc/init.d/corosync-notifyd start
+
+I start up wireshark to see if there are any snmp traps been sent
+as I am too lazy to setup a manager to receive traps.
+
+run a program that talks to corosync e.g.
+$ corosync-objctl
+
+And you should get traps
+
+How to run/test (dbus only mode)
+--------------------------------
+$ ./configure --enable-dbus
+Basically the same as above except I run
+$ echo "OPTIONS=\"-s\"" > /etc/sysconfig/corosync-notifyd
+$ /etc/init.d/corosync-notifyd start
+$ dbus-monitor --system
+to see the signals getting sent
+
 ------------------------------------------------------------------------------
 topic-udpu
 ------------------------------------------------------------------------------
diff --git a/conf/COROSYNC-MIB.txt b/conf/COROSYNC-MIB.txt
new file mode 100644
index 0000000..5143190
--- /dev/null
+++ b/conf/COROSYNC-MIB.txt
@@ -0,0 +1,186 @@
+COROSYNC-MIB DEFINITIONS ::= BEGIN
+
+--
+-- MIB objects for the corosync
+--
+
+IMPORTS
+    MODULE-IDENTITY,NOTIFICATION-TYPE,
+    Integer32,enterprises                       FROM SNMPv2-SMI
+    TEXTUAL-CONVENTION                          FROM SNMPv2-TC
+    SnmpAdminString                             FROM SNMP-FRAMEWORK-MIB
+    netSnmp                                     FROM NET-SNMP-MIB
+    InetAddressType, InetAddress                FROM INET-ADDRESS-MIB
+;
+
+corosync MODULE-IDENTITY
+    LAST-UPDATED    "200911061318Z"
+    ORGANIZATION    "www.corosync.org"
+    CONTACT-INFO    "name:  Yuki Sato
+                     email: [email protected]"
+    DESCRIPTION     "MIB objects for the corosync"
+    REVISION        "200911061318Z"
+    DESCRIPTION     "First draft"
+    REVISION        "201003251209Z"
+    DESCRIPTION
+        "Private Enterprise Number has been assigned."
+        ::= { enterprises 35488 }
+
+--
+-- top level structure
+--
+corosyncNotice OBJECT IDENTIFIER ::= { corosync 1 }
+
+--
+--  corosync MIB entries
+--
+
+--
+-- Node Information
+--
+corosyncNoticeNodeStatusTable OBJECT-TYPE
+    SYNTAX      SEQUENCE OF corosyncNoticeNodeEntry
+    MAX-ACCESS  accessible-for-notify
+    STATUS      current
+    DESCRIPTION
+        "The table contains information about the nodes in the corosync."
+::= { corosyncNotice 1 }
+
+corosyncNoticeNodeEntry OBJECT-TYPE
+    SYNTAX      corosyncNoticeNodeEntry
+    MAX-ACCESS  accessible-for-notify
+    STATUS      current
+    DESCRIPTION
+        "The entry containing information about the iface."
+    INDEX   { corosyncNoticeNodeIndex }
+::= { corosyncNoticeNodeStatusTable 1 }
+
+corosyncNoticeNodeEntry ::= SEQUENCE {
+    corosyncNoticeNodeIndex     Integer32,
+    corosyncNoticeNodeid        Integer32,
+    corosyncNoticeNode          OCTET STRING,
+    corosyncNoticeNodeStatus    INTEGER
+}
+
+corosyncNoticeNodeIndex OBJECT-TYPE
+    SYNTAX      Integer32
+    MAX-ACCESS  accessible-for-notify
+    STATUS      current
+    DESCRIPTION "The unique integer of the node."
+::= { corosyncNoticeNodeEntry 1 }
+
+corosyncNoticeNodeid OBJECT-TYPE
+    SYNTAX      Integer32
+    MAX-ACCESS  accessible-for-notify
+    STATUS      current
+    DESCRIPTION "The id of the node."
+::= { corosyncNoticeNodeEntry 2 }
+
+corosyncNoticeNode OBJECT-TYPE
+    SYNTAX      OCTET STRING (SIZE(1..64))
+    MAX-ACCESS  accessible-for-notify
+    STATUS      current
+    DESCRIPTION
+        "The iface of the node."
+::= { corosyncNoticeNodeEntry 3 }
+
+corosyncNoticeNodeStatus OBJECT-TYPE
+    SYNTAX      INTEGER {
+                unknown (0),
+                joined  (1),
+                left    (2)
+            }
+    MAX-ACCESS  accessible-for-notify
+    STATUS      current
+    DESCRIPTION
+        "The status change of the node."
+::= { corosyncNoticeNodeEntry 4 }
+
+--
+-- Iface(s) Information
+--
+corosyncNoticeIfaceStatusTable OBJECT-TYPE
+    SYNTAX      SEQUENCE OF corosyncNoticeIfaceEntry
+    MAX-ACCESS  accessible-for-notify
+    STATUS      current
+    DESCRIPTION
+        "The table describes the iface(s) that are used by the corosync."
+::= { corosyncNotice 2 }
+
+corosyncNoticeIfaceEntry OBJECT-TYPE
+    SYNTAX      corosyncNoticeIfaceEntry
+    MAX-ACCESS  accessible-for-notify
+    STATUS      current
+    DESCRIPTION
+        "The entry containing information about the iface."
+    INDEX   { corosyncNoticeIfaceIndex }
+::= { corosyncNoticeIfaceStatusTable 1 }
+
+corosyncNoticeIfaceEntry ::= SEQUENCE {
+    corosyncNoticeIfaceIndex    INTEGER,
+    corosyncNoticeIface         OCTET STRING,
+    corosyncNoticeIfaceStatus   OCTET STRING
+}
+
+corosyncNoticeIfaceIndex OBJECT-TYPE
+    SYNTAX      Integer32
+    MAX-ACCESS  accessible-for-notify
+    STATUS      current
+    DESCRIPTION
+        "The unique integer of the iface(s)."
+::= { corosyncNoticeIfaceEntry 1 }
+
+corosyncNoticeIface OBJECT-TYPE
+    SYNTAX      OCTET STRING (SIZE(1..64))
+    MAX-ACCESS  accessible-for-notify
+    STATUS      current
+    DESCRIPTION
+        "The iface(s) of the change happened node."
+::= { corosyncNoticeIfaceEntry 2 }
+
+corosyncNoticeIfaceStatus OBJECT-TYPE
+    SYNTAX      INTEGER {
+                unknown (0),
+                up      (1),
+                down    (2),
+                faulty  (3)
+            }
+    MAX-ACCESS accessible-for-notify
+    STATUS      current
+    DESCRIPTION
+        "The status change of the iface."
+::= { corosyncNoticeIfaceEntry 3 }
+
+--corosyncNoticeIfaceStatus OBJECT-TYPE
+--   SYNTAX     OCTET STRING (SIZE(1..1024))
+--   MAX-ACCESS accessible-for-notify
+--   STATUS     current
+--   DESCRIPTION
+--       "The iface(s) status of the change happened node."
+--::= { corosyncNoticeIfaceEntry 3 }
+
+--
+-- Trap Information
+--
+corosyncNoticeTrap OBJECT IDENTIFIER ::= { corosync 100 }
+
+corosyncNoticeNodeTrap NOTIFICATION-TYPE
+    OBJECTS
+        { corosyncNoticeNodeid corosyncNoticeNode corosyncNoticeNodeStatus }
+    STATUS      current
+    DESCRIPTION
+        "The node status change event just happened."
+::= { corosyncNoticeTrap 1 }
+
+corosyncNoticeIfaceTrap NOTIFICATION-TYPE
+    OBJECTS
+        { corosyncNoticeNodeid corosyncNoticeIface corosyncNoticeIfaceStatus }
+    STATUS  current
+    DESCRIPTION
+        "The iface status change event just happened."
+::= { corosyncNoticeTrap 2 }
+
+END
+
+
+
diff --git a/conf/Makefile.am b/conf/Makefile.am
new file mode 100644
index 0000000..3d7adcc
--- /dev/null
+++ b/conf/Makefile.am
@@ -0,0 +1,44 @@
+# Copyright (c) 2009 Red Hat, Inc.
+#
+# Authors: Andrew Beekhof
+#         Steven Dake ([email protected])
+#
+# This software licensed under BSD license, the text of which follows:
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# - Redistributions of source code must retain the above copyright notice,
+#   this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright notice,
+#   this list of conditions and the following disclaimer in the documentation
+#   and/or other materials provided with the distribution.
+# - Neither the name of the MontaVista Software, Inc. nor the names of its
+#   contributors may be used to endorse or promote products derived from this
+#   software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+EXTRA_DIST     = COROSYNC-MIB.txt corosync-signals.conf
+
+MAINTAINERCLEANFILES    = Makefile.in
+
+if INSTALL_MIB
+mibdir = $(datadir)/snmp/mibs
+mib_DATA = COROSYNC-MIB.txt
+endif
+
+if INSTALL_DBUSCONF
+dbusdir = $(sysconfdir)/dbus-1/system.d
+dbus_DATA = corosync-signals.conf
+endif
diff --git a/conf/corosync-signals.conf b/conf/corosync-signals.conf
new file mode 100644
index 0000000..4bd94d2
--- /dev/null
+++ b/conf/corosync-signals.conf
@@ -0,0 +1,26 @@
+<!DOCTYPE busconfig PUBLIC
+         "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+         "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd";>
+<busconfig>
+
+       <!-- Only root can own the corosync service. -->
+       <policy user="root">
+               <allow own="com.redhat.cluster.corosync"/>
+       </policy>
+
+       <policy context="default">
+               <allow send_destination="com.redhat.cluster.corosync"
+                      send_path="/com/redhat/cluster/corosync"
+                      send_interface="com.redhat.cluster.corosync"
+                      send_member="NodeStateChange"/>
+               <allow send_destination="com.redhat.cluster.corosync"
+                      send_path="/com/redhat/cluster/corosync"
+                      send_interface="com.redhat.cluster.corosync"
+                      send_member="ConnectionStateChange"/>
+               <allow send_destination="com.redhat.cluster.corosync"
+                      send_path="/com/redhat/cluster/corosync"
+                      send_interface="com.redhat.cluster.corosync"
+                      send_member="QuorumStateChange"/>
+       </policy>
+
+</busconfig>
diff --git a/configure.ac b/configure.ac
index 0a26adf..9beb38d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -130,7 +130,8 @@ AC_CONFIG_FILES([Makefile
                 cts/Makefile
                 cts/agents/Makefile
                 cts/CTSvars.py
-                tools/Makefile])
+                tools/Makefile
+                conf/Makefile])
 
 ### Local business
 
@@ -241,6 +242,10 @@ AC_ARG_ENABLE([nss],
        [  --enable-nss                    : Network Security Services 
encryption. ],,
        [ enable_nss="yes" ])
 
+AC_ARG_ENABLE([dbus],
+       [  --enable-dbus                    : dbus events. ],,
+       [ enable_dbus="no" ])
+
 AC_ARG_ENABLE([testagents],
        [  --enable-testagents             : Install Test Agents. ],,
        [ default="no" ])
@@ -281,6 +286,10 @@ AC_ARG_WITH([socket-dir],
        [ SOCKETDIR="$withval" ],
        [ SOCKETDIR="$localstatedir/run" ])
 
+AC_ARG_ENABLE([snmp],
+       [  --enable-snmp           : SNMP protocol support ],
+       [ default="no" ])
+
 # OS detection
 # THIS SECTION MUST DIE!
 CP=cp
@@ -385,6 +394,13 @@ if test "x${enable_nss}" = xyes; then
        PACKAGE_FEATURES="$PACKAGE_FEATURES nss"
 fi
 
+# Look for dbus-1
+if test "x${enable_dbus}" = xyes; then
+       PKG_CHECK_MODULES([DBUS],[dbus-1])
+       AC_DEFINE_UNQUOTED([HAVE_DBUS], 1, [have dbus])
+       PACKAGE_FEATURES="$PACKAGE_FEATURES dbus"
+fi
+
 if test "x${enable_testagents}" = xyes; then
        AC_DEFINE_UNQUOTED([HAVE_TESTAGENTS], 1, [have testagents])
        PACKAGE_FEATURES="$PACKAGE_FEATURES testagents"
@@ -425,6 +441,60 @@ if test "x${enable_augeas}" = xyes; then
        PACKAGE_FEATURES="$PACKAGE_FEATURES augeas"
 fi
 
+if test "x${enable_snmp}" = xyes; then
+       SNMPCONFIG=""
+       AC_CHECK_HEADERS(net-snmp/net-snmp-config.h)
+
+       if test "x${ac_cv_header_net_snmp_net_snmp_config_h}" != "xyes"; then
+               enable_snmp=no
+       fi
+
+       if test $enable_snmp != no; then
+               AC_PATH_PROGS(SNMPCONFIG, net-snmp-config)
+               if test "X${SNMPCONFIG}" = "X"; then
+                       AC_MSG_RESULT(You need the net_snmp development package 
to continue.)
+                       enable_snmp=no
+               fi
+       fi
+
+       if test $enable_snmp != no; then
+               AC_MSG_CHECKING(for special snmp libraries)
+               SNMPLIBS=`$SNMPCONFIG --libs`
+               AC_MSG_RESULT($SNMPLIBS)
+       fi
+
+       if test $enable_snmp != no; then
+               savedLibs=$LIBS
+               LIBS="$LIBS $SNMPLIBS"
+               AC_CHECK_FUNCS(netsnmp_transport_open_client)
+               if test $ac_cv_func_netsnmp_transport_open_client != yes; then
+                       AC_CHECK_FUNCS(netsnmp_tdomain_transport)
+                       if test $ac_cv_func_netsnmp_tdomain_transport != yes; 
then
+                               enable_snmp=no
+                       fi
+               else
+                       AC_DEFINE_UNQUOTED([NETSNMPV54], $NETSNMP_NEW_SUPPORT, 
[have net-snmp5.4 over])
+               fi
+               LIBS=$savedLibs
+       fi
+
+       AC_MSG_CHECKING(for snmp)
+       AC_MSG_RESULT($enable_snmp)
+   if test $enable_snmp = no; then
+               enable_snmp=0
+               AC_MSG_ERROR(Unable to support SNMP)
+   else
+               enable_snmp=1
+               PACKAGE_FEATURES="$PACKAGE_FEATURES snmp"
+               AC_DEFINE_UNQUOTED([ENABLE_SNMP], $enable_snmp, [Build in 
support for sending SNMP traps])
+   fi
+else
+       enable_snmp=0
+fi
+AC_SUBST([SNMPLIBS])
+AC_SUBST([SNMP_LCRSO])
+AM_CONDITIONAL(BUILD_SNMP, test "${enable_snmp}" = "1")
+
 # extra warnings
 EXTRA_WARNINGS=""
 
@@ -521,6 +591,8 @@ AC_SUBST([OS_DYFLAGS])
 
 AC_SUBST([OS_LDL])
 AM_CONDITIONAL(INSTALL_TESTAGENTS, test -n "${enable_testagents}")
+AM_CONDITIONAL(INSTALL_MIB, test "${enable_snmp}" = "1")
+AM_CONDITIONAL(INSTALL_DBUSCONF, test "${enable_dbus}" = "1")
 AM_CONDITIONAL(AUGTOOL, test -n "${AUGTOOL}")
 AC_SUBST([NSS_LDFLAGS])
 
diff --git a/corosync.spec.in b/corosync.spec.in
index 51f4404..aec13c6 100644
--- a/corosync.spec.in
+++ b/corosync.spec.in
@@ -8,6 +8,8 @@
 %bcond_with testagents
 %bcond_with watchdog
 %bcond_with monitoring
+%bcond_with snmp
+%bcond_with dbus
 
 Name: corosync
 Summary: The Corosync Cluster Engine and Application Programming Interfaces
@@ -35,6 +37,12 @@ BuildRequires: autoconf automake
 %endif
 BuildRequires: nss-devel
 BuildRequires: libibverbs-devel librdmacm-devel
+%if %{with snmp}
+BuildRequires: net-snmp-devel
+%endif
+%if %{with dbus}
+BuildRequires: dbus-devel
+%endif
 
 BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
 
@@ -61,6 +69,12 @@ export rdmacm_LIBS=-lrdmacm \
 %if %{with monitoring}
        --enable-monitoring \
 %endif
+%if %{with snmp}
+       --enable-snmp \
+%endif
+%if %{with dbus}
+       --enable-dbus \
+%endif
        --enable-rdma \
        --with-initddir=%{_initrddir}
 
@@ -107,13 +121,21 @@ fi
 %{_sbindir}/corosync-pload
 %{_sbindir}/corosync-cpgtool
 %{_sbindir}/corosync-quorumtool
+%{_sbindir}/corosync-notifyd
 %{_bindir}/corosync-blackbox
 %dir %{_sysconfdir}/corosync
 %dir %{_sysconfdir}/corosync/service.d
 %dir %{_sysconfdir}/corosync/uidgid.d
 %config(noreplace) %{_sysconfdir}/corosync/corosync.conf.example
 %config(noreplace) %{_sysconfdir}/corosync/corosync.conf.example.udpu
+%if %{with dbus}
+%{_sysconfdir}/dbus-1/system.d/corosync-signals.conf
+%endif
+%if %{with snmp}
+%(_datadir)/snmp/mibs/COROSYNC-MIB.txt
+%endif
 %{_initrddir}/corosync
+%{_initrddir}/corosync-notifyd
 %dir %{_libexecdir}/lcrso
 %{_libexecdir}/lcrso/coroparse.lcrso
 %{_libexecdir}/lcrso/objdb.lcrso
@@ -143,6 +165,7 @@ fi
 %{_mandir}/man8/corosync-cpgtool.8*
 %{_mandir}/man8/corosync-fplay.8*
 %{_mandir}/man8/corosync-pload.8*
+%{_mandir}/man8/corosync-notifyd.8*
 %{_mandir}/man8/corosync-quorumtool.8*
 %{_mandir}/man5/corosync.conf.5*
 
diff --git a/cts/.gitignore b/cts/.gitignore
index 6f99c8f..e9e3b0c 100644
--- a/cts/.gitignore
+++ b/cts/.gitignore
@@ -1,2 +1,3 @@
 CTSvars.py
 *_test_agent
+*.pyc
diff --git a/init/.gitignore b/init/.gitignore
index 6ab3892..0a75c32 100644
--- a/init/.gitignore
+++ b/init/.gitignore
@@ -1 +1,2 @@
 generic
+notifyd
diff --git a/init/Makefile.am b/init/Makefile.am
index 2d079d1..0ca9ee9 100644
--- a/init/Makefile.am
+++ b/init/Makefile.am
@@ -34,9 +34,9 @@
 
 MAINTAINERCLEANFILES   = Makefile.in
 
-EXTRA_DIST             = generic.in
+EXTRA_DIST             = generic.in notifyd.in
 
-target_INIT            = generic
+target_INIT            = generic notifyd
 
 %: %.in Makefile
        rm -f $@-t $@
@@ -57,7 +57,8 @@ clean-local:
 install-exec-local:
        $(INSTALL) -d $(DESTDIR)/$(INITDDIR)
        $(INSTALL) -m 755 generic $(DESTDIR)/$(INITDDIR)/corosync
+       $(INSTALL) -m 755 notifyd $(DESTDIR)/$(INITDDIR)/corosync-notifyd
 
 uninstall-local:
        cd $(DESTDIR)/$(INITDDIR) && \
-               rm -f corosync
+               rm -f corosync corosync-notifyd
diff --git a/init/notifyd.in b/init/notifyd.in
new file mode 100755
index 0000000..85aa108
--- /dev/null
+++ b/init/notifyd.in
@@ -0,0 +1,147 @@
+#!/bin/bash
+
+# Authors:
+#  Angus Salkeld <[email protected]>
+#
+# License: Revised BSD
+
+# chkconfig: - 22 78
+# description: Corosync Dbus and snmp notifier
+# processname: corosync-notifyd
+#
+### BEGIN INIT INFO
+# Provides:            corosync-notifyd
+# Required-Start:      $corosync
+# Required-Stop:       $corosync
+# Default-Start:
+# Default-Stop:
+# Short-Description:   Starts and stops Corosync Notifier.
+# Description:         Starts and stops Corosync Notifier.
+### END INIT INFO
+
+desc="Corosync Notifier"
+prog="corosync-notifyd"
+
+# set secure PATH
+PATH="/sbin:/bin:/usr/sbin:/usr/bin:@SBINDIR@"
+
+success()
+{
+       echo -ne "[  OK  ]\r"
+}
+
+failure()
+{
+       echo -ne "[FAILED]\r"
+}
+
+status()
+{
+       pid=$(pidof $1 2>/dev/null)
+       rtrn=$?
+       if [ $rtrn -ne 0 ]; then
+               echo "$1 is stopped"
+       else
+               echo "$1 (pid $pid) is running..."
+       fi
+       return $rtrn
+}
+
+# rpm based distros
+if [ -d @SYSCONFDIR@/sysconfig ]; then
+       [ -f @INITDDIR@/functions ] && . @INITDDIR@/functions
+       [ -f @SYSCONFDIR@/sysconfig/$prog ] && . @SYSCONFDIR@/sysconfig/$prog
+       [ -z "$LOCK_FILE" ] && LOCK_FILE="@LOCALSTATEDIR@/lock/subsys/$prog"
+fi
+
+# deb based distros
+if [ -d @SYSCONFDIR@/default ]; then
+       [ -f @SYSCONFDIR@/default/$prog ] && . @SYSCONFDIR@/default/$prog
+       [ -z "$LOCK_FILE" ] && LOCK_FILE="@LOCALSTATEDIR@/lock/$prog"
+fi
+
+# The version of __pids_pidof in /etc/init.d/functions calls pidof with -x
+# This means it matches scripts, including this one.
+# Redefine it here so that status (from the same file) works.
+# Otherwise simultaneous calls to stop() will loop forever
+__pids_pidof() {
+        pidof -c -o $$ -o $PPID -o %PPID "$1" || \
+                pidof -c -o $$ -o $PPID -o %PPID "${1##*/}"
+}
+
+start()
+{
+       echo -n "Starting $desc ($prog): "
+
+       # most recent distributions use tmpfs for @LOCALSTATEDIR@/run
+       # to avoid to clean it up on every boot.
+       # they also assume that init scripts will create
+       # required subdirectories for proper operations
+       mkdir -p @LOCALSTATEDIR@/run
+
+       if status $prog > /dev/null 2>&1; then
+               success
+       else
+               $prog $OPTIONS > /dev/null 2>&1
+
+               # give it time to fail
+               sleep 2
+               if status $prog > /dev/null 2>&1; then
+                       touch $LOCK_FILE
+                       success
+               else
+                       failure
+                       rtrn=1
+               fi
+       fi
+       echo
+}
+
+stop()
+{
+       ! status $prog > /dev/null 2>&1 && return
+
+       echo -n "Signaling $desc ($prog) to terminate: "
+       kill -TERM $(pidof $prog) > /dev/null 2>&1
+       success
+       echo
+
+       rm -f $LOCK_FILE
+       success
+       echo
+}
+
+restart()
+{
+       stop
+       start
+}
+
+rtrn=0
+
+case "$1" in
+start)
+       start
+;;
+restart|reload|force-reload)
+       restart
+;;
+condrestart|try-restart)
+       if status $prog > /dev/null 2>&1; then
+               restart
+       fi
+;;
+status)
+       status $prog
+       rtrn=$?
+;;
+stop)
+       stop
+;;
+*)
+       echo "usage: $0 
{start|stop|restart|reload|force-reload|condrestart|try-restart|status}"
+       rtrn=2
+;;
+esac
+
+exit $rtrn
diff --git a/man/Makefile.am b/man/Makefile.am
index 58923f0..33d1585 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -47,6 +47,7 @@ dist_man_MANS = \
        corosync-cpgtool.8 \
        corosync-fplay.8 \
        corosync-pload.8 \
+       corosync-notifyd.8 \
        corosync-quorumtool.8 \
        corosync_overview.8 \
        cpg_overview.8 \
diff --git a/man/corosync-notifyd.8 b/man/corosync-notifyd.8
new file mode 100644
index 0000000..1ff8994
--- /dev/null
+++ b/man/corosync-notifyd.8
@@ -0,0 +1,145 @@
+.\"/*
+.\" * Copyright (C) 2010 Red Hat, Inc.
+.\" *
+.\" * All rights reserved.
+.\" *
+.\" * Author: Angus Salkeld <[email protected]>
+.\" *
+.\" * This software licensed under BSD license, the text of which follows:
+.\" *
+.\" * Redistribution and use in source and binary forms, with or without
+.\" * modification, are permitted provided that the following conditions are 
met:
+.\" *
+.\" * - Redistributions of source code must retain the above copyright notice,
+.\" *   this list of conditions and the following disclaimer.
+.\" * - Redistributions in binary form must reproduce the above copyright 
notice,
+.\" *   this list of conditions and the following disclaimer in the 
documentation
+.\" *   and/or other materials provided with the distribution.
+.\" * - Neither the name of Red Hat, Inc. nor the names of its
+.\" *   contributors may be used to endorse or promote products derived from 
this
+.\" *   software without specific prior written permission.
+.\" *
+.\" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
IS"
+.\" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
PURPOSE
+.\" * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+.\" * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+.\" * THE POSSIBILITY OF SUCH DAMAGE.
+.\" */
+.TH COROSYNC-NOTIFYD 8 2011-01-14
+.SH NAME
+corosync-notifyd \- Listen for important corosync events and send dbus and/or 
snmp traps.
+.SH SYNOPSIS
+.B "corosync-notifyd [\-f] [\-l] [\-o] [\-s] [\-m] [manager] [\-d] [-h]"
+.SH DESCRIPTION
+.B corosync-notifyd
+uses corosync API to listen for important cluster events and can log them,
+generate dbus signals or genterate snmp traps.
+.SH OPTIONS
+.TP
+.B -f
+Start application in foreground.
+.TP
+.B -l
+Log all events.
+.TP
+.B -o
+Print events to stdout (turns on -l).
+.TP
+.B -s
+Send SNMP traps on all events.
+.TP
+.B -m
+Set the SNMP manager address.
+.TP
+.B -d
+Send DBUS signals on all events.
+.TP
+.B -h
+Print this help
+.SH EXAMPLES
+.br
+$ corosync-notifyd -o
+.br
+corosync-notifyd[18505]: troll[23374016] corosync-notify:18505:12 is now 
connected to corosync
+.br
+corosync-notifyd[18505]: troll[23374016] corosync-notify:18505:13 is now 
disconnected from corosync
+.br
+corosync-notifyd[18505]: troll[23374016] is now quorate
+.br
+corosync-notifyd[18505]: r2[1550100672] ip:192.168.100.92 joined
+.br
+corosync-notifyd[18505]: r2[1550100672] ip:192.168.100.92 left
+.br
+
+.br
+$ corosync-notifyd -o
+.br
+
+Note this output below is from "dbus-monitor --system"
+
+.br
+signal sender=:1.216 -> dest=(null destination) serial=2 
path=/com/redhat/cluster/corosync;
+ interface=com.redhat.cluster.corosync; member=ConnectionStateChange
+.br
+   string "troll"
+.br
+   uint32 23374016
+.br
+   string "corosync-notify:18900:12"
+.br
+   string "connected"
+.br
+signal sender=:1.216 -> dest=(null destination) serial=3 
path=/com/redhat/cluster/corosync;
+ interface=com.redhat.cluster.corosync; member=ConnectionStateChange
+.br
+   string "troll"
+.br
+   uint32 23374016
+.br
+   string "corosync-notify:18900:13"
+.br
+   string "disconnected"
+.br
+signal sender=:1.216 -> dest=(null destination) serial=4 
path=/com/redhat/cluster/corosync;
+ interface=com.redhat.cluster.corosync; member=QorumStateChange
+.br
+   string "troll"
+.br
+   uint32 23374016
+.br
+   string "quorate"
+.br
+signal sender=:1.216 -> dest=(null destination) serial=5 
path=/com/redhat/cluster/corosync;
+ interface=com.redhat.cluster.corosync; member=NodeStateChange
+.br
+   string "r2"
+.br
+   uint32 1550100672
+.br
+   string "192.168.100.92"
+.br
+   string "joined"
+.br
+signal sender=:1.216 -> dest=(null destination) serial=6 
path=/com/redhat/cluster/corosync;
+ interface=com.redhat.cluster.corosync; member=NodeStateChange
+.br
+   string "r2"
+.br
+   uint32 1550100672
+.br
+   string "192.168.100.92"
+.br
+   string "left"
+.SH SEE ALSO
+.BR corosync (8),
+.BR corosync-objctl (8),
+.BR dbus-monitor (1),
+.SH AUTHOR
+Angus Salkeld
+.PP
diff --git a/tools/.gitignore b/tools/.gitignore
index aa2eb2e..0dc0f7e 100644
--- a/tools/.gitignore
+++ b/tools/.gitignore
@@ -5,3 +5,4 @@ corosync-keygen
 corosync-objctl
 corosync-pload
 corosync-quorumtool
+corosync-notifyd
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 3a52f4a..2e97185 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -34,7 +34,8 @@ INCLUDES                      = -I$(top_builddir)/include  
-I$(top_srcdir)/include
 
 sbin_PROGRAMS          = corosync-fplay corosync-cfgtool \
                          corosync-keygen corosync-objctl \
-                         corosync-pload corosync-cpgtool corosync-quorumtool
+                         corosync-pload corosync-cpgtool corosync-quorumtool \
+                         corosync-notifyd
 
 bin_SCRIPTS            = corosync-blackbox
 
@@ -52,5 +53,14 @@ corosync_quorumtool_LDADD = -lconfdb -lcfg -lquorum \
                            -lvotequorum -lcoroipcc ../lcr/liblcr.a
 corosync_quorumtool_LDFLAGS = -L../lib
 
+corosync_notifyd_LDADD = -L../lib
+corosync_notifyd_LDFLAGS = -lcfg -lconfdb ../lcr/liblcr.a -lcoroipcc \
+                          ../exec/coropoll.o $(DBUS_LIBS) $(SNMPLIBS) \
+                          -lquorum
+corosync_notifyd_CPPFLAGS = $(DBUS_CFLAGS)
+
+
 lint:
        -splint $(LINT_FLAGS) $(CFLAGS) *.c
+
+
diff --git a/tools/corosync-notifyd.c b/tools/corosync-notifyd.c
new file mode 100644
index 0000000..d0565de
--- /dev/null
+++ b/tools/corosync-notifyd.c
@@ -0,0 +1,1035 @@
+/*
+ * Copyright (c) 2011 Red Hat
+ *
+ * All rights reserved.
+ *
+ * Author: Angus Salkeld <[email protected]>
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * - Neither the name of the MontaVista Software, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <poll.h>
+#include <signal.h>
+#include <syslog.h>
+
+#include <corosync/corotypes.h>
+#include <corosync/totem/coropoll.h>
+#include <corosync/confdb.h>
+#include <corosync/cfg.h>
+#include <corosync/quorum.h>
+
+/*
+ * generic declarations
+ */
+enum {
+       CS_NTF_LOG,
+       CS_NTF_STDOUT,
+       CS_NTF_SNMP,
+       CS_NTF_DBUS,
+       CS_NTF_FG,
+       CS_NTF_MAX,
+};
+static int conf[CS_NTF_MAX];
+
+typedef void (*node_membership_fn_t)(char *nodename, uint32_t nodeid, char 
*state, char* ip);
+typedef void (*node_quorum_fn_t)(char *nodename, uint32_t nodeid, const char 
*state);
+typedef void (*application_connection_fn_t)(char *nodename, uint32_t nodeid, 
char *app_name, const char *state);
+
+struct notify_callbacks {
+       node_membership_fn_t node_membership_fn;
+       node_quorum_fn_t node_quorum_fn;
+       application_connection_fn_t application_connection_fn;
+};
+
+#define MAX_NOTIFIERS 5
+static int num_notifiers = 0;
+static struct notify_callbacks notifiers[MAX_NOTIFIERS];
+static uint32_t local_nodeid = 0;
+static char local_nodename[CS_MAX_NAME_LENGTH];
+static hdb_handle_t poll_handle;
+static quorum_handle_t quorum_handle;
+
+static void _cs_node_membership_event(char *nodename, uint32_t nodeid, char 
*state, char* ip);
+static void _cs_node_quorum_event(const char *state);
+static void _cs_application_connection_event(char *app_name, const char 
*state);
+
+#ifdef HAVE_DBUS
+#include <dbus/dbus.h>
+/*
+ * dbus/foghorn
+ */
+#define DBUS_CS_NAME   "com.redhat.cluster.corosync"
+#define DBUS_CS_IFACE  "com.redhat.cluster.corosync"
+#define DBUS_CS_PATH   "/com/redhat/cluster/corosync"
+
+static DBusConnection *db = NULL;
+static char _err[512];
+static int err_set = 0;
+static void _cs_dbus_init(void);
+#endif /* HAVE_DBUS */
+
+#ifdef ENABLE_SNMP
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/snmpv3_api.h>
+#include <net-snmp/agent/agent_trap.h>
+#include <net-snmp/library/mib.h>
+#include <net-snmp/library/snmp_api.h>
+#include <net-snmp/library/snmp_client.h>
+#include <net-snmp/library/snmp_debug.h>
+
+enum snmp_node_status {
+       SNMP_NODE_STATUS_UNKNOWN = 0,
+       SNMP_NODE_STATUS_JOINED = 1,
+       SNMP_NODE_STATUS_LEFT = 2
+};
+
+#define SNMP_OID_COROSYNC "1.3.6.1.4.1.35488"
+#define SNMP_OID_NOTICE_ROOT SNMP_OID_COROSYNC ".1"
+#define SNMP_OID_NOTICE_NODE_TABLE  SNMP_OID_NOTICE_ROOT ".1"
+#define SNMP_OID_NOTICE_NODE_ENTRY  SNMP_OID_NOTICE_NODE_TABLE ".1"
+#define SNMP_OID_NOTICE_NODE_INDEX  SNMP_OID_NOTICE_NODE_ENTRY ".1"
+#define SNMP_OID_NOTICE_NODE_ID     SNMP_OID_NOTICE_NODE_ENTRY ".2"
+#define SNMP_OID_NOTICE_NODE        SNMP_OID_NOTICE_NODE_ENTRY ".3"
+#define SNMP_OID_NOTICE_NODE_STATE  SNMP_OID_NOTICE_NODE_ENTRY ".4"
+
+#define SNMP_OID_TRAPS_ROOT  SNMP_OID_COROSYNC ".100"
+#define SNMP_OID_TRAPS_NODE  SNMP_OID_TRAPS_ROOT ".1"
+
+static const char *local_host = "localhost";
+#endif /* ENABLE_SNMP */
+static char snmp_manager_buf[CS_MAX_NAME_LENGTH];
+static char *snmp_manager = NULL;
+
+
+/*
+ * confdb
+ */
+#define SEPERATOR_STR "."
+
+static confdb_handle_t confdb_handle;
+
+static void _cs_confdb_key_changed(confdb_handle_t handle,
+       confdb_change_type_t change_type,
+       hdb_handle_t parent_object_handle,
+       hdb_handle_t object_handle,
+       const void *object_name, size_t object_name_len,
+       const void *key_name, size_t key_name_len,
+       const void *key_value, size_t key_value_len);
+
+static void _cs_confdb_object_created(confdb_handle_t handle,
+       hdb_handle_t parent_object_handle,
+       hdb_handle_t object_handle,
+       const void *name_pt, size_t name_len);
+
+static void _cs_confdb_object_deleted(confdb_handle_t handle,
+       hdb_handle_t parent_object_handle,
+       const void *name_pt, size_t name_len);
+
+static confdb_callbacks_t callbacks = {
+       .confdb_key_change_notify_fn = _cs_confdb_key_changed,
+       .confdb_object_create_change_notify_fn = _cs_confdb_object_created,
+       .confdb_object_delete_change_notify_fn = _cs_confdb_object_deleted,
+};
+
+static int32_t _cs_ip_to_hostname(char* ip, char* name_out)
+{
+       struct sockaddr_in sa;
+       int rc;
+
+       if (strchr(ip, ':') == NULL) {
+               sa.sin_family = AF_INET;
+       } else {
+               sa.sin_family = AF_INET6;
+       }
+
+       rc = inet_pton(sa.sin_family, ip, &sa.sin_addr);
+       if (rc == 0) {
+               return -EINVAL;
+       }
+
+       rc = getnameinfo((struct sockaddr*)&sa, sizeof(sa),
+                       name_out, CS_MAX_NAME_LENGTH, NULL, 0, 0);
+       if (rc != 0) {
+               syslog (LOG_ERR, "error looking up %s : %s\n", ip, 
gai_strerror(rc));
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void
+_cs_confdb_key_changed(confdb_handle_t handle,
+       confdb_change_type_t change_type,
+       hdb_handle_t parent_object_handle,
+       hdb_handle_t object_handle,
+       const void *object_name_pt, size_t  object_name_len,
+       const void *key_name_pt, size_t key_name_len,
+       const void *key_value_pt, size_t key_value_len)
+{
+       char parent_name[CS_MAX_NAME_LENGTH];
+       size_t len = 0;
+       hdb_handle_t real_parent_object_handle;
+       cs_error_t rc = CS_OK;
+       char nodename[CS_MAX_NAME_LENGTH];
+       char nodeid_str[CS_MAX_NAME_LENGTH];
+       uint32_t nodeid;
+       char status[CS_MAX_NAME_LENGTH];
+       char ip[CS_MAX_NAME_LENGTH];
+       size_t ip_len;
+       confdb_value_types_t type;
+       char* open_bracket = NULL;
+       char* close_bracket = NULL;
+
+       rc = confdb_object_parent_get (handle,
+               parent_object_handle, &real_parent_object_handle);
+       assert(rc == CS_OK);
+
+       rc = confdb_object_name_get (handle,
+               real_parent_object_handle,
+               parent_name,
+               &len);
+       parent_name[len] = '\0';
+       assert(rc == CS_OK);
+
+       if (strcmp(parent_name, "members") == 0) {
+               if (strncmp(key_name_pt, "status", strlen("status")) == 0) {
+
+                       memcpy(nodeid_str, object_name_pt, object_name_len);
+                       nodeid_str[object_name_len] = '\0';
+                       nodeid = atoi(nodeid_str);
+
+                       memcpy(status, key_value_pt, key_value_len);
+                       status[key_value_len] = '\0';
+
+                       rc = confdb_key_get_typed(handle, parent_object_handle,
+                               "ip", ip, &ip_len, &type);
+                       assert(rc == CS_OK);
+                       ip[ip_len-1] = '\0';
+
+                       /*
+                        * We want the ip out of: "r(0) ip(192.168.100.92)"
+                        */
+                       open_bracket = strrchr(ip, '(');
+                       open_bracket++;
+                       close_bracket = strrchr(open_bracket, ')');
+                       *close_bracket = '\0';
+                       _cs_ip_to_hostname(open_bracket, nodename);
+
+                       _cs_node_membership_event(nodename, nodeid, status, 
open_bracket);
+               }
+       }
+}
+
+static void
+_cs_confdb_object_created(confdb_handle_t handle,
+       hdb_handle_t parent_object_handle,
+       hdb_handle_t object_handle,
+       const void *name_pt,
+       size_t name_len)
+{
+       char parent_name[CS_MAX_NAME_LENGTH];
+       size_t len = 0;
+       char obj_name[CS_MAX_NAME_LENGTH];
+       hdb_handle_t real_parent_object_handle;
+       cs_error_t rc = CS_OK;
+
+       memcpy(obj_name, name_pt, name_len);
+       obj_name[name_len] = '\0';
+
+       rc = confdb_object_parent_get (handle,
+               parent_object_handle, &real_parent_object_handle);
+       if (rc != CS_OK) {
+               /* this error is normally from our own cfg connection
+                * which is short lived.
+                */
+               return;
+       }
+
+       rc = confdb_object_name_get (handle,
+               real_parent_object_handle, parent_name, &len);
+       parent_name[len] = '\0';
+       assert(rc == CS_OK);
+
+       if (strcmp(parent_name, "connections") == 0) {
+               _cs_application_connection_event(obj_name, "connected");
+       }
+}
+
+static void
+_cs_confdb_object_deleted(confdb_handle_t handle,
+       hdb_handle_t parent_object_handle,
+       const void *name_pt,
+       size_t name_len)
+{
+       char obj_name[CS_MAX_NAME_LENGTH];
+       char parent_name[CS_MAX_NAME_LENGTH];
+       size_t len = 0;
+       cs_error_t rc;
+
+       memcpy(obj_name, name_pt, name_len);
+       obj_name[name_len] = '\0';
+
+       rc = confdb_object_name_get (handle,
+               parent_object_handle, parent_name, &len);
+       parent_name[len] = '\0';
+       assert(rc == CS_OK);
+
+       if (strcmp(parent_name, "connections") == 0) {
+               _cs_application_connection_event(obj_name, "disconnected");
+       }
+}
+
+static cs_error_t
+_cs_confdb_find_object (confdb_handle_t handle,
+       const char * name_pt,
+       hdb_handle_t * out_handle)
+{
+       char * obj_name_pt;
+       char * save_pt;
+       hdb_handle_t obj_handle;
+       confdb_handle_t parent_object_handle = OBJECT_PARENT_HANDLE;
+       char tmp_name[CS_MAX_NAME_LENGTH];
+       cs_error_t res = CS_OK;
+
+       strncpy (tmp_name, name_pt, CS_MAX_NAME_LENGTH);
+       obj_name_pt = strtok_r(tmp_name, SEPERATOR_STR, &save_pt);
+
+       while (obj_name_pt != NULL) {
+               res = confdb_object_find_start(handle, parent_object_handle);
+               if (res != CS_OK) {
+                       syslog (LOG_ERR, "Could not start object_find %d\n", 
res);
+                       exit (EXIT_FAILURE);
+               }
+
+               res = confdb_object_find(handle, parent_object_handle,
+                               obj_name_pt, strlen (obj_name_pt), &obj_handle);
+               if (res != CS_OK) {
+                       return res;
+               }
+
+               parent_object_handle = obj_handle;
+               obj_name_pt = strtok_r (NULL, SEPERATOR_STR, &save_pt);
+       }
+
+       *out_handle = parent_object_handle;
+       return res;
+}
+
+static int
+_cs_confdb_dispatch(hdb_handle_t handle,
+       int fd, int revents, void *data)
+{
+       confdb_dispatch(confdb_handle, CONFDB_DISPATCH_ALL);
+       return 0;
+}
+
+static void _cs_quorum_notification(quorum_handle_t handle,
+       uint32_t quorate, uint64_t ring_seq,
+       uint32_t view_list_entries, uint32_t *view_list)
+{
+       if (quorate) {
+               _cs_node_quorum_event("quorate");
+       } else {
+               _cs_node_quorum_event("not quorate");
+       }
+}
+
+static int
+_cs_quorum_dispatch(hdb_handle_t handle,
+       int fd, int revents, void *data)
+{
+       quorum_dispatch(quorum_handle, CS_DISPATCH_ALL);
+       return 0;
+}
+
+static void
+_cs_quorum_init(void)
+{
+       cs_error_t rc;
+       int fd;
+
+       quorum_callbacks_t quorum_callbacks = {
+               .quorum_notify_fn = _cs_quorum_notification,
+       };
+
+       rc = quorum_initialize (&quorum_handle, &quorum_callbacks);
+       if (rc != CS_OK) {
+               syslog(LOG_ERR, "Could not connect to corosync(quorum)");
+               return;
+       }
+       quorum_fd_get(quorum_handle, &fd);
+       poll_dispatch_add (poll_handle, fd, POLLIN|POLLNVAL, NULL,
+               _cs_quorum_dispatch);
+       quorum_trackstart(quorum_handle, CS_TRACK_CHANGES);
+}
+
+static void
+_cs_quorum_finalize(void)
+{
+       quorum_finalize (quorum_handle);
+}
+
+
+#ifdef HAVE_DBUS
+/*
+ * Foghorn notifications
+ */
+static void
+_cs_dbus_auto_flush(void)
+{
+       dbus_connection_ref(db);
+       dbus_connection_read_write(db, 500);
+       dbus_connection_unref(db);
+}
+
+static void
+_cs_dbus_release(void)
+{
+       DBusError err;
+
+       if (!db)
+               return;
+
+       dbus_error_init(&err);
+       dbus_bus_release_name(db, DBUS_CS_NAME, &err);
+       dbus_error_free(&err);
+       dbus_connection_unref(db);
+       db = NULL;
+}
+
+static void
+_cs_dbus_node_quorum_event(char *nodename, uint32_t nodeid, const char *state)
+{
+       DBusMessage *msg = NULL;
+       int ret = -1;
+
+       if (err_set) {
+               syslog (LOG_ERR, "%s\n", _err);
+               err_set = 0;
+       }
+
+       if (!db) {
+               goto out_free;
+       }
+
+       if (dbus_connection_get_is_connected(db) != TRUE) {
+               err_set = 1;
+               snprintf(_err, sizeof(_err), "DBus connection lost");
+               _cs_dbus_release();
+               goto out_unlock;
+       }
+
+       _cs_dbus_auto_flush();
+
+       if (!(msg = dbus_message_new_signal(DBUS_CS_PATH,
+                                           DBUS_CS_IFACE,
+                                           "QorumStateChange"))) {
+               syslog (LOG_ERR, "%s(%d) error\n", __func__, __LINE__);
+               goto out_unlock;
+       }
+
+       if (!dbus_message_append_args(msg,
+                       DBUS_TYPE_STRING, &nodename,
+                       DBUS_TYPE_UINT32, &nodeid,
+                       DBUS_TYPE_STRING, &state,
+                       DBUS_TYPE_INVALID)) {
+               syslog (LOG_ERR, "%s(%d) error\n", __func__, __LINE__);
+               goto out_unlock;
+       }
+
+       dbus_connection_send(db, msg, NULL);
+       ret = 0;
+
+out_unlock:
+       if (ret == -1) {
+               syslog (LOG_ERR, "%s() error\n", __func__);
+       }
+       if (msg)
+               dbus_message_unref(msg);
+out_free:
+       return;
+}
+
+static void
+_cs_dbus_node_membership_event(char *nodename, uint32_t nodeid, char *state, 
char* ip)
+{
+       DBusMessage *msg = NULL;
+       int ret = -1;
+
+       if (err_set) {
+               syslog (LOG_ERR, "%s\n", _err);
+               err_set = 0;
+       }
+
+       if (!db) {
+               goto out_free;
+       }
+
+       if (dbus_connection_get_is_connected(db) != TRUE) {
+               err_set = 1;
+               snprintf(_err, sizeof(_err), "DBus connection lost");
+               _cs_dbus_release();
+               goto out_unlock;
+       }
+
+       _cs_dbus_auto_flush();
+
+       if (!(msg = dbus_message_new_signal(DBUS_CS_PATH,
+                                           DBUS_CS_IFACE,
+                                           "NodeStateChange"))) {
+               syslog (LOG_ERR, "%s(%d) error\n", __func__, __LINE__);
+               goto out_unlock;
+       }
+
+       if (!dbus_message_append_args(msg,
+                       DBUS_TYPE_STRING, &nodename,
+                       DBUS_TYPE_UINT32, &nodeid,
+                       DBUS_TYPE_STRING, &ip,
+                       DBUS_TYPE_STRING, &state,
+                       DBUS_TYPE_INVALID)) {
+               syslog (LOG_ERR, "%s(%d) error\n", __func__, __LINE__);
+               goto out_unlock;
+       }
+
+       dbus_connection_send(db, msg, NULL);
+       ret = 0;
+
+out_unlock:
+       if (ret == -1) {
+               syslog (LOG_ERR, "%s() error\n", __func__);
+       }
+       if (msg)
+               dbus_message_unref(msg);
+out_free:
+       return;
+}
+
+static void
+_cs_dbus_application_connection_event(char *nodename, uint32_t nodeid, char 
*app_name, const char *state)
+{
+       DBusMessage *msg = NULL;
+       int ret = -1;
+
+       if (err_set) {
+               syslog (LOG_ERR, "%s\n", _err);
+               err_set = 0;
+       }
+
+       if (!db) {
+               goto out_free;
+       }
+
+       if (dbus_connection_get_is_connected(db) != TRUE) {
+               err_set = 1;
+               snprintf(_err, sizeof(_err), "DBus connection lost");
+               _cs_dbus_release();
+               goto out_unlock;
+       }
+
+       _cs_dbus_auto_flush();
+
+       if (!(msg = dbus_message_new_signal(DBUS_CS_PATH,
+                               DBUS_CS_IFACE,
+                               "ConnectionStateChange"))) {
+               syslog (LOG_ERR, "%s(%d) error\n", __func__, __LINE__);
+               goto out_unlock;
+       }
+
+       if (!dbus_message_append_args(msg,
+                       DBUS_TYPE_STRING, &nodename,
+                       DBUS_TYPE_UINT32, &nodeid,
+                       DBUS_TYPE_STRING, &app_name,
+                       DBUS_TYPE_STRING, &state,
+                       DBUS_TYPE_INVALID)) {
+               syslog (LOG_ERR, "%s(%d) error\n", __func__, __LINE__);
+               goto out_unlock;
+       }
+
+       dbus_connection_send(db, msg, NULL);
+       ret = 0;
+
+out_unlock:
+       if (msg)
+               dbus_message_unref(msg);
+out_free:
+       return;
+}
+
+static void
+_cs_dbus_init(void)
+{
+       DBusConnection *dbc = NULL;
+       DBusError err;
+
+       dbus_error_init(&err);
+
+       dbc = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+       if (!dbc) {
+               snprintf(_err, sizeof(_err),
+                        "dbus_bus_get: %s", err.message);
+               err_set = 1;
+               dbus_error_free(&err);
+               return;
+       }
+
+       dbus_connection_set_exit_on_disconnect(dbc, FALSE);
+
+       db = dbc;
+
+       notifiers[num_notifiers].node_membership_fn =
+               _cs_dbus_node_membership_event;
+       notifiers[num_notifiers].node_quorum_fn =
+               _cs_dbus_node_quorum_event;
+       notifiers[num_notifiers].application_connection_fn =
+               _cs_dbus_application_connection_event;
+       num_notifiers++;
+}
+
+#endif /* HAVE_DBUS */
+
+#ifdef ENABLE_SNMP
+static netsnmp_session *snmp_init (const char *target)
+{
+       static netsnmp_session *session = NULL;
+#ifndef NETSNMPV54
+       char default_port[128];
+       snprintf (default_port, sizeof (default_port), "%s:162", target);
+#endif
+       if (session) {
+               return (session);
+       }
+
+       if (target == NULL) {
+               return NULL;
+       }
+
+       session = malloc (sizeof (netsnmp_session));
+       snmp_sess_init (session);
+       session->version = SNMP_VERSION_2c;
+       session->callback = NULL;
+       session->callback_magic = NULL;
+
+       session = snmp_add(session,
+#ifdef NETSNMPV54
+               netsnmp_transport_open_client ("snmptrap", target),
+#else
+               netsnmp_tdomain_transport (default_port, 0, "udp"),
+#endif
+               NULL, NULL);
+
+       if (session == NULL) {
+               syslog(LOG_ERR, "Could not create snmp transport");
+       }
+       return (session);
+}
+
+static inline void add_field (
+       netsnmp_pdu *trap_pdu,
+       u_char asn_type,
+       const char *prefix,
+       void *value,
+       size_t value_size)
+{
+       oid _oid[MAX_OID_LEN];
+       size_t _oid_len = MAX_OID_LEN;
+       if (snmp_parse_oid(prefix, _oid, &_oid_len)) {
+               snmp_pdu_add_variable (trap_pdu, _oid, _oid_len, asn_type, 
(u_char *) value, value_size);
+       }
+}
+
+static void
+_cs_snmp_node_membership_event(char *nodename, uint32_t nodeid, char *state, 
char* ip)
+{
+       int ret;
+       char csysuptime[20];
+       static oid snmptrap_oid[]  = { 1,3,6,1,6,3,1,1,4,1,0 };
+       static oid sysuptime_oid[] = { 1,3,6,1,2,1,1,3,0 };
+       time_t now = time (NULL);
+       int node_status;
+
+       netsnmp_pdu *trap_pdu;
+       netsnmp_session *session = snmp_init (snmp_manager);
+       if (session == NULL) {
+               syslog (LOG_NOTICE, "Failed to init SNMP session.\n");
+               return ;
+       }
+
+       trap_pdu = snmp_pdu_create (SNMP_MSG_TRAP2);
+       if (!trap_pdu) {
+               syslog (LOG_NOTICE, "Failed to create SNMP notification.\n");
+               return ;
+       }
+
+       if (strcmp(state, "joined") == 0) {
+               node_status = SNMP_NODE_STATUS_JOINED;
+       } else if (strcmp(state, "left") == 0) {
+               node_status = SNMP_NODE_STATUS_LEFT;
+       } else {
+               node_status = SNMP_NODE_STATUS_UNKNOWN;
+       }
+
+       /* send uptime */
+       sprintf (csysuptime, "%ld", now);
+       snmp_add_var (trap_pdu, sysuptime_oid, sizeof (sysuptime_oid) / sizeof 
(oid), 't', csysuptime);
+       snmp_add_var (trap_pdu, snmptrap_oid, sizeof (snmptrap_oid) / sizeof 
(oid), 'o', SNMP_OID_TRAPS_NODE);
+
+       /* Add extries to the trap */
+       add_field (trap_pdu, ASN_INTEGER, SNMP_OID_NOTICE_NODE_ID, 
(void*)&nodeid, sizeof (nodeid));
+       add_field (trap_pdu, ASN_OCTET_STR, SNMP_OID_NOTICE_NODE, (void*)ip, 
strlen (ip));
+       add_field (trap_pdu, ASN_INTEGER, SNMP_OID_NOTICE_NODE_STATE, 
(void*)&node_status, sizeof (node_status));
+
+       /* Send and cleanup */
+       ret = snmp_send (session, trap_pdu);
+       if (ret == 0) {
+               /* error */
+               syslog (LOG_ERR, "Could not send SNMP trap");
+               snmp_free_pdu (trap_pdu);
+       }
+}
+
+static void
+_cs_snmp_init(void)
+{
+       if (snmp_manager == NULL) {
+               snmp_manager = (char*)local_host;
+       }
+
+       notifiers[num_notifiers].node_membership_fn =
+               _cs_snmp_node_membership_event;
+       notifiers[num_notifiers].node_quorum_fn = NULL;
+       notifiers[num_notifiers].application_connection_fn = NULL;
+       num_notifiers++;
+}
+
+#endif /* ENABLE_SNMP */
+
+static void
+_cs_syslog_node_membership_event(char *nodename, uint32_t nodeid, char *state, 
char* ip)
+{
+       syslog (LOG_NOTICE, "%s[%d] ip:%s %s\n", nodename, nodeid, ip, state);
+}
+
+static void
+_cs_syslog_node_quorum_event(char *nodename, uint32_t nodeid, const char 
*state)
+{
+       if (strcmp(state, "quorate") == 0) {
+               syslog (LOG_NOTICE, "%s[%d] is now %s\n", nodename, nodeid, 
state);
+       } else {
+               syslog (LOG_NOTICE, "%s[%d] has lost quorum\n", nodename, 
nodeid);
+       }
+}
+
+static void
+_cs_syslog_application_connection_event(char *nodename, uint32_t nodeid, char* 
app_name, const char *state)
+{
+       if (strcmp(state, "connected") == 0) {
+               syslog (LOG_ERR, "%s[%d] %s is now %s to corosync\n", nodename, 
nodeid, app_name, state);
+       } else {
+               syslog (LOG_ERR, "%s[%d] %s is now %s from corosync\n", 
nodename, nodeid, app_name, state);
+       }
+}
+
+static void
+_cs_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* 
ip)
+{
+       int i;
+
+       for (i = 0; i < num_notifiers; i++) {
+               if (notifiers[i].node_membership_fn) {
+                       notifiers[i].node_membership_fn(nodename, nodeid, 
state, ip);
+               }
+       }
+}
+
+static void
+_cs_local_node_info_get(char **nodename, uint32_t *nodeid)
+{
+       cs_error_t rc;
+       corosync_cfg_handle_t cfg_handle;
+
+       if (local_nodeid == 0) {
+               corosync_cfg_initialize(&cfg_handle, NULL);
+               rc = corosync_cfg_local_get (cfg_handle, &local_nodeid);
+               corosync_cfg_finalize(cfg_handle);
+               if (rc != CS_OK) {
+                       local_nodeid = 0;
+                       strcpy(local_nodename, "localhost");
+               } else {
+                       gethostname(local_nodename, CS_MAX_NAME_LENGTH);
+               }
+       }
+       *nodeid = local_nodeid;
+       *nodename = local_nodename;
+}
+
+static void
+_cs_node_quorum_event(const char *state)
+{
+       int i;
+       char *nodename;
+       uint32_t nodeid;
+
+       _cs_local_node_info_get(&nodename, &nodeid);
+
+       for (i = 0; i < num_notifiers; i++) {
+               if (notifiers[i].node_quorum_fn) {
+                       notifiers[i].node_quorum_fn(nodename, nodeid, state);
+               }
+       }
+}
+
+static void
+_cs_application_connection_event(char *app_name, const char *state)
+{
+       int i;
+       char *nodename;
+       uint32_t nodeid;
+
+       _cs_local_node_info_get(&nodename, &nodeid);
+
+       for (i = 0; i < num_notifiers; i++) {
+               if (notifiers[i].application_connection_fn) {
+                       notifiers[i].application_connection_fn(nodename, 
nodeid, app_name, state);
+               }
+       }
+}
+
+static void
+sig_exit_handler (int num)
+{
+       poll_stop(poll_handle);
+}
+
+static void
+_cs_confdb_init(void)
+{
+       hdb_handle_t obj_handle;
+       cs_error_t rc;
+       int conf_fd = 0;
+
+       rc = confdb_initialize (&confdb_handle, &callbacks);
+       if (rc != CS_OK) {
+               syslog (LOG_ERR, "Failed to initialize the objdb API. Error 
%d\n", rc);
+               exit (EXIT_FAILURE);
+       }
+       confdb_fd_get(confdb_handle, &conf_fd);
+
+       poll_dispatch_add (poll_handle, conf_fd, POLLIN|POLLNVAL, NULL,
+               _cs_confdb_dispatch);
+
+       rc = _cs_confdb_find_object (confdb_handle, "runtime.connections.",
+               &obj_handle);
+       if (rc != CS_OK) {
+               syslog (LOG_ERR,
+                       "Failed to find the connections object. Error %d\n", 
rc);
+               exit (EXIT_FAILURE);
+       }
+
+       rc = confdb_track_changes (confdb_handle, obj_handle,
+               CONFDB_TRACK_DEPTH_ONE);
+       if (rc != CS_OK) {
+               syslog (LOG_ERR,
+                       "Failed to track the connections object. Error %d\n", 
rc);
+               exit (EXIT_FAILURE);
+       }
+       rc = _cs_confdb_find_object(confdb_handle,
+               "runtime.totem.pg.mrp.srp.members.", &obj_handle);
+       if (rc != CS_OK) {
+               syslog (LOG_ERR, "Failed to find the object. Error %d\n", rc);
+               exit (EXIT_FAILURE);
+       }
+
+       rc = confdb_track_changes(confdb_handle,
+               obj_handle, CONFDB_TRACK_DEPTH_RECURSIVE);
+       if (rc != CS_OK) {
+               syslog (LOG_ERR,
+                       "Failed to track the object. Error %d\n", rc);
+               exit (EXIT_FAILURE);
+       }
+}
+
+static void
+_cs_confdb_finalize(void)
+{
+       confdb_stop_track_changes (confdb_handle);
+       confdb_finalize (confdb_handle);
+}
+
+static void
+_cs_check_config(void)
+{
+       if (conf[CS_NTF_LOG] == 0 &&
+               conf[CS_NTF_STDOUT] == 0 &&
+               conf[CS_NTF_SNMP] == 0 &&
+               conf[CS_NTF_DBUS] == 0) {
+               syslog(LOG_ERR, "no event type enabled, see corosync-notifyd 
-h, exiting.");
+               exit(EXIT_FAILURE);
+       }
+
+#ifndef ENABLE_SNMP
+       syslog(LOG_ERR, "Not compiled with Snmp support enabled, exiting.");
+       exit(EXIT_FAILURE);
+#endif
+#ifndef HAVE_DBUS
+       syslog(LOG_ERR, "Not compiled with Dbus support enabled, exiting.");
+       exit(EXIT_FAILURE);
+#endif
+
+       if (conf[CS_NTF_STDOUT] && !conf[CS_NTF_FG]) {
+               syslog(LOG_ERR, "configured to print to stdout and run in the 
background, exiting");
+               exit(EXIT_FAILURE);
+       }
+       if (conf[CS_NTF_SNMP] && conf[CS_NTF_DBUS]) {
+               syslog(LOG_ERR, "configured to send snmp traps and dbus signals 
- are you sure?.");
+       }
+}
+
+static void
+_cs_usage(void)
+{
+       fprintf(stderr, "usage:\n"\
+               "        -f     : Start application in foreground.\n"\
+               "        -l     : Log all events.\n"\
+               "        -o     : Print events to stdout (turns on -l).\n"\
+               "        -s     : Send SNMP traps on all events.\n"\
+               "        -m     : SNMP Manager IP address (defaults to 
localhost).\n"\
+               "        -d     : Send DBUS signals on all events.\n"\
+               "        -h     : Print this help\n\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+       int ch;
+
+       conf[CS_NTF_FG] = 0;
+       conf[CS_NTF_LOG] = 0;
+       conf[CS_NTF_STDOUT] = 0;
+       conf[CS_NTF_SNMP] = 0;
+       conf[CS_NTF_DBUS] = 0;
+
+       while ((ch = getopt (argc, argv, "floshdm:")) != EOF) {
+               switch (ch) {
+                       case 'f':
+                               conf[CS_NTF_FG] = 1;
+                               break;
+                       case 'l':
+                               conf[CS_NTF_LOG] = 1;
+                               break;
+                       case 'm':
+                               conf[CS_NTF_SNMP] = 1;
+                               strcpy(snmp_manager_buf, optarg);
+                               snmp_manager = snmp_manager_buf;
+                               break;
+                       case 'o':
+                               conf[CS_NTF_LOG] = 1;
+                               conf[CS_NTF_STDOUT] = 1;
+                               break;
+                       case 's':
+                               conf[CS_NTF_SNMP] = 1;
+                               break;
+                       case 'd':
+                               conf[CS_NTF_DBUS] = 1;
+                               break;
+                       case 'h':
+                       default:
+                               _cs_usage();
+                               return EXIT_FAILURE;
+               }
+       }
+
+       if (conf[CS_NTF_STDOUT]) {
+               openlog(NULL, LOG_PID|LOG_PERROR, LOG_DAEMON);
+       } else {
+               openlog(NULL, LOG_PID, LOG_DAEMON);
+       }
+       _cs_check_config();
+
+       if (!conf[CS_NTF_FG]) {
+               daemon(0, 0);
+       }
+
+       num_notifiers = 0;
+       if (conf[CS_NTF_LOG]) {
+               notifiers[num_notifiers].node_membership_fn =
+                       _cs_syslog_node_membership_event;
+               notifiers[num_notifiers].node_quorum_fn =
+                       _cs_syslog_node_quorum_event;
+               notifiers[num_notifiers].application_connection_fn =
+                       _cs_syslog_application_connection_event;
+               num_notifiers++;
+       }
+
+       poll_handle = poll_create();
+
+       _cs_confdb_init();
+       _cs_quorum_init();
+
+#ifdef HAVE_DBUS
+       if (conf[CS_NTF_DBUS]) {
+               _cs_dbus_init();
+       }
+#endif /* HAVE_DBUS */
+
+#ifdef ENABLE_SNMP
+       if (conf[CS_NTF_SNMP]) {
+               _cs_snmp_init();
+       }
+#endif /* ENABLE_SNMP */
+
+       (void)signal (SIGINT, sig_exit_handler);
+       (void)signal (SIGQUIT, sig_exit_handler);
+       (void)signal (SIGTERM, sig_exit_handler);
+
+       poll_run(poll_handle);
+
+#ifdef HAVE_DBUS
+       if (conf[CS_NTF_DBUS]) {
+               _cs_dbus_release();
+       }
+#endif /* HAVE_DBUS */
+
+       _cs_quorum_finalize();
+       _cs_confdb_finalize();
+
+       return 0;
+}
+
-- 
1.7.3.4

_______________________________________________
Openais mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/openais

Reply via email to