cluster-agents-1.0.4/0000755000175000017500000000000011527051501014711 5ustar roaksoaxroaksoaxcluster-agents-1.0.4/GNUmakefile0000644000175000017500000000267611527003731017000 0ustar roaksoaxroaksoax# # Copyright (C) 2008 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # -include Makefile PACKAGE ?= resource-agents TARFILE ?= $(PACKAGE).tar.bz2 RPM_ROOT = $(shell pwd) RPM_OPTS = --define "_sourcedir $(RPM_ROOT)" \ --define "_specdir $(RPM_ROOT)" \ --define "_srcrpmdir $(RPM_ROOT)" \ getdistro = $(shell test -e /etc/SuSE-release || echo fedora; test -e /etc/SuSE-release && echo suse) DISTRO ?= $(call getdistro) TAG ?= tip hgarchive: rm -f $(TARFILE) hg archive -t tbz2 -r $(TAG) $(TARFILE) echo `date`: Rebuilt $(TARFILE) srpm: hgarchive rm -f *.src.rpm @echo To create custom builds, edit the flags and options in $(PACKAGE).spec first rpmbuild -bs --define "dist .$(DISTRO)" $(RPM_OPTS) $(PACKAGE).spec rpm: srpm rpmbuild --rebuild $(RPM_ROOT)/*.src.rpm cluster-agents-1.0.4/resource-agents.spec0000755000175000017500000001552511527003731020710 0ustar roaksoaxroaksoax# # Copyright (c) 2009 SUSE LINUX Products GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed # upon. The license for this file, and modifications and additions to the # file, is the same license as for the pristine package itself (unless the # license for the pristine package is not an Open Source License, in which # case the license is the MIT License). An "Open Source License" is a # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. # Please submit bugfixes or comments via http://bugs.opensuse.org/ # # norootforbuild # Directory where we install documentation %if 0%{?fedora} || 0%{?centos_version} || 0%{?rhel} %global agents_docdir %{_defaultdocdir}/%{name}-%{version} %endif %if 0%{?suse_version} %global agents_docdir %{_defaultdocdir}/%{name} %endif # # Since this spec file supports multiple distributions, ensure we # use the correct group for each. # %if 0%{?fedora} || 0%{?centos_version} || 0%{?rhel} %define pkg_group System Environment/Daemons %else %define pkg_group Productivity/Clustering/HA %endif %define SSLeay perl-Net-SSLeay %if 0%{?suse_version} %define SSLeay perl-Net_SSLeay %endif Name: resource-agents Summary: Reusable cluster resource scripts Version: 1.0.4 Release: 1%{?dist} License: GPL v2 or later; LGPL v2.1 or later Url: http://www.linux-ha.org Group: %{pkg_group} Source: resource-agents.tar.bz2 BuildRoot: %{_tmppath}/%{name}-%{version}-build AutoReqProv: on Obsoletes: heartbeat-resources Conflicts: heartbeat-resources BuildRequires: autoconf automake glib2-devel pkgconfig python-devel BuildRequires: help2man %if 0%{?suse_version} BuildRequires: libnet libglue-devel BuildRequires: libxslt docbook_4 docbook-xsl-stylesheets %endif %if 0%{?fedora} || 0%{?centos_version} || 0%{?rhel} BuildRequires: which cluster-glue-libs-devel BuildRequires: libxslt docbook-dtds docbook-style-xsl %endif %description Scripts to allow common services to operate in a High Availability environment. %package -n ldirectord License: GPL v2 or later Summary: A Monitoring Daemon for Maintaining High Availability Resources Group: Productivity/Clustering/HA Requires: %{SSLeay} perl-libwww-perl ipvsadm Provides: heartbeat-ldirectord Obsoletes: heartbeat-ldirectord Requires: perl-MailTools %if 0%{?suse_version} Requires: logrotate %endif %if 0%{?fedora_version} Requires(post): /sbin/chkconfig Requires(preun):/sbin/chkconfig %endif %description -n ldirectord The Linux Director Daemon (ldirectord) was written by Jacob Rief. ldirectord is a stand alone daemon for monitoring the services on real servers. Currently, HTTP, HTTPS, and FTP services are supported. lditrecord is simple to install and works with the heartbeat code (http://www.linux-ha.org/). See 'ldirectord -h' and linux-ha/doc/ldirectord for more information. %prep ########################################################### %setup -n resource-agents -q ########################################################### %build CFLAGS="${CFLAGS} ${RPM_OPT_FLAGS}" export CFLAGS ./autogen.sh %if 0%{?suse_version} >= 1020 || 0%{?fedora} >= 11 || 0%{?centos_version} > 5 || 0%{?rhel} > 5 %configure \ --enable-fatal-warnings=yes \ --with-package-name=%{name} \ --docdir=%{agents_docdir} %else export docdir=%{agents_docdir} %configure \ --enable-fatal-warnings=yes \ --with-package-name=%{name} %endif export MAKE="make %{?jobs:-j%jobs}" make %{?jobs:-j%jobs} ########################################################### %install ########################################################### make DESTDIR=$RPM_BUILD_ROOT install ( mkdir -p $RPM_BUILD_ROOT/etc/ha.d/resource.d ln -s %{_sbindir}/ldirectord $RPM_BUILD_ROOT/etc/ha.d/resource.d/ldirectord ) || true test -d $RPM_BUILD_ROOT/sbin || mkdir $RPM_BUILD_ROOT/sbin ( cd $RPM_BUILD_ROOT/sbin ln -sf /etc/init.d/ldirectord rcldirectord ) || true # Dont package static libs or compiled python find $RPM_BUILD_ROOT -name '*.a' -type f -print0 | xargs -0 rm -f find $RPM_BUILD_ROOT -name '*.la' -type f -print0 | xargs -0 rm -f find $RPM_BUILD_ROOT -name '*.pyc' -type f -print0 | xargs -0 rm -f find $RPM_BUILD_ROOT -name '*.pyo' -type f -print0 | xargs -0 rm -f # Unset execute permissions from things that shouln't have it find $RPM_BUILD_ROOT -name 'ocf-*' -type f -print0 | xargs -0 chmod a-x find $RPM_BUILD_ROOT -name '*.dtd' -type f -print0 | xargs -0 chmod a-x chmod 0755 $RPM_BUILD_ROOT/usr/sbin/ocf-tester chmod 0755 $RPM_BUILD_ROOT/usr/sbin/ocft ( cd $RPM_BUILD_ROOT/usr/lib/ocf/resource.d/heartbeat for f in ocf-binaries ocf-directories ocf-returncodes ocf-shellfuncs do ln -s ../../lib/heartbeat/$f .$f done ) ########################################################### %clean ########################################################### if [ -n "${RPM_BUILD_ROOT}" -a "${RPM_BUILD_ROOT}" != "/" ] then rm -rf $RPM_BUILD_ROOT fi rm -rf $RPM_BUILD_DIR/resource-agents ########################################################### %if 0%{?suse_version} %preun -n ldirectord %stop_on_removal ldirectord %postun -n ldirectord %insserv_cleanup %endif %if 0%{?fedora} %preun -n ldirectord /sbin/chkconfig --del ldirectord %postun -n ldirectord -p /sbin/ldconfig %post -n ldirectord /sbin/chkconfig --add ldirectord %endif %files ########################################################### %defattr(-,root,root) %dir /usr/lib/ocf %dir /usr/lib/ocf/resource.d %dir /usr/lib/ocf/lib %dir %{_datadir}/%{name} %dir %{_datadir}/%{name}/ocft %{_datadir}/%{name}/ocft/configs %{_datadir}/%{name}/ocft/caselib %{_datadir}/%{name}/ocft/README %{_datadir}/%{name}/ocft/README.zh_CN /usr/lib/ocf/resource.d/heartbeat /usr/lib/ocf/lib/heartbeat %{_sbindir}/ocf-tester %{_sbindir}/ocft %{_sbindir}/sfex_init %{_sbindir}/sfex_stat %{_includedir}/heartbeat %dir %attr (1755, root, root) %{_var}/run/resource-agents %doc AUTHORS %doc COPYING %doc COPYING.GPLv3 %doc ChangeLog %doc %{_datadir}/%{name}/ra-api-1.dtd %doc %{_mandir}/man7/*.7* %doc %{_mandir}/man8/ocf-tester.8* %doc doc/README.webapps # For compatability with pre-existing agents %dir /etc/ha.d /etc/ha.d/shellfuncs %{_libdir}/heartbeat/send_arp %{_libdir}/heartbeat/sfex_daemon %{_libdir}/heartbeat/findif %{_libdir}/heartbeat/tickle_tcp %files -n ldirectord ########################################################### %defattr(-,root,root) %doc ldirectord/ldirectord.cf %doc %{_mandir}/man8/ldirectord.8* %dir /etc/ha.d/resource.d #%doc %{_mandir}/man8/supervise-ldirectord-config.8* %{_sbindir}/ldirectord /sbin/rcldirectord #%{_sbindir}/supervise-ldirectord-config %{_sysconfdir}/init.d/ldirectord %{_sysconfdir}/ha.d/resource.d/ldirectord %config(noreplace) %{_sysconfdir}/logrotate.d/ldirectord %changelog cluster-agents-1.0.4/AUTHORS0000644000175000017500000000201111527003731015755 0ustar roaksoaxroaksoaxAuthors: -------- Akamatsu Hiroshi Alan Robertson Andrew Beekhof Christian Rishoj Daiki Matsuda David Lee Dejan Muhamedagic Dominik Klein Florian Haas Hideo Yamauchi Horms Huang Zhen Jean-Francois Larvoire Keisuke MORI Lars Marowsky-Bree Matthew Soffen Michael Schwartzkopff Nakahira Kazutomo Philipp Kolmann Raoul Bhatia Ron Terry Sebastian Reitenbach Serge Dubrouski Simon Horman Stephan Berlet Takenaka Kazuhiro Xinwei Hu cluster-agents-1.0.4/ldirectord/0000755000175000017500000000000011527051501017044 5ustar roaksoaxroaksoaxcluster-agents-1.0.4/ldirectord/ldirectord.cf0000644000175000017500000002015311527003731021514 0ustar roaksoaxroaksoax# # Sample ldirectord configuration file to configure various virtual services. # # Ldirectord will connect to each real server once per second and request # /index.html. If the data returned by the server does not contain the # string "Test Message" then the test fails and the real server will be # taken out of the available pool. The real server will be added back into # the pool once the test succeeds. If all real servers are removed from the # pool then localhost:80 is added to the pool as a fallback measure. # Global Directives checktimeout=3 checkinterval=1 #fallback=127.0.0.1:80 #fallback6=[::1]:80 autoreload=yes #logfile="/var/log/ldirectord.log" #logfile="local0" #emailalert="admin@x.y.z" #emailalertfreq=3600 #emailalertstatus=all quiescent=no # Sample for an http virtual service virtual=192.168.6.240:80 real=192.168.6.2:80 gate real=192.168.6.3:80 gate real=192.168.6.6:80 gate fallback=127.0.0.1:80 gate service=http scheduler=rr #persistent=600 #netmask=255.255.255.255 protocol=tcp checktype=negotiate checkport=80 request="index.html" receive="Test Page" virtualhost=www.x.y.z # Sample configuration for a fwmark based service For an explanation of # fwmark see the ipvsadm(8) man page #virtual=1 # real=192.168.6.2 gate # real=192.168.6.3 gate # real=192.168.6.6 gate # fallback=127.0.0.1:80 gate # service=http # scheduler=rr # #persistent=600 # #netmask=255.255.255.255 # protocol=fwm # checktype=negotiate # checkport=80 # request="index.html" # receive="Test Page" # virtualhost=x.y.z # Sample configuration for a service using a range of real servers # and a single real server for a virtual service #virtual=192.168.6.240:80 # real=192.168.6.2->192.168.6.7:80 gate # real=192.168.6.32:80 gate # fallback=127.0.0.1:80 gate # service=http # scheduler=rr # #persistent=600 # #netmask=255.255.255.255 # protocol=tcp # checktype=negotiate # checkport=80 # request="index.html" # receive="Test Page" # virtualhost=x.y.z #Sample configuration for an https virtual service. #Fallback setting overrides global #virtual=192.168.6.240:443 # real=192.168.16.3:443 masq # real=192.168.16.5:443 masq # fallback=127.0.0.1:443 # service=https # scheduler=rr # #persistent=600 # #netmask=255.255.255.255 # protocol=tcp # checktype=negotiate # checkport=443 # request="index.html" # receive="Test Page" # virtualhost=x.y.z #Sample configuration for an ftp virtual service. #Fallback setting overrides global #virtual=192.168.6.240:21 # real=192.168.16.3:21 masq # real=192.168.16.5:21 masq # fallback=127.0.0.1:21 # service=ftp # checkport=21 # scheduler=wlc # scheduler=rr # #persistent=600 # #netmask=255.255.255.255 # protocol=tcp # checktype=negotiate # login="anonymous" # passwd="ldirectord@localhost" # request="welcome.msg" # receive="test" #Sample configuration for an smtp virtual service. #Fallback setting overrides global #virtual=192.168.6.240:25 # real=192.168.16.3:25 masq # real=192.168.16.5:25 masq # fallback=127.0.0.1:25 # service=smtp # scheduler=wlc # protocol=tcp # persistent=600 # #netmask=255.255.255.255 # checktype=negotiate # checkport=25 #Sample configuration for an submission virtual service. #Fallback setting overrides global #virtual=192.168.6.240:587 # real=192.168.16.3:587 masq # real=192.168.16.5:587 masq # fallback=127.0.0.1:587 # service=submission # scheduler=wlc # protocol=tcp # persistent=600 # #netmask=255.255.255.255 # checktype=negotiate # checkport=587 #Sample configuration for a pop virtual service. #Fallback setting overrides global #virtual=192.168.6.240:110 # real=192.168.16.3:110 masq # real=192.168.16.5:110 masq # fallback=127.0.0.1:110 # service=pop # scheduler=wlc # persistent=600 # #netmask=255.255.255.255 # protocol=tcp # checktype=negotiate # checkport=110 # #login="test" # #passwd="test" ##Sample configuration for an imap virtual service. #Fallback setting overrides global #virtual=192.168.6.240:143 # real=127.0.0.1:143 masq # real=192.168.16.3:143 masq # real=192.168.16.5:143 masq # fallback=127.0.0.1:143 # service=imap # scheduler=wlc # #persistent=600 # #netmask=255.255.255.255 # protocol=tcp # checktype=negotiate # checkport=143 # #login="test" # #passwd="test" #Sample configuration for an ldap virtual service. #Fallback setting overrides global #virtual=192.168.84.5:389 # real=10.0.1.4:389 masq # real=10.0.1.6:389 masq # fallback=127.0.0.1:389 # service=ldap # scheduler=wlc # #persistent=600 # #netmask=255.255.255.255 # protocol=tcp # checktype=negotiate # checkport=369 # request="dc=upmc, dc=fr" # receive="dc=upmc, dc=fr" # #login="test" # #passwd="test" #Sample configuration for an nntp virtual service. #Fallback setting overrides global #virtual=192.168.84.5:119 # real=10.0.1.4:119 masq # real=10.0.1.6:119 masq # fallback=127.0.0.1:119 # service=nntp # scheduler=wlc # #persistent=600 # #netmask=255.255.255.255 # protocol=tcp # checktype=negotiate # checkport=119 #Sample configuration for a UDP DNS virtual service. #Fallback setting overrides global #virtual=192.168.84.5:53 # real=10.0.1.4:53 masq # real=10.0.1.6:53 masq # fallback=127.0.0.1:53 # service=dns # scheduler=wlc # #persistent=600 # #netmask=255.255.255.255 # protocol=udp # checktype=negotiate # checkport=53 # request="x.y.z" # receive="127.0.0.1" #Sample configuration for a MySQL virtual service. #virtual = 192.168.10.74:3306 # real=sql01->sql03:3306 gate 10 # fallback=127.0.0.1:3306 # service=mysql # scheduler=wrr # #persistent=600 # #netmask=255.255.255.255 # protocol=tcp # checktype=negotiate # login="readuser" # passwd="genericpassword" # database="portal" # request="SELECT * FROM link" #Sample configuration for a PostgreSQL virtual service. #virtual = 192.168.10.74:5432 # real=sql01->sql03:5432 gate 10 # fallback=127.0.0.1:5432 # service=pgsql # scheduler=wrr # #persistent=600 # #netmask=255.255.255.255 # protocol=tcp # checktype=negotiate # login="readuser" # passwd="genericpassword" # database="portal" # request="SELECT * FROM link" #Sample configuration for a Oracle virtual service. #virtual = 192.168.10.74:1521 # real=sql01->sql03:1521 gate 10 # fallback=127.0.0.1:1521 # service=oracle # scheduler=wrr # #persistent=600 # #netmask=255.255.255.255 # protocol=tcp # checktype=negotiate # login="readuser" # passwd="genericpassword" # database="portal" # request="SELECT * FROM link" #Sample configuration for an unsuported protocol #The real servers will just be brought up without checking for availability #virtual=192.168.6.240:23 # real=192.168.16.3:23 masq # real=192.168.16.5:23 masq # fallback=127.0.0.1:23 # service=none # scheduler=wlc # persistent=600 # #netmask=255.255.255.255 # protocol=tcp # checktype=on # A sample virtual services that uses a ping check. # Note that using checktype=connect and protocol=udp # will also effect ping checks #virtual=192.168.6.240:53 # real=192.168.6.2:53 gate # real=192.168.6.3:53 gate # real=192.168.6.6:53 gate # fallback=127.0.0.1:53 gate # scheduler=rr # #persistent=600 # #netmask=255.255.255.255 # protocol=udp # checktype=ping # checkcount=3 # A sample virtual services that uses a Radius check on UDP. # Note that using checktype=connect and protocol=udp # will also effect ping checks #virtual=192.168.6.240:1812 # real=192.168.6.2::1812 gate # real=192.168.6.3::1812 gate # real=192.168.6.6::1812 gate # fallback=127.0.0.1:1812 gate # scheduler=rr # #persistent=600 # #netmask=255.255.255.255 # protocol=udp # checktype=negotiate # service=radius # password="readuser" # passwd="genericpassword" # secret="somesecret" # checktimeout=1 # A sample virtual services that uses a SIP check on UDP. # Note that using checktype=connect and protocol=udp # will also effect ping checks #virtual=192.168.6.240:5060 # real=192.168.6.2::5060 gate # real=192.168.6.3::5060 gate # real=192.168.6.6::5060 gate # fallback=127.0.0.1:5060 gate # scheduler=rr # #persistent=600 # #netmask=255.255.255.255 # protocol=udp # checktype=negotiate # service=sip # checktimeout=1 #Sample configuration for an nntp virtual service with IPv6. #Fallback setting overrides global #virtual6=[2001:db8::5]:119 # real6=[2001:db8:0:1::4]:119 masq # real6=[2001:db8:0:1::6]:119 masq # fallback6=[::1]:119 # service=nntp # scheduler=wlc # #persistent=600 # #netmask=255.255.255.255 # protocol=tcp # checktype=negotiate # checkport=119 cluster-agents-1.0.4/ldirectord/init.d/0000755000175000017500000000000011527051501020231 5ustar roaksoaxroaksoaxcluster-agents-1.0.4/ldirectord/init.d/ldirectord.debian.default.in0000644000175000017500000000027511527003731025566 0ustar roaksoaxroaksoax# @sysconfdir@/default/ldirectord # Defaults for the Debian ldirectord script # Set to the configuration file # May be absolute or relative to @sysconfdir@/ha.d/ CONFIG_FILE=ldirectord.cf cluster-agents-1.0.4/ldirectord/init.d/ldirectord.debian.in0000644000175000017500000000133211527003731024136 0ustar roaksoaxroaksoax#!/bin/sh # ### BEGIN INIT INFO # Provides: ldirectord # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 ### END INIT INFO # # Author: Horms # # Debian init script for ldirectord # NAME=ldirectord DAEMON="@sbindir@/$NAME" CONFIG="@sysconfdir@/default/$NAME" test -x $DAEMON || exit 0 CONFIG_FILE="" [ -f "$CONFIG" ] && . "$CONFIG" CONFIG_FILE="${CONFIG_FILE:=ldirectord.cf}" case "$1" in start|stop|restart|try-restart|status|reload|force-reload) exec "$DAEMON" $1 ;; *) echo "Usage: /etc/init.d/$NAME" \ "{start|stop|restart|try-restart|status|reload|force-reload}" >&2 exit 1 ;; esac cluster-agents-1.0.4/ldirectord/init.d/Makefile.am0000644000175000017500000000341311527003731022270 0ustar roaksoaxroaksoax# # ldirectord: Linux-HA heartbeat code # # Copyright (C) 2001 Michael Moerz # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in initddir = @INITDIR@ initd_SCRIPTS = ldirectord install-initdSCRIPTS: $(initd_SCRIPTS) @$(NORMAL_INSTALL) $(mkinstalldirs) $(DESTDIR)$(initddir) @list='$(initd_SCRIPTS)'; for p in $$list; do \ f="`echo $$p|sed '$(transform)'`"; \ if test -f $$p; then \ echo " $(INSTALL_SCRIPT) $$p $(DESTDIR)$(initddir)/$$f@INIT_EXT@"; \ $(INSTALL_SCRIPT) $$p $(DESTDIR)$(initddir)/$$f@INIT_EXT@; \ elif test -f $(srcdir)/$$p; then \ echo " $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(initddir)/$$f@INIT_EXT@"; \ $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(initddir)/$$f@INIT_EXT@; \ else :; fi; \ done uninstall-initdSCRIPTS: @$(NORMAL_UNINSTALL) @list='$(initd_SCRIPTS)'; for p in $$list; do \ f="`echo $$p|sed '$(transform)'`"; \ echo " rm -f $(DESTDIR)$(initddir)/$$f@INIT_EXT@"; \ rm -f $(DESTDIR)$(initddir)/$$f@INIT_EXT@; \ done EXTRA_DIST = $(initd_SCRIPTS) \ ldirectord.debian ldirectord.debian.default.in cluster-agents-1.0.4/ldirectord/init.d/ldirectord.in0000755000175000017500000000426011527003731022723 0ustar roaksoaxroaksoax#!/bin/sh # # ldirectord Linux Director Daemon # # chkconfig: - 92 40 # description: Start and stop ldirectord on non-heartbeat systems # Using the config file /etc/ha.d/ldirectord.cf # Normally ldirectord is started and stopped by heartbeat # # processname: ldirectord # config: /etc/ha.d/ldirectord.cf # # Author: Horms # Released: April 2000 # Licence: GNU General Public Licence # ### BEGIN INIT INFO # Provides: ldirectord # Required-Start: $syslog $network $remote_fs # Required-Stop: $syslog $network $remote_fs # Should-Start: $time sshd # Default-Start: 3 5 # Default-Stop: 0 1 2 6 # Short-Description: Control Linux Virtual Server via ldirectord on non-heartbeat systems # Description: Starts (and stops) the ldirectord service if # running outside a heartbeat managed environment. # ldirectord manages the Linux Virtual Server component for # TCP/UDP load-balancing. # It uses the config file @sysconfdir@/ha.d/ldirectord.cf. ### END INIT INFO DAEMON=@sbindir@/ldirectord . @sysconfdir@/ha.d/shellfuncs # Source function library. if [ -f /etc/rc.d/init.d/functions ] then . /etc/rc.d/init.d/functions fi [ -x $DAEMON ] || exit 0 action() { echo -n "$1... " shift $@ stat=$? if [ $stat = 0 ]; then echo success else echo failure fi return $stat } ###################################################################### # Read arument and take action as appropriate ###################################################################### case "$1" in start) action "Starting ldirectord" $DAEMON start touch /var/lock/subsys/ldirectord ;; stop) action "Stopping ldirectord" $DAEMON stop rm -f /var/lock/subsys/ldirectord ;; restart) action "Restarting ldirectord" $DAEMON restart ;; try-restart) action "Try-Restarting ldirectord" $DAEMON try-restart ;; status) $DAEMON status ;; reload) action "Reloading ldirectord" $DAEMON reload ;; force-reload) action "Force-Reloading ldirectord" $DAEMON force-reload ;; *) echo "Usage: ldirectord {start|stop|restart|try-restart|status|reload|force-reload}" exit 1 esac exit $? cluster-agents-1.0.4/ldirectord/OCF/0000755000175000017500000000000011527051501017453 5ustar roaksoaxroaksoaxcluster-agents-1.0.4/ldirectord/OCF/Makefile.am0000644000175000017500000000162511527003731021515 0ustar roaksoaxroaksoax# # ldirectord: Linux-HA heartbeat code # # Copyright (C) 2007 Horms # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = $(ocf_SCRIPTS) ocfdir = @OCF_RA_DIR@/heartbeat ocf_SCRIPTS = ldirectord cluster-agents-1.0.4/ldirectord/OCF/ldirectord.in0000644000175000017500000001763211527003731022151 0ustar roaksoaxroaksoax#!/bin/sh # # ldirectord OCF RA. Wrapper around @sbindir@/ldirectord to # be OCF RA compliant and therefore to get the possibility # to monitor ldirectord by HAv2. # Tested on SuSE Linux Enterprise Server 10. # # Should conform to the specification found at # http://www.linux-ha.org/OCFResourceAgent # and # http://www.opencf.org/cgi-bin/viewcvs.cgi/specs/ra/resource-agent-api.txt?rev=HEAD # # ToDo: Add parameter to start several instances of ldirectord # with different config files. # # Copyright (c) 2007 Andreas Mock (andreas.mock@web.de) # All Rights Reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # ####################################################################### # # OCF Parameters # OCF_RESKEY_configfile # OCF_RESKEY_ldirectord # ####################################################################### # Initialization: HA_VARRUNDIR=${HA_VARRUN} . ${OCF_ROOT}/lib/heartbeat/ocf-shellfuncs LDIRCONF=${OCF_RESKEY_configfile:-@sysconfdir@/ha.d/ldirectord.cf} LDIRECTORD=${OCF_RESKEY_ldirectord:-@sbindir@/ldirectord} meta_data() { cat < 1.0 It's a simple OCF RA wrapper for ldirectord and uses the ldirectord interface to create the OCF compliant interface. You win monitoring of ldirectord. Be warned: Asking ldirectord status is an expensive action. Wrapper OCF Resource Agent for ldirectord The full pathname of the ldirectord configuration file. configuration file path The full pathname of the ldirectord. ldirectord binary path END } ####################################################################### ldir_init() { # check the supplied parameters exist enough that we can do all the other # operations if [ ! -f $LDIRCONF ]; then ocf_log warn "$LDIRCONF not found, ldirectord not installed" exit $OCF_ERR_INSTALLED fi if [ ! -x $LDIRECTORD ]; then ocf_log warn "$LDIRECTORD not found, ldirectord not installed" exit $OCF_ERR_INSTALLED fi } ldirectord_usage() { cat <&1` RET=$? if [ $RET -eq 0 ]; then ocf_log warn "Killing ldirectord($PID) with SIGTERM" kill $PID fi pgrep -f "$LDIRECTORD $LDIRCONF start" >/dev/null 2>&1 RET=$? # if ldirectord is not running any more, we've (kind of) successfully # stopped it if [ $RET -eq 1 ]; then return $OCF_SUCCESS else # ldirectord is still running? Kill it badly ocf_log warn "Killing ldirectord($PID) with SIGKILL" kill -9 $PID pgrep -f "$LDIRECTORD $LDIRCONF start" >/dev/null 2>&1 RET=$? # if it's not dead after here, we can't really do anything more if [ $RET -eq 1 ]; then return $OCF_SUCCESS fi fi # if none of our kills work, return an error. This should force the # resource unmanaged # on this node, requiring manual intervention. return $OCF_ERR_GENERIC else ocf_log info "Stopping ldirectord" # if ldirectord status is not an error, issue a stop. Multiple stops # will return 0 $LDIRECTORD $LDIRCONF stop RET=$? case $RET in 0) return $RET;; *) return 1;; esac fi } # simple check to see if ldirectord is running, returns the proper OCF codes. ldirectord_status() { OUTPUT=`$LDIRECTORD $LDIRCONF status 2>&1` case $? in 0) return $OCF_SUCCESS;; 1) expr match "$OUTPUT" '.*ldirectord stale pid file.*' >/dev/null if [ $? -eq 0 ]; then return $OCF_NOT_RUNNING else return $OCF_ERR_GENERIC fi;; 2) ocf_log err "$LDIRCONF has configuration errors" echo $OUTPUT return $OCF_ERR_GENERIC;; 3) return $OCF_NOT_RUNNING;; *) echo $OUTPUT return $OCF_ERR_GENERIC;; esac } ldirectord_monitor() { # check if the process is running first ldirectord_status RET=$? if [ $RET -ne $OCF_SUCCESS ]; then return $RET fi # do more advanced checks here for high OCF_CHECK_LEVELs. Don't know what # more we can do at this time, # a status call already hits LVS in the kernel. } ldirectord_validate() { #ldir_init is already called, there's nothing more we can validate unless #we add more attributes return 0 } case $1 in meta-data|usage|help) : ;; *) ldir_init $@ ;; esac case $__OCF_ACTION in meta-data) meta_data exit $OCF_SUCCESS ;; start) ldirectord_start ldirectord_exit $? ;; stop) ldirectord_stop ldirectord_exit $? ;; monitor) ldirectord_monitor ldirectord_exit $? ;; validate-all) ldirectord_validate ldirectord_exit $? ;; usage|help) ldirectord_usage exit $OCF_SUCCESS ;; *) ldirectord_usage exit $OCF_ERR_UNIMPLEMENTED ;; esac cluster-agents-1.0.4/ldirectord/logrotate.d/0000755000175000017500000000000011527051501021266 5ustar roaksoaxroaksoaxcluster-agents-1.0.4/ldirectord/logrotate.d/ldirectord0000644000175000017500000000005211527003731023343 0ustar roaksoaxroaksoax/var/log/ldirectord.log { missingok } cluster-agents-1.0.4/ldirectord/logrotate.d/Makefile.am0000644000175000017500000000165411527003731023332 0ustar roaksoaxroaksoax# # ldirectord: Linux-HA heartbeat code # # Copyright (C) 2001 Michael Moerz # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = ldirectord logrotatedir = $(sysconfdir)/logrotate.d logrotate_DATA = ldirectord cluster-agents-1.0.4/ldirectord/Makefile.am0000644000175000017500000000252211527003731021103 0ustar roaksoaxroaksoax# # ldirectord: Linux-HA heartbeat code # # Copyright (C) 2001 Michael Moerz # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in CLEANFILES = ldirectord.8 EXTRA_DIST = ldirectord ldirectord.cf README SUBDIRS = logrotate.d init.d OCF ldirectord.8: ldirectord $(POD2MAN) --section=8 $< > $@ sbin_SCRIPTS = ldirectord if BUILD_POD_DOC man_MANS = ldirectord.8 endif harddir = $(sysconfdir)/ha.d/resource.d .PHONY: install-exec-hook install-exec-hook: $(mkinstalldirs) $(DESTDIR)$(harddir) cd $(DESTDIR)$(harddir) && ln -s -f $(sbindir)/ldirectord . .PHONY: uninstall-hook uninstall-hook: rm -f $(DESTDIR)$(harddir)/ldirectord cluster-agents-1.0.4/ldirectord/ldirectord.in0000644000175000017500000043245211527003731021543 0ustar roaksoaxroaksoax#!/usr/bin/perl -w ###################################################################### # ldirectord http://www.vergenet.net/linux/ldirectord/ # Linux Director Daemon - run "perldoc ldirectord" for details # # 1999-2006 (C) Jacob Rief , # Horms and others # # License: GNU General Public License (GPL) # # Note: * The original author of this software was Jacob Rief circa 1999 # * It was maintained by Jacob Rief and Horms # from November 1999 to July 2003. # * From July 2003 Horms is the maintainer # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA # 02111-1307 USA # ###################################################################### # A Brief history of versions: # # From oldest to newest # 1.1-1.144: ldirecord maintained in CVS HEAD branch # 1.145-1.186: ldirectord.in maintained in CVS HEAD BRANCH # 1.186-ha-VERSION: ldirectord.in maintained in mercurial =head1 NAME ldirectord - Linux Director Daemon Daemon to monitor remote services and control Linux Virtual Server =head1 SYNOPSIS B [B<-d|--debug>] [--] [I] B | B | B | B | B | B | B B [B<-h|-?|--help|-v|--version>] =head1 DESCRIPTION B is a daemon to monitor and administer real servers in a cluster of load balanced virtual servers. B typically is started from heartbeat but can also be run from the command line. On startup B reads the file B<@sysconfdir@/ha.d/conf/>I. After parsing the file, entries for virtual servers are created on the LVS. Now at regular intervals the specified real servers are monitored and if they are considered alive, added to a list for each virtual server. If a real server fails, it is removed from that list. Only one instance of B can be started for each configuration, but more instances of B may be started for different configurations. This helps to group clusters of services. Normally one would put an entry inside B<@sysconfdir@/ha.d/haresources> I to start ldirectord from heartbeat. =head1 OPTIONS I: This is the name for the configuration as specified in the file B<@sysconfdir@/ha.d/conf/>I B<-d|--debug> Don't start as daemon and log verbosely. B<-h|--help> Print user manual and exit. B<-v|--version> Print version and exit. B the daemon for the specified configuration. B the daemon for the specified configuration. This is the same as sending a TERM signal to the running daemon. B the daemon for the specified configuration. The same as stopping and starting. B the configuration file. This is only useful for modifications inside a virtual server entry. It will have no effect on adding or removing a virtual server block. This is the same as sending a HUP signal to the running daemon. B of the running daemon for the specified configuration. =head1 SYNTAX =head2 Description of how to write configuration files BI<(ip_address|hostname:portnumber|servicename)|firewall-mark> Defines a virtual service by IP-address (or hostname) and port (or servicename) or firewall-mark. A firewall-mark is an integer greater than zero. The configuration of marking packets is controlled using the C<-m> option to B(8). All real services and flags for a virtual service must follow this line immediately and be indented. BI Timeout in seconds for connect, external, external-perl and ping checks. If the timeout is exceeded then the real server is declared dead. If defined in a virtual server section then the global value is overridden. If undefined then the value of negotiatetimeout is used. negotiatetimeout is also a global value that may be overridden by a per-virtual setting. If both checktimeout and negotiatetimeout are unset, the default is used. Default: 5 seconds BI Timeout in seconds for negotiate checks. If defined in a virtual server section then the global value is overridden. If undefined then the value of connecttimeout is used. connecttimeout is also a global value that may be overridden by a per-virtual setting. If both negotiatetimeout and connecttimeout are unset, the default is used. Default: 30 seconds BI Defines the number of second between server checks. When fork=no this option defines the amount of time ldirectord sleeps between running all of the realserver checks in all virtual service pools. When fork=yes this option defines the amount of time each forked child sleeps per virtual service pool after running all realserver checks for that pool. If set in the virtual server section then the global value is overridden, but ONLY if using forking mode (BI). Default: 10 seconds BI This option is deprecated and slated for removal in a future version. Please see the 'failurecount' option. The number of times a check will be attempted before it is considered to have failed. Only works with ping checks. Note that the checktimeout/negotiatetimeout is additive, so if a connect check is used, checkcount is 3 and checktimeout is 2 seconds, then a total of 6 seconds worth of timeout will occur before the check fails. If defined in a virtual server section then the global value is overridden. Default: 1 BI The number of consecutive times a failure will have to be reported by a check before the realserver is considered to have failed. A value of 1 will have the realserver considered failed on the first failure. A successful check will reset the failure counter to 0. If defined in a virtual server section then the global value is overridden. Default: 1 BB | B Defines if should continuously check the configuration file for modification. If this is set to 'yes' and the configuration file changed on disk and its modification time (mtime) is newer than the previous version, the configuration is automatically reloaded. Default: no BIB<"> If this directive is defined, B automatically calls the executable I after the configuration file has changed on disk. This is useful to update the configuration file through B on the other heartbeated host. The first argument to the callback is the name of the configuration. This directive might also be used to restart B automatically after the configuration file changed on disk. However, if B is set to yes, the configuration is reloaded anyway. BI [B | B | B] the server onto which a webservice is redirected if all real servers are down. Typically this would be 127.0.0.1 with an emergency page. If defined in a virtual server section then the global value is overridden. BIB<"> If this directive is defined, the supplied script is executed whenever all real servers for a virtual service are down or when the first real server comes up again. In the first case, it is called with "start" as its first argument, in the latter with "stop". If defined in a virtual server section then the global value is overridden. BIB<">|syslog_facility An alternative logfile might be specified with this directive. If the logfile does not have a leading '/', it is assumed to be a syslog(3) facility name. Default: log directly to the file I. BI[, I]...B<"> A valid email address for sending alerts about the changed connection status to any real server defined in the virtual service. This option requires perl module MailTools to be installed. Automatically tries to send email using any of the built-in methods. See perldoc Mail::Mailer for more info on methods. Multiple addresses may be supplied, comma delimited. If defined in a virtual server section then the global value is overridden. BI A valid email address to use as the from address of the email alerts. You can use a plain email address or any RFC-compliant string for the From header in the body of an email message (such as: "ldirectord Alerts" ) Do not quote this string unless you want the quotes passed in as part of the From header. Default: unset, take system generated default (probably root@hostname) B I Delay in seconds between repeating email alerts while any given real server in the virtual service remains inaccessible. A setting of zero seconds will inhibit the repeating alerts. The email timing accuracy of this setting is dependent on the number of seconds defined in the checkinterval configuration option. If defined in a virtual server section then the global value is overridden. Default: 0 BB | B | B | B | B | B,... Comma delimited list of server states in which email alerts should be sent. B is a short-hand for "B,B,B,B". If B is specified, no other option may be specified, otherwise options are ored with each other. If defined in a virtual server section then the global value is overridden. Default: all BIB<"> A valid SMTP server address to use for sending email via SMTP. If defined in a virtual server section then the global value is overridden. BIB<"> Use this directive to start an instance of ldirectord for the named I. BB | B If I, then ldirectord does not go into background mode. All log-messages are redirected to stdout instead of a logfile. This is useful to run B supervised from daemontools. See http://untroubled.org/rpms/daemontools/ or http://cr.yp.to/daemontools.html for details. Default: I BB | B If I, then ldirectord will spawn a child process for every virtual server, and run checks against the real servers from them. This will increase response times to changes in real server status in configurations with many virtual servers. This may also use less memory then running many separate instances of ldirectord. Child processes will be automatically restarted if they die. Default: I BB | B If I, then when real or failback servers are determined to be down, they are not actually removed from the kernel's LVS table. Rather, their weight is set to zero which means that no new connections will be accepted. This has the side effect, that if the real server has persistent connections, new connections from any existing clients will continue to be routed to the real server, until the persistent timeout can expire. See L for more information on persistent connections. This side-effect can be avoided by running the following: echo 1 > /proc/sys/net/ipv4/vs/expire_quiescent_template If the proc file isn't present this probably means that the kernel doesn't have LVS support, LVS support isn't loaded, or the kernel is too old to have the proc file. Running ipvsadm as root should load LVS into the kernel if it is possible. If I, then the real or failback servers will be removed from the kernel's LVS table. The default is I. If defined in a virtual server section then the global value is overridden. Default: I BB | B If I, then when ldirectord exits it will remove all of the virtual server pools that it is managing from the kernel's LVS table. If I, then the virtual server pools it is managing and any real or failback servers listed in them at the time ldirectord exits will be left as-is. If you want to be able to stop ldirectord without having traffic to your realservers interrupted you will want to set this to I. If defined in a virtual server section then the global value is overridden. Default: I BI If this option is set ldirectord will look for a special file in the specified directory and, if found, force the status of the real server identified by the file to down, skipping the normal health check. This would be useful if you wish to force servers down for maintenance without having to modify the actual ldirectord configuration file. For example, given a realserver with IP 172.16.1.2, service on port 4444, and a resolvable reverse DNS entry pointing to "realserver2.example.com" ldirectord will check for the existence of the following files: =over =item 172.16.1.2:4444 =item 172.16.1.2 =item realserver2.example.com:4444 =item realserver2.example.com =item realserver2:4444 =item realserver2 =back If any one of those files is found then ldirectord will immediately force the status of the server to down as if the check had failed. Note: Since it checks for the IP/hostname without the port this means you can decide to place an entire realserver into maintenance across a large number of virtual service pools with a single file (if you were going to reboot the server, for instance) or include the port number and put just a particular service into maintenance. This option is not valid in a virtual server section. Default: disabled =head2 Section virtual The following commands must follow a B entry and must be indented with a minimum of 4 spaces or one tab. B Iip_address|hostname][:portnumber|servicename>] B | B | B [I] [B<">IB<", ">IB<">] Defines a real service by IP-address (or hostname) and port (or servicename). If the port is omitted then a 0 will be used, this is intended primarily for fwmark services where the port for real servers is ignored. Optionally a range of IPv4 addresses (or two hostnames) may be given, in which case each IPv4 address in the range will be treated as a real server using the given port. The second argument defines the forwarding method, must be B, B or B. The third argument is optional and defines the weight for that real server. If omitted then a weight of 1 will be used. The last two arguments are also optional. They define a request-receive pair to be used to check if a server is alive. They override the request-receive pair in the virtual server section. These two strings must be quoted. If the request string starts with I the IP-address and port of the real server is overridden, otherwise the IP-address and port of the real server is used. =head2 For TCP and UDP (non fwmark) virtual services, unless the forwarding method is B and the IP address of a real server is non-local (not present on a interface on the host running ldirectord) then the port of the real server will be set to that of its virtual service. That is, port-mapping is only available to if the real server is another machine and the forwarding method is B. This is due to the way that the underlying LVS code in the kernel functions. =head2 More than one of these entries may be inside a virtual section. The checktimeout, negotiatetimeout, checkcount, fallback, emailalert, emailalertfreq and quiescent options listed above may also appear inside a virtual section, in which case the global setting is overridden. BB | B | B | B | B | B | B | BI Type of check to perform. Negotiate sends a request and matches a receive string. Connect only attempts to make a TCP/IP connection, thus the request and receive strings may be omitted. If checktype is a number then negotiate and connect is combined so that after each N connect attempts one negotiate attempt is performed. This is useful to check often if a service answers and in much longer intervals a negotiating check is done. Ping means that ICMP ping will be used to test the availability of real servers. Ping is also used as the connect check for UDP services. Off means no checking will take place and no real or fallback servers will be activated. On means no checking will take place and real servers will always be activated. Default is I. BB | B | B | B | B | B | B | B | B | B | B | B | B | B | B | B | B | B | B | B The type of service to monitor when using checktype=negotiate. None denotes a service that will not be monitored. simpletcp sends the B string to the server and tests it against the B regexp. The other types of checks connect to the server using the specified protocol. Please see the B and B sections for protocol specific information. Default: =over 4 =item * Virtual server port is 21: ftp =item * Virtual server port is 25: smtp =item * Virtual server port is 53: dns =item * Virtual server port is 80: http =item * Virtual server port is 110: pop =item * Virtual server port is 119: nntp =item * Virtual server port is 143: imap =item * Virtual server port is 389: ldap =item * Virtual server port is 443: https =item * Virtual server port is 587: submission =item * Virtual server port is 993: imaps =item * Virtual server port is 995: pops =item * Virtual server port is 1521: oracle =item * Virtual server port is 1812: radius =item * Virtual server port is 3128: http_proxy =item * Virtual server port is 3306: mysql =item * Virtual server port is 5432: pgsql =item * Virtual server port is 5060: sip =item * Otherwise: none =back BIB<"> This setting is used if checktype is external or external-perl and is the command to be run to check the status of a real server. It should exit with status 0 if everything is ok, or non-zero otherwise. Four parameters are passed to the script: =over 4 =item * virtual server ip/firewall mark =item * virtual server port =item * real server ip =item * real server port =back If the checktype is external-perl then the command is assumed to be a Perl script and it is evaluated into an anonymous subroutine which is called at check time, avoiding a fork-exec. The argument signature and exit code conventions are identical to checktype external. That is, an external-perl checktype should also work as an external checktype. Default: /bin/true BI Number of port to monitor. Sometimes check port differs from service port. Default: port specified for each real server BIB<"> This object will be requested each checkinterval seconds on each real server. The string must be inside quotes. Note that this string may be overridden by an optional per real-server based request-string. For an HTTP/HTTPS check, this should be a relative URI, while it has to be absolute for the 'http_proxy' check type. In the latter case, this URI will be requested through the proxy backend that is being checked. For a DNS check this should the name of an A record, or the address of a PTR record to look up. For a MySQL, Oracle or PostgeSQL check, this should be an SQL SELECT query. The data returned is not checked, only that the answer is one or more rows. This is a required setting. For a simpletcp check, this string is sent verbatim except any occurrences of \n are replaced with a new line character. BIB<"> If the requested result contains this I, the real server is declared alive. The regexp must be inside quotes. Keep in mind that regexps are not plain strings and that you need to escape the special characters if they should as literals. Note that this regexp may be overridden by an optional per real-server based receive regexp. For a DNS check this should be any one the A record's addresses or any one of the PTR record's names. For a MySQL check, the receive setting is not used. B | B Sets the HTTP method which should be used to fetch the URI specified in the request-string. GET is the method used by default if the parameter is not set. If HEAD is used, the receive-string should be unset. Default: GET BIB<"> Used when using a negotiate check with HTTP or HTTPS. Sets the host header used in the HTTP request. In the case of HTTPS this generally needs to match the common name of the SSL certificate. If not set then the host header will be derived from the request url for the real server if present. As a last resort the IP address of the real server will be used. BIB<"> For FTP, IMAP, LDAP, MySQL, Oracle, POP and PostgreSQL, the username used to log in. For Radius the passwd is used for the attribute User-Name. For SIP, the username is used as both the to and from address for an OPTIONS query. Default: =over 4 =item * FTP: Anonymous =item * MySQL Oracle, and PostgreSQL: Must be specified in the configuration =item * SIP: ldirectord\@, hostname is derived as per the passwd option below. =item * Otherwise: empty string, which denotes that case authentication will not be attempted. =back BIB<"> Password to use to login to FTP, IMAP, LDAP, MySQL, Oracle, POP, PostgreSQL and SIP servers. For Radius the passwd is used for the attribute User-Password. Default: =over 4 =item * FTP: ldirectord\@, where hostname is the environment variable HOSTNAME evaluated at run time, or sourced from uname if unset. =item * Otherwise: empty string. In the case of LDAP, MySQL, Oracle, and PostgreSQL this means that authentication will not be performed. =back BIB<"> Database to use for MySQL, Oracle and PostgreSQL servers, this is the database that the query (set by B above) will be performed against. This is a required setting. BIB<"> Secret to use for Radius servers, this is the secret used to perform an Access-Request with the username (set by B above) and passwd (set by B above). Default: empty string B I Scheduler to be used by LVS for loadbalancing. For an information on the available sehedulers please see the ipvsadm(8) man page. Default: "wrr" B I Number of seconds for persistent client connections. B I Netmask to be used for granularity of persistent client connections. B | B | B Protocol to be used. If the virtual is specified as an IP address and port then it must be one of tcp or udp. If a firewall mark then the protocol must be fwm. Default: =over 4 =item * Virtual is an IP address and port, and the port is not 53: tcp =item * Virtual is an IP address and port, and the port is 53: udp =item * Virtual is a firewall mark: fwm =back BIB<"> File to continuously log the real service checks to for this virtual service. This is useful for monitoring when and why real services were down or for statistics. The log format is: [timestamp|pid|real_service_id|status|message] Default: no separate logging of service checks. =head1 IPv6 Directives for IPv6 are virtual6, real6, fallback6. IPv6 addresses specified for virtual6, real6, fallback6 and a file of maintenance directory should be enclosed by brackets ([2001:db8::abcd]:80). Following checktype and service are supported. BB | B | B | B | B | B | BI BB | B | B | B | B =head1 FILES B<@sysconfdir@/ha.d/ldirectord.cf> B BIB<.pid> B =head1 SEE ALSO L, L Ldirectord Web Page: http://www.vergenet.net/linux/ldirectord/ =head1 AUTHORS Horms Jacob Rief =cut use strict; # Set defaults for configuration variables in the "set_defaults" function use vars qw( $VERSION_STR $AUTOCHECK $CHECKINTERVAL $LDIRECTORD $LDIRLOG $NEGOTIATETIMEOUT $DEFAULT_NEGOTIATETIMEOUT $RUNPID $CHECKTIMEOUT $DEFAULT_CHECKTIMEOUT $CHECKCOUNT $FAILURECOUNT $QUIESCENT $FORKING $EMAILALERT $EMAILALERTFREQ $EMAILALERTSTATUS $EMAILALERTFROM $SMTP $CLEANSTOP $MAINTDIR $CALLBACK $CFGNAME $CMD $CONFIG $DEBUG $FALLBACK $FALLBACK6 $FALLBACKCOMMAND $SUPERVISED $IPVSADM $checksum $DAEMON_STATUS $DAEMON_STATUS_STARTING $DAEMON_STATUS_RUNNING $DAEMON_STATUS_STOPPING $DAEMON_STATUS_RELOADING $DAEMON_STATUS_ALL $DAEMON_TERM $DAEMON_HUP $DAEMON_CHLD $opt_d $opt_h $stattime %LD_INSTANCE @OLDVIRTUAL @REAL @VIRTUAL $HOSTNAME %EMAILSTATUS %FORK_CHILDREN $SERVICE_UP $SERVICE_DOWN %check_external_perl__funcs $CRLF ); $VERSION_STR = "Linux Director v1.186-ha"; $DAEMON_STATUS_STARTING = 0x1; $DAEMON_STATUS_RUNNING = 0x2; $DAEMON_STATUS_STOPPING = 0x4; $DAEMON_STATUS_RELOADING = 0x8; $DAEMON_STATUS_ALL = $DAEMON_STATUS_STARTING | $DAEMON_STATUS_RUNNING | $DAEMON_STATUS_STOPPING | $DAEMON_STATUS_RELOADING; $SERVICE_UP = 0; $SERVICE_DOWN =1; # default values $DAEMON_TERM = undef; $DAEMON_HUP = undef; $LDIRECTORD = ld_find_cmd("ldirectord", 1); if (! defined $LDIRECTORD) { $LDIRECTORD = "@sbindir@/ldirectord"; } $RUNPID = "/var/run/ldirectord"; $CRLF = "\x0d\x0a"; # Set global configuration default values: set_defaults(); use Getopt::Long; use Pod::Usage; #use English; #use Time::HiRes qw( gettimeofday tv_interval ); use Socket; use Socket6; use Sys::Hostname; use POSIX qw(setsid :sys_wait_h); use Sys::Syslog qw(:DEFAULT setlogsock); BEGIN { # wrap exit() to preserve replacability *CORE::GLOBAL::exit = sub { CORE::exit(@_ ? shift : 0); }; } # command line options my @OLD_ARGV = @ARGV; my $opt_d = ''; my $opt_h = ''; my $opt_v = ''; Getopt::Long::Configure ("bundling", "no_auto_abbrev", "require_order"); GetOptions("debug|d" => \$opt_d, "help|h|?" => \$opt_h, "version|v" => \$opt_v) or usage(); # main code $DEBUG = $opt_d ? 3 : 0; if ($opt_h) { exec_wrapper("/usr/bin/perldoc -U $LDIRECTORD"); &ld_exit(127, "Exec failed"); } if ($opt_v) { print("$VERSION_STR\n" . "1999-2006 Jacob Rief, Horms and others\n" . "\n". "\n" . "ldirectord comes with ABSOLUTELY NO WARRANTY.\n" . "This is free software, and you are welcome to redistribute it\n". "under certain conditions. " . "See the GNU General Public Licence for details.\n"); &ld_exit(0, ""); } if ($DEBUG>0 and -f "./ipvsadm") { $IPVSADM="./ipvsadm"; } else { if (-x "/sbin/ipvsadm") { $IPVSADM="/sbin/ipvsadm"; } elsif (-x "/usr/sbin/ipvsadm") { $IPVSADM="/usr/sbin/ipvsadm"; } else { die "Can not find ipvsadm"; } } # There is a memory leak in perl's socket code when # the default IO layer is used. So use "perlio" unless # something else has been explicitly set. # http://archive.develooper.com/perl5-porters@perl.org/msg85468.html unless(defined($ENV{'PERLIO'})) { $ENV{'PERLIO'} = "perlio"; exec_wrapper($0, @OLD_ARGV); } $DAEMON_STATUS = $DAEMON_STATUS_STARTING; ld_init(); ld_setup(); ld_start(); ld_cmd_children("start", %LD_INSTANCE); $DAEMON_STATUS = $DAEMON_STATUS_RUNNING; ld_main(); &ld_rm_file("$RUNPID.$CFGNAME.pid"); &ld_exit(0, "Reached end of \"main\""); # functions sub ld_init { # install signal handlers (this covers TERM) #require Net::LDAP; $SIG{'INT'} = \&ld_handler_term; $SIG{'QUIT'} = \&ld_handler_term; $SIG{'ILL'} = \&ld_handler_term; $SIG{'ABRT'} = \&ld_handler_term; $SIG{'FPE'} = \&ld_handler_term; $SIG{'SEGV'} = \&ld_handler_term; $SIG{'TERM'} = \&ld_handler_term; $SIG{'BUS'} = \&ld_handler_term; $SIG{'SYS'} = \&ld_handler_term; $SIG{'XCPU'} = \&ld_handler_term; $SIG{'XFSZ'} = \&ld_handler_term; $SIG{'IOT'} = \&ld_handler_term; # This used to call a signal handler, that logged a message # However, this typically goes to syslog and if syslog # is playing up a loop will occur. $SIG{'PIPE'} = "IGNORE"; # HUP is actually used $SIG{'HUP'} = \&ld_handler_hup; # Reap Children $SIG{'CHLD'} = \&ld_handler_chld; if (defined $ENV{HOSTNAME}) { $HOSTNAME = "$ENV{HOSTNAME}"; } else { use POSIX "uname"; my ($s, $n, $r, $v, $m) = uname; $HOSTNAME = $n; } # search for the correct configuration file if ( !defined $ARGV[0] ) { usage(); } if ( defined $ARGV[0] && defined $ARGV[1] ) { $CONFIG = $ARGV[0]; if ($CONFIG =~ /([^\/]+)$/) { $CFGNAME = $1; } $CMD = $ARGV[1]; } elsif ( defined $ARGV[0] ) { $CONFIG = "ldirectord.cf"; $CFGNAME = "ldirectord"; $CMD = $ARGV[0]; } if ( $CMD ne "start" and $CMD ne "stop" and $CMD ne "status" and $CMD ne "restart" and $CMD ne "try-restart" and $CMD ne "reload" and $CMD ne "force-reload") { usage(); } if ( -f "@sysconfdir@/ha.d/$CONFIG" ) { $CONFIG = "@sysconfdir@/ha.d/$CONFIG"; } elsif ( -f "@sysconfdir@/ha.d/conf/$CONFIG" ) { $CONFIG = "@sysconfdir@/ha.d/conf/$CONFIG"; } elsif ( ! -f "$CONFIG" ) { init_error("Config file $CONFIG not found"); } read_config(); undef @OLDVIRTUAL; { my $log_str = "Invoking ldirectord invoked as: $0 "; for my $i (@ARGV) { $log_str .= $i . " "; } ld_log($log_str); } my $oldpid; my $filepid; if (open(FILE, "<$RUNPID.$CFGNAME.pid")) { $_ = ; chomp; $filepid = $_; close(FILE); # Check to make sure this isn't a stale pid file if (open(FILE, "; if (/ldirectord/) { $oldpid = $filepid; } close(FILE); } } if (defined $oldpid) { if ($CMD eq "start") { ld_exit(0, "Exiting from ldirectord $CMD"); } elsif ($CMD eq "stop") { kill 15, $oldpid; ld_exit(0, "Exiting from ldirectord $CMD"); } elsif ($CMD eq "restart" or $CMD eq "try-restart") { kill 15, $oldpid; while (-f "$RUNPID.$CFGNAME.pid") { # wait until old pid file is removed sleep 1; } # N.B Fall through } elsif ($CMD eq "reload" or $CMD eq "force-reload") { kill 1, $oldpid; ld_exit(0, "Exiting from ldirectord $CMD"); } else { # status print STDERR "ldirectord for $CONFIG is running with pid: $oldpid\n"; ld_cmd_children("status", %LD_INSTANCE); ld_log("ldirectord for $CONFIG is running with pid: $oldpid"); ld_log("Exiting from ldirectord $CMD"); ld_exit(0, "Exiting from ldirectord $CMD"); } } else { if ($CMD eq "start" or $CMD eq "restart") { ; } elsif ($CMD eq "stop" or $CMD eq "try-restart") { ld_exit(0, "Exiting from ldirectord $CMD"); } elsif ($CMD eq "status") { my $status; if (defined $filepid) { print STDERR "ldirectord stale pid file " . "$RUNPID.$CFGNAME.pid for $CONFIG\n"; ld_log("ldirectord stale pid file " . "$RUNPID.$CFGNAME.pid for $CONFIG"); $status = 1; } else { $status = 3; } print "ldirectord is stopped for $CONFIG\n"; ld_exit($status, "Exiting from ldirectord $CMD"); } else { ld_log("ldirectord is stopped for $CONFIG"); ld_exit(1, "Exiting from ldirectord $CMD"); } } # Run as daemon if ($SUPERVISED eq "yes" || $opt_d) { &ld_log("Starting $VERSION_STR with pid: $$"); } else { &ld_log("Starting $VERSION_STR as daemon"); open(FILE, ">$RUNPID.$CFGNAME.pid") || init_error("Can not open $RUNPID.$CFGNAME.pid"); &ld_daemon(); print FILE "$$\n"; close(FILE); } } sub usage { pod2usage(-input => $LDIRECTORD, -exitval => -1); } sub init_error { my $msg = shift; chomp($msg); &ld_log("$msg"); unless ($opt_d) { print STDERR "$msg\n"; } ld_exit(1, "Initialisation Error"); } # ld_handler_term # If we get a signal then log it and quit sub ld_handler_term { my ($signal) = (@_); if (defined $DAEMON_TERM) { $SIG{'__DIE__'} = "IGNORE"; $SIG{"$signal"} = "IGNORE"; die("Exit Handler Repeatedly Called\n"); } $DAEMON_TERM = $signal; $DAEMON_STATUS = $DAEMON_STATUS_STOPPING; } sub ld_process_term { $DAEMON_STATUS = $DAEMON_STATUS_STOPPING; ld_cmd_children("stop", %LD_INSTANCE); ld_stop(); &ld_log("Linux Director Daemon terminated on signal: $DAEMON_TERM"); &ld_rm_file("$RUNPID.$CFGNAME.pid"); &ld_exit(0, "Linux Director Daemon terminated on signal: $DAEMON_TERM"); } sub ld_handler_hup { $DAEMON_HUP=1; } sub ld_process_hup { &ld_log("Reloading Linux Director Daemon config on signal"); $DAEMON_HUP = undef; &reread_config(); } sub ld_handler_chld { $DAEMON_CHLD=1; # NOTE: calling waitpid here would mess up $? } sub ld_process_chld { my $i = 0; undef $DAEMON_CHLD; while (waitpid(-1, WNOHANG) > 0) { print "child: $i\n"; $i++; } } sub check_signal { if (defined $DAEMON_TERM) { ld_process_term(); } if (defined $DAEMON_HUP) { ld_process_hup(); } if (defined $DAEMON_CHLD) { ld_process_chld(); } } sub reread_config { @OLDVIRTUAL = @VIRTUAL; @VIRTUAL = (); my %OLD_INSTANCE = %LD_INSTANCE; my %RELOAD; my %STOP; my %START; my $child; $DAEMON_STATUS = $DAEMON_STATUS_RELOADING; eval { &read_config(); foreach $child (keys %LD_INSTANCE) { if (defined $OLD_INSTANCE{$child}) { $RELOAD{$child} = 1; } else { $START{$child} = 1; } } foreach $child (keys %OLD_INSTANCE) { if (not defined $LD_INSTANCE{$child}) { $STOP{$child} = 1; } } &ld_cmd_children("stop", %STOP); &ld_cmd_children("reload_or_start", %RELOAD); &ld_cmd_children("start", %START); foreach my $vid (keys %FORK_CHILDREN) { &ld_log("Killing child $vid (PID=$FORK_CHILDREN{$vid})"); kill 15, $FORK_CHILDREN{$vid}; } &ld_setup(); &ld_start(); }; if ($@) { @VIRTUAL = @OLDVIRTUAL; %LD_INSTANCE = %OLD_INSTANCE; } $DAEMON_STATUS = $DAEMON_STATUS_RUNNING; undef @OLDVIRTUAL; } sub parse_emailalertstatus { my ($line, $arg) = (@_); my @s = split/\s*,\s*/, $arg; my $none = 0; my $status = 0; for my $i (@s) { if ($i eq "none") { $none++; } } for my $i (@s) { if ($i eq "none") { next; } elsif ($i eq "all") { $status = $DAEMON_STATUS_ALL; } elsif ($i eq "starting") { $status |= $DAEMON_STATUS_STARTING; } elsif ($i eq "stopping") { $status |= $DAEMON_STATUS_STOPPING; } elsif ($i eq "running") { $status |= $DAEMON_STATUS_RUNNING; } elsif ($i eq "reloading") { $status |= $DAEMON_STATUS_RELOADING; } else { &config_error($line, "invalid email alert status at: \"$i\"") } if ($none > 0) { &config_error($line, "invalid email alert status: " . "\"$i\" specified with \"none\""); } } return $status; } sub set_defaults { $AUTOCHECK = "no"; $CALLBACK = undef; $CHECKCOUNT = 1; $CHECKINTERVAL = 10; $CHECKTIMEOUT = -1; $CLEANSTOP = "yes"; $DEFAULT_CHECKTIMEOUT = 5; $DEFAULT_NEGOTIATETIMEOUT = 30; $EMAILALERT = ""; $EMAILALERTFREQ = 0; $EMAILALERTFROM = undef; $EMAILALERTSTATUS = $DAEMON_STATUS_ALL; $FAILURECOUNT = 1; $FALLBACK = undef; $FALLBACK6 = undef; $FALLBACKCOMMAND = undef; $FORKING = "no"; $LDIRLOG = "/var/log/ldirectord.log"; $MAINTDIR = undef; $NEGOTIATETIMEOUT = -1; $QUIESCENT = "no"; $SUPERVISED = "no"; $SMTP = undef; } sub read_emailalert { my ($line, $addr) = (@_); # Strip of enclosing quotes $addr =~ s/^\"([^"]*)\"$/$1/; $addr =~ /(.+)/ or &config_error($line, "no email address specified"); return $addr; } sub read_config { undef @VIRTUAL; undef @REAL; undef $CALLBACK; undef %LD_INSTANCE; undef $checksum; # Reset/set global config variables to defaults before parsing the config file. set_defaults(); $stattime = 0; my %virtual_seen; open(CFGFILE, "<$CONFIG") or &config_error(0, "can not open file $CONFIG"); my $line = 0; my $linedata; while() { $line++; $linedata = $_; outer_loop: if ($linedata =~ /^virtual(6)?\s*=\s*(.*)/) { my $af = defined($1) ? AF_INET6 : AF_INET; my $vattr = $2; my $ip_port = undef; my $fwm = undef; my $virtual_id; my $virtual_line = $line; my $virtual_port; my $fallback_line; my @rsrv_todo; if ($vattr =~ /^(\d+\.\d+\.\d+\.\d+):([0-9A-Za-z-_]+)/ && $af == AF_INET) { $virtual_id = $ip_port = "$1:$2"; $virtual_port = $2; } elsif ($vattr =~ /^([0-9A-Za-z._+-]+):([0-9A-Za-z-_]+)/) { $virtual_id = $ip_port = "$1:$2"; $virtual_port = $2; } elsif ($vattr =~ /^(\d+)/){ $virtual_id = $fwm = $1; } elsif ($vattr =~ /^\[([0-9A-Fa-f:]+)\]:([0-9A-Za-z-_]+)/ && $af == AF_INET) { &config_error($line, "cannot specify an IPv6 address here. please use \"virtual6\" instead."); } elsif ($vattr =~ /^\[([0-9A-Fa-f:]+)\]:([0-9A-Za-z-_]+)/ && $af == AF_INET6) { my $v6addr = $1; my $v6port = $2; if (!inet_pton(AF_INET6,$v6addr)) { &config_error($line,"invalid ipv6 address for virtual server"); } $virtual_id = $ip_port = "[$v6addr]:$v6port"; $virtual_port = $v6port; } else { &config_error($line, "invalid address for virtual server"); } my (%vsrv, @rsrv); if ($ip_port) { $vsrv{checktype} = "negotiate"; $vsrv{protocol} = "tcp"; if ($ip_port =~ /:(53|domain)$/) { $vsrv{protocol} = "udp"; } $vsrv{port} = $virtual_port; } else { $vsrv{fwm} = $fwm; $vsrv{checktype} = "negotiate"; $vsrv{protocol} = "fwm"; $vsrv{service} = "none"; $vsrv{port} = "0"; } $vsrv{addressfamily} = $af; $vsrv{real} = \@rsrv; $vsrv{scheduler} = "wrr"; $vsrv{checkcommand} = "/bin/true"; $vsrv{request} = "/"; $vsrv{receive} = ""; $vsrv{login} = ""; $vsrv{passwd} = ""; $vsrv{database} = ""; $vsrv{checktimeout} = -1; $vsrv{checkcount} = -1; $vsrv{negotiatetimeout} = -1; $vsrv{failurecount} = -1; $vsrv{num_connects} = 0; $vsrv{httpmethod} = "GET"; $vsrv{secret} = ""; push(@VIRTUAL, \%vsrv); while() { $line++; $linedata=$_; if(m/^\s*#/) { next; } s/#.*//; s/\t/ /g; unless (/^ {4,}(.+)/) { last; } my $rcmd = $1; if ($rcmd =~ /^(real(6)?)\s*=\s*(.*)/) { if ($af == AF_INET && defined($2) || $af == AF_INET6 && ! defined($2)) { &config_error($line, join("", ("cannot specify \"$1\" here. please use \"real", ($af == AF_INET) ? "" : "6", "\" instead"))); } push @rsrv_todo, [$3, $line]; } elsif ($rcmd =~ /^request\s*=\s*\"(.*)\"/) { $1 =~ /(.+)/ or &config_error($line, "no request string specified"); $vsrv{request} = $1; unless($vsrv{request}=~/^\//){ $vsrv{request} = "/" . $vsrv{request}; } } elsif ($rcmd =~ /^receive\s*=\s*\"(.*)\"/) { $1 =~ /(.+)/ or &config_error($line, "invalid receive string"); $vsrv{receive} = $1; } elsif ($rcmd =~ /^checktype\s*=\s*(.*)/){ if ($1 =~ /(\d+)/ && $1>=0) { $vsrv{num_connects} = $1; $vsrv{checktype} = "combined"; } elsif ( $1 =~ /([\w-]+)/ && ($1 eq "connect" || $1 eq "negotiate" || $1 eq "ping" || $1 eq "off" || $1 eq "on" || $1 eq "external" || $1 eq "external-perl") ) { $vsrv{checktype} = $1; } else { &config_error($line, "checktype must be \"connect\", \"negotiate\", \"on\", \"off\", \"ping\", \"external\", \"external-perl\" or a positive number"); } } elsif ($rcmd =~ /^checkcommand\s*=\s*\"(.*)\"/ or $rcmd =~ /^checkcommand\s*=\s*(.*)/){ $1 =~ /(.+)/ or &config_error($line, "invalid check command"); $vsrv{checkcommand} = $1; } elsif ($rcmd =~ /^checktimeout\s*=\s*(.*)/){ $1 =~ /(\d+)/ && $1 or &config_error($line, "invalid check timeout"); $vsrv{checktimeout} = $1; } elsif ($rcmd =~ /^connecttimeout\s*=\s*(.*)/){ &config_error($line, "connecttimeout directive " . "deprecated in favour of " . "negotiatetimeout"); } elsif ($rcmd =~ /^negotiatetimeout\s*=\s*(.*)/){ $1 =~ /(\d+)/ && $1 or &config_error($line, "invalid negotiate timeout"); $vsrv{negotiatetimeout} = $1; } elsif ($rcmd =~ /^checkcount\s*=\s*(.*)/){ $1 =~ /(\d+)/ && $1 or &config_error($line, "invalid check count"); $vsrv{checkcount} = $1; &config_warn($line, "checkcount option is deprecated and slated for removal. please see 'failurecount'"); } elsif ($rcmd =~ /^failurecount\s*=\s*(.*)/){ $1 =~ /(\d+)/ && $1 or &config_error($line, "invalid failure count"); $vsrv{failurecount} = $1; } elsif ($rcmd =~ /^checkinterval\s*=\s*(.*)/){ $1 =~ /(\d+)/ && $1 or &config_error($line, "invalid checkinterval"); $vsrv{checkinterval} = $1 } elsif ($rcmd =~ /^checkport\s*=\s*(.*)/){ $1 =~ /(\d+)/ or &config_error($line, "invalid port"); ( $1 > 0 && $1 < 65536 ) or &config_error($line, "checkport must be in range 1..65536"); $vsrv{checkport} = $1; } elsif ($rcmd =~ /^login\s*=\s*\"(.*)\"/) { $1 =~ /(.+)/ or &config_error($line, "invalid login string"); $vsrv{login} = $1; } elsif ($rcmd =~ /^passwd\s*=\s*\"(.*)\"/) { $1 =~ /(.+)/ or &config_error($line, "invalid password"); $vsrv{passwd} = $1; } elsif ($rcmd =~ /^database\s*=\s*\"(.*)\"/) { $1 =~ /(.+)/ or &config_error($line, "invalid database"); $vsrv{database} = $1; } elsif ($rcmd =~ /^secret\s*=\s*\"(.*)\"/) { $1 =~ /(.+)/ or &config_error($line, "invalid secret"); $vsrv{secret} = $1; } elsif ($rcmd =~ /^load\s*=\s*\"(.*)\"/) { $1 =~ /(\w+)/ or &config_error($line, "invalid string for load testing"); $vsrv{load} = $1; } elsif ($rcmd =~ /^scheduler\s*=\s*(.*)/) { # Intentionally ommit checking the # scheduler against a list of know # schedulers. This is because from # time to time new schedulers are # added. But ldirectord is # maintained distributed # independently of this. Thus # ldirectord needs to be manually # updated/upgraded. So just accept # any scheduler that matches # [a-z]+. I.e. is syntactically # correct (all schedulers so far # match that pattern). Ipvsadm will # report an error is a scheduler # isn't available / doesn't exist. $1 =~ /([a-z]+)/ or &config_error($line, "invalid scheduler, should be only lowercase letters (a-z)"); $vsrv{scheduler} = $1; } elsif ($rcmd =~ /^persistent\s*=\s*(.*)/) { $1 =~ /(\d+)/ or &config_error($line, "invalid persistent timeout"); $vsrv{persistent} = $1; } elsif ($rcmd =~ /^netmask\s*=\s*(.*)/) { $1 =~ /(\d+\.\d+\.\d+\.\d+)/ or &config_error($line, "invalid netmask"); $vsrv{netmask} = $1; } elsif ($rcmd =~ /^protocol\s*=\s*(.*)/) { if ( $1 =~ /(\w+)/ ) { if ( $vsrv{protocol} eq "fwm" ) { if ($1 eq "fwm" ) { ; #Do nothing, it is already set } else { &config_error($line, "protocol must be fwm if the virtual service is a fwmark (a number)"); } } else { # tcp or udp if ($1 eq "tcp" || $1 eq "udp") { $vsrv{protocol} = $1; } else { &config_error($line, "protocol must be tcp or udp if the virtual service is an address and port"); } } } else { &config_error($line, "invalid protocol"); } } elsif ($rcmd =~ /^service\s*=\s*(.*)/) { $1 =~ /(\w+)/ && ($1 eq "dns" || $1 eq "ftp" || $1 eq "http" || $1 eq "https" || $1 eq "http_proxy" || $1 eq "imap" || $1 eq "imaps" || $1 eq "ldap" || $1 eq "nntp" || $1 eq "mysql" || $1 eq "none" || $1 eq "oracle"|| $1 eq "pop" || $1 eq "pops" || $1 eq "radius"|| $1 eq "pgsql" || $1 eq "sip" || $1 eq "smtp" || $1 eq "submission" || $1 eq "simpletcp") or &config_error($line, "service must " . "be dns, ftp, " . "http, https, " . "http_proxy, " . "imap, imaps, " . "ldap, nntp, " . "mysql, none, " . "oracle, " . "pop, pops, " . "radius, " . "pgsql, " . "simpletcp, " . "sip, smtp " . "or submission"); $vsrv{service} = $1; if($vsrv{service} eq "ftp" and $vsrv{login} eq "") { $vsrv{login} = "anonymous"; } elsif($vsrv{service} eq "sip" and $vsrv{login} eq "") { $vsrv{login} = "ldirectord\@$HOSTNAME"; } if($vsrv{service} eq "ftp" and $vsrv{passwd} eq "") { $vsrv{passwd} = "ldirectord\@$HOSTNAME"; } } elsif ($rcmd =~ /^httpmethod\s*=\s*(.*)/) { $1 =~ /(\w+)/ && (uc($1) eq "GET" || uc($1) eq "HEAD") or &config_error($line, "httpmethod must be GET or HEAD"); $vsrv{httpmethod} = uc($1); } elsif ($rcmd =~ /^virtualhost\s*=\s*(.*)/) { $1 =~ /\"?([^\"]*)\"?/ or &config_error($line, "invalid virtualhost"); $vsrv{virtualhost} = $1; } elsif ($rcmd =~ /^(fallback(6)?)\s*=\s*(.*)/) { # Allow specification of a virtual-specific fallback host if ($af == AF_INET && defined($2) || $af == AF_INET6 && ! defined($2)) { &config_error($line, join("", ("cannot specify \"$1\" here. please use \"fallback", ($af == AF_INET) ? "" : "6", "\" instead"))); } $fallback_line=$line; $vsrv{fallback} = parse_fallback($line, $3, \%vsrv); } elsif ($rcmd =~ /^fallbackcommand\s*=\s*\"(.*)\"/ or $rcmd =~ /^fallbackcommand\s*=\s*(.*)/) { $1 =~ /(.+)/ or &config_error($line, "invalid fallback command"); $vsrv{fallbackcommand} = $1; } elsif ($rcmd =~ /^quiescent\s*=\s*(.*)/) { ($1 eq "yes" || $1 eq "no") or &config_error($line, "quiescent must be 'yes' or 'no'"); $vsrv{quiescent} = $1; } elsif ($rcmd =~ /^emailalert\s*=\s*(.*)/) { $vsrv{emailalert} = read_emailalert($line, $1); } elsif ($rcmd =~ /^emailalertfreq\s*=\s*(\d*)/) { $1 =~ /(\d+)/ or &config_error($line, "invalid email alert frequency"); $vsrv{emailalertfreq} = $1; } elsif ($rcmd =~ /^emailalertstatus\s*=\s*(.*)/) { $vsrv{emailalertstatus} = &parse_emailalertstatus($line, $1); } elsif ($rcmd =~ /^monitorfile\s*=\s*\"(.*)\"/ or $rcmd =~ /^monitorfile\s*=\s*(.*)/) { my $monitorfile = $1; unless (open(MONITORFILE, ">>$monitorfile") and close(MONITORFILE)) { &config_error($line, "unable to open monitorfile $monitorfile: $!"); } $vsrv{monitorfile} = $monitorfile; } elsif ($rcmd =~ /^cleanstop\s*=\s*(.*)/) { ($1 eq "yes" || $1 eq "no") or &config_error($line, "cleanstop must be 'yes' or 'no'"); $vsrv{cleanstop} = $1; } elsif ($rcmd =~ /^smtp\s*=\s*(.*)/) { $1 =~ /(^([0-9A-Za-z._+-]+))/ or &config_error($line, "invalid SMTP server address"); $vsrv{smtp} = $1; } else { &config_error($line, "Unknown command \"$linedata\""); } undef $linedata; } # As the protocol needs to be known to call # getservbyname() all resolution must be # delayed until the protocol is finalised. # That is after the entire configuration # for a virtual service has been parsed. &_ld_read_config_fallback_resolve($fallback_line, $vsrv{protocol}, $vsrv{fallback}, $af); &_ld_read_config_virtual_resolve($virtual_line, \%vsrv, $ip_port, $af); &_ld_read_config_real_resolve(\%vsrv, \@rsrv_todo, $af); # Check for duplicate now we have all the # information to generate the id $virtual_id = get_virtual_id_str(\%vsrv); if (defined $virtual_seen{$virtual_id}) { &config_error($line, "duplicate virtual server"); } $virtual_seen{$virtual_id} = 1; unless(defined($linedata)) { last; } #Arggh a goto :( goto outer_loop; } next if ($linedata =~ /^\s*$/ || $linedata =~ /^\s*#/); if ($linedata =~ /^checktimeout\s*=\s*(.*)/) { ($1 =~ /(\d+)/ && $1 && $1>0) or &config_error($line, "invalid check timeout value"); $CHECKTIMEOUT = $1; } elsif ($linedata =~ /^connecttimeout\s*=\s*(.*)/) { &config_error($line, "connecttimeout directive " . "deprecated in favour of " . "negotiatetimeout"); } elsif ($linedata =~ /^negotiatetimeout\s*=\s*(.*)/) { ($1 =~ /(\d+)/ && $1 && $1>0) or &config_error($line, "invalid negotiate timeout value"); $NEGOTIATETIMEOUT = $1; } elsif ($linedata =~ /^checkinterval\s*=\s*(.*)/) { $1 =~ /(\d+)/ && $1 or &config_error($line, "invalid check interval value"); $CHECKINTERVAL = $1; } elsif ($linedata =~ /^checkcount\s*=\s*(.*)/) { $1 =~ /(\d+)/ && $1 or &config_error($line, "invalid check count value"); $CHECKCOUNT = $1; &config_warn($line, "checkcount option is deprecated and slated for removal. please see 'failurecount'"); } elsif ($linedata =~ /^failurecount\s*=\s*(.*)/) { $1 =~ /(\d+)/ && $1 or &config_error($line, "invalid failure count value"); $FAILURECOUNT = $1; } elsif ($linedata =~ /^fallback(6)?\s*=\s*(.*)/) { my $af = defined($1) ? AF_INET6 : AF_INET; my $tcp = parse_fallback($line, $2, undef); my $udp = parse_fallback($line, $2, undef); &_ld_read_config_fallback_resolve($line, "tcp", $tcp, $af); &_ld_read_config_fallback_resolve($line, "udp", $udp, $af); if ($af == AF_INET) { $FALLBACK = { "tcp" => $tcp, "udp" => $udp }; } else { $FALLBACK6 = { "tcp" => $tcp, "udp" => $udp }; } } elsif ($linedata =~ /^fallbackcommand\s*=\s*(.*)/) { $1 =~ /(.+)/ or &config_error($line, "invalid fallback command"); $FALLBACKCOMMAND = $1; } elsif ($linedata =~ /^autoreload\s*=\s*(.*)/) { ($1 eq "yes" || $1 eq "no") or &config_error($line, "autoreload must be 'yes' or 'no'"); $AUTOCHECK = $1; } elsif ($linedata =~ /^callback\s*=\s*\"(.*)\"/) { $CALLBACK = $1; } elsif ($linedata =~ /^logfile\s*=\s*\"(.*)\"/) { my $tmpLDIRLOG = $LDIRLOG; $LDIRLOG = $1; if (&ld_openlog()) { $LDIRLOG = $tmpLDIRLOG; &config_error($line, "unable to open logfile: $1"); } } elsif ($linedata =~ /^execute\s*=\s*(.*)/) { $LD_INSTANCE{$1} = 1; } elsif ($linedata =~ /^fork\s*=\s*(.*)/) { ($1 eq "yes" || $1 eq "no") or &config_error($line, "fork must be 'yes' or 'no'"); $FORKING = $1; } elsif ($linedata =~ /^supervised/) { if (($linedata =~ /^supervised\s*=\s*(.*)/) and ($1 eq "yes" || $1 eq "no")) { $SUPERVISED = $1; } elsif ($linedata =~ /^supervised\s*$/) { $SUPERVISED = "yes"; &config_warn($line, "please update your config not to " . "use a bare supervised directive"); } else { &config_error($line, "supervised must be 'yes' or 'no'"); } } elsif ($linedata =~ /^quiescent\s*=\s*(.*)/) { ($1 eq "yes" || $1 eq "no") or &config_error($line, "quiescent must be 'yes' or 'no'"); $QUIESCENT = $1; } elsif ($linedata =~ /^emailalert\s*=\s*(.*)/) { $EMAILALERT = read_emailalert($line, $1); } elsif ($linedata =~ /^emailalertfreq\s*=\s*(\d*)/) { $1 =~ /(\d+)/ or &config_error($line, "invalid email alert frequency"); $EMAILALERTFREQ = $1; } elsif ($linedata =~ /^emailalertstatus\s*=\s*(.*)/) { $EMAILALERTSTATUS = &parse_emailalertstatus($line, $1); } elsif ($linedata =~ /^emailalertfrom\s*=\s*(.*)/) { $1 =~ /(.+)/ or &config_error($line, "no email from address specified"); $EMAILALERTFROM = $1; } elsif ($linedata =~ /^cleanstop\s*=\s*(.*)/) { ($1 eq "yes" || $1 eq "no") or &config_error($line, "cleanstop must be 'yes' or 'no'"); $CLEANSTOP = $1; } elsif ($linedata =~ /^smtp\s*=\s*(.*)/) { $1 =~ /(^([0-9A-Za-z._+-]+))/ or &config_error($line, "invalid SMTP server address"); $SMTP = $1; } elsif ($linedata =~ /^maintenancedir\s*=\s*(.*)/) { $1 =~ /(.+)/ or &config_error($line, "maintenance directory not specified"); $MAINTDIR = $1; -d $MAINTDIR or &config_warn($line, "maintenance directory does not exist"); } else { if ($linedata =~ /^timeout\s*=\s*(.*)/) { &config_error($line, "timeout directive " . "deprecated in favour of " . "checktimeout and " . "negotiatetimeout"); } &config_error($line, "Unknown command $linedata "); } } close(CFGFILE); # Check for sensible use of checkinterval, warn if it is used in a virtual # service when fork=no if ($FORKING eq 'no') { foreach my $v (@VIRTUAL) { if (defined($$v{checkinterval})) { config_warn(-1, "checkinterval in virtual service ". get_virtual_id_str($v)." ignored when fork=no"); } } } return(0); } # _ld_read_config_virtual_resolve # Note: Should not need to be called directly, but won't do any damage if # you do. # Resolve the server (ip address) and port for a virtual service # pre: line: Line of configuration file fallback server was read from # Used for debugging messages # vsrv: Virtual Service to resolve server and port of # ip_port: server and port in the form # ip_address|hostname:port|service # af: Address family: AF_INET or AF_INET6 # post: Take ip_port, resolve it as per ld_gethostservbyname # and set $vsrv->{server} and $vsrv->{port} accordingly. # If $vsrv->{service} is not set, then set according to the value of # $vsrv->{port} # return: none # Debugging message will be reported and programme will exit # on error. sub _ld_read_config_virtual_resolve { my($line, $vsrv, $ip_port, $af)=(@_); if($ip_port){ $ip_port=&ld_gethostservbyname($ip_port, $vsrv->{protocol}, $af); if ($ip_port =~ /(\[[0-9A-Fa-f:]+\]):(\d+)/) { $vsrv->{server} = $1; $vsrv->{port} = $2; } elsif($ip_port){ ($vsrv->{server}, $vsrv->{port}) = split /:/, $ip_port; } else { &config_error($line, "invalid address for virtual service"); } if(!defined($vsrv->{service})){ $vsrv->{service} = ld_port_to_service($vsrv->{port}); } } } # ld_service_to_port # Resolve an ldirectord service name from its port number # pre: port: port number of the service # return: port name # "none" if the service is unknown sub ld_port_to_service { my ($port) = (@_); if ($port eq 21) { return "ftp"; } if ($port eq 25) { return "smtp"; } if ($port eq 53) { return "dns"; } if ($port eq 80) { return "http"; } if ($port eq 110) { return "pop"; } if ($port eq 119) { return "nntp"; } if ($port eq 143) { return "imap"; } if ($port eq 389) { return "ldap"; } if ($port eq 443) { return "https"; } if ($port eq 587) { return "submission"; } if ($port eq 995) { return "pops"; } if ($port eq 993) { return "imaps"; } if ($port eq 1521) { return "oracle"; } if ($port eq 1812) { return "radius"; } if ($port eq 3128) { return "http_proxy"; } if ($port eq 3306) { return "mysql"; } if ($port eq 5060) { return "sip"; } if ($port eq 5432) { return "pgsql"; } return "none"; } # ld_service_to_port # Resolve the port number from an ldirectord service name # pre: service: name of the service # return: port number # undef if the service is unknown sub ld_service_to_port { my ($service) = (@_); if ($service eq "ftp") { return 21; } if ($service eq "smtp") { return 25; } if ($service eq "dns") { return 53; } if ($service eq "http") { return 80; } if ($service eq "pop") { return 110; } if ($service eq "nntp") { return 119; } if ($service eq "imap") { return 143; } if ($service eq "ldap") { return 389; } if ($service eq "https") { return 443; } if ($service eq "submission") { return 587; } if ($service eq "imaps") { return 993; } if ($service eq "pops") { return 995; } if ($service eq "oracle") { return 1521; } if ($service eq "radius") { return 1812; } if ($service eq "http_proxy") { return 3128; } if ($service eq "mysql") { return 3306; } if ($service eq "sip") { return 5060; } if ($service eq "pgsql") { return 5432; } return undef; } # ld_checkport # Resolve the port to connect to for service checks # Note: Should only be used inside service checks, # as its not the same as the port of the real server # pre: v: virtual service # r: real server # return: port number # undef if the service is unknown sub ld_checkport { my ($v, $r) = (@_); if (defined $v->{checkport}) { return $v->{checkport}; } if ($r->{port} > 0) { return $r->{port}; } return ld_service_to_port($v->{service}); } # _ld_read_config_fallback_resolve # Note: Should not need to be called directly, but won't do any damage if # you do. # Resolve the fallback server for a virtual service # pre: line: Line of configuration file fallback server was read from # Used for debugging messages # vsrv: Virtual Service to resolve fallback server of # af: Address family: AF_INET or AF_INET6 # post: Take $vsrv->{fallback}, resolve it as per ld_gethostservbyname # and set $vsrv->{fallback} to the result # return: none # Debugging message will be reported and programme will exit # on error. sub _ld_read_config_fallback_resolve { my($line, $protocol, $fallback, $af)=(@_); my ($ipversion, $ipaddress); unless($fallback) { return; } if ($af == AF_INET) { $ipversion = "IPv4"; } elsif ($af == AF_INET6) { $ipversion = "IPv6"; } else { $ipversion = "IP??($af)"; } unless ($ipaddress = &ld_gethostbyname($fallback->{server}, $af)) { &config_error($line, "invalid $ipversion address or could not resolve for fallback server: " . $fallback->{server}); } $fallback->{server} = $ipaddress; unless($fallback->{"port"}) { return; } $fallback->{port} = &ld_getservbyname($fallback->{port}, $protocol) or &config_error($line, "invalid port for fallback server"); } # _ld_read_config_real_resolve # Note: Should not need to be called directly, but won't do any damage if # you do. # Run through the list of real servers read in the configuration file for a # virtual server and parse these entries # pre: vsrv: Virtual Service to parse real servers for # rsrv_todo: List of real servers read from config but not parsed. # List is a list of list reference. The first element in # each list reference is the line read from the # configuration after "real=". The second element is the # line number, used for error reporting # af: Address family: AF_INET or AF_INET6 # post: Run through rsrv_todo and parse real servers # return: none # Debugging message will be reported and programme will exit # on error. sub _ld_read_config_real_resolve { my ($vsrv, $rsrv_todo, $af)=(@_); my $i; my $str; my $line; my $ip1; my $ip2; my $port; my $resolved_ip1; my $resolved_ip2; my $resolved_port; my $flags; for $i (@$rsrv_todo) { ($str, $line)=@$i; $str =~ /(\d+\.\d+\.\d+\.\d+|[A-Za-z0-9.-]+|\[[0-9A-fa-f:]+\])(->(\d+\.\d+\.\d+\.\d+|[A-Za-z0-9.-]+|\[[0-9A-fa-f:]+\]))?(:(\d+|[A-Za-z0-9-_]+))?\s+(.*)/ or &config_error($line, "invalid address for real server" . " (wrong format)"); $ip1=$1; $ip2=$3; if(defined($5)){ $port=$5; } else { $port="0"; } $flags=$6; $resolved_ip1=&ld_gethostbyname($ip1, $af); unless( defined($resolved_ip1) ) { &config_error($line, "invalid address ($ip1) for real server" . " (could not resolve host)"); } if( defined($port) ){ $resolved_port=&ld_getservbyname($port); unless( defined($resolved_port) ){ &config_error($line, "invalid port ($port) for real server" . " (could not resolve port)"); } } if ( defined ($ip2) ) { $resolved_ip2=&ld_gethostbyname($ip2, $af); unless( defined ($resolved_ip2) ) { &config_error($line, "invalid address ($ip2) for " . "real server" . " (could not resolve end host)"); } &add_real_server_range($line, $vsrv, $resolved_ip1, $resolved_ip2, $resolved_port, $flags, $af); } else { &add_real_server($line, $vsrv, $resolved_ip1, $resolved_port, $flags); } } } # add_real_server_range # Add a real server for each IP address in a range # pre: line: line number real server was read from # Used for debugging information # vsrv: virtual server to add real server to # first: First IP address in range # last: First IP address in range # port: Port of real servers # flags: Flags for real servers. Should be of the form # gate|masq|ipip [] [">I", ""] # af: Address family: AF_INET or AF_INET6 # post: real servers are added to virtual server # return: none # Debugging message will be reported and programme will exit # on error. sub add_real_server_range { my ($line, $vsrv, $first, $last, $port, $flags, $af) = (@_); my (@tmp, $first_i, $last_i, $i, $rsrv); if ($af == AF_INET) { if ( ($first_i=&ip_to_int($first)) <0 ) { &config_error($line, "Invalid IP address: $first"); } if ( ($last_i=&ip_to_int($last)) <0 ) { &config_error($line, "Invalid IP address: $last"); } if ($first_i>$last_i) { &config_error($line, "Invalid Range: $first-$last: First value must be " . "greater than or equal to the second value"); } # A for loop didn't seem to want to work $i=$first_i; while ( $i le $last_i ) { &add_real_server($line, $vsrv, &int_to_ip($i), $port, $flags); $i++; } } elsif ($af == AF_INET6) { # not supported yet &config_error($line, "Address range for IPv6 is not supported yet"); } else { die "address family must be AF_INET or AF_INET6\n"; } } # add_real_server # Add a real server to a virtual # pre: line: line number real server was read from # Used for debugging information # vsrv: virtual server to add real server to # ip: IP address of real server # port: Port of real server # flags: Flags for real server. Should be of the form # gate|masq|ipip [] [">I", ""] # post: real server is added to virtual server # return: none # Debugging message will be reported and programme will exit # on error. sub add_real_server { my ($line, $vsrv, $ip, $port, $flags) = (@_); my $ref; my $realsrv=0; my $new_rsrv; my $rsrv; $new_rsrv = {"server"=>$ip, "port"=>$port}; $flags =~ /(\w+)(.*)/ && ($1 eq "gate" || $1 eq "masq" || $1 eq "ipip") or &config_error($line, "forward method must be gate, masq or ipip"); $new_rsrv->{"forward"} =$1; $flags = $2; $rsrv=$vsrv->{"real"}; if(defined($flags) and $flags =~ /\s+(\d+)(.*)/) { $new_rsrv->{"weight"} = $1; $flags = $2; } else { $new_rsrv->{"weight"} = 1; } if(defined($flags) and $flags =~ /\s+\"(.*)\"[, ]\s*\"(.*)\"(.*)/) { $new_rsrv->{"request"} = $1; unless ($new_rsrv->{request}=~/^\//) { $new_rsrv->{request} = "/" . $new_rsrv->{request}; } $new_rsrv->{"receive"} = $2; $flags = $3; } if (defined($flags) and $flags =~/\S/) { &config_error($line, "Invalid real server line, around " . "\"$flags\""); } push(@$rsrv, $new_rsrv); my $real = get_real_id_str($new_rsrv, $vsrv); my $virtual = get_virtual_id_str($vsrv); for my $r (@REAL){ if($r->{"real"} eq $real){ my $ref=$r->{"virtual"}; push(@$ref, $virtual); $realsrv=1; last; } } if($realsrv==0){ push(@REAL, { "real"=>$real, "virtual"=>[ $virtual ] }); } } # parse_fallback # Parse a fallback server # pre: line: line number real server was read from # fallback: line read from configuration file # Should be of the form # ip_address|hostname[:port|:service_name] [gate|masq|ipip] # post: fallback is parsed # return: Reference to hash of the form # { server => blah, forward => blah } # Debugging message will be reported and programme will exit # on error. sub parse_fallback { my ($line, $fallback, $vsrv) = (@_); my $parse_line; my $server; my $port; my $fwd; $parse_line = $fallback; if ($parse_line =~ /(\S+)(\s+(\S+))?\s*$/) { # get "ip:port" and a forwarding method $fwd = $3; $parse_line = $1; } if ($parse_line =~ /(:(\d+|[A-Za-z0-9-_]+))?$/) { # get host and port $port=$2; $parse_line =~ s/(:(\d+|[A-Za-z0-9-_]+))?$//; $server = $parse_line; } unless(defined($server)) { &config_error($line, "invalid fallback server: $fallback"); } if (not defined($port) and defined($vsrv)) { $port = $vsrv->{"port"}; } if($fwd) { ($fwd eq "gate" || $fwd eq "masq" || $fwd eq "ipip") or &config_error($line, "forward method must be gate, masq or ipip"); } else { $fwd="gate" } return({"server"=>$server, "port"=>$port, "forward"=>$fwd, "weight"=>1}); } sub __config_log { my ($line, $prefix, $msg) = @_; chomp($msg); $msg .= "\n"; my $msg_prefix = "$prefix [$$]"; if ($line > 0) { $msg_prefix .= " reading file $CONFIG at line $line"; } $msg = "$msg_prefix: $msg"; if ($opt_d or $DAEMON_STATUS == $DAEMON_STATUS_STARTING) { print STDERR $msg; } else { &ld_log("$msg"); } } sub config_warn { my ($line, $msg) = @_; __config_log($line, "Warning", $msg); } sub config_error { my ($line, $msg) = @_; __config_log($line, "Error", $msg); if ($DAEMON_STATUS == $DAEMON_STATUS_STARTING) { &ld_rm_file("$RUNPID.$CFGNAME.pid"); &ld_exit(2, "config_error: Configuration Error"); } else { die; } } sub ld_setup { for my $v (@VIRTUAL) { if ($$v{protocol} eq "tcp") { $$v{proto} = "-t"; } elsif ($$v{protocol} eq "udp") { $$v{proto} = "-u"; } elsif ($$v{protocol} eq "fwm") { $$v{proto} = "-f"; } $$v{flags} = "$$v{proto} " . &get_virtual_option($v) . " "; $$v{flags} .= "-s $$v{scheduler} " if defined ($$v{scheduler}); if (defined $$v{persistent}) { $$v{flags} .= "-p $$v{persistent} "; $$v{flags} .= "-M $$v{netmask} " if defined ($$v{netmask}); } my $real = $$v{real}; for my $r (@$real) { $$r{forw} = get_forward_flag($$r{forward}); my $port=ld_checkport($v, $r); my $schema = $$v{service}; if ($$v{service} eq 'http_proxy') { $schema = 'http'; } if (defined $$r{request} && defined $$r{receive}) { my $uri = $$r{request}; $uri =~ s/^\///g; if ($$r{request} =~ /$schema:\/\//) { $$r{url} = "$uri"; } else { $$r{url} = "$schema:\/\/$$r{server}:$port\/$uri"; } } else { my $uri = $$v{request}; $uri =~ s/^\///g; if ($$v{service} eq 'http_proxy') { $$r{url} = "$uri"; } else { $$r{url} = "$schema:\/\/$$r{server}:$port\/$uri"; } $$r{request} = $$v{request} unless defined $$r{request}; $$r{receive} = $$v{receive}; } if ($$v{checktype} eq "combined") { $$r{num_connects} = 999999; } else { $$r{num_connects} = -1; } } # checktimeout and negotiate timeout are # mutual defaults for each other, so calculate # checktimeout in a temporary variable so as not # to affect the calculation of negotiatetimeout. my $checktimeout = $$v{checktimeout}; if ($checktimeout < 0) { $checktimeout = $$v{negotiatetimeout}; } if ($checktimeout < 0) { $checktimeout = $CHECKTIMEOUT; } if ($checktimeout < 0) { $checktimeout = $NEGOTIATETIMEOUT; } if ($checktimeout < 0) { $checktimeout = $DEFAULT_CHECKTIMEOUT; } if ($$v{negotiatetimeout} < 0) { $$v{negotiatetimeout} = $$v{checktimeout}; } if ($$v{negotiatetimeout} < 0) { $$v{negotiatetimeout} = $NEGOTIATETIMEOUT; } if ($$v{negotiatetimeout} < 0) { $$v{negotiatetimeout} = $CHECKTIMEOUT; } if ($$v{negotiatetimeout} < 0) { $$v{negotiatetimeout} = $DEFAULT_NEGOTIATETIMEOUT; } $$v{checktimeout} = $checktimeout; if ($$v{checkcount} < 0) { $$v{checkcount} = $CHECKCOUNT; } if ($$v{failurecount} < 0) { $$v{failurecount} = $FAILURECOUNT; } } } # ld_read_ipvsadm # # Net::FTP seems to set the input record separator ($\) to null # putting IO into slurp (whole file at a time, rather than line at a time) # mode. Net::FTP does this using local $\, which should mean # that the change doesn' effect code here, but it does. It also # seems to be impossible to turn it off, by say setting $\ back to '\n' # Perhaps there is more to this than meets the eye. Perhaps it's a perl bug. # In any case, this should fix the problem. # # This should not affect pid or config file parsing as they are called # before Net::FTP and as this appears to be a bit of a work around, # I'd rather use it in as few places as possible # # Observed with perl v5.8.8 (Debian's perl 5.8.8-6) # -- Horms, 17th July 2005 sub ld_readline { my ($fd, $buf) = (@_); my $line; # Uncomment the following line to turn off this work around # return readline($fd); $line = shift @$buf; if (defined $line) { return $line . "\n"; } push @$buf, split /\n/, readline($fd); $line = shift @$buf; if (defined $line) { return $line . "\n"; } return undef; } # ld_read_ipvsadm # Parses the output of "ipvsadm -L -n" and puts into a structure of # the following from: # # { # (vip_address:vport|fwmark) protocol => { # "scheduler" => scheduler, # "persistent" => timeout, # May be omitted # "netmask" => netmask, # May be omitted # "real" => { # rip_address:rport => { # "forward" => forwarding_mechanism, # "weight" => weight # }, # ... # } # }, # ... # } # # where: # vip_address: IP address of virtual service # vport: Port of virtual service # fwmark: Firewall Mark of virtual service # scheduler: Scheduler for virtual service # timeout: Timeout for persistency. Omitted if service is not persistent. # nemask: Netmask for persistency. Omitted if service is not persistent. # # rip_address: IP address of real server # rport: Port of real server # forwarding_mechanism: Forwarding mechanism for real server. # One of: gate, ipip, masq. # weight: Weight of real server # # pre: none # post: ipvsadm -L -n is parsed # result: reference to sructure detailed above. sub ld_read_ipvsadm { my %oldsrv; my $real_service; my $fwd; my $buf = []; my $fh; my $line; # read status of current ipvsadm -L -n unless(open($fh, "$IPVSADM -L -n 2>&1|")){ &ld_exit(1, "Could not run $IPVSADM -L -n: $!"); } # Skip the first three lines $line = ld_readline($fh, $buf); $line = ld_readline($fh, $buf); $line = ld_readline($fh, $buf); while (1) { $line = ld_readline($fh, $buf); if (not defined $line) { last; } if ($line =~ /^(\w+)\s+(\d+\.\d+\.\d+\.\d+\:\d+|\[[0-9A-Fa-f:]+\]:\d+|\d+)\s+(\w+)\s+persistent\s+(\d+)\s+mask\s+(.*)/) { $real_service = "$2 ".lc($1); $oldsrv{"$real_service"} = {"real"=>{}, "scheduler"=>$3, "persistent"=>$4, "netmask"=>$5}; } elsif ($line =~ /^(\w+)\s+(\d+\.\d+\.\d+\.\d+\:\d+|\[[0-9A-Fa-f:]+\]:\d+|\d+)\s+(\w+)\s+persistent\s+(\d+)/) { $real_service = "$2 ".lc($1); $oldsrv{"$real_service"} = {"real"=>{}, "scheduler"=>$3, "persistent"=>$4}; } elsif ($line =~ /^(\w+)\s+(\d+\.\d+\.\d+\.\d+\:\d+|\[[0-9A-Fa-f:]+\]:\d+|\d+)\s+(\w+)/) { $real_service = "$2 ".lc($1); $oldsrv{"$real_service"} = {"real"=>{}, "scheduler"=>$3}; } elsif ($line =~ /^ ->\s+(\d+\.\d+\.\d+\.\d+\:\d+|\[[0-9A-Fa-f:]+\]:\d+)\s+(\w+)\s+(\d+)/) { if (not defined( $real_service)) { &ld_debug(2, "Real server read from ipvsadm " . "doesn't seem to be inside a " . "virtual service: \"$line\"\n"); next; } if ($2 eq "Route") { $fwd = "gate"; } elsif ($2 eq "Tunnel") { $fwd = "ipip"; } elsif ($2 eq "Masq") { $fwd = "masq"; } $oldsrv{"$real_service"}->{"real"}->{"$1"} = {"forward"=>$fwd, "weight"=>$3}; } else { &ld_debug(2, "Unknown line read from ipvsadm: " . "\"$line\"\n"); next; } } close($fh); return(\%oldsrv); } sub ld_start { my $oldsrv; my $real_service; my $nv; my $nr; my $server_down = {}; # read status of current ipvsadm -L -n $oldsrv=&ld_read_ipvsadm(); # make sure virtual servers are up to date foreach $nv (@VIRTUAL) { my $real_service = &get_virtual($nv) . " " . $nv->{protocol}; if (exists($oldsrv->{"$real_service"})) { # service exists, modify it &system_wrapper("$IPVSADM -E $$nv{flags}"); &ld_log("Changed virtual server: " . &get_virtual($nv)); } else { # no such service, create a new one &system_wrapper("$IPVSADM -A $$nv{flags}"); &ld_log("Added virtual server: " . &get_virtual($nv)); } } # make sure real servers are up to date foreach $nv (@VIRTUAL) { my $nreal = $nv->{real}; my $ov = $oldsrv->{&get_virtual($nv) . " " . $nv->{protocol}}; my $or = $ov->{real}; my $fallback = fallback_find($nv); if (defined($fallback)) { delete($or->{"$fallback->{server}:$fallback->{port}"}); } for $nr (@$nreal) { my $real_str = "$nr->{server}:$nr->{port}"; if (! defined($or->{$real_str}) or $or->{$real_str}->{weight} == 0) { $server_down->{$real_str} = [$nv, $nr]; #service_set($nv, $nr, "down", {force => 1}); } else { if (defined $server_down->{$real_str}) { delete($server_down->{$real_str}); } service_set($nv, $nr, "up", {force => 1}); } delete($or->{$real_str}); } # remove remaining entries for real servers for my $k (keys %$or) { purge_untracked_service($nv, $k, "start"); delete($$or{$k}); } delete($oldsrv->{&get_virtual($nv) . " " . $nv->{protocol}}); &fallback_on($nv); } for my $k (keys (%$server_down)) { my $v = $server_down->{$k}; service_set(@$v[0], @$v[1], "down", {force => 1}); delete($server_down->{$k}); #sleep 5; } # remove remaining entries for virtual servers foreach $nv (@OLDVIRTUAL) { if (! defined($oldsrv->{&get_virtual($nv) . " " . $nv->{protocol}})) { next; } purge_virtual($nv, "start"); } } sub ld_cmd_children { my ($cmd, %children) = (@_); # instantiate other ldirectord, if specified my $child; foreach $child (keys %children) { if ($cmd eq "reload_or_start") { if (&system_wrapper("$LDIRECTORD $child reload")) { &system_wrapper("$LDIRECTORD $child start"); } } else { &system_wrapper("$LDIRECTORD $child $cmd"); } } } sub ld_stop { # Kill children if ($FORKING eq 'yes') { foreach my $virtual_id (keys (%FORK_CHILDREN)) { my $pid = $FORK_CHILDREN{$virtual_id}; ld_log("Killing child $virtual_id PID=$pid"); kill 15, $pid; } } foreach my $v (@VIRTUAL) { next if ( (! defined($$v{cleanstop}) and $CLEANSTOP eq 'no') or (defined($$v{cleanstop}) and $$v{cleanstop} eq 'no') ); my $real = $$v{real}; foreach my $r (@$real) { if (defined $$r{virtual_status}) { purge_service($v, $r, "stop"); } } purge_virtual($v, "stop"); } } sub ld_main { # Main failover checking code while (1) { if ($FORKING eq 'yes') { foreach my $v (@VIRTUAL) { my $virtual_id = get_virtual_id_str($v); if (!exists($FORK_CHILDREN{$virtual_id})) { &ld_log("Child not running for $virtual_id, spawning"); my $pid = fork; if (!defined($pid)) { &ld_log("fork failed"); } elsif ($pid == 0) { run_child($v); } else { $FORK_CHILDREN{get_virtual_id_str($v)} = $pid; &ld_log("Spawned child $virtual_id PID=$pid"); } } elsif (waitpid($FORK_CHILDREN{get_virtual_id_str($v)}, WNOHANG)) { delete $FORK_CHILDREN{get_virtual_id_str($v)}; } } check_signal(); if (!check_cfgfile()) { sleep 1; } check_signal(); } else { my @real_checked; foreach my $v (@VIRTUAL) { my $real = $$v{real}; my $virtual_id = get_virtual_id_str($v); REAL: foreach my $r (@$real) { my $real_id = get_real_id_str($r, $v); check_signal(); foreach my $tmp_id (@real_checked) { if($real_id eq $tmp_id) { &ld_debug(3, "Already checked: real server=$real_id (virtual=$virtual_id)"); next REAL; } } _check_real($v, $r); push(@real_checked, $real_id); } } check_signal(); if (!check_cfgfile()) { sleep $CHECKINTERVAL; } check_signal(); ld_emailalert_resend(); check_signal(); } } } sub run_child { my $v = shift; # Just exit on signals $SIG{'INT'} = "DEFAULT"; $SIG{'QUIT'} = "DEFAULT"; $SIG{'ILL'} = "DEFAULT"; $SIG{'ABRT'} = "DEFAULT"; $SIG{'FPE'} = "DEFAULT"; $SIG{'SEGV'} = "DEFAULT"; $SIG{'TERM'} = "DEFAULT"; $SIG{'BUS'} = "DEFAULT"; $SIG{'SYS'} = "DEFAULT"; $SIG{'XCPU'} = "DEFAULT"; $SIG{'XFSZ'} = "DEFAULT"; $SIG{'IOT'} = "DEFAULT"; $SIG{'PIPE'} = "IGNORE"; $SIG{'HUP'} = sub { exit 1 }; my $real = $$v{real}; my $virtual_id = get_virtual_id_str($v); my $checkinterval = $$v{checkinterval} || $CHECKINTERVAL; $0 = "ldirectord $virtual_id"; while (1) { foreach my $r (@$real) { $0 = "ldirectord $virtual_id checking $$r{server}"; _check_real($v, $r); } $0 = "ldirectord $virtual_id"; sleep $checkinterval; ld_emailalert_resend(); } } sub _check_real { my $v = shift; my $r = shift; my $real_id = get_real_id_str($r, $v); my $virtual_id = get_virtual_id_str($v); if (_check_real_for_maintenance($r)) { service_set($v, $r, "down", {do_log => 1, force => 1}, "Server in maintenance"); return; } elsif ($$v{checktype} eq "negotiate" || $$r{num_connects}>=$$v{num_connects}) { &ld_debug(2, "Checking negotiate: real server=$real_id (virtual=$virtual_id)"); if (grep $$v{service} eq $_, ("http", "https", "http_proxy")) { $$r{num_connects} = 0 if (check_http($v, $r) == $SERVICE_UP); } elsif ($$v{service} eq "pop") { $$r{num_connects} = 0 if (check_pop($v, $r, 0) == $SERVICE_UP); } elsif ($$v{service} eq "pops") { $$r{num_connects} = 0 if (check_pop($v, $r, 1) == $SERVICE_UP); } elsif ($$v{service} eq "imap") { $$r{num_connects} = 0 if (check_imap($v, $r) == $SERVICE_UP); } elsif ($$v{service} eq "imaps") { $$r{num_connects} = 0 if (check_imaps($v, $r) == $SERVICE_UP); } elsif ($$v{service} eq "smtp" or $$v{service} eq "submission") { $$r{num_connects} = 0 if (check_smtp($v, $r) == $SERVICE_UP); } elsif ($$v{service} eq "ftp") { $$r{num_connects} = 0 if (check_ftp($v, $r) == $SERVICE_UP); } elsif ($$v{service} eq "ldap") { $$r{num_connects} = 0 if (check_ldap($v, $r) == $SERVICE_UP); } elsif ($$v{service} eq "nntp") { $$r{num_connects} = 0 if (check_nntp($v, $r) == $SERVICE_UP); } elsif ($$v{service} eq "dns") { $$r{num_connects} = 0 if (check_dns($v, $r) == $SERVICE_UP); } elsif ($$v{service} eq "sip") { $$r{num_connects} = 0 if (check_sip($v, $r) == $SERVICE_UP); } elsif ($$v{service} eq "radius") { $$r{num_connects} = 0 if (check_radius($v, $r) == $SERVICE_UP); } elsif ($$v{service} eq "mysql") { $$r{num_connects} = 0 if (check_mysql($v, $r) == $SERVICE_UP); } elsif ($$v{service} eq "pgsql") { $$r{num_connects} = 0 if (check_pgsql($v, $r) == $SERVICE_UP); } elsif ($$v{service} eq "oracle") { $$r{num_connects} = 0 if (check_oracle($v, $r) == $SERVICE_UP); } elsif ($$v{service} eq "simpletcp") { $$r{num_connects} = 0 if (check_simpletcp($v, $r) == $SERVICE_UP); } else { $$r{num_connects} = 0 if (check_none($v, $r) == $SERVICE_UP); } } elsif ($$v{checktype} eq "connect") { if ($$v{protocol} ne "udp") { &ld_debug(2, "Checking connect: real server=$real_id (virtual=$virtual_id)"); check_connect($v, $r); } else { &ld_debug(2, "Checking connect (ping): real server=$real_id (virtual=$virtual_id)"); check_ping($v, $r); } } elsif ($$v{checktype} eq "ping") { &ld_debug(2, "Checking ping: real server=$real_id (virtual=$virtual_id)"); check_ping($v, $r); } elsif ($$v{checktype} eq "external") { &ld_debug(2, "Checking external: real server=$real_id (virtual=$virtual_id)"); check_external($v, $r); } elsif ($$v{checktype} eq "external-perl") { &ld_debug(2, "Checking external-perl: real server=$real_id (virtual=$virtual_id)"); check_external_perl($v, $r); } elsif ($$v{checktype} eq "off") { &ld_debug(2, "Checking off: No real or fallback servers to be added\n"); } elsif ($$v{checktype} eq "on") { &ld_debug(2, "Checking on: Real servers are added without any checks\n"); &service_set($v, $r, "up"); } elsif ($$v{checktype} eq "combined") { &ld_debug(2, "Checking combined-connect: real server=$real_id (virtual=$virtual_id)"); if (check_connect($v, $r) == $SERVICE_UP) { $$r{num_connects}++; } else { $$r{num_connects} = 999999; } } } sub _check_real_for_maintenance { my $r = shift; return undef if(!$MAINTDIR); my $servername = ld_gethostbyaddr($$r{server}); # Extract just the first component of the full name so we can match short or FQDN names $servername =~ /^([a-z][a-z0-9\-]+)\./; my $servershortname = $1; if (-e "$MAINTDIR/$$r{server}:$$r{port}") { &ld_debug(2, "Server maintenance: Found file $$r{server}:$$r{port}"); return 1; } elsif (-e "$MAINTDIR/$$r{server}") { &ld_debug(2, "Server maintenance: Found file $$r{server}"); return 1; } elsif ($servername && -e "$MAINTDIR/$servername:$$r{port}") { &ld_debug(2, "Server maintenance: Found file $servername:$$r{port}"); return 1; } elsif ($servername && -e "$MAINTDIR/$servername") { &ld_debug(2, "Server maintenance: Found file $servername"); return 1; } elsif ($servershortname && -e "$MAINTDIR/$servershortname:$$r{port}") { &ld_debug(2, "Server maintenance: Found file $servershortname:$$r{port}"); return 1; } elsif ($servershortname && -e "$MAINTDIR/$servershortname") { &ld_debug(2, "Server maintenance: Found file $servershortname"); return 1; } return undef; } sub check_http { use LWP::UserAgent; use LWP::Debug; if($DEBUG > 2) { LWP::Debug::level('+'); } my ($v, $r) = @_; $$r{url} =~ /(http|https):\/\/([^:\/]+)(:([^\/]+))?(\/.*)/; my $host = $2; #my $port = $3; my $uri = $4; my $virtualhost = (defined $$v{virtualhost} ? $$v{virtualhost} : $host); &ld_debug(2, "check_http: url=\"$$r{url}\" " . "virtualhost=\"$virtualhost\""); my $ua = new LWP::UserAgent(); my $h = undef; if ($$v{service} eq "http_proxy") { my $port = ld_checkport($v, $r); $ua->proxy("http", "http://$$r{server}:$port/"); } else { $h = new HTTP::Headers("Host" => $virtualhost); } my $req = new HTTP::Request("$$v{httpmethod}", "$$r{url}", $h); my $res; # LWP does not seem to honour timeouts set using $ua->timeout() # for HTTPS. So use an alarm instead. This also has the advantage # of being cumulative timeout, rather than a per send/receive # timeout. eval { # LWP makes unguarded calls to eval # which throw a fatal exception if they fail # Needless to say, this is completely stupid. # Resetting of $SIG{'__DIE__'} is also # needed now that alarm() is used. local $SIG{'__DIE__'} = "DEFAULT"; local $SIG{'ALRM'} = sub { die "Timeout Alarm" }; &ld_debug(4, "Timeout is $$v{negotiatetimeout}"); &ld_debug(2, "Starting Check"); alarm $$v{negotiatetimeout}; &ld_debug(2, "Starting HTTP/HTTPS"); $res = $ua->request($req); &ld_debug(2, "Finished HTTP/HTTPS"); alarm 0; # Cancel the alarm }; if (not defined $res) { &ld_debug(2, "check_http: timeout"); goto down; } if ($$v{service} eq "https") { &ld_debug(2, "SSL-Cipher: " . $res->header('Client-SSL-Cipher')); &ld_debug(2, "SSL-Cert-Subject: " . $res->header('Client-SSL-Cert-Subject')); &ld_debug(2, "SSL-Cert-Issuer: " . $res->header('Client-SSL-Cert-Issuer')); } my $recstr = $$r{receive}; if ($res->is_success && (!($recstr =~ /.+/) || $res->content =~ /$recstr/)) { service_set($v, $r, "up", {do_log => 1}, $res->status_line); &ld_debug(2, "check_http: $$r{url} is up\n"); return $SERVICE_UP; } my $log_message = $res->is_success ? $res->content : $res->status_line; service_set($v, $r, "down", {do_log => 1}, $log_message); &ld_debug(3, "Headers " . $res->headers->as_string); down: &ld_debug(2, "check_http: $$r{url} is down\n"); return $SERVICE_DOWN; } sub check_smtp { require Net::SMTP; my ($v, $r) = @_; my $port = ld_checkport($v, $r); &ld_debug(2, "Checking $$v{service}: server=$$r{server} port=$port"); my $smtp = new Net::SMTP($$r{server}, Port => $port, Timeout => $$v{negotiatetimeout}); if ($smtp) { $smtp->quit; service_set($v, $r, "up", {do_log => 1}); return $SERVICE_UP; } else { service_set($v, $r, "down", {do_log => 1}); return $SERVICE_DOWN; } } sub check_pop { require Mail::POP3Client; my ($v, $r, $ssl) = @_; my $port = ld_checkport($v, $r); &ld_debug(2, "Checking pop server=$$r{server} port=$port ssl=$ssl"); my $pop = new Mail::POP3Client(USER => $$v{login}, PASSWORD => $$v{passwd}, HOST => $$r{server}, USESSL => $ssl, PORT => $port, DEBUG => 0, TIMEOUT => $$v{negotiatetimeout}); if (!$pop) { service_set($v, $r, "down", {do_log => 1}); return $SERVICE_DOWN; } if($$v{login} ne "") { my $authres = $pop->login(); $pop->close(); if (!$authres) { service_set($v, $r, "down", {do_log => 1}); return $SERVICE_DOWN; } } $pop->close(); service_set($v, $r, "up", {do_log => 1}); return $SERVICE_UP; } sub check_imap { require Net::IMAP::Simple; my ($v, $r) = @_; my $port = ld_checkport($v, $r); &ld_debug(2, "Checking imap server=$$r{server} port=$port"); my $imap = Net::IMAP::Simple->new($$r{server}, port => $port, timeout => $$v{negotiatetimeout}); if (!$imap) { service_set($v, $r, "down", {do_log => 1}); return $SERVICE_DOWN; } if($$v{login} ne "") { my $authres = $imap->login($$v{login},$$v{passwd}); $imap->quit; if (!$authres) { service_set($v, $r, "down", {do_log => 1}); return $SERVICE_DOWN; } } $imap->quit(); service_set($v, $r, "up", {do_log => 1}); return $SERVICE_UP; } sub check_imaps { require Net::IMAP::Simple::SSL; my ($v, $r) = @_; my $port = ld_checkport($v, $r); &ld_debug(2, "Checking imaps server=$$r{server} port=$port"); my $imaps = Net::IMAP::Simple::SSL->new($$r{server}, port => $port, timeout => $$v{negotiatetimeout}); if (!$imaps) { service_set($v, $r, "down", {do_log => 1}); return $SERVICE_DOWN; } if($$v{login} ne "") { my $authres = $imaps->login($$v{login},$$v{passwd}); $imaps->quit; if (!$authres) { service_set($v, $r, "down", {do_log => 1}); return $SERVICE_DOWN; } } $imaps->quit(); service_set($v, $r, "up", {do_log => 1}); return $SERVICE_UP; } sub check_ldap { my ($v, $r) = @_; require Net::LDAP; my $port = ld_checkport($v, $r); &ld_debug(2, "Checking ldap server=$$r{server} port=$port"); my $recstr = $$r{receive}; my $ldap = Net::LDAP->new("$$r{server}", port => $port, timeout => $$v{negotiatetimeout}); if(!$ldap) { service_set($v, $r, "down", {do_log => 1}, "Connection failed"); &ld_debug(4, "Connection failed"); return $SERVICE_DOWN; } my $mesg; if ($$v{login} && $$v{passwd}) { $mesg = $ldap->bind($$v{login}, password=>$$v{passwd}) ; } else { $mesg = $ldap->bind ; } if ($mesg->is_error) { service_set($v, $r, "down", {do_log => 1}, "Bind failed"); &ld_debug(4, "Bind failed"); return $SERVICE_DOWN; } &ld_debug(4, "Base : " . substr($$r{request},1)); my $result = $ldap->search ( base => substr($$r{request},1) . "", scope => "base", filter => "(objectClass=*)" ); if($result->count != 1) { service_set($v, $r, "down", {do_log => 1}, "No answer received"); &ld_debug(2, "Count failed : " . $result->count); return $SERVICE_DOWN; } my $href = $result->as_struct; my @arrayOfDNs = keys %$href ; if (!($recstr =~ /.+/) || $arrayOfDNs[0] =~ /$recstr/) { service_set($v, $r, "up", {do_log => 1}, "Success"); return $SERVICE_UP; } else { service_set($v, $r, "down", {do_log => 1}, "Response mismatch"); &ld_debug(4,"Message differs : " . ", " . $$r{receive} . ", " . $arrayOfDNs[0] . "."); return $SERVICE_DOWN; } } sub check_nntp { use IO::Socket; use IO::Socket::INET6; use IO::Select; my ($v, $r) = @_; my $sock; my $s; my $buf; my $port = ld_checkport($v, $r); my $status = 1; &ld_debug(2, "Checking nntp server=$$r{server} port=$port"); unless ($sock = IO::Socket::INET6->new(PeerAddr => $$r{server}, PeerPort => $port, Proto => 'tcp', TimeOut => $$v{negotiatetimeout})) { service_set($v, $r, "down", {do_log => 1}); return $SERVICE_DOWN; } $s = IO::Select->new(); $s->add($sock); if (scalar($s->can_read($$v{negotiatetimeout})) == 0) { service_set($v, $r, "down", {do_log => 1}); } else { sysread($sock, $buf, 64); if ($buf =~ /^2/) { service_set($v, $r, "up", {do_log => 1}); $status = 0; } else { service_set($v, $r, "down", {do_log => 1}); } } $s->remove($sock); $sock->close; return $status; } sub check_radius { require Authen::Radius; my ($v, $r) = @_; &ld_debug(2, "Checking radius"); my $port = ld_checkport($v, $r); my $radius; my $result = ""; eval { local $SIG{'__DIE__'} = "DEFAULT"; local $SIG{'ALRM'} = sub { die "Timeout Alarm" }; &ld_debug(4, "Timeout is $$v{checktimeout}"); &ld_debug(2, "Starting Check"); alarm $$v{checktimeout}; &ld_debug(2, "Starting Radius"); $radius = new Authen::Radius(Host => "$$r{server}:$port", Secret=>$$v{secret}, TimeOut=>$$v{negotiatetimeout}, Errmode=>'die'); $result = $radius->check_pwd($$v{login}, $$v{passwd}); &ld_debug(2, "Finished Radius"); alarm 0; # Cancel the alarm }; if ($result eq "") { &service_set($v, $r, "down", {do_log => 1}); &ld_debug(3, "Deactivated service $$r{server}:$$r{port}: $@"); &ld_debug(3, "Radius Error: ".$radius->get_error); return $SERVICE_DOWN; } else { &service_set($v, $r, "up", {do_log => 1}); &ld_debug(3, "Activated service $$r{server}:$$r{port}"); return $SERVICE_UP; } } sub check_mysql { return check_sql(@_, "mysql", "database"); } sub check_pgsql { return check_sql(@_, "Pg", "dbname"); } sub check_sql_log_errstr { my ($prefix, $errstr) = (@_); for $_ (split /\n/, $errstr) { &ld_debug(4, "$prefix $_\n"); } } sub check_oracle { return check_sql(@_, "Oracle", "sid"); } sub check_sql { require DBI; my ($v, $r, $dbd, $dbname) = @_; my $port = ld_checkport($v, $r); my ($dbh, $sth, $query, $rows, $result); $result = $SERVICE_DOWN; $query = $$r{request}; $query =~ s#^/##; unless ($$v{login} && $query) { &ld_log("Error: Must specify a login and request string " . "for MySQL, Oracle and PostgreSQL checks. " . "Not adding $$r{server}.\n"); goto err_down; } $result=2; # Set result flag. Only ok if ends up at zero. &ld_debug(2, "Checking $$v{server} server=$$r{server} port=$port\n"); $dbh = DBI->connect("dbi:$dbd:$dbname=$$v{database};" . "host=$$r{server};port=$port", $$v{login}, $$v{passwd}); unless ($dbh) { &ld_debug(2, "Failed to bind to $$r{server} with DBI->errstr\n"); check_sql_log_errstr("Failed to bind to $$r{server} with", DBI->errstr); goto err_down; } $result--; $sth = $dbh->prepare($query); unless ($sth) { &ld_debug(2, "Error preparing statement: $dbh->errstr\n"); check_sql_log_errstr("Error preparing statement:", $dbh->errstr); goto err_disconect; } # Test to see if any errors are returned $sth->execute; if ($dbh->err) { &ld_debug(2, "Error executing statement: $dbh->errstr : $dbh->err\n"); check_sql_log_errstr("Error executing statement:", $dbh->errstr, $dbh->err); goto err_finish; } # On error "execute" will return undef. # # Assuming you're using 'SELECT' you will get the number of rows # returned from the db when running execute: the 'rows' method is # only used when doing something that is NOT a select. I cannot # imagine that you would ever want to insert or update from a # regular polling on this system, so we will assume you are using # SELECT here. # # Ideally you will do something like this: 'select * from # director_slave where enabled=1' This way you can have, say, a # MEMORY table in MySQL where you insert a value into a row # (enabled) that says whether or not you want to actually use this # in the pool from ldirector / ipvs, and disable them without # actually turning off your sql server. $sth->execute; if ($dbd eq "Oracle") { $sth->fetchrow_hashref() } unless ($rows = $sth->rows) { check_sql_log_errstr("Error executing statement:", $dbh->errstr, $dbh->err); goto err_finish; } # Actually look to see if there was data returned in this statement, # else disable node if($rows > 0) { goto out; } else { goto err_finish; } out: $result = $SERVICE_UP; err_finish: $sth->finish(); err_disconnect: $dbh->disconnect(); err_down: service_set($v, $r, $result == $SERVICE_UP ? "up" : "down", {do_log => 1}); return $result; } sub check_connect { my ($v, $r) = @_; my $port = ld_checkport($v, $r); eval { local $SIG{'__DIE__'} = "DEFAULT"; local $SIG{'ALRM'} = sub { die "Timeout Alarm" }; &ld_debug(4, "Timeout is $$v{checktimeout}"); alarm $$v{checktimeout}; my $sock = &ld_open_socket($$r{server}, $port, $$v{protocol}); if ($sock) { close($sock); } else { alarm 0; # Cancel the alarm die("Socket Connect Failed"); } &ld_debug(3, "Connected to $$r{server} (port $port)"); alarm 0; # Cancel the alarm }; if ($@) { &service_set($v, $r, "down", {do_log => 1}); &ld_debug(3, "Deactivated service $$r{server}:$$r{port}: $@"); return $SERVICE_DOWN; } else { &service_set($v, $r, "up", {do_log => 1}); &ld_debug(3, "Activated service $$r{server}:$$r{port}"); return $SERVICE_UP; } } sub check_external { my ($v, $r) = @_; my $v_server; if (defined $$v{server}) { $v_server = $$v{server}; } else { $v_server = $$v{fwm}; } my $result = system_timeout($$v{checktimeout}, $$v{checkcommand}, $v_server, $$v{port}, $$r{server}, $$r{port}); if ($result) { &service_set($v, $r, "down", {do_log => 1}); &ld_debug(3, "Deactivated service $$r{server}:$$r{port}: " . "$@ after calling $$v{checkcommand} with result " . "$result"); return 0; } else { &service_set($v, $r, "up", {do_log => 1}); &ld_debug(3, "Activated service $$r{server}:$$r{port}"); return 1; } } sub check_external_perl { my ($v, $r) = @_; my $result; my $v_server; eval { local $SIG{'__DIE__'} = "DEFAULT"; local $SIG{'ALRM'} = sub { die "Timeout Alarm" }; &ld_debug(4, "Timeout is $$v{checktimeout}"); alarm $$v{checktimeout}; if (defined $$v{server}) { $v_server = $$v{server}; } else { $v_server = $$v{fwm}; } my $cmdfunc = $check_external_perl__funcs{$$v{checkcommand}}; if (!defined($cmdfunc)) { open(CMDFILE, "<$$v{checkcommand}") || die "cannot open external-perl checkcommand file: $$v{checkcommand}"; $cmdfunc = eval("sub { \@ARGV=\@_; " . join("", ) . " }"); close(CMDFILE); $check_external_perl__funcs{$$v{checkcommand}} = $cmdfunc; } no warnings 'redefine'; local *CORE::GLOBAL::exit = sub { $result = shift; goto external_exit; }; $cmdfunc->($v_server, $$v{port}, $$r{server}, $$r{port}); external_exit: alarm 0; }; if ($@ or $result != 0) { &service_set($v, $r, "down"); &ld_debug(3, "Deactivated service $$r{server}:$$r{port}: " . "$@ after calling (external-perl) $$v{checkcommand} with result " . "$result"); return 0; } else { &service_set($v, $r, "up"); &ld_debug(3, "Activated service $$r{server}:$$r{port}"); return 1; } } sub check_sip { my ($v, $r) = @_; my $sip_d_port = ld_checkport($v, $r); &ld_debug(2, "Checking sip server=$$r{server} port=$sip_d_port"); eval { use Socket; local $SIG{'__DIE__'} = "DEFAULT"; local $SIG{'ALRM'} = sub { die "Timeout Alarm" }; &ld_debug(4, "Timeout is $$v{checktimeout}"); alarm $$v{negotiatetimeout}; my $sock = &ld_open_socket($$r{server}, $sip_d_port, $$v{protocol}); unless ($sock) { alarm 0; die("Socket Connect Failed"); } my ($sip_s_addr_str, $sip_s_port) = &ld_get_addrport($sock); &ld_debug(3, "Connected from $sip_s_addr_str:$sip_s_port to " . $$r{server} . ":$sip_d_port"); select $sock; $|=1; select STDOUT; my $request = "OPTIONS sip:" . $$v{login} . " SIP/2.0\r\n" . "Via: SIP/2.0/UDP $sip_s_addr_str:$sip_s_port;" . "branch=z9hG4bKhjhs8ass877\r\n" . "Max-Forwards: 70\r\n" . "To: \r\n" . "From: ;tag=1928301774\r\n" . "Call-ID: " . (join "", map { unpack "H*", chr(rand(256)) } 1..8) . "\r\n" . "CSeq: 63104 OPTIONS\r\n" . "Contact: \r\n" . "Accept: application/sdp\r\n" . "Content-Length: 0\r\n\r\n"; print "Request:\n$request"; print $sock $request; my $ok; my $reply; while (<$sock>) { chomp; $/="\r"; chomp; $/="\n"; last if ($_ eq ""); if (!defined $ok) { # Check status $ok = $_; if ($ok !~ m/^SIP\/2.0 200 OK/) { alarm 0; # Cancel the alarm close($sock); die "$ok\n"; } next; } $reply .= "$_\n"; # Add more checks here as desired } alarm 0; # Cancel the alarm close($sock); if (!defined $ok) { die "No OK\n"; } print "Reply:\n$ok\n$reply\n"; }; if ($@) { &service_set($v, $r, "down", {do_log => 1}); &ld_debug(3, "Deactivated service $$r{server}:$$r{port}: $@"); return $SERVICE_DOWN; } else { &service_set($v, $r, "up", {do_log => 1}); &ld_debug(3, "Activated service $$r{server}:$$r{port}"); return $SERVICE_UP; } } sub check_simpletcp { my ($v, $r) = @_; my $d_port = ld_checkport($v, $r); &ld_debug(2, "Checking simpletcp server=$$r{server} port=$d_port"); eval { use Socket; local $SIG{'__DIE__'} = "DEFAULT"; local $SIG{'ALRM'} = sub { die "Timeout Alarm" }; &ld_debug(4, "Timeout is $$v{checktimeout}"); alarm $$v{negotiatetimeout}; my $sock = &ld_open_socket($$r{server}, $d_port, $$v{protocol}); unless ($sock) { alarm 0; die("Socket Connect Failed"); } my ($s_addr_str, $s_port) = &ld_get_addrport($sock); &ld_debug(3, "Connected from $s_addr_str:$s_port to " . $$r{server} . ":$d_port"); select $sock; $|=1; select STDOUT; my $request = substr($$r{request}, 1); $request =~ s/\\n/\n/g ; &ld_debug(2, "Checking simpletcp server=$$r{server} port=$d_port request:\n$request"); print $sock $request; shutdown($sock, SHUT_WR); my $ok; my $reply; while (<$sock>) { &ld_debug(2, "Checking simpletcp server=$$r{server} port=$d_port receive=" . $$r{receive} ." got: $_\n"); if ( $_ =~ /$$r{receive}/ ) { $ok = 1; last; } } alarm 0; # Cancel the alarm close($sock); if (!defined $ok) { die "No OK\n"; } }; if ($@) { &service_set($v, $r, "down", {do_log => 1}); &ld_debug(3, "Deactivated service $$r{server}:$$r{port}: $@"); return $SERVICE_DOWN; } else { &service_set($v, $r, "up", {do_log => 1}); &ld_debug(3, "Activated service $$r{server}:$$r{port}"); return $SERVICE_UP; } } sub check_ftp { require Net::FTP; my ($v, $r) = @_; my $ftp; my $memory; my $debug = ($DEBUG > 2) ? 1 : 0; my $port = ld_checkport($v, $r); &ld_debug(2, "Checking ftp server=$$r{server} port=$port"); &ld_debug(4, "Timeout is $$v{negotiatetimeout}"); open(TMP,'+>', undef); # In some cases Net::FTP dies if there is a timeout eval { unless ($ftp = Net::FTP->new("$$r{server}:$port", Timeout=>$$v{negotiatetimeout}, Debug=>$debug)) { die "Could not connect\n"; } $ftp->login($$v{login}, $$v{passwd}); $ftp->cwd("/"); $ftp->binary(); $ftp->pasv(); $ftp->get("$$r{request}", *TMP); $ftp->quit(); }; if ($@) { &ld_debug(2, "Warning: $@"); } seek TMP, 0, 0; local $/; $memory = ; close TMP; if ($memory =~ /$$r{receive}/) { service_set($v, $r, "up", {do_log => 1}); return $SERVICE_UP; } service_set($v, $r, "down", {do_log => 1}); return $SERVICE_DOWN; } sub check_dns { my $res; my $query; my $rr; my $request; my $server; my ($v,$r) = @_; { # Net::DNS makes unguarded calls to eval # which throw a fatal exception if they fail # Needless to say, this is completely stupid. local $SIG{'__DIE__'} = "DEFAULT"; require Net::DNS; } $res = new Net::DNS::Resolver; if($DEBUG > 2) { $res->debug(1); } $$r{"request"} =~ m/^\/?(.*)/; $request=$1; $server = &ld_strip_brackets($$r{server}); &ld_debug(2, "Checking dns: request=\"$request\" receive=\"" . $$r{"receive"} . "\"\n"); eval { local $SIG{'__DIE__'} = "DEFAULT"; local $SIG{'ALRM'} = sub { die "timeout\n"; }; alarm($$v{negotiatetimeout}); $res->nameservers($server); if ($$v{"protocol"} eq "tcp") { $res->usevc(1); } $query = $res->search($request); alarm(0); }; if (@$ eq "timeout\n" or ! $query) { service_set($v, $r, "down", {do_log => 1}, "Connection timed out"); return $SERVICE_DOWN; } foreach $rr ($query->answer) { if (($rr->type eq "A" and $rr->address eq $$r{"receive"}) or ($rr->type eq "PTR" and $rr->ptrdname eq $$r{"receive"})) { service_set($v, $r, "up", {do_log => 1}, "Success"); return $SERVICE_UP; } } service_set($v, $r, "down", {do_log => 1}, "Response mismatch"); return $SERVICE_DOWN; } sub check_ping { use Net::Ping; my ($v,$r) = (@_); &ld_debug(2, "Checking ping: " . "host=\"" . $$r{server} . "\" checktimeout=\"" . $$v{"checktimeout"} . "\" checkcount=\"" . $$v{"checkcount"} . "\"\n"); my $p = Net::Ping->new("icmp","1","64"); for (my $attempt = 0; $attempt < $$v{"checkcount"}; $attempt++) { if ($p->ping($$r{server}, $$v{"checktimeout"})) { &ld_debug(2, "pong from $$r{server}\n"); service_set($v, $r, "up", {do_log => 1}); return $SERVICE_UP; } &ld_debug(2, "ping to $$r{server} timed out " . "(attempt " . ($attempt + 1) . "/" . $$v{"checkcount"} . ")\n"); } service_set($v, $r, "down"); return $SERVICE_DOWN; } # check_none # Dummy function to check service if service type is none. # Just activates the real server sub check_none { my ($v, $r) = @_; &ld_debug(2, "Checking none"); service_set($v, $r, "up", {do_log => 1}); return $SERVICE_UP; } # service_set # Used to bring up and down real servers. # This is the function you should call if you want to bring a real # server up or down. # This function is safe to call regardless of the current state of a # real server. # Do _not_ call _service_up or _service_down directly. # pre: v: virtual that the real service belongs to # Only used to determine the protocol of the service # r: real server to take down # state: up or down # up to bring the real service up # down to bring the real service up # flags: hash with the following (optional) keys: # force => 1 - force setting of the specified state # do_log => 1 - log the state to the monitorfile # (when called as the result of a check) # post: The real server is brought up or down for each virtual service # it belongs to. # return: none sub service_set { my ($v, $r, $state, $flags, $log_msg) = @_; my ($real, $virtual, $virt, $now); if ($$flags{'do_log'}) { $now = localtime(); if (!defined($log_msg)) { $log_msg = "-"; } # URI-escape special log characters ('|' and newlines) $log_msg =~ s/([%|\r\n])/sprintf("%%%.2x", ord($1))/eg; } # Find the real server in @REAL foreach $real (@REAL) { if($real->{"real"} eq get_real_id_str($r, $v)) { $virtual = $real->{"virtual"}; last; } } return unless (defined($virtual)); # Check each virtual service for the real server and make # changes as necessary foreach $v (@VIRTUAL){ # Use found rather than relying on tmp_id being # set when we leave the foreach loop. There # seems to some weirdness in Perl (5.6.0 on Redhat 7.2) my $found = 0; my $tmp_id; my $virtual_id = get_virtual_id_str($v); my $real_id = get_real_id_str($r, $v); my $log_str = "real server=$real_id" . " (virtual=$virtual_id)"; foreach $tmp_id (@$virtual) { if($virtual_id eq $tmp_id) { $found = 1; last; } } if ($found == 1) { if ($state=~/up/i) { _service_up($v, $r, $$flags{"force"}); &ld_debug(2, "Enabled $log_str"); } elsif ($state=~/down/i) { _service_down($v, $r, $$flags{"force"}); &ld_debug(2, "Disabled $log_str"); } if ($$v{"monitorfile"} and $$flags{"do_log"}) { my $real_log_msg = $real_id; $real_log_msg =~ tr/:/ /s; $real_log_msg =~ s/\\//g; unless( open(CHECKLOG, ">>$$v{monitorfile}") and print CHECKLOG "[$now] [$$] $real_log_msg [$state] $log_msg\n" and close(CHECKLOG) ) { die("Error writing to monitorfile '$$v{monitorfile}': $!"); } } } } } # _remove_service # Remove a real server by either making it quiescent or deleting it # Should be called by _service_down or fallback_off # I.e. If you want to change the state of a real server call service_set. # If you call this function directly then ldirectord will lose track # of the state of real servers. # If the real server exists (which it should) make it quiescent or # delete it, depending on the global and per virtual service quiescent flag. # If it # doesn't exist, just leave it as it will be added by the # _service_up code as appropriate. # pre: v: reference to virtual service to with the real server belongs # rservice: service to restore. Of the form server:port for a tcp or # udp service. Of the form fwmark for a fwm service. # rforw: Forwarding mechanism of service. Sould be one of "-g" "-i" or # "-m" # tag: Tag to use for logging. Should be either "real" or "fallback" # post: real service is taken up from the respective virtual service # if it is inactive # return: none sub _remove_service { my ($v, $rservice, $rforw, $tag) = (@_); my $oldsrv; my $ov; my $or; my $ipvsadm_args; my $log_args; my $virtual_str; my $old_rservice; my $is_quiescent; $virtual_str = &get_virtual($v); $oldsrv=&ld_read_ipvsadm(); $ov=$oldsrv->{$virtual_str . " " . $v->{"protocol"}}; if(!defined($ov)){ return; } if ($tag ne "fallback" and ((defined $$v{quiescent} and $$v{quiescent} eq "yes") or (!defined($$v{quiescent}) and $QUIESCENT eq "yes"))){ $is_quiescent = "quiescent"; } $or=$ov->{"real"}->{$rservice}; # If a virtual service is a IP/port service (not fwmark) # and a real-servers uses a forwarding mechanism other than masq # then the port will always be that of the virtual service. # This includes real-servers that LVS has set to use # the local forwarding mechanism because their IP address # is local. Thus, if $rservice does not exist test # for the same ip address with the virtual servers port. # N.B: This could cause strange things to happen if # there is a clash between two real servers on different ports # that LVS has mapped to being the same thing. if(!defined($or)) { $old_rservice = $rservice; $rservice =~ /(.*):(.*)/; $rservice = $1; $virtual_str =~ /(.*):(.*)/; $rservice .= ":" . $2; $or=$ov->{"real"}->{$rservice}; # If this doesn't exist either, use the original service. # Otherwise if masq and quiescence is in use, the # real server is not local, and it has an alternate port to # the virtual server, using the mapped service will # result in a quiescent service being created on the # virtual server's port, which is not wanted. if(!defined($or)) { $rservice = $old_rservice; $old_rservice = undef; } } if((!defined($or) and !defined($is_quiescent)) or (defined($is_quiescent) and defined($or) and $or->{"weight"} eq 0 and get_forward_flag($or->{"forward"}) eq $rforw)){ return; } $ipvsadm_args = "$$v{proto} " . &get_virtual_option($v) . " -r $rservice"; $log_args = "$tag server: $rservice "; if(defined($old_rservice)) { $log_args .= "mapped from $old_rservice " } $log_args .= "($virtual_str)"; my $server_str=$rservice . " " . $virtual_str; my $currenttime=time(); if(defined($is_quiescent)) { if (defined($or)) { &system_wrapper("$IPVSADM -e " . "$ipvsadm_args $rforw -w 0"); } else { &system_wrapper("$IPVSADM -a " . "$ipvsadm_args $rforw -w 0"); } &ld_log("Quiescent $log_args (Weight set to 0)"); &ld_emailalert_send("Quiescent $log_args (Weight set to 0)", $v, $rservice, $currenttime); } else { &system_wrapper("$IPVSADM -d $ipvsadm_args"); &ld_log("Deleted $log_args"); &ld_emailalert_send("Deleted $log_args", $v, $rservice, $currenttime); } } # _restore_service # Make a retore a real server. The opposite of _quiescent_server. # Should be called by _service_up or fallback_on # I.e. If you want to change the state of a real server call service_set. # If you call this function directly then ldirectord will lose track # of the state of real servers. # If the real server exists (which it should) make it quiescent. If it # doesn't exist, just leave it as it will be added by the _service_up code # as appropriate. # pre: v: reference to virtual service to with the real server belongs # rservice: service to restore. Of the form server:port for a tcp or # udp service. Of the form fwmark for a fwm service. # rforw: Forwarding mechanism of service. Sould be one of "-g" "-i" or # "-m" # rwght: Weight of service. Sold be of the form "" # e.g. "1" # tag: Tag to use for logging. Should be either "real" or "fallback" # post: real service is taken up from the respective virtual service # if it is inactive # return: none sub _restore_service { my ($v, $rservice, $rforw, $rwght, $tag) = (@_); my $oldsrv; my $ov; my $or; my $ipvsadm_args; my $log_args; $ipvsadm_args = "$$v{proto} " . &get_virtual_option($v) . " -r $rservice $rforw -w $rwght"; $log_args = "$tag server: $rservice " . "(" #. scalar(%{$v->{real_status}}) . &get_virtual($v) . ")"; #if the server exists then restore its weight # otherwise add the server $oldsrv=&ld_read_ipvsadm(); $ov=$oldsrv->{&get_virtual($v) . " " . $v->{"protocol"}}; if(defined($ov)){ $or=$ov->{"real"}->{$rservice}; } if(defined($or)){ unless($or->{"weight"} eq $rwght and get_forward_flag($or->{"forward"}) eq $rforw){ &system_wrapper("$IPVSADM -e $ipvsadm_args"); &ld_log("Restored $log_args (Weight set to $rwght)"); &ld_emailalert_send("Restored $log_args " . "(Weight set to $rwght)", $v, $rservice, 0); } } else { &system_wrapper("$IPVSADM -a $ipvsadm_args"); &ld_log("Added $log_args (Weight set to $rwght)"); &ld_emailalert_send("Added $log_args (Weight set to $rwght)", $v, $rservice, 0); } } # Check the status of a server # Should only be called from _status_up, _status_down, # _service_up, or _service_down # Returns 1 if the server is up, 0 if down sub _status_check { my ($v, $r, $is_fallback) = (@_); my $virtual_id = get_virtual_id_str($v); my $real_id = get_real_id_str($r, $v); if (defined($is_fallback)) { if (defined($v->{real_status}) or (defined($v->{fallback_status}) and $v->{fallback_status}->{"$real_id"})) { return 1; } } else { if (defined ($v->{real_status}) and $v->{real_status}->{"$real_id"}) { return 1; } } return 0; } # Set the status of a server as up # Should only be called from _service_up or _ld_start sub _status_up { my ($v, $r, $is_fallback) = (@_); my $virtual_id = get_virtual_id_str($v); my $real_id = get_real_id_str($r, $v); return undef if(_status_check($v, $r, $is_fallback)); $r->{virtual_status}->{"$virtual_id"} = 1; if (defined $is_fallback) { $v->{fallback_status}->{"$real_id"} = 1; } else { $v->{real_status}->{"$real_id"} = 1; } return 1; } # Set the status of a server as down # Should only be called from _service_down or ld_stop sub _status_down { my ($v, $r, $is_fallback) = (@_); my $virtual_id = get_virtual_id_str($v); my $real_id = get_real_id_str($r, $v); return undef if (!_status_check($v, $r, $is_fallback)); if (defined($is_fallback)) { delete $v->{fallback_status}->{"$real_id"}; if (! %{$v->{fallback_status}}) { $v->{fallback_status} = undef; } } else { delete $v->{real_status}->{"$real_id"}; if (! %{$v->{real_status}}) { $v->{real_status} = undef; } } delete $r->{virtual_status}->{"$virtual_id"}; if (! %{$r->{virtual_status}}) { $r->{virtual_status} = undef; } return 1; } # _service_up # Bring a real service up if it is down # Should be called by service_set only # I.e. If you want to change the state of a real server call service_set. # If you call this function directly then ldirectord will lose track # of the state of real servers. # pre: v: reference to virtual service to with the real server belongs # r: reference to the real server to take down # post: real service is taken up from the respective virtual service # if it is inactive # return: none sub _service_up { my ($v, $r, $force) = (@_); if ($r->{failcount} > 0) { ld_log("Resetting soft failure count: " . $r->{server} . ":" . $r->{port} . " (" . get_virtual_id_str($v) . ")"); } $r->{failcount} = 0; if (! _status_up($v, $r) and ! defined($force)) { return; } &_restore_service($v, $r->{server} . ":" . $r->{port}, $r->{forw}, $r->{weight}, "real"); &fallback_off($v); } # _service_down # Bring a real service down if it is up # Should be called by service_set only # I.e. if you want to change the state of a real server call service_set. # If you call this function directly then ldirectord will lose track # of the state of real servers. # pre: v: reference to virtual service to with the real server belongs # r: reference to the real server to take down # post: real service is taken down from the respective virtual service # if it is active # return: none sub _service_down { my ($v, $r, $force) = @_; if (!_status_check($v, $r) and !defined($force)) { return; } $r->{failcount}++; if (!defined($force) and _status_check($v, $r) and ($r->{failcount} < $v->{failurecount})) { ld_log("Soft failure real server: " . $r->{server} . ":" . $r->{port} . " (" . get_virtual_id_str($v) . ") failure " . $r->{failcount} . "/" . $v->{failurecount}); return; } _status_down($v, $r); &_remove_service($v, $r->{server} . ":" . $r->{port}, $r->{forw}, "real"); &fallback_on($v); } # fallback_on # Turn on the fallback server for a virtual service if it is inactive # pre: v: virtual to turn fallback service on for # post: fallback server is turned on if it was inactive # return: none sub fallback_on { my ($v, $force) = (@_); my $fallback=&fallback_find($v); if (defined($fallback) and (_status_up($v, $fallback, "fallback") or defined($force))) { &_restore_service($v, $fallback->{server} . ":" . $fallback->{port}, get_forward_flag($fallback->{forward}), "1", "fallback"); } if (!defined ($v->{real_status})) { &do_fallback_command($v, "start"); } } # fallback_off # Turn off the fallback server for a virtual service if it is active # pre: v: virtual to turn fallback service off for # post: fallback server is turned off if it was active # return: none sub fallback_off { my ($v, $force) = (@_); my $fallback=&fallback_find($v); if (defined($fallback) and (_status_down($v, $fallback, "fallback") or defined($force))) { &_remove_service($v, $fallback->{server} . ":" . $fallback->{port}, get_forward_flag($fallback->{forward}), "fallback"); } if (defined ($v->{real_status})) { &do_fallback_command($v, "stop"); } } # fallback_find # Determine the fallback for a virtual service # pre: virtual: reference to a virtual service # post: none # return: $virtual->{"fallback"} if defined # else $FALLBACK->{$virtual->{"protocol"}} if defined # else undef sub fallback_find { my ($virtual) = (@_); my($global_fallback_ptr); # fallback pointer my $ipv6p = $virtual->{server} =~ /[\[\]]/ ? 1 : 0; if( defined $virtual->{"fallback"} ) { return($virtual->{"fallback"}); } elsif ( not defined($FALLBACK) and not $ipv6p ) { return undef; } elsif ( not defined($FALLBACK6) and $ipv6p ) { return undef; } if ($ipv6p) { # IPv6 $global_fallback_ptr = $FALLBACK6; } else { $global_fallback_ptr = $FALLBACK; } # If the global fallback has a port, it can be used as is if (defined($global_fallback_ptr->{$virtual->{"protocol"}}->{"port"})) { return $global_fallback_ptr->{$virtual->{"protocol"}}; } # Else create an anonymous fallback my %anon_fallback = %{$global_fallback_ptr->{$virtual->{"protocol"}}}; $anon_fallback{"port"} = $virtual->{"port"}; return \%anon_fallback; } # fallback_command # Execute the fallback command with the given status if it wasn't executed # with this status already for the supplied virtual service. sub do_fallback_command { my ($v, $status) = (@_); if (defined $v->{fallbackcommand_status} and $v->{fallbackcommand_status} eq $status) { return; } $v->{fallbackcommand_status} = $status; if (defined($v->{fallbackcommand})) { &system_wrapper($v->{fallbackcommand} . " " . $status); } elsif (defined($FALLBACKCOMMAND)) { &system_wrapper($FALLBACKCOMMAND . " " . $status); } } # Used during stop, start and reload to remove stale real servers from LVS sub purge_untracked_service { my ($v, $rservice, $tag) = (@_); my $log_arg = "Purged real server ($tag): $rservice (" . &get_virtual($v) . ")"; &system_wrapper("$IPVSADM -d $v->{proto} " . &get_virtual_option($v) . " -r $rservice"); &ld_log($log_arg); &ld_emailalert_send($log_arg, $v, $rservice, 0); } # Used during stop, start and reload to remove stale real servers from LVS sub purge_service { my ($v, $r, $tag) = (@_); purge_untracked_service($v, "$r->{server}:$r->{port}", $tag); _status_down($v, $r); } # Used during stop, start and reload to remove stale virtual services from LVS sub purge_virtual { my ($v, $tag) = (@_); &system_wrapper("$IPVSADM -D $v->{proto} " . &get_virtual_option($v)); &ld_log("Purged virtual server ($tag): " . &get_virtual($v)); } sub check_cfgfile { my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime) = stat($CONFIG); my ($status); return if ($stattime==$mtime); $stattime = $mtime; use Digest::MD5 qw(md5 md5_hex); my $ctx = Digest::MD5->new; unless (open(CFGFILE, "<$CONFIG")) { &config_warn(0, "can not open file $CONFIG for checking"); return 0; } $ctx->addfile(*CFGFILE); close(CFGFILE); my $digest = $ctx->hexdigest; if (defined $checksum && $checksum ne $digest) { &ld_log("Configuration file '$CONFIG' has changed on disk"); if ($AUTOCHECK eq "yes") { &ld_log(" - reread new configuration"); &reread_config(); } else { &ld_log(" - ignore new configuration\n"); } if (defined($CALLBACK) and -x $CALLBACK) { &system_wrapper("$CALLBACK $CONFIG"); } $status = 1; } $checksum = $digest; return $status; } # ld_openlog # Open logger # make log rotation work # pre: none # post: If logger is a file, it opened and closed again as a test # If logger is syslog, it is opened so it can be used without # needing to be opened again. # Otherwise, nothing is done. # return: 0 on success # 1 on error sub ld_openlog { if ($opt_d or $SUPERVISED eq "yes") { # Instantly do nothing return(0); } if( $LDIRLOG =~ /^\/(.*)/ ) { # Open and close the file as a test. # We open the file each time we want to log to it unless (open(LOGFILE, ">>$LDIRLOG") and close(LOGFILE)) { return 1; } } else { # Assume LDIRLOG is a logfacility, log to syslog setlogsock( "unix" ); openlog( "ldirectord", "pid", "$LDIRLOG" ); } return(0); } # ld_log # Log a message. # pre: message: Message to write # post: message and timetsamp is written to loged # If logger is a file, it is opened and closed again as a # primitive means to make log rotation work # return: 0 on success # 1 on error sub ld_log { my ($message) = (@_); my $now = localtime(); &ld_debug(2, $message); chomp $message; if ($opt_d) { print STDERR "$message\n"; } elsif ($SUPERVISED eq "yes") { print "[$now] $message\n"; } elsif ( $LDIRLOG =~ /^\/(.*)/ ) { unless (open(LOGFILE, ">>$LDIRLOG") and print LOGFILE "[$now|$CFGNAME|$$] $message\n" and close(LOGFILE)) { print STDERR "$message\n"; return 1; } } else { # Assume LDIRLOG is a logfacility, log to syslog syslog( "info", "$message" ); } return(0); } sub daemon_status_str { if ($DAEMON_STATUS == $DAEMON_STATUS_STARTING) { return "starting"; } elsif ($DAEMON_STATUS == $DAEMON_STATUS_RUNNING) { return "running"; } elsif ($DAEMON_STATUS == $DAEMON_STATUS_STOPPING) { return "stopping"; } elsif ($DAEMON_STATUS == $DAEMON_STATUS_RELOADING) { return "reloading"; } return "UNKNOWN"; } # ld_emailalert_send # Send email alerts per virtual server # pre: message: Message to email # post: message is emailed if emailalert defined for virtualserver # return: 0 on success # 1 on error sub ld_emailalert_send { my ($subject, $v, $rserver, $currenttime) = (@_); my $status = 0; my $to_addr; my $frequency; my $virtual_str; my $id; my $statusfilter; my $smtp_server; $frequency = defined $v->{emailalertfreq} ? $v->{emailalertfreq} : $EMAILALERTFREQ; $virtual_str = &get_virtual($v); $id = "$rserver ($virtual_str)"; if ($currenttime == 0 or $frequency == 0) { delete $EMAILSTATUS{"$id"}; } else { $EMAILSTATUS{$id}->{v} = $v; $EMAILSTATUS{$id}->{alerttime} = $currenttime; } $statusfilter = defined $v->{emailalertstatus} ? $v->{emailalertstatus} : $EMAILALERTSTATUS; if (($DAEMON_STATUS & $statusfilter) == 0) { return 0; } $to_addr = defined $v->{emailalert} ? $v->{emailalert} : $EMAILALERT; if ($to_addr eq "") { return 0; } $smtp_server = defined $v->{smtp} ? $v->{smtp} : $SMTP; &ld_log("emailalert: $subject"); if (defined $smtp_server) { $status = &ld_emailalert_net_smtp($smtp_server, $to_addr, $subject); } else { $status = &ld_emailalert_mail_send($to_addr, $subject); } return($status); } # ld_emailalert_net_smtp # Send email alerts via SMTP server # pre: smtp: SMTP server defined # post: message is emailed if SMTP server is valid and working # return: 0 on success # 1 on error sub ld_emailalert_net_smtp { my ($smtp_server, $to_addr, $subject) = (@_); my $status = 0; use Net::SMTP; use Sys::Hostname; my $hostname = hostname; my $smtp = Net::SMTP->new($smtp_server); if ($smtp) { $smtp->mail("$ENV{USER}\@$hostname"); $smtp->to($to_addr); $smtp->data(); if($EMAILALERTFROM) { $smtp->datasend("From: $EMAILALERTFROM\n"); } else { $smtp->datasend("From: $ENV{USER}\@$hostname\n"); } $smtp->datasend("To: $to_addr\n"); $smtp->datasend("Subject: $subject\n\n"); $smtp->datasend("ldirectord host: $hostname\n" . "Log-Message: $subject\n" . "Daemon-Status: " . &daemon_status_str() . "\n"); $smtp->dataend(); $smtp->quit; } else { &ld_log("failed to send SMTP email message\n"); $status = 1; } return($status); } # ld_emailalert_mail_send # Send email alerts via Mail::Send # pre: smtp: SMTP server not defined # post: message is emailed if one of the Mail::Send methods works # return: 0 on success # 1 on error sub ld_emailalert_mail_send { my ($to_addr, $subject) = (@_); my $emailmsg; my $emailfh; my $status = 0; use Mail::Send; $emailmsg = new Mail::Send Subject=>$subject, To=>$to_addr; $emailmsg->set('From', $EMAILALERTFROM) if ($EMAILALERTFROM); $emailfh = $emailmsg->open; print $emailfh "ldirectord host: " . hostname() . "\n" . "Log-Message: $subject\n" . "Daemon-Status: " . &daemon_status_str() . "\n"; unless ($emailfh->close) { &ld_log("failed to send email message\n"); $status = 1; } return($status); } # ld_emailalert_resend # Resend email alerts as necessary # pre: none # post: EMAILSTATUS array is updated and alerts are sent as necessary # return: none sub ld_emailalert_resend { my $currenttime = time(); my $es; my $id; my $rserver; my $frequency; foreach $id (keys %EMAILSTATUS) { $es = $EMAILSTATUS{$id}; $frequency = defined $es->{v}->{emailalertfreq} ? $es->{v}->{emailalertfreq} : $EMAILALERTFREQ; $id =~ m/(.*) /; $rserver = $1; if ($currenttime - $es->{alerttime} < $frequency) { next; } &ld_emailalert_send("Inaccessible real server: $id", $es->{v}, $rserver, $currenttime); } } # ld_debug # Log a message to a STDOUT. # pre: priority: priority of message # message: Message to write # post: message is written to STDOUT if $DEBUG >= priority # return: none sub ld_debug { my ($priority, $message) = (@_); if ( $DEBUG >= $priority ) { chomp $message; print STDERR "DEBUG${priority}: $message\n"; } } # system_wrapper # Wrapper around system() to log errors # # WARNING: Do not use alarm() together with this function. A internal # pipe will not be reclaimed (at least with Perl 5.8.8). This can # cause ldirectord to run out of file handles. # # pre: LIST: arguments to pass to system() # post: system() is called and if it returns non-zero a failure # message is logged # return: return value of system() sub system_wrapper { my (@args)=(@_); my $status; &ld_log("Running system(@args)") if $DEBUG>2; $status = system(@args); if($status != 0) { &ld_log("system(@args) failed: $!"); } return($status) } # system_timeout # Emulate system() with timeout via fork(), exec(), and waitpid() and # TERMinate the child on timeout. Set an alarm() for the timeout. # # This function does not suffer the deficiencies of system_wrapper() # of leaving pipes unreclaimed. Zombies are reaped by ld_handler_chld # and the related code. # # pre: timeout: timeout in seconds # LIST: arguments to pass to exec() # return: >= 0 exit status of the child process # 127 exec failed # -1 timeout # -2 fork failed sub system_timeout { my $timeout = shift; my (@args) = (@_); my $status; &ld_log("Running system_timeout($timeout, @args)") if $DEBUG>2; my $childpid = fork(); if (!defined($childpid)) { &ld_log("fork failed: $!"); return(-2); } elsif ($childpid) { # parent eval { local $SIG{'ALRM'} = sub { die "timeout\n"; }; alarm $timeout; waitpid($childpid, 0); $status = $? >> 8; # When die()-ing in the SIGALRM handler we # will never reach this point. Child/Zombie # is left behind. The grim reaper # (ld_handler_chld + ld_process_chld) will # take care of the zombie. }; alarm 0; if ($@) { # timeout if ($@ ne "timeout\n") { # log unexpected errors &ld_log("system_timeout($timeout, @args) " . "unexpected error: $@"); } else { &ld_log("system_timeout($timeout, @args) " . "timed out, kill -TERM child"); } # TERMinate child kill 15, $childpid; return(-1); } else { # did not timeout return($status); } } else { # child exec(@args) or &ld_exit(127, "exec(@args) failed: $!"); die "ld_exit() broken?, stopped"; } } # exec_wrapper # Wrapper around exec() to log errors # pre: LIST: arguments to pass to exec() # post: exec() is called and if it returns non-zero a failure # message is logged # return: return value of exec() on failure # does not return on success sub exec_wrapper { my (@args)=(@_); my $status; &ld_log("Running exec(@args)") if $DEBUG>2; $status = exec(@args) or &ld_log("exec(@args) failed"); return($status) } # ld_rm_file # Remove a file, symink, or anything that isn't a directory # and exists # pre: filename: file to delete # post: If filename does not exist or is a directory an # error state is reached # Else filename is delete # If $DEBUG >=2 errors are logged # return: 0 on success # -1 on error sub ld_rm_file { my ($filename)=(@_); my ($status); if(-d "$filename"){ &ld_debug(2, "ld_rm_file: $filename is a directory, skipping"); return(-1); } if(! -e "$filename"){ &ld_debug(2, "ld_rm_file: $filename doesn't exist, skipping"); return(-1); } $status = unlink($filename); if($status!=1){ &ld_debug(2, "ld_rm_file: Error deleting: $filename: $!"); } return(($status==1)?0:-1) } # is_octet # See if a number is an octet, that is >=0 and <=255 # pre: alleged_octet: the octet to test # post: alleged_octet is checked to see if it is valid # return: 1 if the alleged_octet is an octet # 0 otherwise sub is_octet { my ($alleged_octet)=(@_); if($alleged_octet<0){ return 0; } if($alleged_octet>255){ return 0; } return(1); } # is_ip # Check that a given string is an IP address # pre: alleged_ip: string representing ip address # post: alleged_ip is checked to see if it is valid # return: 1 if alleged_ip is a valid ip address # 0 otherwise sub is_ip { my ($alleged_ip)=(@_); if ($alleged_ip =~ /:/) { unless(inet_pton(AF_INET6,$alleged_ip)){ return 0; } return(1); } #If we don't have four, . delimited numbers then we have no hope unless($alleged_ip=~m/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { return 0; } #Each octet mist be >=0 and <=255 unless(&is_octet($1)){ return 0; } unless(&is_octet($2)){ return 0; } unless(&is_octet($3)){ return 0; } unless(&is_octet($4)){ return 0; } return(1); } # ip_to_int # Turn an IP address given as a dotted quad into an integer # pre: ip_address: string representing IP address # post: post ip_address is converted to an integer # return: -1 if an error occurs # integer representation of IP address otherwise sub ip_to_int { my ($ip_address)=(@_); unless(&is_ip($ip_address)){ return(-1); } unless($ip_address=~m/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/){ return(-1); } return(((((($1<<8)+$2)<<8)+$3)<<8)+$4); } # int_to_ip # Turn an IP address given as a dotted quad into an integer # pre: ip_address: string representing IP address # post: Decimal is converted to a dotted quad # return: -1 if an error occurs # integer representation of IP address otherwise sub int_to_ip { my ($ip_address)=(@_); my $result = ""; return(sprintf( "%d.%d.%d.%d", ($ip_address>>24)&255, ($ip_address>>16)&255, ($ip_address>>8)&255, $ip_address&255 )); } # get_virtual # Get the service for a virtual # pre: nv: virtual to get the service for # post: none # return: fwmark of service if it is a fwm service # ip_address:port otherwise sub get_virtual { my ($nv) = (@_); if ($nv->{"protocol"} eq "fwm"){ return $nv->{"fwm"}; } else { return $nv->{"server"} . ":" . $nv->{"port"}; } } # get_virtual_option # Get the ipvsadm option corresponding to a virtual service # pre: nv: virtual to get the service for # post: none # return: fwmark of service if it is a fwm service # fwmark of service + "-6" if it is a fwm service and the address family is AF_INET6 # ip_address:port otherwise sub get_virtual_option { my ($nv) = (@_); my ($cmdline) = &get_virtual($nv); if ($nv->{"protocol"} eq "fwm" && $nv->{addressfamily} == AF_INET6) { $cmdline .= " -6"; } return $cmdline; } # get_real_id_str # Get an id string for a real server # pre: r: Real service. # protocol: protocol of the real service # tcp or udp # service: type of service # post: none # return: Id string for the real server sub get_real_id_str { my ($r, $v) = (@_); my $request = ""; my $receive = ""; my $checkport = ""; my $virtualhost = ""; my $check; my $real; if(defined($r->{"request"})) { $request = $r->{"request"}; } else { $request = $v->{"request"}; } if(defined($r->{"receive"})) { $receive = $r->{"receive"}; } else { $receive = $v->{"receive"}; } if($v->{"checktype"} eq "negotiate" or $v->{"checktype"} eq "combined") { $check = $v->{"checktype"} . ":" . $v->{"service"}; } elsif($v->{"checktype"} eq "external" or $v->{"checktype"} eq "external-perl") { $check = $v->{"checktype"} . ":" . $v->{"checkcommand"}; } else { $check = $v->{"checktype"}; } if(defined($v->{"checkport"})) { $checkport = $v->{"checkport"}; } if(defined($v->{"virtualhost"})) { $virtualhost = $v->{"virtualhost"}; } $real = $check . ":" . $v->{"protocol"} . ":" . $r->{"server"} . ":" . $r->{"port"} . ":" . $virtualhost . ":" . $checkport . ":" . $r->{"weight"} . ":" . $r->{"forward"} . ":" . quotemeta($request) . ":" . quotemeta($receive); } # get_virtual_id_str # Get an id string for a virtual service # pre: v: Virtual service # post: none # return: Id string for the virtual service sub get_virtual_id_str { my ($v) = (@_); return $v->{"protocol"} . ":" . &get_virtual($v); } # get_forward_flag # Get the ipvsadm flag corresponding to a forwarding mechanism # pre: forward: Name of forwarding mechanism. u # Should be one of ipip, masq or gate # post: none # return: ipvsadm flag corresponding to the forwarding mechanism # " " if $forward is unknown sub get_forward_flag { my ($forward) = (@_); unless(defined($forward)) { return(" "); } if ($forward eq "masq") { return("-m"); } elsif ($forward eq "gate") { return("-g"); } elsif ($forward eq "ipip") { return("-i"); } return(" "); } # ld_exit # Exit and log a message # pre: exit_status: Integer exit status to exit with # 0 will be used if parameter is omitted # message: Message to log when exiting. May be omitted # post: If exit_status is non-zero or $DEBUG>2 then # message logged. # Programme exits with exit_status # return: does not return sub ld_exit { my ($exit_status, $message)=(@_); unless(defined($exit_status)) { $exit_status=0; } unless(defined($message)) { $message=""; } if ($exit_status!=0 or $DEBUG>2) { &ld_log("Exiting with exit_status $exit_status: $message"); } exit($exit_status); } # ld_open_socket # Open a socket connection # pre: remote: IP address as a dotted quad of remote host to connect to # port: port to connect to # protocol: Protocol to use. Should be either "tcp" or "udp" # post: A Socket connection is opened to the remote host # return: Open socket # undef on error sub ld_open_socket { my ($remote, $port, $protocol) = @_; my ($iaddr, $paddr, $pro, $result, $pf); local *SOCK; $remote = &ld_strip_brackets($remote); if (inet_pton(AF_INET6,$remote)) { $iaddr = inet_pton(AF_INET6,$remote); $paddr = pack_sockaddr_in6($port, $iaddr); $pf = PF_INET6; } else { $iaddr = inet_aton($remote) || die "no host: $remote"; $paddr = sockaddr_in($port, $iaddr); $pf = PF_INET; } $pro = getprotobyname($protocol); if ($protocol eq "udp") { socket(SOCK, $pf, SOCK_DGRAM, $pro) || die "socket: $!"; } else { socket(SOCK, $pf, SOCK_STREAM, $pro) || die "socket: $!"; } $result = connect(SOCK, $paddr); unless ($result) { return undef; } return *SOCK; } # daemon # Close and fork to become a daemon. # # Notes from unix programmer faq # http://www.landfield.com/faqs/unix-faq/programmer/faq/ # # Almost none of this is necessary (or advisable) if your daemon is being # started by `inetd'. In that case, stdin, stdout and stderr are all set up # for you to refer to the network connection, and the `fork()'s and session # manipulation should *not* be done (to avoid confusing `inetd'). Only the # `chdir()' step remains useful. # # Gratuitously over documented, because it can be # # Written by Horms, horms@verge.net.au for an unrelated project while # working for Zip World, http://www.zipworld.com.au/, 1997-1999. sub ld_daemon { # `fork()' so the parent can exit, this returns control to the command # line or shell invoking your program. This step is required so that # the new process is guaranteed not to be a process group leader. The # next step, `setsid()', fails if you're a process group leader. &ld_daemon_become_child(); # setsid()' to become a process group and session group leader. Since a # controlling terminal is associated with a session, and this new # session has not yet acquired a controlling terminal our process now # has no controlling terminal, which is a Good Thing for daemons. if(POSIX::setsid()<0){ &ld_exit(1, "ld_daemon: Could not setsid"); } # fork()' again so the parent, (the session group leader), can exit. # This means that we, as a non-session group leader, can never regain a # controlling terminal. &ld_daemon_become_child(); # `chdir("/")' to ensure that our process doesn't keep any directory in # use. Failure to do this could make it so that an administrator # couldn't unmount a filesystem, because it was our current directory. if(chdir("/")<0){ &ld_exit(1, "ld_daemon: Could not chdir"); } # `close()' fds 0, 1, and 2. This releases the standard in, out, and # error we inherited from our parent process. We have no way of knowing # where these fds might have been redirected to. Note that many daemons # use `sysconf()' to determine the limit `_SC_OPEN_MAX'. `_SC_OPEN_MAX' # tells you the maximum open files/process. Then in a loop, the daemon # can close all possible file descriptors. You have to decide if you # need to do this or not. If you think that there might be # file-descriptors open you should close them, since there's a limit on # number of concurrent file descriptors. close(STDIN); close(STDOUT); close(STDERR); # Establish new open descriptors for stdin, stdout and stderr. Even if # you don't plan to use them, it is still a good idea to have them open. # The precise handling of these is a matter of taste; if you have a # logfile, for example, you might wish to open it as stdout or stderr, # and open `/dev/null' as stdin; alternatively, you could open # `/dev/console' as stderr and/or stdout, and `/dev/null' as stdin, or # any other combination that makes sense for your particular daemon. # # This code used to open /dev/console for STDOUT and STDERR, # but that was changed to /dev/null to stop the code hanging in # the case where /dev/console is unavailable for some reason # http://www.osdl.org/developer_bugzilla/show_bug.cgi?id=1180 if(open(STDIN, ">/dev/null")<0){ &ld_exit(-1, "ld_daemon: Could not open /dev/null"); } if(open(STDERR, ">>/dev/null")<0){ &ld_exit(-1, "ld_daemon: Could not open /dev/null"); } } # ld_daemon_become_child # Fork, kill parent and return child process # pre: none # post: process forks and parent exits # All process exit with exit status -1 if an error occurs # return: parent: exits # child: none (this is the process that returns) # Written by Horms, horms@verge.net.au for an unrelated project while # working for Zip World, http://www.zipworld.com.au/, 1997-1999. sub ld_daemon_become_child { my($status); $status = fork(); if ($status<0){ &ld_exit(-1, "ld_daemon_become_child: Could not fork: $!"); } if ($status>0){ &ld_exit(0, "ld_daemon_become_child: Parent exiting as it should"); } } # ld_gethostbyname # Wrapper to gethostbyname. Look up the/an IP address of a hostname # If an IP address is given is it returned # pre: name: Hostname of IP address to lookup # af: Address Family: AF_INET etc.. # post: gethostbyname is called to find an IP address for $name # This is converted to a string # return: IP address # undef on error sub ld_gethostbyname { my ($name, $af)=(@_); if ($name =~ /\[(.*)\]/) { $name = $1; } my @host = getaddrinfo($name, 0, $af); if (!defined($host[3])) { return undef; } my @ret = getnameinfo($host[3], NI_NUMERICHOST | NI_NUMERICSERV); if ($host[0] == AF_INET6) { return "[$ret[0]]"; } else { return $ret[0]; } } # ld_gethostbyaddr # Wrapper to gethostbyaddr. Look up the hostname from an IP address. # If no reverse DNS record is found, return undef # pre: ip: IP address of host to lookup # post: gethostbyaddr is called to find a hostname for IP $ip # return: hostname # undef on error sub ld_gethostbyaddr { my ($ip)=(@_); $ip = &ld_strip_brackets($ip); my @host = getaddrinfo($ip,0); if (!defined($host[3])) { return undef; } my @ret = getnameinfo($host[3], NI_NAMEREQD); return undef unless(scalar(@ret) == 2); return $ret[0]; } # ld_getservbyname # Wrapper for getservbyname. Look up the port for a service name # If a port is given it is returned. # pre: name: Port or Service name to look up # post: if $name is a number # if 0<=$name<=65536 $name is returned # else undef is returned # else getservbyname is called to look up the port for the service # return: Port # undef on error sub ld_getservbyname { my ($name, $protocol)=(@_); if($name=~/^[0-9]+$/){ return(($name>=0 and $name<65536)?$name:undef); } my @serv=getservbyname($name, $protocol); return((@serv and defined($serv[2]))?$serv[2]:undef); } # ld_getservhostbyname # Wrapper for ld_gethostbyname and ld_getservbyname. Given a server of the # form ip_address|hostname[:port|servicename] return ip_address[:port] # pre: hostserv: Servver of the form ip_address|hostname[:port|servicename] # protocol: Protocol for service. Should be either "tcp" or "udp" # af: Address Family: AF_INET etc.. # post: lookups performed as per ld_getservbyname and ld_gethostbyname # return: ip_address[:port] # undef on error sub ld_gethostservbyname{ my ($hostserv, $protocol, $af) = (@_); my $ip; my $port; if ($hostserv =~ /(:(\d+|[A-Za-z0-9-_]+))?$/) { $port = $2; $ip = $hostserv; $ip =~ s/(:(\d+|[A-Za-z0-9-_]+))?$//; } else { $ip = $hostserv; } $ip=&ld_gethostbyname($ip, $af) or return(undef); if(defined($port)){ $port=&ld_getservbyname($port, $protocol); if (defined($port)) { return("$ip:$port"); } else { return(undef); } } return($ip); } # ld_find_cmd_path # Find executable in path # pre: cmd: command to find # path: ':' delimited paths to check # relative: if set, allow cmd to be a relative path, # which is checked first # return: path to command # undef if not found sub ld_find_cmd_path { my ($cmd, $path, $relative) = (@_); if (defined $relative and $relative and -f "$cmd" ) { return $cmd; } if ($cmd =~ /^\// and -x "$cmd" ) { return $cmd; } if ($cmd =~ /\//) { return undef; } for my $p (split /:/, $path) { if ( -x "$p/$cmd" ) { return "$p/$cmd"; } } return undef; } # ld_find_cmd_path # Find executable in $ENV{'PATH'} # pre: cmd: command to find # relative: if set, allow cmd to be a relative path, # which is checked first # return: path to command # undef if not found sub ld_find_cmd { return ld_find_cmd_path($_[0], $ENV{'PATH'}, $_[1]); } # ld_get_addrport # Get address string and port number from a given socket. # pre: socket # return: (address, port) # undef if cannot get sub ld_get_addrport { my($sock) = @_; my ($s_addr_str, $s_port, $s_addr, $len); my $s_sockaddr = getsockname($sock); $len = length($s_sockaddr); if ($len == 28) { # IPv6 ($s_port, $s_addr) = unpack_sockaddr_in6($s_sockaddr); $s_addr_str = inet_ntop(AF_INET6, $s_addr); $s_addr_str = "[$s_addr_str]"; } elsif ($len == 16) { # IPv4 ($s_port, $s_addr) = unpack_sockaddr_in($s_sockaddr); $s_addr_str = inet_ntop(AF_INET, $s_addr); } else { die "unexpected length of sockaddr\n"; } return ($s_addr_str, $s_port); } # ld_strip_brackets # Strip brackets in the string # pre: string # return: string sub ld_strip_brackets { my($str) = @_; $str =~ s/[\[\]]//g; return $str; } cluster-agents-1.0.4/autogen.sh0000755000175000017500000001075311527003731016722 0ustar roaksoaxroaksoax#!/bin/sh # # License: GNU General Public License (GPL) # Copyright 2001 horms # (heavily mangled by alanr) # # bootstrap: set up the project and get it ready to make # # Basically, we run autoconf, automake and libtool in the # right way to get things set up for this environment. # # We also look and see if those tools are installed, and # tell you where to get them if they're not. # # Our goal is to not require dragging along anything # more than we need. If this doesn't work on your system, # (i.e., your /bin/sh is broken) send us a patch. # # This code loosely based on the corresponding named script in # enlightenment, and also on the sort-of-standard autoconf # bootstrap script. # Run this to generate all the initial makefiles, etc. testProgram() { cmd=$1 if [ -z "$cmd" ]; then return 1; fi arch=`uname -s` # Make sure the which is in an if-block... on some platforms it throws exceptions # # The ERR trap is not executed if the failed command is part # of an until or while loop, part of an if statement, part of a && # or || list. if which $cmd /dev/null 2>&1 then : else return 1 fi # The GNU standard is --version if $cmd --version /dev/null 2>&1 then return 0 fi # Maybe it suppports -V instead if $cmd -V /dev/null 2>&1 then return 0 fi # Nope, the program seems broken return 1 } case "$*" in --help) IsHelp=yes;; -?) IsHelp=yes; set -- --help;; *) IsHelp=no;; esac arch=`uname -s` # Disable the errors on FreeBSD until a fix can be found. if [ ! "$arch" = "FreeBSD" ]; then set -e # # All errors are fatal from here on out... # The shell will complain and exit on any "uncaught" error code. # # # And the trap will ensure sure some kind of error message comes out. # trap 'echo ""; echo "$0 exiting due to error (sorry!)." >&2' 0 fi RC=0 gnu="ftp://ftp.gnu.org/pub/gnu" # Check for Autoconf pkg="autoconf" URL=$gnu/$pkg/ for command in autoconf213 autoconf253 autoconf259 autoconf do if testProgram $command == 1 then : OK $pkg is installed autoconf=$command autoheader=`echo "$autoconf" | sed -e 's/autoconf/autoheader/'` autom4te=`echo "$autoconf" | sed -e 's/autoconf/autmo4te/'` autoreconf=`echo "$autoconf" | sed -e 's/autoconf/autoreconf/'` autoscan=`echo "$autoconf" | sed -e 's/autoconf/autoscan/'` autoupdate=`echo "$autoconf" | sed -e 's/autoconf/autoupdate/'` ifnames=`echo "$autoconf" | sed -e 's/autoconf/ifnames/'` fi done # Check to see if we got a valid command. if $autoconf --version /dev/null 2>&1 then echo "Autoconf package $autoconf found." else RC=$? cat <<-EOF >&2 You must have $pkg installed to compile the linux-ha package. Download the appropriate package for your system, or get the source tarball at: $URL EOF fi # Create local copy so that the incremental updates will work. rm -f ./autoconf ln -s `which $autoconf` ./autoconf # Check for automake pkg="automake" URL=$gnu/$pkg/ for command in automake14 automake-1.4 automake15 automake-1.5 automake17 automake-1.7 automake19 automake-1.9 automake do if testProgram $command then : OK $pkg is installed automake=$command aclocal=`echo "$automake" | sed -e 's/automake/aclocal/'` fi done # Check to see if we got a valid command. if $automake --version /dev/null 2>&1 then echo "Automake package $automake found." else RC=$? cat <<-EOF >&2 You must have $pkg installed to compile the linux-ha package. Download the appropriate package for your system, or get the source tarball at: $URL EOF fi # Create local copy so that the incremental updates will work. rm -f ./automake ln -s `which $automake` ./automake case $IsHelp in yes) $CONFIG "$@"; trap '' 0; exit 0;; esac oneline() { read x; echo "$x" } echo $aclocal $ACLOCAL_FLAGS $aclocal $ACLOCAL_FLAGS # Create local copy so that the incremental updates will work. rm -f ./autoheader ln -s `which $autoheader` ./autoheader if echo $autoheader --version < /dev/null > /dev/null 2>&1 $autoheader --version < /dev/null > /dev/null 2>&1 then echo $autoheader $autoheader fi echo $aclocal $ACLOCAL_FLAGS $aclocal $ACLOCAL_FLAGS echo $automake --add-missing --include-deps --copy $automake --add-missing --include-deps --copy echo $autoconf $autoconf test -f libtool.m4 || touch libtool.m4 test -f ltdl.m4 || touch ltdl.m4 echo Now run ./configure trap '' 0 cluster-agents-1.0.4/configure.in0000644000175000017500000005703011527003731017231 0ustar roaksoaxroaksoaxdnl dnl autoconf for Agents dnl dnl License: GNU General Public License (GPL) dnl =============================================== dnl Bootstrap dnl =============================================== AC_PREREQ(2.53) dnl Suggested structure: dnl information on the package dnl checks for programs dnl checks for libraries dnl checks for header files dnl checks for types dnl checks for structures dnl checks for compiler characteristics dnl checks for library functions dnl checks for system services AC_INIT(resource-agents, 1.0.4, linux-ha-dev@lists.linux-ha.org) CRM_DTD_VERSION="1.0" PKG_FEATURES="" AC_CONFIG_AUX_DIR(.) AC_CANONICAL_HOST dnl Where #defines go (e.g. `AC_CHECK_HEADERS' below) dnl dnl Internal header: include/config.h dnl - Contains ALL defines dnl - include/config.h.in is generated automatically by autoheader dnl - NOT to be included in any header files except lha_internal.h dnl (which is also not to be included in any other header files) dnl dnl External header: include/agent_config.h dnl - Contains a subset of defines checked here dnl - Manually edit include/agent_config.h.in to have configure include new defines dnl - Should not include HAVE_* defines dnl - Safe to include anywhere AM_CONFIG_HEADER(include/config.h include/agent_config.h) ALL_LINGUAS="en fr" AC_ARG_WITH(version, [ --with-version=version Override package version (if you're a packager needing to pretend) ], [ PACKAGE_VERSION="$withval" ]) AC_ARG_WITH(pkg-name, [ --with-pkg-name=name Override package name (if you're a packager needing to pretend) ], [ PACKAGE_NAME="$withval" ]) AM_INIT_AUTOMAKE($PACKAGE_NAME, $PACKAGE_VERSION) AC_DEFINE_UNQUOTED(AGENTS_VERSION, "$PACKAGE_VERSION", Current agents version) CC_IN_CONFIGURE=yes export CC_IN_CONFIGURE LDD=ldd dnl ======================================================================== dnl Compiler characteristics dnl ======================================================================== AC_PROG_CC dnl Can force other with environment variable "CC". AM_PROG_CC_C_O AC_PROG_CC_STDC AC_C_STRINGIZE AC_TYPE_SIZE_T AC_CHECK_SIZEOF(char) AC_CHECK_SIZEOF(short) AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long long) AC_STRUCT_TIMEZONE dnl =============================================== dnl Helpers dnl =============================================== cc_supports_flag() { local CFLAGS="$@" AC_MSG_CHECKING(whether $CC supports "$@") AC_COMPILE_IFELSE([int main(){return 0;}] ,[RC=0; AC_MSG_RESULT(yes)],[RC=1; AC_MSG_RESULT(no)]) return $RC } extract_header_define() { AC_MSG_CHECKING(for $2 in $1) Cfile=/tmp/extract_define.$2.${$} printf "#include \n" > ${Cfile}.c printf "#include <%s>\n" $1 >> ${Cfile}.c printf "int main(int argc, char **argv) { printf(\"%%s\", %s); return 0; }\n" $2 >> ${Cfile}.c $CC $CFLAGS ${Cfile}.c -o ${Cfile} value=`${Cfile}` AC_MSG_RESULT($value) printf $value rm -f ${Cfile}.c ${Cfile} } dnl =============================================== dnl Configure Options dnl =============================================== dnl Some systems, like Solaris require a custom package name AC_ARG_WITH(pkgname, [ --with-pkgname=name name for pkg (typically for Solaris) ], [ PKGNAME="$withval" ], [ PKGNAME="LXHAhb" ], ) AC_SUBST(PKGNAME) AC_ARG_ENABLE([ansi], [ --enable-ansi force GCC to compile to ANSI/ANSI standard for older compilers. [default=yes]]) AC_ARG_ENABLE([fatal-warnings], [ --enable-fatal-warnings very pedantic and fatal warnings for gcc [default=yes]]) INITDIR="" AC_ARG_WITH(initdir, [ --with-initdir=DIR directory for init (rc) scripts [${INITDIR}]], [ INITDIR="$withval" ]) OCF_ROOT_DIR="/usr/lib/ocf" AC_ARG_WITH(ocf-root, [ --with-ocf-root=DIR directory for OCF scripts [${OCF_ROOT_DIR}]], [ if test x"$withval" = xprefix; then OCF_ROOT_DIR=${prefix}; else OCF_ROOT_DIR="$withval"; fi ]) HA_RSCTMPDIR=${localstatedir}/run/resource-agents AC_ARG_WITH(rsctmpdir, [ --with-rsctmpdir=DIR directory for resource agents state files [${HA_RSCTMPDIR}]], [ if test x"$withval" = xprefix; then HA_RSCTMPDIR=${prefix}; else HA_RSCTMPDIR="$withval"; fi ]) AC_ARG_ENABLE([libnet], [ --enable-libnet Use libnet for ARP based funcationality, [default=try]], [enable_libnet="$enableval"], [enable_libnet=try]) dnl =============================================== dnl General Processing dnl =============================================== INIT_EXT="" echo Our Host OS: $host_os/$host AC_MSG_NOTICE(Sanitizing prefix: ${prefix}) case $prefix in NONE) prefix=/usr;; esac AC_MSG_NOTICE(Sanitizing exec_prefix: ${exec_prefix}) case $exec_prefix in dnl For consistency with Heartbeat, map NONE->$prefix NONE) exec_prefix=$prefix;; prefix) exec_prefix=$prefix;; esac AC_MSG_NOTICE(Sanitizing INITDIR: ${INITDIR}) case $INITDIR in prefix) INITDIR=$prefix;; "") AC_MSG_CHECKING(which init (rc) directory to use) for initdir in /etc/init.d /etc/rc.d/init.d /sbin/init.d \ /usr/local/etc/rc.d /etc/rc.d do if test -d $initdir then INITDIR=$initdir break fi done if test -z $INITDIR then INITDIR=${sysconfdir}/init.d fi AC_MSG_RESULT($INITDIR);; esac AC_SUBST(INITDIR) AC_MSG_NOTICE(Sanitizing libdir: ${libdir}) case $libdir in dnl For consistency with Heartbeat, map NONE->$prefix *prefix*|NONE) AC_MSG_CHECKING(which lib directory to use) for aDir in lib64 lib do trydir="${exec_prefix}/${aDir}" if test -d ${trydir} then libdir=${trydir} break fi done AC_MSG_RESULT($libdir); ;; esac dnl Expand autoconf variables so that we dont end up with '${prefix}' dnl in #defines and python scripts dnl NOTE: Autoconf deliberately leaves them unexpanded to allow dnl make exec_prefix=/foo install dnl No longer being able to do this seems like no great loss to me... eval prefix="`eval echo ${prefix}`" eval exec_prefix="`eval echo ${exec_prefix}`" eval bindir="`eval echo ${bindir}`" eval sbindir="`eval echo ${sbindir}`" eval libexecdir="`eval echo ${libexecdir}`" eval datadir="`eval echo ${datadir}`" eval sysconfdir="`eval echo ${sysconfdir}`" eval sharedstatedir="`eval echo ${sharedstatedir}`" eval localstatedir="`eval echo ${localstatedir}`" eval libdir="`eval echo ${libdir}`" eval includedir="`eval echo ${includedir}`" eval oldincludedir="`eval echo ${oldincludedir}`" eval infodir="`eval echo ${infodir}`" eval mandir="`eval echo ${mandir}`" dnl docdir is a recent addition to autotools eval docdir="`eval echo ${docdir}`" if test "x$docdir" = "x"; then docdir="`eval echo ${datadir}/doc`" fi AC_SUBST(docdir) dnl Home-grown variables eval INITDIR="${INITDIR}" for j in prefix exec_prefix bindir sbindir libexecdir datadir sysconfdir \ sharedstatedir localstatedir libdir includedir oldincludedir infodir \ mandir INITDIR docdir do dirname=`eval echo '${'${j}'}'` if test ! -d "$dirname" then AC_MSG_WARN([$j directory ($dirname) does not exist!]) fi done dnl This OS-based decision-making is poor autotools practice; dnl feature-based mechanisms are strongly preferred. dnl dnl So keep this section to a bare minimum; regard as a "necessary evil". REBOOT_OPTIONS="-f" POWEROFF_OPTIONS="-f" case "$host_os" in *bsd*) LIBS="-L/usr/local/lib" CPPFLAGS="$CPPFLAGS -I/usr/local/include" INIT_EXT=".sh" ;; *solaris*) REBOOT_OPTIONS="-n" POWEROFF_OPTIONS="-n" ;; *linux*) AC_DEFINE_UNQUOTED(ON_LINUX, 1, Compiling for Linux platform) POWEROFF_OPTIONS="-nf" REBOOT_OPTIONS="-nf" ;; darwin*) AC_DEFINE_UNQUOTED(ON_DARWIN, 1, Compiling for Darwin platform) LIBS="$LIBS -L${prefix}/lib" CFLAGS="$CFLAGS -I${prefix}/include" ;; esac AC_SUBST(INIT_EXT) AC_DEFINE_UNQUOTED(HA_LOG_FACILITY, LOG_DAEMON, Default logging facility) AC_MSG_NOTICE(Host CPU: $host_cpu) case "$host_cpu" in ppc64|powerpc64) case $CFLAGS in *powerpc64*) ;; *) if test "$GCC" = yes; then CFLAGS="$CFLAGS -m64" fi ;; esac esac AC_MSG_CHECKING(which format is needed to print uint64_t) case "$host_cpu" in s390x)U64T="%lu";; *64*) U64T="%lu";; *) U64T="%llu";; esac AC_MSG_RESULT($U64T) AC_DEFINE_UNQUOTED(U64T, "$U64T", Correct printf format for logging uint64_t) dnl Variables needed for substitution AC_CHECK_HEADERS(heartbeat/glue_config.h) if test "$ac_cv_header_heartbeat_glue_config_h" = "yes"; then OCF_ROOT_DIR=`extract_header_define heartbeat/glue_config.h OCF_ROOT_DIR` else enable_libnet=no fi AC_DEFINE_UNQUOTED(OCF_ROOT_DIR,"$OCF_ROOT_DIR", OCF root directory - specified by the OCF standard) AC_SUBST(OCF_ROOT_DIR) GLUE_STATE_DIR=${localstatedir}/run AC_DEFINE_UNQUOTED(GLUE_STATE_DIR,"$GLUE_STATE_DIR", Where to keep state files and sockets) AC_SUBST(GLUE_STATE_DIR) AC_DEFINE_UNQUOTED(HA_VARRUNDIR,"$GLUE_STATE_DIR", Where Heartbeat keeps state files and sockets - old name) HA_VARRUNDIR="$GLUE_STATE_DIR" AC_SUBST(HA_VARRUNDIR) # Expand $prefix eval HA_RSCTMPDIR="`eval echo ${HA_RSCTMPDIR}`" AC_DEFINE_UNQUOTED(HA_RSCTMPDIR,"$HA_RSCTMPDIR", Where Resouce agents keep state files) AC_SUBST(HA_RSCTMPDIR) dnl Eventually move out of the heartbeat dir tree and create symlinks when needed HA_VARLIBHBDIR=${localstatedir}/lib/heartbeat AC_DEFINE_UNQUOTED(HA_VARLIBHBDIR,"$HA_VARLIBHBDIR", Whatever this used to mean) AC_SUBST(HA_VARLIBHBDIR) OCF_RA_DIR="${OCF_ROOT_DIR}/resource.d/" AC_DEFINE_UNQUOTED(OCF_RA_DIR,"$OCF_RA_DIR", Location for OCF RAs) AC_SUBST(OCF_RA_DIR) OCF_LIB_DIR="${OCF_ROOT_DIR}/lib/" AC_DEFINE_UNQUOTED(OCF_LIB_DIR,"$OCF_LIB_DIR", Location for shared code for OCF RAs) AC_SUBST(OCF_LIB_DIR) AC_PATH_PROGS(HG, hg false) AC_MSG_CHECKING(build version) BUILD_VERSION=unknown if test -f $srcdir/.hg_archival.txt; then BUILD_VERSION=`cat $srcdir/.hg_archival.txt | awk '/node:/ { print $2 }'` elif test -x $HG -a -d .hg; then BUILD_VERSION=`$HG id -i` if test $? != 0; then BUILD_VERSION=unknown fi fi AC_DEFINE_UNQUOTED(BUILD_VERSION, "$BUILD_VERSION", Build version) AC_MSG_RESULT($BUILD_VERSION) AC_SUBST(BUILD_VERSION) dnl =============================================== dnl Program Paths dnl =============================================== PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin" export PATH AM_PATH_PYTHON AC_CHECK_PROGS(MAKE, gmake make) AC_PATH_PROGS(SSH, ssh, /usr/bin/ssh) AC_PATH_PROGS(SCP, scp, /usr/bin/scp) AC_PATH_PROGS(HG, hg, /bin/false) AC_PATH_PROGS(TAR, tar) AC_PATH_PROGS(MD5, md5) AC_PATH_PROGS(TEST, test) AC_PATH_PROGS(PING, ping, /bin/ping) AC_PATH_PROGS(IFCONFIG, ifconfig, /sbin/ifconfig) AC_PATH_PROGS(MAILCMD, mailx mail, mail) AC_PATH_PROGS(EGREP, egrep) AC_PATH_PROGS(PKGCONFIG, pkg-config) AC_PATH_PROGS(HELP2MAN, help2man) AC_SUBST(MAILCMD) AC_SUBST(EGREP) AC_SUBST(SHELL) AC_SUBST(PING) AC_SUBST(TEST) AC_PATH_PROGS(ROUTE, route) AC_DEFINE_UNQUOTED(ROUTE, "$ROUTE", path to route command) AC_MSG_CHECKING(ifconfig option to list interfaces) for IFCONFIG_A_OPT in "-A" "-a" "" do $IFCONFIG $IFCONFIG_A_OPT > /dev/null 2>&1 if test "$?" = 0 then AC_DEFINE_UNQUOTED(IFCONFIG_A_OPT, "$IFCONFIG_A_OPT", option for ifconfig command) AC_MSG_RESULT($IFCONFIG_A_OPT) break fi done AC_SUBST(IFCONFIG_A_OPT) if test x"${MAKE}" = x""; then AC_MSG_ERROR(You need (g)make installed in order to build ${PACKAGE}) fi AM_CONDITIONAL(BUILD_HELP, test x"${HELP2MAN}" != x"") dnl =============================================== dnl Libraries dnl =============================================== AC_CHECK_LIB(socket, socket) AC_CHECK_LIB(gnugetopt, getopt_long) dnl if available if test x"${PKGCONFIG}" = x""; then AC_MSG_ERROR(You need pkgconfig installed in order to build ${PACKAGE}) fi if test "x${enable_thread_safe}" = "xyes"; then GPKGNAME="gthread-2.0" else GPKGNAME="glib-2.0" fi if $PKGCONFIG --exists $GPKGNAME then GLIBCONFIG="$PKGCONFIG $GPKGNAME" else set -x echo PKG_CONFIG_PATH=$PKG_CONFIG_PATH $PKGCONFIG --exists $GPKGNAME; echo $? $PKGCONFIG --cflags $GPKGNAME; echo $? $PKGCONFIG $GPKGNAME; echo $? set +x AC_MSG_ERROR(You need glib2-devel installed in order to build ${PACKAGE}) fi AC_MSG_RESULT(using $GLIBCONFIG) if test "X$GLIBCONFIG" != X; then AC_MSG_CHECKING(for special glib includes: ) GLIBHEAD=`$GLIBCONFIG --cflags` AC_MSG_RESULT($GLIBHEAD) CPPFLAGS="$CPPFLAGS $GLIBHEAD" AC_MSG_CHECKING(for glib library flags) GLIBLIB=`$GLIBCONFIG --libs` AC_MSG_RESULT($GLIBLIB) LIBS="$LIBS $GLIBLIB" fi dnl ======================================================================== dnl Headers dnl ======================================================================== AC_HEADER_STDC AC_CHECK_HEADERS(sys/socket.h) AC_CHECK_HEADERS(sys/sockio.h) dnl ======================================================================== dnl Functions dnl ======================================================================== AC_CHECK_FUNCS(strnlen) dnl 'reboot()' system call: one argument (e.g. Linux) or two (e.g. Solaris)? dnl AC_CACHE_CHECK([number of arguments in reboot system call], ac_cv_REBOOT_ARGS,[ AC_TRY_COMPILE( [#include ], [(void)reboot(0);], ac_cv_REBOOT_ARGS=1, [AC_TRY_COMPILE( [#include ], [(void)reboot(0,(void *)0);], ac_cv_REBOOT_ARGS=2, ac_cv_REBOOT_ARGS=0 )], ac_cv_REBOOT_ARGS=0 ) ] ) dnl Argument count of 0 suggests no known 'reboot()' call. if test "$ac_cv_REBOOT_ARGS" -ge "1"; then AC_DEFINE_UNQUOTED(REBOOT_ARGS,$ac_cv_REBOOT_ARGS,[number of arguments for reboot system call]) fi AC_PATH_PROGS(REBOOT, reboot, /sbin/reboot) AC_SUBST(REBOOT) AC_SUBST(REBOOT_OPTIONS) AC_DEFINE_UNQUOTED(REBOOT, "$REBOOT", path to the reboot command) AC_DEFINE_UNQUOTED(REBOOT_OPTIONS, "$REBOOT_OPTIONS", reboot options) AC_PATH_PROGS(POWEROFF_CMD, poweroff, /sbin/poweroff) AC_SUBST(POWEROFF_CMD) AC_SUBST(POWEROFF_OPTIONS) AC_DEFINE_UNQUOTED(POWEROFF_CMD, "$POWEROFF_CMD", path to the poweroff command) AC_DEFINE_UNQUOTED(POWEROFF_OPTIONS, "$POWEROFF_OPTIONS", poweroff options) AC_PATH_PROGS(XSLTPROC, xsltproc) AM_CONDITIONAL(BUILD_DOC, test "x$XSLTPROC" != "x" ) if test "x$XSLTPROC" = "x"; then AC_MSG_WARN([xsltproc not installed, unable to (re-)build manual pages]) fi AC_SUBST(XSLTPROC) AC_PATH_PROGS(POD2MAN, pod2man) AM_CONDITIONAL(BUILD_POD_DOC, test "x$POD2MAN" != "x" ) if test "x$POD2MAN" = "x"; then AC_MSG_WARN([pod2man not installed, unable to (re-)build ldirector manual page]) fi AC_SUBST(POD2MAN) dnl ======================================================================== dnl Functions dnl ======================================================================== AC_CHECK_FUNCS(getopt, AC_DEFINE(HAVE_DECL_GETOPT, 1, [Have getopt function])) dnl ======================================================================== dnl sfex dnl ======================================================================== build_sfex=no case $host_os in *Linux*|*linux*) if test "$ac_cv_header_heartbeat_glue_config_h" = "yes"; then build_sfex=yes fi ;; esac AM_CONDITIONAL(BUILD_SFEX, test "$build_sfex" = "yes" ) dnl ======================================================================== dnl tickle (needs port to BSD platforms) dnl ======================================================================== AC_CHECK_MEMBERS([struct iphdr.saddr],,,[[#include ]]) AM_CONDITIONAL(BUILD_TICKLE, test "$ac_cv_member_struct_iphdr_saddr" = "yes" ) dnl ======================================================================== dnl libnet dnl ======================================================================== libnet="" libnet_version="none" LIBNETLIBS="" LIBNETDEFINES="" AC_MSG_CHECKING(if libnet is required) libnet_fatal=$enable_libnet case $enable_libnet in no) ;; yes|libnet10|libnet11|10|11) libnet_fatal=yes;; try) case $host_os in *Linux*|*linux*) libnet_fatal=no;; *) libnet_fatal=yes;; dnl legacy behavior esac ;; *) libnet_fatal=yes; enable_libnet=try;; esac AC_MSG_RESULT($libnet_fatal) if test "x$enable_libnet" != "xno"; then AC_PATH_PROGS(LIBNETCONFIG, libnet-config) AC_CHECK_LIB(nsl, t_open) dnl -lnsl AC_CHECK_LIB(socket, socket) dnl -lsocket AC_CHECK_LIB(net, libnet_get_hwaddr, LIBNETLIBS=" -lnet", []) fi AC_MSG_CHECKING(for libnet) if test "x$LIBNETLIBS" != "x" -o "x$enable_libnet" = "xlibnet11"; then LIBNETDEFINES="" if test "$ac_cv_lib_nsl_t_open" = yes; then LIBNETLIBS="-lnsl $LIBNETLIBS" fi if test "$ac_cv_lib_socket_socket" = yes; then LIBNETLIBS="-lsocket $LIBNETLIBS" fi libnet=net libnet_version="libnet1.1" fi if test "x$enable_libnet" = "xtry" -o "x$enable_libnet" = "xlibnet10"; then if test "x$LIBNETLIBS" = x -a "x${LIBNETCONFIG}" != "x" ; then LIBNETDEFINES="`$LIBNETCONFIG --defines` `$LIBNETCONFIG --cflags`"; LIBNETLIBS="`$LIBNETCONFIG --libs`"; libnet_version="libnet1.0 (old)" case $LIBNETLIBS in *-l*) libnet=`echo $LIBNETLIBS | sed 's%.*-l%%'`;; *) libnet_version=none;; esac CPPFLAGS="$CPPFLAGS $LIBNETDEFINES" AC_CHECK_HEADERS(libnet.h) if test "$ac_cv_header_libnet_h" = no; then libnet_version=none fi fi fi AC_MSG_RESULT(found $libnet_version) if test "$libnet_version" = none; then LIBNETLIBS="" LIBNETDEFINES="" if test $libnet_fatal = yes; then AC_MSG_ERROR(libnet not found) fi else AC_CHECK_LIB($libnet,libnet_init, [new_libnet=yes; AC_DEFINE(HAVE_LIBNET_1_1_API, 1, Libnet 1.1 API)], [new_libnet=no; AC_DEFINE(HAVE_LIBNET_1_0_API, 1, Libnet 1.0 API)],$LIBNETLIBS) AC_SUBST(LIBNETLIBS) fi if test "$new_libnet" = yes; then AC_MSG_CHECKING(for libnet API 1.1.4: ) save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fgnu89-inline -Wall -Werror" AC_COMPILE_IFELSE([#include int main(){libnet_t *l=NULL; libnet_pblock_record_ip_offset(l, l->total_size); return(0); }], [AC_MSG_RESULT(no)], [AC_DEFINE(HAVE_LIBNET_1_1_4_API, 1, Libnet 1.1.4 API) AC_MSG_RESULT(yes)]) CFLAGS="$save_CFLAGS" fi sendarp_linux=0 case $host_os in *Linux*|*linux*) sendarp_linux=1;; esac AC_SUBST(LIBNETLIBS) AC_SUBST(LIBNETDEFINES) AM_CONDITIONAL(SENDARP_LINUX, test $sendarp_linux = 1 ) AM_CONDITIONAL(USE_LIBNET, test "x$libnet_version" != "xnone" ) dnl ************************************************************************ dnl * Check for netinet/icmp6.h to enable the IPv6addr resource agent AC_CHECK_HEADERS(netinet/icmp6.h,[],[],[#include ]) AM_CONDITIONAL(USE_IPV6ADDR, test "$ac_cv_header_netinet_icmp6_h" = yes ) dnl ======================================================================== dnl Compiler flags dnl ======================================================================== dnl Make sure that CFLAGS is not exported. If the user did dnl not have CFLAGS in their environment then this should have dnl no effect. However if CFLAGS was exported from the user's dnl environment, then the new CFLAGS will also be exported dnl to sub processes. CC_ERRORS="" CC_EXTRAS="" if export | fgrep " CFLAGS=" > /dev/null; then export -n CFLAGS || true # We don't want to bomb out if this fails SAVED_CFLAGS="$CFLAGS" unset CFLAGS CFLAGS="$SAVED_CFLAGS" unset SAVED_CFLAGS fi if test "$GCC" != yes; then CFLAGS="$CFLAGS -g" enable_fatal_warnings=no else CFLAGS="$CFLAGS -ggdb3 -O0" # We had to eliminate -Wnested-externs because of libtool changes # Also remove -Waggregate-return because we use one libnet # call which returns a struct EXTRA_FLAGS="-fgnu89-inline -fstack-protector-all -Wall -Wbad-function-cast -Wcast-qual -Wcast-align -Wdeclaration-after-statement -Wendif-labels -Wfloat-equal -Wformat=2 -Wformat-security -Wformat-nonliteral -Winline -Wmissing-prototypes -Wmissing-declarations -Wmissing-format-attribute -Wnested-externs -Wno-long-long -Wno-strict-aliasing -Wpointer-arith -Wstrict-prototypes -Wunsigned-char -Wwrite-strings" # Additional warnings it might be nice to enable one day # -Wshadow # -Wunreachable-code for j in $EXTRA_FLAGS do if cc_supports_flag $j then CC_EXTRAS="$CC_EXTRAS $j" fi done dnl In lib/ais/Makefile.am there's a gcc option available as of v4.x GCC_MAJOR=`gcc -v 2>&1 | awk 'END{print $3}' | sed 's/[.].*//'` AM_CONDITIONAL(GCC_4, test "${GCC_MAJOR}" = 4) dnl System specific options case "$host_os" in *linux*|*bsd*) if test "${enable_fatal_warnings}" = "unknown"; then enable_fatal_warnings=yes fi ;; esac if test "x${enable_fatal_warnings}" != xno && cc_supports_flag -Werror ; then enable_fatal_warnings=yes else enable_fatal_warnings=no fi if test "x${enable_ansi}" != xno && cc_supports_flag -std=iso9899:199409 ; then AC_MSG_NOTICE(Enabling ANSI Compatibility) CC_EXTRAS="$CC_EXTRAS -ansi -D_GNU_SOURCE -DANSI_ONLY" fi AC_MSG_NOTICE(Activated additional gcc flags: ${CC_EXTRAS}) fi CFLAGS="$CFLAGS $CC_EXTRAS" NON_FATAL_CFLAGS="$CFLAGS" AC_SUBST(NON_FATAL_CFLAGS) dnl dnl We reset CFLAGS to include our warnings *after* all function dnl checking goes on, so that our warning flags don't keep the dnl AC_*FUNCS() calls above from working. In particular, -Werror will dnl *always* cause us troubles if we set it before here. dnl dnl if test "x${enable_fatal_warnings}" = xyes ; then AC_MSG_NOTICE(Enabling Fatal Warnings) CFLAGS="$CFLAGS -Werror" fi AC_SUBST(CFLAGS) dnl This is useful for use in Makefiles that need to remove one specific flag CFLAGS_COPY="$CFLAGS" AC_SUBST(CFLAGS_COPY) AC_SUBST(LOCALE) AC_SUBST(CC) AC_SUBST(MAKE) dnl The Makefiles and shell scripts we output AC_CONFIG_FILES(Makefile \ include/Makefile \ heartbeat/Makefile \ heartbeat/ocf-binaries \ heartbeat/ocf-directories \ heartbeat/ocf-shellfuncs \ heartbeat/shellfuncs \ tools/Makefile \ tools/ocf-tester \ tools/ocft/Makefile \ tools/ocft/ocft \ tools/ocft/caselib \ tools/ocft/README \ tools/ocft/README.zh_CN \ ldirectord/Makefile \ ldirectord/ldirectord \ ldirectord/init.d/Makefile \ ldirectord/init.d/ldirectord \ ldirectord/init.d/ldirectord.debian \ ldirectord/init.d/ldirectord.debian.default \ ldirectord/logrotate.d/Makefile \ ldirectord/OCF/Makefile \ ldirectord/OCF/ldirectord \ doc/Makefile \ ) dnl Now process the entire list of files added by previous dnl calls to AC_CONFIG_FILES() AC_OUTPUT() dnl ***************** dnl Configure summary dnl ***************** AC_MSG_RESULT([]) AC_MSG_RESULT([$PACKAGE configuration:]) AC_MSG_RESULT([ Version = ${VERSION} (Build: $BUILD_VERSION)]) AC_MSG_RESULT([ Features =${PKG_FEATURES}]) AC_MSG_RESULT([]) AC_MSG_RESULT([ Prefix = ${prefix}]) AC_MSG_RESULT([ Executables = ${sbindir}]) AC_MSG_RESULT([ Man pages = ${mandir}]) AC_MSG_RESULT([ Libraries = ${libdir}]) AC_MSG_RESULT([ Header files = ${includedir}]) AC_MSG_RESULT([ Arch-independent files = ${datadir}]) AC_MSG_RESULT([ Documentation = ${docdir}]) AC_MSG_RESULT([ State information = ${localstatedir}]) AC_MSG_RESULT([ System configuration = ${sysconfdir}]) AC_MSG_RESULT([ RA state files = ${HA_RSCTMPDIR}]) AC_MSG_RESULT([ AIS Plugins = ${LCRSODIR}]) AC_MSG_RESULT([]) AC_MSG_RESULT([ CFLAGS = ${CFLAGS}]) AC_MSG_RESULT([ Libraries = ${LIBS}]) AC_MSG_RESULT([ Stack Libraries = ${CLUSTERLIBS}]) cluster-agents-1.0.4/tools/0000755000175000017500000000000011527051501016051 5ustar roaksoaxroaksoaxcluster-agents-1.0.4/tools/findif.c0000644000175000017500000005534411527003731017471 0ustar roaksoaxroaksoax/* * findif.c: Finds an interface which can route a given address * * It's really simple to write in C, but hard to write in the shell... * * This code is dependent on IPV4 addressing conventions... * Sorry. * * Copyright (C) 2000 Alan Robertson * Copyright (C) 2001 Matt Soffen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * *********************************************************** * * All our arguments come through the environment as OCF * environment variables as below: * * OCF_RESKEY_ip * OCF_RESKEY_broadcast * OCF_RESKEY_nic * OCF_RESKEY_cidr_netmask * * If the CIDR netmask is omitted, we choose the netmask associated with * the route we selected. * * If the broadcast address was omitted, we assume the highest address * in the subnet. * * If the interface is omitted, we choose the interface associated with * the route we selected. * * * See http://www.doom.net/docs/netmask.html for a table explaining * CIDR address format and their relationship to life, the universe * and everything. * */ #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_SYS_SOCKIO_H #include #endif #include #include #include #ifdef __linux__ #undef __OPTIMIZE__ /* * This gets rid of some silly -Wtraditional warnings on Linux * because the netinet header has some slightly funky constants * in it. */ #endif /* __linux__ */ #include #include #include #include #define DEBUG 0 #define EOS '\0' #define PROCROUTE "/proc/net/route" #define ROUTEPARM "-n get" #ifndef HAVE_STRNLEN /* Any system that don't provide strnlen() only has itself to blame */ #define strnlen(str, max) strlen(str) #endif /* * "route -n get iii.jjj.kkk.lll" can, on Solaris at least, * return the word "default" as the value from "mask" and "dest", * typically if the host is remote, reached over a default route. * We should probably treat such a mask as "0.0.0.0". * * Define "MASK_DEFAULT_TO_ZERO" to enable this interpretation. * * This is better for Solaris and is probably suitable (or irrelevant) * for others OSes also. But if it breaks another OS, then reduce the * "hash-if 1" below to exclude that OS. * (David Lee, Jan 2006) */ #if 1 # define MASK_DEFAULT_TO_ZERO #endif static int OutputInCIDR=0; /* * Different OSes offer different mechnisms to obtain this information. * Not all this can be determined at configure-time; need a run-time element. * * typedef ... SearchRoute ...: * For routines that interface on these mechanisms. * Return code: * <0: mechanism invalid, so try next mechanism * 0: mechanism worked: good answer * >0: mechanism worked: bad answer * On non-zero, errmsg may have been filled with an error message */ typedef int SearchRoute (char *address, struct in_addr *in , struct in_addr *addr_out, char *best_if, size_t best_iflen , unsigned long *best_netmask, char *errmsg , int errmsglen); static SearchRoute SearchUsingProcRoute; static SearchRoute SearchUsingRouteCmd; static SearchRoute *search_mechs[] = { &SearchUsingProcRoute, &SearchUsingRouteCmd, NULL }; void GetAddress (char **address, char **netmaskbits , char **bcast_arg, char **if_specified); void ValidateNetmaskBits (char *netmaskbits, unsigned long *netmask); int ValidateIFName (const char *ifname, struct ifreq *ifr); int netmask_bits (unsigned long netmask); char * get_first_loopback_netdev(char * ifname); int is_loopback_interface(char * ifname); char * get_ifname(char * buf, char * ifname); int ConvertQuadToInt(char *dest); static const char *cmdname = "findif"; #define OCF_SUCCESS 0 #define OCF_ERR_GENERIC 1 #define OCF_ERR_ARGS 2 #define OCF_ERR_UNIMPLEMENTED 3 #define OCF_ERR_PERM 4 #define OCF_ERR_INSTALLED 5 #define OCF_ERR_CONFIGURED 6 #define OCF_NOT_RUNNING 7 void usage(int ec); #define PATH_PROC_NET_DEV "/proc/net/dev" #define DELIM '/' #define BAD_BROADCAST (0L) #define MAXSTR 128 static int SearchUsingProcRoute (char *address, struct in_addr *in , struct in_addr *addr_out, char *best_if, size_t best_iflen , unsigned long *best_netmask , char *errmsg, int errmsglen) { unsigned long flags, refcnt, use, gw, mask; unsigned long dest; long metric = LONG_MAX; long best_metric = LONG_MAX; int rc = OCF_SUCCESS; char buf[2048]; char interface[MAXSTR]; FILE *routefd = NULL; if ((routefd = fopen(PROCROUTE, "r")) == NULL) { snprintf(errmsg, errmsglen , "Cannot open %s for reading" , PROCROUTE); rc = OCF_ERR_GENERIC; goto out; } /* Skip first (header) line */ if (fgets(buf, sizeof(buf), routefd) == NULL) { snprintf(errmsg, errmsglen , "Cannot skip first line from %s" , PROCROUTE); rc = OCF_ERR_GENERIC; goto out; } while (fgets(buf, sizeof(buf), routefd) != NULL) { if (sscanf(buf, "%[^\t]\t%lx%lx%lx%lx%lx%lx%lx" , interface, &dest, &gw, &flags, &refcnt, &use , &metric, &mask) != 8) { snprintf(errmsg, errmsglen, "Bad line in %s: %s" , PROCROUTE, buf); rc = OCF_ERR_GENERIC; goto out; } if ( (in->s_addr&mask) == (in_addr_t)(dest&mask) && metric < best_metric) { best_metric = metric; *best_netmask = mask; strncpy(best_if, interface, best_iflen); } } if (best_metric == LONG_MAX) { snprintf(errmsg, errmsglen, "No route to %s\n", address); rc = OCF_ERR_GENERIC; } out: if (routefd) { fclose(routefd); } return(rc); } static int SearchUsingRouteCmd (char *address, struct in_addr *in , struct in_addr *addr_out, char *best_if, size_t best_iflen , unsigned long *best_netmask , char *errmsg, int errmsglen) { char mask[20]; char routecmd[MAXSTR]; int best_metric = INT_MAX; char buf[2048]; char interface[MAXSTR]; char *cp, *sp; int done = 0; FILE *routefd = NULL; uint32_t maskbits; /* Open route and get the information */ snprintf (routecmd, sizeof(routecmd), "%s %s %s" , ROUTE, ROUTEPARM, address); routefd = popen (routecmd, "r"); if (routefd == NULL) return (OCF_ERR_GENERIC); mask[0] = EOS; interface[0] = EOS; while ((done < 3) && fgets(buf, sizeof(buf), routefd)) { int buflen = strnlen(buf, sizeof(buf)); cp = buf; sp = buf + buflen; while (sp!=buf && isspace((int)*(sp-1))) { --sp; } *sp = EOS; buf[buflen] = EOS; if (strstr (buf, "mask:")) { /*strsep(&cp, ":");cp++;*/ cp = strtok(buf, ":"); cp = strtok(NULL, ":");cp++; strncpy(mask, cp, sizeof(mask)); done++; } if (strstr (buf, "interface:")) { /*strsep(&cp, ":");cp++;*/ cp = strtok(buf, ":"); cp = strtok(NULL, ":");cp++; strncpy(interface, cp, sizeof(interface)); done++; } } fclose(routefd); /* * Check to see if mask isn't available. It may not be * returned if multiple IP's are defined. * use 255.255.255.255 for mask then */ /* I'm pretty sure this is the wrong behavior... * I think the right behavior is to declare an error and give up. * The admin didn't define his routes correctly. Fix them. * It's useless to take over an IP address with no way to * return packets to the originator. Without the right subnet * mask, you can't reply to any packets you receive. */ if (strnlen(mask, sizeof(mask)) == 0) { strncpy (mask, "255.255.255.255", sizeof(mask)); } /* * Solaris (at least) can return the word "default" for mask and dest. * For the moment, let's interpret this as: * mask: 0.0.0.0 * This was manifesting itself under "BasicSanityCheck", which tries * to use a remote IP number; these typically use the "default" route. * Better schemes are warmly invited... */ #ifdef MASK_DEFAULT_TO_ZERO if (strncmp(mask, "default", sizeof("default")) == 0) { strncpy (mask, "0.0.0.0", sizeof(mask)); } #endif if (inet_pton(AF_INET, mask, &maskbits) <= 0) { snprintf(errmsg, errmsglen, "mask [%s] not valid.", mask); return(OCF_ERR_CONFIGURED); } if (inet_pton(AF_INET, address, addr_out) <= 0) { snprintf(errmsg, errmsglen , "IP address [%s] not valid.", address); return(OCF_ERR_CONFIGURED); } if ((in->s_addr & maskbits) == (addr_out->s_addr & maskbits)) { if (interface[0] == EOS) { snprintf(errmsg, errmsglen, "No interface found."); return(OCF_ERR_GENERIC); } best_metric = 0; *best_netmask = maskbits; strncpy(best_if, interface, best_iflen); } if (best_metric == INT_MAX) { snprintf(errmsg, errmsglen, "No route to %s\n", address); return(OCF_ERR_GENERIC); } return (OCF_SUCCESS); } /* * Getaddress gets all its real parameters from the OCF environment * variables that its callers already use. */ void GetAddress (char **address, char **netmaskbits , char **bcast_arg, char **if_specified) { /* * Here are out input environment variables: * * OCF_RESKEY_ip ip address * OCF_RESKEY_cidr_netmask netmask of interface * OCF_RESKEY_broadcast broadcast address for interface * OCF_RESKEY_nic interface to assign to * */ *address = getenv("OCF_RESKEY_ip"); *netmaskbits = getenv("OCF_RESKEY_cidr_netmask"); if (*netmaskbits == NULL || **netmaskbits == EOS) { *netmaskbits = getenv("OCF_RESKEY_netmask"); } *bcast_arg = getenv("OCF_RESKEY_broadcast"); *if_specified = getenv("OCF_RESKEY_nic"); } void ValidateNetmaskBits (char *netmaskbits, unsigned long *netmask) { if (netmaskbits != NULL && *netmaskbits != EOS) { size_t nmblen = strnlen(netmaskbits, 3); /* Maximum netmask is 32 */ if (nmblen > 2 || nmblen == 0 || (strspn(netmaskbits, "0123456789") != nmblen)) { fprintf(stderr, "Invalid netmask specification" " [%s]", netmaskbits); usage(OCF_ERR_CONFIGURED); /*not reached */ }else{ unsigned long bits = atoi(netmaskbits); if (bits < 1 || bits > 32) { fprintf(stderr , "Invalid netmask specification [%s]" , netmaskbits); usage(OCF_ERR_CONFIGURED); /*not reached */ exit(1); } bits = 32 - bits; *netmask = (1L<<(bits))-1L; *netmask = ((~(*netmask))&0xffffffffUL); *netmask = htonl(*netmask); } } } int ValidateIFName(const char *ifname, struct ifreq *ifr) { int skfd = -1; char *colonptr; if ( (skfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1 ) { fprintf(stderr, "%s\n", strerror(errno)); return -2; } strncpy(ifr->ifr_name, ifname, IFNAMSIZ); /* Contain a ":"? Probably an error, but treat as warning at present */ if ((colonptr = strchr(ifname, ':')) != NULL) { fprintf(stderr, "%s: warning: name may be invalid\n", ifr->ifr_name); } if (ioctl(skfd, SIOCGIFFLAGS, ifr) < 0) { fprintf(stderr, "%s: unknown interface: %s\n" , ifr->ifr_name, strerror(errno)); close(skfd); /* return -1 only if ifname is known to be invalid */ return -1; } close(skfd); return 0; } int netmask_bits(unsigned long netmask) { int j; netmask = netmask & 0xFFFFFFFFUL; for (j=0; j <= 32; ++j) { if ((netmask >> j)&0x1) { break; } } return 32 - j; } char * get_first_loopback_netdev(char * output) { char buf[512]; FILE * fd = NULL; char *rc = NULL; if (!output) { fprintf(stderr, "output buf is a null pointer.\n"); goto out; } fd = fopen(PATH_PROC_NET_DEV, "r"); if (!fd) { fprintf(stderr, "Warning: cannot open %s (%s).\n", PATH_PROC_NET_DEV, strerror(errno)); goto out; } /* Skip the first two lines */ if (!fgets(buf, sizeof(buf), fd) || !fgets(buf, sizeof(buf), fd)) { fprintf(stderr, "Warning: cannot read header from %s.\n", PATH_PROC_NET_DEV); goto out; } while (fgets(buf, sizeof(buf), fd)) { char name[IFNAMSIZ]; if (NULL == get_ifname(buf, name)) { /* Maybe somethin is wrong, anyway continue */ continue; } if (is_loopback_interface(name)) { strncpy(output, name, IFNAMSIZ); rc = output; goto out; } } out: if (fd) { fclose(fd); } return rc; } int is_loopback_interface(char * ifname) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); if (ValidateIFName(ifname, &ifr) < 0) return 0; if (ifr.ifr_flags & IFF_LOOPBACK) { /* this is a loopback device. */ return 1; } else { return 0; } } char * get_ifname(char * buf, char * ifname) { char * start, * end, * buf_border; buf_border = buf + strnlen(buf, 512); start = buf; while (isspace((int) *start) && (start != buf_border)) { start++; } end = start; while ((*end != ':') && (end != buf_border)) { end++; } if ( start == buf_border || end == buf_border ) { /* Over the border of buf */ return NULL; } *end = '\0'; strncpy(ifname, start, IFNAMSIZ); return ifname; } int ConvertQuadToInt(char *dest) { struct in_addr ad; int bits, j; inet_pton(AF_INET, dest, &ad); bits = 0; j = ntohl(ad.s_addr); while(j != 0){ bits++; j <<= 1; } return (bits); } int main(int argc, char ** argv) { char * address = NULL; char * bcast_arg = NULL; char * netmaskbits = NULL; struct in_addr in; struct in_addr addr_out; unsigned long netmask; char best_if[MAXSTR]; char * if_specified = NULL; struct ifreq ifr; unsigned long best_netmask = INT_MAX; int argerrs = 0; cmdname=argv[0]; memset(&addr_out, 0, sizeof(addr_out)); memset(&in, 0, sizeof(in)); memset(&ifr, 0, sizeof(ifr)); switch (argc) { case 1: /* No -C argument */ break; case 2: /* Hopefully a -C argument */ if (strncmp(argv[1], "-C", sizeof("-C")) != 0) { argerrs=1; } OutputInCIDR=1; break; default: argerrs=1; break; } if (argerrs) { usage(OCF_ERR_ARGS); /* not reached */ return(1); } GetAddress (&address, &netmaskbits, &bcast_arg , &if_specified); if (address == NULL || *address == EOS) { fprintf(stderr, "ERROR: IP address parameter is mandatory."); usage(OCF_ERR_CONFIGURED); /* not reached */ } /* Is the IP address we're supposed to find valid? */ if (inet_pton(AF_INET, address, (void *)&in) <= 0) { fprintf(stderr, "IP address [%s] not valid.", address); usage(OCF_ERR_CONFIGURED); /* not reached */ } if(netmaskbits != NULL && *netmaskbits != EOS && strchr(netmaskbits, '.') != NULL) { int len = strlen(netmaskbits); snprintf(netmaskbits, len, "%d", ConvertQuadToInt(netmaskbits)); fprintf(stderr, "Converted dotted-quad netmask to CIDR as: %s\n", netmaskbits); } /* Validate the netmaskbits field */ ValidateNetmaskBits (netmaskbits, &netmask); if (if_specified != NULL && *if_specified != EOS) { if(ValidateIFName(if_specified, &ifr) < 0) { usage(OCF_ERR_CONFIGURED); /* not reached */ } strncpy(best_if, if_specified, sizeof(best_if)); *(best_if + sizeof(best_if) - 1) = '\0'; }else{ SearchRoute **sr = search_mechs; char errmsg[MAXSTR] = "No valid mecahnisms"; int rc = OCF_ERR_GENERIC; strcpy(best_if, "UNKNOWN"); while (*sr) { errmsg[0] = '\0'; rc = (*sr) (address, &in, &addr_out, best_if , sizeof(best_if) , &best_netmask, errmsg, sizeof(errmsg)); if (!rc) { /* Mechanism worked */ break; } sr++; } if (rc != 0) { /* No route, or all mechanisms failed */ if (*errmsg) { fprintf(stderr, "%s", errmsg); } return(rc); } } if (netmaskbits) { best_netmask = netmask; }else if (best_netmask == 0L) { /* On some distirbutions, there is no loopback related route item, this leads to the error here. My fix may be not good enough, please FIXME */ if (0 == strncmp(address, "127", 3)) { if (NULL != get_first_loopback_netdev(best_if)) { best_netmask = 0x000000ff; } else { fprintf(stderr, "No loopback interface found.\n"); return(OCF_ERR_GENERIC); } } else { fprintf(stderr , "ERROR: Cannot use default route w/o netmask [%s]\n" , address); return(OCF_ERR_GENERIC); } } /* Did they tell us the broadcast address? */ if (bcast_arg && *bcast_arg != EOS) { /* Yes, they gave us a broadcast address. * It at least should be a valid IP address */ struct in_addr bcast_addr; if (inet_pton(AF_INET, bcast_arg, (void *)&bcast_addr) <= 0) { fprintf(stderr, "Invalid broadcast address [%s].", bcast_arg); usage(OCF_ERR_CONFIGURED); /* not reached */ } best_netmask = htonl(best_netmask); if (!OutputInCIDR) { printf("%s\tnetmask %d.%d.%d.%d\tbroadcast %s\n" , best_if , (int)((best_netmask>>24) & 0xff) , (int)((best_netmask>>16) & 0xff) , (int)((best_netmask>>8) & 0xff) , (int)(best_netmask & 0xff) , bcast_arg); }else{ printf("%s\tnetmask %d\tbroadcast %s\n" , best_if , netmask_bits(best_netmask) , bcast_arg); } }else{ /* No, we use a common broadcast address convention */ unsigned long def_bcast; /* Common broadcast address */ def_bcast = (in.s_addr | (~best_netmask)); #if DEBUG fprintf(stderr, "best_netmask = %08lx, def_bcast = %08lx\n" , best_netmask, def_bcast); #endif /* Make things a bit more machine-independent */ best_netmask = htonl(best_netmask); def_bcast = htonl(def_bcast); if (!OutputInCIDR) { printf("%s\tnetmask %d.%d.%d.%d\tbroadcast %d.%d.%d.%d\n" , best_if , (int)((best_netmask>>24) & 0xff) , (int)((best_netmask>>16) & 0xff) , (int)((best_netmask>>8) & 0xff) , (int)(best_netmask & 0xff) , (int)((def_bcast>>24) & 0xff) , (int)((def_bcast>>16) & 0xff) , (int)((def_bcast>>8) & 0xff) , (int)(def_bcast & 0xff)); }else{ printf("%s\tnetmask %d\tbroadcast %d.%d.%d.%d\n" , best_if , netmask_bits(best_netmask) , (int)((def_bcast>>24) & 0xff) , (int)((def_bcast>>16) & 0xff) , (int)((def_bcast>>8) & 0xff) , (int)(def_bcast & 0xff)); } } return(0); } void usage(int ec) { fprintf(stderr, "\n" "%s version 2.99.1 Copyright Alan Robertson\n" "\n" "Usage: %s [-C]\n" "Options:\n" " -C: Output netmask as the number of bits rather " "than as 4 octets.\n" "Environment variables:\n" "OCF_RESKEY_ip ip address (mandatory!)\n" "OCF_RESKEY_cidr_netmask netmask of interface\n" "OCF_RESKEY_broadcast broadcast address for interface\n" "OCF_RESKEY_nic interface to assign to\n" , cmdname, cmdname); exit(ec); } /* Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT eth0 33D60987 00000000 0005 0 0 0 FFFFFFFF 0 0 0 eth0 00D60987 00000000 0001 0 0 0 00FFFFFF 0 0 0 lo 0000007F 00000000 0001 0 0 0 000000FF 0 0 0 eth0 00000000 FED60987 0003 0 0 0 00000000 0 0 0 netstat -rn outpug from RedHat Linux 6.0 Kernel IP routing table Destination Gateway Genmask Flags MSS Window irtt Iface 192.168.85.2 0.0.0.0 255.255.255.255 UH 0 0 0 eth1 10.0.0.2 0.0.0.0 255.255.255.255 UH 0 0 0 eth2 208.132.134.61 0.0.0.0 255.255.255.255 UH 0 0 0 eth0 208.132.134.32 0.0.0.0 255.255.255.224 U 0 0 0 eth0 192.168.85.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1 10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth2 127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo 0.0.0.0 208.132.134.33 0.0.0.0 UG 0 0 0 eth0 |-------------------------------------------------------------------------------- netstat -rn output from FreeBSD 3.3 Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default 209.61.94.161 UGSc 3 8 pn0 192.168 link#1 UC 0 0 xl0 192.168.0.2 0:60:8:a4:91:fd UHLW 0 38 lo0 192.168.0.255 ff:ff:ff:ff:ff:ff UHLWb 1 7877 xl0 209.61.94.160/29 link#2 UC 0 0 pn0 209.61.94.161 0:a0:cc:26:c2:ea UHLW 6 17265 pn0 1105 209.61.94.162 0:a0:cc:27:1c:fb UHLW 1 568 pn0 1098 209.61.94.163 0:a0:cc:29:1f:86 UHLW 0 4749 pn0 1095 209.61.94.166 0:a0:cc:27:2d:e1 UHLW 0 12 lo0 209.61.94.167 ff:ff:ff:ff:ff:ff UHLWb 0 10578 pn0 |-------------------------------------------------------------------------------- netstat -rn output from FreeBSD 4.2 Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default 64.65.195.1 UGSc 1 11 dc0 64.65.195/24 link#1 UC 0 0 dc0 => 64.65.195.1 0:3:42:3b:0:dd UHLW 2 0 dc0 1131 64.65.195.184 0:a0:cc:29:1f:86 UHLW 2 18098 dc0 1119 64.65.195.194 0:a0:cc:27:2d:e1 UHLW 3 335161 dc0 943 64.65.195.200 52:54:0:db:33:b3 UHLW 0 13 dc0 406 64.65.195.255 ff:ff:ff:ff:ff:ff UHLWb 1 584 dc0 127.0.0.1 127.0.0.1 UH 0 0 lo0 192.168/16 link#2 UC 0 0 vx0 => 192.168.0.1 0:20:af:e2:f0:36 UHLW 0 2 lo0 192.168.255.255 ff:ff:ff:ff:ff:ff UHLWb 0 1 vx0 Internet6: Destination Gateway Flags Netif Expire ::1 ::1 UH lo0 fe80::%dc0/64 link#1 UC dc0 fe80::%vx0/64 link#2 UC vx0 fe80::%lo0/64 fe80::1%lo0 Uc lo0 ff01::/32 ::1 U lo0 ff02::%dc0/32 link#1 UC dc0 ff02::%vx0/32 link#2 UC vx0 ff02::%lo0/32 fe80::1%lo0 UC lo0 */ cluster-agents-1.0.4/tools/sfex_init.c0000644000175000017500000001207211527003731020211 0ustar roaksoaxroaksoax/*------------------------------------------------------------------------- * * Shared Disk File EXclusiveness Control Program(SF-EX) * * sfex_init.c --- Initialize SF-EX meta-data. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Copyright (c) 2007 NIPPON TELEGRAPH AND TELEPHONE CORPORATION * * $Id$ * *------------------------------------------------------------------------- * * sfex_init [-b ] [-n ] * * -b --- The size of the block is specified by the number of * bytes. In general, to prevent a partial writing to the disk, the size * of block is set to 512 bytes etc. * Note a set value because this value is used also for the alignment * adjustment in the input-output buffer in the program when direct I/O * is used(When you specify --enable-directio option for configure script). * (In Linux kernel 2.6, "direct I/O " does not work if this value is not * a multiple of 512.) Default is 512 bytes. * * -n --- The number of storing lock data is specified by integer * of one or more. When you want to control two or more resources by one * meta-data, you set the value of two or more to numlocks. A necessary disk * area for meta data are (blocksize*(1+numlocks))bytes. Default is 1. * * --- This is file path which stored meta-data. It is usually * expressed in "/dev/...", because it is partition on the shared disk. * * exit code --- 0 - Normal end. 3 - Error occurs while processing it. * The content of the error is displayed into stderr. 4 - The mistake is * found in the command line parameter. * *-------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include "sfex.h" #include "sfex_lib.h" const char *progname; char *nodename; /* * usage --- display command line syntax * * display command line syntax. By the purpose, it can specify destination * stream. stdout or stderr is set usually. * * dist --- destination stream of the command line syntax, such as stderr. * * return value --- void */ static void usage(FILE *dist) { fprintf(dist, "usage: %s [-n ] \n", progname); } /* * main --- main function * * entry point of sfex_init command. * * exit code --- 0 - Normal end. 3 - Error occurs while processing it. * The content of the error is displayed into stderr. 4 - The mistake is * found in the command line parameter. */ int main(int argc, char *argv[]) { sfex_controldata cdata; sfex_lockdata ldata; /* command line parameter */ int numlocks = 1; /* default 1 locks */ const char *device; /* * startup process */ /* get a program name */ progname = get_progname(argv[0]); /* enable the cl_log output from the sfex library */ cl_log_set_entity(progname); /* The cl_log is output only to the standard error output */ cl_log_enable_stderr(TRUE); /* read command line option */ opterr = 0; while (1) { int c = getopt(argc, argv, "hn:"); if (c == -1) break; switch (c) { case 'h': /* help */ usage(stdout); exit(0); case 'n': /* -n */ { unsigned long l = strtoul(optarg, NULL, 10); if (l < SFEX_MIN_NUMLOCKS || l > SFEX_MAX_NUMLOCKS) { fprintf(stderr, "%s: ERROR: numlocks %s is out of range or invalid. it must be integer value between %lu and %lu.\n", progname, optarg, (unsigned long)SFEX_MIN_NUMLOCKS, (unsigned long)SFEX_MAX_NUMLOCKS); exit(4); } numlocks = l; } break; case '?': /* error */ usage(stderr); exit(4); } } /* check parameter except the option */ if (optind >= argc) { fprintf(stderr, "%s: ERROR: no device specified.\n", progname); usage(stderr); exit(4); } else if (optind + 1 < argc) { fprintf(stderr, "%s: ERROR: too many arguments.\n", progname); usage(stderr); exit(4); } device = argv[optind]; prepare_lock(device); /* main processes start */ /* get a node name */ nodename = get_nodename(); /* create and control data and lock data */ init_controldata(&cdata, sector_size, numlocks); init_lockdata(&ldata); /* write out control data and lock data */ write_controldata(&cdata); { int index; for (index = 1; index <= numlocks; index++) write_lockdata(&cdata, &ldata, index); } exit(0); } cluster-agents-1.0.4/tools/sfex_lib.c0000644000175000017500000003142211527003731020014 0ustar roaksoaxroaksoax/*------------------------------------------------------------------------- * * Shared Disk File EXclusiveness Control Program(SF-EX) * * sfex_lib.c --- Libraries for other SF-EX modules. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Copyright (c) 2007 NIPPON TELEGRAPH AND TELEPHONE CORPORATION * * $Id$ * *-------------------------------------------------------------------------*/ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include "sfex.h" #include "sfex_lib.h" static void *locked_mem; static int dev_fd; unsigned long sector_size = 0; int prepare_lock (const char *device) { do { dev_fd = open (device, O_RDWR | O_DIRECT | O_SYNC); if (dev_fd == -1) { if (errno == EINTR || errno == EAGAIN) continue; cl_log(LOG_ERR, "can't open device %s: %s\n", device, strerror (errno)); exit (3); } break; } while (1); ioctl(dev_fd, BLKSSZGET, §or_size); if (sector_size == 0) { cl_log(LOG_ERR, "Get sector size failed: %s\n", strerror(errno)); exit(EXIT_FAILURE); } if (posix_memalign ((void **) (&locked_mem), SFEX_ODIRECT_ALIGNMENT, sector_size) != 0) { cl_log(LOG_ERR, "Failed to allocate aligned memory\n"); exit (3); } memset (locked_mem, 0, sector_size); return 0; } /* * get_progname --- a program name * * We get program name from directory path. It does not include delimiter * characters. Return value is pointer that point string of program name. * We assume delimiter is '/'. */ const char * get_progname (const char *argv0) { char *p; p = strrchr (argv0, '/'); if (p) return p + 1; else return argv0; } /* * get_nodename --- get a node name(hostname) * * We get a node name by using uname(2) and return pointer of it. * The error checks are done in this function. The caller does not have * to check return value. */ char * get_nodename (void) { struct utsname u; char *n; if (uname (&u)) { cl_log(LOG_ERR, "%s\n", strerror (errno)); exit (3); } if (strlen (u.nodename) > SFEX_MAX_NODENAME) { cl_log(LOG_ERR, "nodename %s is too long. must be less than %lu byte.\n", u.nodename, (unsigned long)SFEX_MAX_NODENAME); exit (3); } n = strdup (&u.nodename[0]); if (!n) { cl_log(LOG_ERR, "%s\n", strerror (errno)); exit (3); } return n; } /* * init_controldata --- initialize control data * * We initialize each member of sfex_controldata structure. */ void init_controldata (sfex_controldata * cdata, size_t blocksize, int numlocks) { memcpy (cdata->magic, SFEX_MAGIC, sizeof (cdata->magic)); cdata->version = SFEX_VERSION; cdata->revision = SFEX_REVISION; cdata->blocksize = blocksize; cdata->numlocks = numlocks; } /* * init_lockdata --- initialize lock data * * We initialize each member of sfex_lockdata structure. */ void init_lockdata (sfex_lockdata * ldata) { ldata->status = SFEX_STATUS_UNLOCK; ldata->count = 0; ldata->nodename[0] = 0; } /* * write_controldata --- write control data into file * * We write sfex_controldata struct into file. We open a file with * synchronization mode and write out control data. * * cdata --- pointer of control data * * device --- name of target file */ void write_controldata (const sfex_controldata * cdata) { sfex_controldata_ondisk *block; int fd; block = (sfex_controldata_ondisk *) (locked_mem); /* We write control data into the buffer with given format. */ /* We write the offset value of each field of the control data directly. * Because a point using this value is limited to two places, we do not * use macro. If you change the following offset values, you must change * values in the read_controldata() function. */ memset (block, 0, cdata->blocksize); memcpy (block->magic, cdata->magic, sizeof (block->magic)); snprintf ((char *) (block->version), sizeof (block->version), "%d", cdata->version); snprintf ((char *) (block->revision), sizeof (block->revision), "%d", cdata->revision); snprintf ((char *) (block->blocksize), sizeof (block->blocksize), "%u", (unsigned)cdata->blocksize); snprintf ((char *) (block->numlocks), sizeof (block->numlocks), "%d", cdata->numlocks); fd = dev_fd; if (lseek (fd, 0, SEEK_SET) == -1) { cl_log(LOG_ERR, "can't seek file pointer: %s\n", strerror (errno)); exit (3); } /* write buffer into a file */ do { ssize_t s = write (fd, block, cdata->blocksize); if (s == -1) { if (errno == EINTR || errno == EAGAIN) continue; cl_log(LOG_ERR, "can't write meta-data: %s\n", strerror (errno)); exit (3); } else break; } while (1); } /* * write_lockdata --- write lock data into file * * We write sfex_lockdata into file and seek file pointer to the given * position of lock data. * * cdata --- pointer for control data * * ldata --- pointer for lock data * * device --- file name for write * * index --- index number for lock data. 1 origine. */ int write_lockdata (const sfex_controldata * cdata, const sfex_lockdata * ldata, int index) { sfex_lockdata_ondisk *block; int fd; block = (sfex_lockdata_ondisk *) locked_mem; /* We write lock data into buffer with given format */ /* We write the offset value of each field of the control data directly. * Because a point using this value is limited to two places, we do not * use macro. If you chage the following offset values, you must change * values in the read_lockdata() function. */ memset (block, 0, cdata->blocksize); block->status = ldata->status; snprintf ((char *) (block->count), sizeof (block->count), "%d", ldata->count); snprintf ((char *) (block->nodename), sizeof (block->nodename), "%s", ldata->nodename); fd = dev_fd; /* seek a file pointer to given position */ if (lseek (fd, cdata->blocksize * index, SEEK_SET) == -1) { cl_log(LOG_ERR, "can't seek file pointer: %s\n", strerror (errno)); return -1; } /* write buffer into file */ do { ssize_t s = write (fd, block, cdata->blocksize); if (s == -1) { if (errno == EINTR || errno == EAGAIN) continue; cl_log(LOG_ERR, "can't write meta-data: %s\n", strerror (errno)); return -1; } else if (s != cdata->blocksize) { /* if writing atomically failed, this process is error */ cl_log(LOG_ERR, "can't write meta-data atomically.\n"); return -1; } break; } while (1); return 0; } /* * read_controldata --- read control data from file * * read sfex_controldata structure from file. * * cdata --- pointer for control data * * device --- file name for reading */ int read_controldata (sfex_controldata * cdata) { sfex_controldata_ondisk *block; block = (sfex_controldata_ondisk *) (locked_mem); if (lseek (dev_fd, 0, SEEK_SET) == -1) { cl_log(LOG_ERR, "can't seek file pointer: %s\n", strerror (errno)); return -1; } /* read data from file */ do { ssize_t s = read (dev_fd, block, sector_size); if (s == -1) { if (errno == EINTR || errno == EAGAIN) continue; cl_log(LOG_ERR, "can't read controldata meta-data: %s\n", strerror (errno)); return -1; } else break; } while (1); /* read control data from buffer */ /* 1. check the magic number. 2. check null terminator of each field 3. check the version number. 4. Unmuch of revision number is allowed */ /* We write the offset value of each field of the control data directly. * Because a point using this value is limited to two places, we do not * use macro. If you chage the following offset values, you must change * values in the write_controldata() function. */ memcpy (cdata->magic, block->magic, 4); if (memcmp (cdata->magic, SFEX_MAGIC, sizeof (cdata->magic))) { cl_log(LOG_ERR, "magic number mismatched. %c%c%c%c <-> %s\n", block->magic[0], block->magic[1], block->magic[2], block->magic[3], SFEX_MAGIC); return -1; } if (block->version[sizeof (block->version)-1] || block->revision[sizeof (block->revision)-1] || block->blocksize[sizeof (block->blocksize)-1] || block->numlocks[sizeof (block->numlocks)-1]) { cl_log(LOG_ERR, "control data format error.\n"); return -1; } cdata->version = atoi ((char *) (block->version)); if (cdata->version != SFEX_VERSION) { cl_log(LOG_ERR, "version number mismatched. program is %d, data is %d.\n", SFEX_VERSION, cdata->version); return -1; } cdata->revision = atoi ((char *) (block->revision)); cdata->blocksize = atoi ((char *) (block->blocksize)); cdata->numlocks = atoi ((char *) (block->numlocks)); return 0; } /* * read_lockdata --- read lock data from file * * read sfex_lockdata from file and seek file pointer to head position of the * file. * * cdata --- pointer for control data * * ldata --- pointer for lock data. Read lock data are stored into this * pointed area. * * device --- file name of source file * * index --- index number. 1 origin. */ int read_lockdata (const sfex_controldata * cdata, sfex_lockdata * ldata, int index) { sfex_lockdata_ondisk *block; int fd; block = (sfex_lockdata_ondisk *) (locked_mem); fd = dev_fd; /* seek a file pointer to given position */ if (lseek (fd, cdata->blocksize * index, SEEK_SET) == -1) { cl_log(LOG_ERR, "can't seek file pointer: %s\n", strerror (errno)); return -1; } /* read from file */ do { ssize_t s = read (fd, block, cdata->blocksize); if (s == -1) { if (errno == EINTR || errno == EAGAIN) continue; cl_log(LOG_ERR, "can't read lockdata meta-data: %s\n", strerror (errno)); return -1; } else if (s != cdata->blocksize) { cl_log(LOG_ERR, "can't read meta-data atomically.\n"); return -1; } break; } while (1); /* read control data form buffer */ /* 1. check null terminator of each field 2. check the status */ /* We write the offset value of each field of the control data directly. * Because a point using this value is limited to two places, we do not * use macro. If you chage the following offset values, you must change * values in the write_lockdata() function. */ if (block->count[sizeof(block->count)-1] || block->nodename[sizeof(block->nodename)-1]) { cl_log(LOG_ERR, "lock data format error.\n"); return -1; } ldata->status = block->status; if (ldata->status != SFEX_STATUS_UNLOCK && ldata->status != SFEX_STATUS_LOCK) { cl_log(LOG_ERR, "lock data format error.\n"); return -1; } ldata->count = atoi ((char *) (block->count)); strncpy ((char *) (ldata->nodename), (const char *) (block->nodename), sizeof(block->nodename)); #ifdef SFEX_DEBUG cl_log(LOG_INFO, "status: %c\n", ldata->status); cl_log(LOG_INFO, "count: %d\n", ldata->count); cl_log(LOG_INFO, "nodename: %s\n", ldata->nodename); #endif return 0; } /* * lock_index_check --- check the value of index * * The lock_index_check function checks whether the value of index exceeds * the number of lock data on the shared disk. * * cdata --- pointer for control data * * index --- index number */ int lock_index_check(sfex_controldata * cdata, int index) { if (read_controldata(cdata) == -1) { cl_log(LOG_ERR, "%s\n", "read_controldata failed in lock_index_check"); return -1; } #ifdef SFEX_DEBUG cl_log(LOG_INFO, "version: %d\n", cdata->version); cl_log(LOG_INFO, "revision: %d\n", cdata->revision); cl_log(LOG_INFO, "blocksize: %d\n", cdata->blocksize); cl_log(LOG_INFO, "numlocks: %d\n", cdata->numlocks); #endif if (index > cdata->numlocks) { cl_log(LOG_ERR, "index %d is too large. %d locks are stored.\n", index, cdata->numlocks); return -1; } if (cdata->blocksize != sector_size) { cl_log(LOG_ERR, "sector_size is not the same as the blocksize.\n"); return -1; } return 0; } cluster-agents-1.0.4/tools/send_arp.linux.c0000644000175000017500000003113711527003731021155 0ustar roaksoaxroaksoax/* * arping.c * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Authors: Alexey Kuznetsov, */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void usage(void) __attribute__((noreturn)); static int quit_on_reply; static char *device; static int ifindex; static char *source; static struct in_addr src, dst; static char *target; static int dad = 0, unsolicited = 0, advert = 0; static int quiet = 0; static int count = -1; static int timeout = 0; static int unicasting = 0; static int s = 0; static int broadcast_only = 0; static struct sockaddr_ll me; static struct sockaddr_ll he; static struct timeval start, last; static int sent, brd_sent; static int received, brd_recv, req_recv; #define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + \ ((tv1).tv_usec-(tv2).tv_usec)/1000 ) static void print_hex(unsigned char *p, int len); static int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM); static void set_signal(int signo, void (*handler)(void)); static int send_pack(int s, struct in_addr src, struct in_addr dst, struct sockaddr_ll *ME, struct sockaddr_ll *HE); static void finish(void); static void catcher(void); void usage(void) { fprintf(stderr, "Usage: arping [-fqbDUAV] [-c count] [-w timeout] [-I device] [-s source] destination\n" " -f : quit on first reply\n" " -q : be quiet\n" " -b : keep broadcasting, don't go unicast\n" " -D : duplicate address detection mode\n" " -U : Unsolicited ARP mode, update your neighbours\n" " -A : ARP answer mode, update your neighbours\n" " -V : print version and exit\n" " -c count : how many packets to send\n" " -w timeout : how long to wait for a reply\n" " -I device : which ethernet device to use (eth0)\n" " -s source : source ip address\n" " destination : ask for what ip address\n" ); exit(2); } void set_signal(int signo, void (*handler)(void)) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = (void (*)(int))handler; sa.sa_flags = SA_RESTART; sigaction(signo, &sa, NULL); } int send_pack(int s, struct in_addr src, struct in_addr dst, struct sockaddr_ll *ME, struct sockaddr_ll *HE) { int err; struct timeval now; unsigned char buf[256]; struct arphdr *ah = (struct arphdr*)buf; unsigned char *p = (unsigned char *)(ah+1); ah->ar_hrd = htons(ME->sll_hatype); if (ah->ar_hrd == htons(ARPHRD_FDDI)) ah->ar_hrd = htons(ARPHRD_ETHER); ah->ar_pro = htons(ETH_P_IP); ah->ar_hln = ME->sll_halen; ah->ar_pln = 4; ah->ar_op = advert ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST); memcpy(p, &ME->sll_addr, ah->ar_hln); p+=ME->sll_halen; memcpy(p, &src, 4); p+=4; if (advert) memcpy(p, &ME->sll_addr, ah->ar_hln); else memcpy(p, &HE->sll_addr, ah->ar_hln); p+=ah->ar_hln; memcpy(p, &dst, 4); p+=4; gettimeofday(&now, NULL); err = sendto(s, buf, p-buf, 0, (struct sockaddr*)HE, sizeof(*HE)); if (err == p-buf) { last = now; sent++; if (!unicasting) brd_sent++; } return err; } void finish(void) { if (!quiet) { printf("Sent %d probes (%d broadcast(s))\n", sent, brd_sent); printf("Received %d response(s)", received); if (brd_recv || req_recv) { printf(" ("); if (req_recv) printf("%d request(s)", req_recv); if (brd_recv) printf("%s%d broadcast(s)", req_recv ? ", " : "", brd_recv); printf(")"); } printf("\n"); fflush(stdout); } if (dad) { fflush(stdout); exit(!!received); } if (unsolicited) exit(0); fflush(stdout); exit(!received); } void catcher(void) { struct timeval tv; gettimeofday(&tv, NULL); if (start.tv_sec==0) start = tv; if (count-- == 0 || (timeout && MS_TDIFF(tv,start) > timeout*1000 + 500)) finish(); if (last.tv_sec==0 || MS_TDIFF(tv,last) > 500) { send_pack(s, src, dst, &me, &he); if (count == 0 && unsolicited) finish(); } alarm(1); } void print_hex(unsigned char *p, int len) { int i; for (i=0; isll_pkttype != PACKET_HOST && FROM->sll_pkttype != PACKET_BROADCAST && FROM->sll_pkttype != PACKET_MULTICAST) return 0; /* Only these types are recognised */ if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY)) return 0; /* ARPHRD check and this darned FDDI hack here :-( */ if (ah->ar_hrd != htons(FROM->sll_hatype) && (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER))) return 0; /* Protocol must be IP. */ if (ah->ar_pro != htons(ETH_P_IP)) return 0; if (ah->ar_pln != 4) return 0; if (ah->ar_hln != me.sll_halen) return 0; if (len < sizeof(*ah) + 2*(4 + ah->ar_hln)) return 0; memcpy(&src_ip, p+ah->ar_hln, 4); memcpy(&dst_ip, p+ah->ar_hln+4+ah->ar_hln, 4); if (!dad) { if (src_ip.s_addr != dst.s_addr) return 0; if (src.s_addr != dst_ip.s_addr) return 0; if (memcmp(p+ah->ar_hln+4, &me.sll_addr, ah->ar_hln)) return 0; } else { /* DAD packet was: src_ip = 0 (or some src) src_hw = ME dst_ip = tested address dst_hw = We fail, if receive request/reply with: src_ip = tested_address src_hw != ME if src_ip in request was not zero, check also that it matches to dst_ip, otherwise dst_ip/dst_hw do not matter. */ if (src_ip.s_addr != dst.s_addr) return 0; if (memcmp(p, &me.sll_addr, me.sll_halen) == 0) return 0; if (src.s_addr && src.s_addr != dst_ip.s_addr) return 0; } if (!quiet) { int s_printed = 0; printf("%s ", FROM->sll_pkttype==PACKET_HOST ? "Unicast" : "Broadcast"); printf("%s from ", ah->ar_op == htons(ARPOP_REPLY) ? "reply" : "request"); printf("%s [", inet_ntoa(src_ip)); print_hex(p, ah->ar_hln); printf("] "); if (dst_ip.s_addr != src.s_addr) { printf("for %s ", inet_ntoa(dst_ip)); s_printed = 1; } if (memcmp(p+ah->ar_hln+4, me.sll_addr, ah->ar_hln)) { if (!s_printed) printf("for "); printf("["); print_hex(p+ah->ar_hln+4, ah->ar_hln); printf("]"); } if (last.tv_sec) { long usecs = (tv.tv_sec-last.tv_sec) * 1000000 + tv.tv_usec-last.tv_usec; long msecs = (usecs+500)/1000; usecs -= msecs*1000 - 500; printf(" %ld.%03ldms\n", msecs, usecs); } else { printf(" UNSOLICITED?\n"); } fflush(stdout); } received++; if (FROM->sll_pkttype != PACKET_HOST) brd_recv++; if (ah->ar_op == htons(ARPOP_REQUEST)) req_recv++; if (quit_on_reply) finish(); if(!broadcast_only) { memcpy(he.sll_addr, p, me.sll_halen); unicasting=1; } return 1; } #include static void byebye(int nsig) { /* Avoid an "error exit" log message if we're killed */ nsig = 0; exit(nsig); } int main(int argc, char **argv) { int socket_errno; int ch; uid_t uid = getuid(); int hb_mode = 0; signal(SIGTERM, byebye); signal(SIGPIPE, byebye); device = strdup("eth0"); s = socket(PF_PACKET, SOCK_DGRAM, 0); socket_errno = errno; if (setuid(uid)) { perror("arping: setuid"); exit(-1); } while ((ch = getopt(argc, argv, "h?bfDUAqc:w:s:I:Vr:i:p:")) != EOF) { switch(ch) { case 'b': broadcast_only=1; break; case 'D': dad++; quit_on_reply=1; break; case 'U': unsolicited++; break; case 'A': advert++; unsolicited++; break; case 'q': quiet++; break; case 'r': /* send_arp compatability option */ hb_mode = 1; case 'c': count = atoi(optarg); break; case 'w': timeout = atoi(optarg); break; case 'I': device = optarg; break; case 'f': quit_on_reply=1; break; case 's': source = optarg; break; case 'V': printf("send_arp utility\n"); exit(0); case 'p': case 'i': hb_mode = 1; /* send_arp compatability options, ignore */ break; case 'h': case '?': default: usage(); } } if(hb_mode) { /* send_arp compatability mode */ if (argc - optind != 5) { usage(); return 1; } /* * argv[optind+1] DEVICE dc0,eth0:0,hme0:0, * argv[optind+2] IP 192.168.195.186 * argv[optind+3] MAC ADDR 00a0cc34a878 * argv[optind+4] BROADCAST 192.168.195.186 * argv[optind+5] NETMASK ffffffffffff */ unsolicited = 1; device = argv[optind]; target = argv[optind+1]; } else { argc -= optind; argv += optind; if (argc != 1) usage(); target = *argv; } if (device == NULL) { fprintf(stderr, "arping: device (option -I) is required\n"); usage(); } if (s < 0) { errno = socket_errno; perror("arping: socket"); exit(2); } if (1) { struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, device, IFNAMSIZ-1); if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { fprintf(stderr, "arping: unknown iface %s\n", device); exit(2); } ifindex = ifr.ifr_ifindex; if (ioctl(s, SIOCGIFFLAGS, (char*)&ifr)) { perror("ioctl(SIOCGIFFLAGS)"); exit(2); } if (!(ifr.ifr_flags&IFF_UP)) { if (!quiet) printf("Interface \"%s\" is down\n", device); exit(2); } if (ifr.ifr_flags&(IFF_NOARP|IFF_LOOPBACK)) { if (!quiet) printf("Interface \"%s\" is not ARPable\n", device); exit(dad?0:2); } } if (inet_aton(target, &dst) != 1) { struct hostent *hp; hp = gethostbyname2(target, AF_INET); if (!hp) { fprintf(stderr, "arping: unknown host %s\n", target); exit(2); } memcpy(&dst, hp->h_addr, 4); } if (source && inet_aton(source, &src) != 1) { fprintf(stderr, "arping: invalid source %s\n", source); exit(2); } if (!dad && unsolicited && src.s_addr == 0) src = dst; if (!dad || src.s_addr) { struct sockaddr_in saddr; int probe_fd = socket(AF_INET, SOCK_DGRAM, 0); if (probe_fd < 0) { perror("socket"); exit(2); } if (device) { if (setsockopt(probe_fd, SOL_SOCKET, SO_BINDTODEVICE, device, strlen(device)+1) == -1) perror("WARNING: interface is ignored"); } memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; if (src.s_addr) { saddr.sin_addr = src; if (bind(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { perror("bind"); exit(2); } } else if (!dad) { int on = 1; socklen_t alen = sizeof(saddr); saddr.sin_port = htons(1025); saddr.sin_addr = dst; if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, (char*)&on, sizeof(on)) == -1) perror("WARNING: setsockopt(SO_DONTROUTE)"); if (connect(probe_fd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) { perror("connect"); exit(2); } if (getsockname(probe_fd, (struct sockaddr*)&saddr, &alen) == -1) { perror("getsockname"); exit(2); } src = saddr.sin_addr; } close(probe_fd); }; me.sll_family = AF_PACKET; me.sll_ifindex = ifindex; me.sll_protocol = htons(ETH_P_ARP); if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) { perror("bind"); exit(2); } if (1) { socklen_t alen = sizeof(me); if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { perror("getsockname"); exit(2); } } if (me.sll_halen == 0) { if (!quiet) printf("Interface \"%s\" is not ARPable (no ll address)\n", device); exit(dad?0:2); } he = me; memset(he.sll_addr, -1, he.sll_halen); if (!quiet) { printf("ARPING %s ", inet_ntoa(dst)); printf("from %s %s\n", inet_ntoa(src), device ? : ""); } if (!src.s_addr && !dad) { fprintf(stderr, "arping: no source address in not-DAD mode\n"); exit(2); } set_signal(SIGINT, finish); set_signal(SIGALRM, catcher); catcher(); while(1) { sigset_t sset, osset; unsigned char packet[4096]; struct sockaddr_ll from; socklen_t alen = sizeof(from); int cc; if ((cc = recvfrom(s, packet, sizeof(packet), 0, (struct sockaddr *)&from, &alen)) < 0) { perror("arping: recvfrom"); continue; } sigemptyset(&sset); sigaddset(&sset, SIGALRM); sigaddset(&sset, SIGINT); sigprocmask(SIG_BLOCK, &sset, &osset); recv_pack(packet, cc, &from); sigprocmask(SIG_SETMASK, &osset, NULL); } } cluster-agents-1.0.4/tools/README.sfex0000644000175000017500000002631611527003731017707 0ustar roaksoaxroaksoaxShared Disk File EXclusiveness Control Program version 1.3 OCF Resource Agent for Heartbeat v2 FOR USE IN LINUX 2.6 KERNEL OPERATING SYSTEM ENVIRONMENTS ONLY. Copyright (c) 2007 NIPPON TELEGRAPH AND TELEPHONE CORPORATION Note: Before using this information and the product it supports, read the general information in section 4.0 "Trademarks and Notices" in this document. Last Update Date: 10/10/2007 ======================================================================= CONTENTS -------- 1.0 Overview 2.0 Installation and Setup Instructions 3.0 Configuration Information 4.0 Trademarks and Notices 5.0 Disclaimer ======================================================================= 1.0 Overview -------------- Shared Disk File EXclusiveness Control Program, called "SF-EX" for short, can prevent a destruction of data on shared disk file system due to Split-Brain. ======================================================================= 1.1 Limitations --------------------- This program is tested on the following environment. Heartbeat 2.1.2-2 Red Hat Enterprise Linux ES release 4 (Nahant Update 5) EM64T ======================================================================= 2.0 Installation and Setup Instructions ----------------------------------------- 2.1.1 Prerequisites SF-EX is released as a source-code package in the format of a gunzip compressed tar file. To unpack the source package, type the following command in the Linux console window: $ tar zxf sfex-1.3.tar.gz The source files will uncompress to the "sf-ex-x.x" directory. 2.1.3 Build and Installation Change unpacked directory first. $ cd sfex-1.3 Type the following command in the Linux console window: Press Enter after each command. $ ./configure $ make $ su (you need root's password) # make install "make install" will copy the modules to /usr/lib64/heartbeat NOTE: "make install" should be done on all nodes which Heartbeat would run. NOTE: in case of 32bit system If you want to run SF-EX on 32bit system, the modules should be setup on /usr/lib/heartbeat. Use the following configure option on 32bit system. $ ./configure --with-lib-dir=/usr/lib/heartbeat 2.1.3 Initialization of a device Before running SF-EX, one device should be initialized as below. sfex_init [-b ] [-n ] Example: # /usr/lib/heartbeat/sfex_init -b 512 -n 10 /dev/sdb1 Initialized device is going to be used as a control area for SF-EX. See 3.2.2, if further information is necessary. 2.1.4 Access without O_DIRECT If you are planning to access a device without using O_DIRECT, the following option is available. Example: $ ./configure -enable-directio=no Default value for --enable-directio is "yes". ======================================================================= 3.0 Configuration Information ----------------------------- 3.1 Configuration Settings -------------------------- 3.1.1 Edit your cib.xml The following example shows a typical configuration for SF-EX and Filesystem. 3.1.2 Example for cib.xml /dev/sda1 control area for SF-EX /dev/sda2 Filesystem --- skip --- --- skip --- 3.2 Outline of each module -------------------------- 3.2.1 sfex Resource Agent script for Heartbeat. 3.2.2 sfex_init sfex_init [-b ] [-n ] -b --- The size of the block is specified by the number of bytes. In general, to prevent a partial writing to the disk, the size of block is set to 512 bytes etc. Note a set value because this value is used also for the alignment adjustment in the input-output buffer in the program when direct I/O is used(When you specify --enable-directio option for configure script). (In Linux kernel 2.6, "direct I/O " does not work if this value is not a multiple of 512.) Default is 512 bytes. -n --- The number of storing lock data is specified by integer of one or more. When you want to control two or more resources by one meta-data, you set the value of two or more to numlocks. A necessary disk area for meta data are (blocksize*(1+numlocks))bytes. Default is 1. --- This is file path which stored mata-data. It is usually expressed in "/dev/...", because it is partition on the shared disk. exit code --- 0 - Normal end. 3 - Error occurs while processing it. The content of the error is displayed into stderr. 4 - The mistake is found in the command line parameter. 3.2.3 sfex_stat sfex_stat [-i ] -i --- The index is number of the resource that display the lock. This number is specified by the integer of one or more. When two or more resources are exclusively controlled by one meta-data, this option is used. Default is 1. --- This is file path which stored mata-data. It is usually expressed in "/dev/...", because it is partition on the shared disk. exit code --- 0 - Normal end. Own node is holding lock. 2 - Normal end. Own node does not hold a lock. 3 - Error occurs while processing it. The content of the error is displayed into stderr. 4 - The mistake is found in the command line parameter. 3.2.4 sfex_lock sfex_lock [-i ] [-c ] [-t ] -i --- The index is number of the resource that acquire the lock. This number is specified by the integer of one or more. When two or more resources are exclusively controlled by one meta-data, this option is used. Default is 1. -c --- The waiting time to detect the collision of the lock with other nodes is specified. Time that is very longer than "once synchronous read from device which stored meta-data + once synchronous write" is specified usually. Default is 1 second. This value need not be changed by using this option usually. Because it is not thought to take one second or more to synchronous read and write. -t --- This specifies the validity term of lock. The unit is a second. This timer prevents the resource being locked for a long time when node crashes with the lock acquired. Therefore, the lock holding node must update lock data at intervals that are shorter than this timer. The sfex_update command is used for updating lock. Default is 60 seconds. --- This is file path which stored mata-data. It is usually expressed in "/dev/...", because it is partition on the shared disk. exit code --- 0 - Acquire a lock from unlock status. 1 - Acquire a lock from lock timeout status. 2 - Lock acquisition failed. 3 - Error occurs while processing it. The content of the error is displayed into stderr. 4 - The mistake is found in the command line parameter. 3.2.5 sfex_unlock sfex_unlock [-i ] -i --- The index is number of the resource that releases the lock. This number is specified by the integer of one or more. When two or more resources are exclusively controlled by one meta-data, this option is used. Default is 1. --- This is file path which stored mata-data. It is usually expressed in "/dev/...", because it is partition on the shared disk. exit code --- 0 - Lock release success. 1 - Lock release done already. The lock has already been acquired by other nodes. 3 - Error occurs while processing it. The content of the error is displayed into stderr. 4 - The mistake is found in the command line parameter. 3.2.6 sfex_update sfex_update [-i ] -i --- The index is number of the resource that update the lock. This number is specified by the integer of one or more. When two or more resources are exclusively controlled by one meta-data, this option is used. Default is 1. --- This is file path which stored mata-data. It is usually expressed in "/dev/...", because it is partition on the shared disk. exit code --- 0 - Lock update success. 2 - Lock update failed. The lock is acquired by other nodes. 3 - Error occurs while processing it. The content of the error is displayed into stderr. 4 - The mistake is found in the command line parameter. ======================================================================= 4.0 Trademarks and Notices ---------------------------- Heartbeat is a registered trademark of The High Availability Linux Project. Linux is a registered trademark of Linus Torvalds. Other company, product, and service names may be trademarks or service marks of others. ======================================================================= 5.0 Disclaimer ---------------- 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 AND PARTICULARLY THE NON-INFRINGEMENT OF ANY THIRD PARTY'S INTELLECTUAL PROPERTY RIGHTS 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. cluster-agents-1.0.4/tools/sfex.h0000644000175000017500000001433211527003731017174 0ustar roaksoaxroaksoax/*------------------------------------------------------------------------- * * Shared Disk File EXclusiveness Control Program(SF-EX) * * sfex.h --- Primary include file for SF-EX *.c files. * * Copyright (c) 2007 NIPPON TELEGRAPH AND TELEPHONE CORPORATION * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * $Id$ * *-------------------------------------------------------------------------*/ #ifndef SFEX_H #define SFEX_H #include #include #include #include /* version, revision */ /* These numbers are integer and, max number is 999. If these numbers change, version numbers in the configure.ac (AC_INIT, AM_INIT_AUTOMAKE) must change together. */ #define SFEX_VERSION 1 #define SFEX_REVISION 3 #if 0 #ifndef TRUE # define TRUE 1 #endif #ifndef FALSE # define FALSE 0 #endif #ifndef MIN # define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #ifndef MAX # define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #endif /* for Linux >= 2.6, the alignment should be 512 for Linux < 2.6, the alignment should be sysconf(_SC_PAGESIZE) we default to _SC_PAGESIZE */ #define SFEX_ODIRECT_ALIGNMENT sysconf(_SC_PAGESIZE) /* * sfex_controldata --- control data * * This is allocated the head of sfex mata-data area. * * magic number --- 4 bytes. This is fixed in {0x01, 0x1f, 0x71, 0x7f}. * * version number --- 4 bytes. This is printable integer number and * range is from 0 to 999. This must be left-justify, null(0x00) padding, and * make a last byte null. * * revision number --- 4 bytes. This is printable integer number and * range is from 0 to 999. This must be left-justify, null(0x00) padding, and * make a last byte null. * * blocksize --- 8bytes. This is printable integer number and range is from * 512 to 9999999. This must be left-justify, null(0x00) padding, and make a * last byte null. This is a size of control data and lock data(one lock data * size when there are plural), and it is shown by number of bytes. * For avoiding partial writing, usually block size is set 512 byte etc. * If you use direct I/O(if you spacificate --enable-directio for configure * script), note that this value is used for input and output buffer alignment. * (In the Linux kernel 2.6, if this value is not 512 multibles, direct I/O * does not work) * number of locks --- 4 bytes. This is printable integer number and range * is from 1 to 999. This must be left-justify, null(0x00) padding, and make * a last byte null. This is the number of locks following this control data. * * padding --- The size of this member depend on blocksize. It is adjusted so * that the whole of the control data including this padding area becomes * blocksize. The contents of padding area are all 0x00. */ typedef struct sfex_controldata { char magic[4]; /* magic number */ int version; /* version number */ int revision; /* revision number */ size_t blocksize; /* block size */ int numlocks; /* number of locks */ } sfex_controldata; typedef struct sfex_controldata_ondisk { uint8_t magic[4]; uint8_t version[4]; uint8_t revision[4]; uint8_t blocksize[8]; uint8_t numlocks[4]; } sfex_controldata_ondisk; /* * sfex_lockdata --- lock data * * This data(number is sfex_controldata.numlocks) are allocated behind of * sfex_controldata in the sfex meta-data area. The meaning of each member * and the storage method to mata data area are following; * * lock status --- 1 byte. printable character. Content is either one of * following; * SFEX_STATUS_UNLOCK: It show the status that no node locks. * SFEX_STATUS_LOCK: It show the status that nodename node is holding lock. * (But there is an exception. Refer to explanation of "count" member.) * * increment counter --- 4 bytes. This is printable integer number and range * is from 1 to 999. This must be left-justify, null(0x00) padding, and make * a last byte null. The node holding a lock increments this counter * periodically. If this counter does not increment for a certain period of * time, we consider that the lock is invalid. If it overflow, return to 0. * Initial value is 0. * * node name --- 256bytes. This is printable string. This must be left-justify, * null(0x00) padding, and make a last byte null. This is node name that update * lock data last. The node name must be same to get uname(2). Initial values * are white spaces. * * padding --- The size of this member depend on blocksize. It is adjusted so * that the whole of the control data including this padding area becomes * blocksize. The contents of padding area are all 0x00. */ typedef struct sfex_lockdata { char status; /* status of lock */ int count; /* increment counter */ char nodename[256]; /* node name */ } sfex_lockdata; typedef struct sfex_lockdata_ondisk { uint8_t status; uint8_t count[4]; uint8_t nodename[256]; } sfex_lockdata_ondisk; /* character for lock status. This is used in sfex_lockdata.status */ #define SFEX_STATUS_UNLOCK 'u' /* unlock */ #define SFEX_STATUS_LOCK 'l' /* lock */ /* features of each member of control data and lock data */ #define SFEX_MAGIC "SFEX" #define SFEX_MIN_NUMLOCKS 1 #define SFEX_MAX_NUMLOCKS 999 #define SFEX_MIN_COUNT 0 #define SFEX_MAX_COUNT 999 #define SFEX_MAX_NODENAME (sizeof(((sfex_lockdata *)0)->nodename) - 1) /* update macro for increment counter */ #define SFEX_NEXT_COUNT(c) (c >= SFEX_MAX_COUNT ? c - SFEX_MAX_COUNT : c + 1) /* extern variables */ extern const char *progname; extern char *nodename; extern unsigned long sector_size; #endif /* SFEX_H */ cluster-agents-1.0.4/tools/ocft/0000755000175000017500000000000011527051501017004 5ustar roaksoaxroaksoaxcluster-agents-1.0.4/tools/ocft/README.in0000644000175000017500000001454511527003731020304 0ustar roaksoaxroaksoaxINTRODUCTION & DESIGN ~~~~~~~~~~~~~~~~~~~~~ - Ocft is a testing tool for resource agents. Instead of the policy of HA, it mainly concerns whether resource agents run correct locally. It can design types of complicated environments to test the reliability of resource agents. Precisely, it is to display whether resource agents can return to correct or expected value. The advantage of the tool provides us with competence to design conditions which can be recorded or reproduced. Hence it is useful to debuggers. * Components ** Test case generator (@sbindir@/ocft) - Turning configuration files of test case to executable scripts. ** Configuration file (@datadir@/@PACKAGE_NAME@/ocft/configs/) - Every configuration file directs only one resource agent and share the same name with resource agent but contains more test cases. ** The testing script (/var/lib/@PACKAGE_NAME@/ocft/cases/) - After the generator reads configuration files and generates many testing scripts and the script is underway, the test begins. * How to customize the environment of testing - Ocft designs the running conditions through two ways, one is changing the environment variables of resource agents (it is the interface left by OCF itself), the other is modifying the OS environment of resource agents, such as altering the permission of some key file or IP address of the machine. * How to test - Firstly, you need to sketch the all complex and uncommon environments against a certain resource agent and keep in mind what consequences may be caused by these uncommon environments. Secondly, write the designed conditions and foreknown consequences into configuration files, and then run the generator to translate the test case to executable scripts. Finally, you need running these scripts to observe the output and learn the running status of each test case, which will compares the predicated result with the actual one. If they differ, you will be able to find the bugs of the resource agent. HOW TO WRITE CONFIGURATION FILE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - There are only 4 top level options that are all spelled by capital letters and "-". Every top level option contains sub-options that they are initials. * 'CONFIG' (top level option) - Grammar: CONFIG - The design in this option is global and influences every test case. ** 'AgentRoot' (sub-option) - Grammar: AgentRoot /usr/lib/ocf/resource.d/xxx - A few agents will go to "linbit" or "pacemaker" directory, if you define this option, ocft will use it to replace the default directory "heartbeat". ** 'InstallPackage' (sub-option) - Grammar: InstallPackage package [package2 [...]] - It will test whether the system have installed the service of the resource agent. If not, it will download from Internet and have it installed automatically. ** 'HangTimeout' (sub-option) - Grammar: HangTimeout secs - If you alter some key options, some resource agents will get puzzled and stop, which will influence the running of the following test case. Hence timeout setting is needed, if the resource agent stops timeout, the scripts will kill this resource agent. * 'SETUP-AGENT' (top level option) - Grammar: SETUP-AGENT bash scripts... ... - Some of Agents may need to be initialized before testing, you can do it here with bash script. The initialization will only be executed once, if you want to reinitialize it, you can manually delete the flag file at /tmp/.[AGENT_NAME]_set. * 'CASE' & 'CASE-BLOCK' (top level option) - Grammar: CASE "description" & CASE-BLOCK macro_name - Usually, the conditions you designed are more than one and a few 'CASE "..."' will appear in configuration file. It is worth noting that the following sub-options have 2 spellings: One is general, where shell affects the local environment; the other is special, where each options added "@ipaddr". It can remotely execute shell codes. In other words, it is to execute the shell codes from a remote host, which is meaningful when a resource agent needs 2 hosts. This remote shell is not a remote execution only through "ssh", but running a remote shell in the background while the test case is running. The remote shell runs in the background till the end and saves the results during the process. That is to say, you can alternatively carry out local and remote shell code segments. The "CASE-BLOCK" option is a macro definer, the statements in "CASE-BLOCK" will be inserted into "CASE" if you "Include" the "macro_name". ** 'Var' (sub-option) - Grammar: Var VARIABLE=value - It is to set up an environment variable of the resource agent. They usually appear to be OCF_RESKEY_xxx. One point is to be noted is there is no blank by both sides of "=". ** 'Unvar' (sub-option) - Grammer: Unvar VARIABLE [VARIABLE2 [...]] - Remove the environment variable. ** 'Include' (sub-option) - Garmmer: Include macro_name - It will be replaced by statements in 'macro_name', of course, you should define the content of 'macro_name' with 'CASE-BLOCK' first. ** 'Bash' (sub-option) - Grammar: Bash bash_codes - This option is to set up the environment of OS, where you can insert BASH code to customize the system randomly. Note, do not cause unrecoverable consequences to the system. ** 'BashAtExit' (sub-option) - Grammar: BashAtExit bash_codes - This option is to recover the OS environment in order to run another test case correctly. Of cause you can use 'Bash' option to recover it. However, if mistakes occur in the process, the script will quit directly instead of running your recovery codes. If it happens, you ought to use BashAtExit which can restore the system environment before you quit. ** 'RunAgent' (sub-option) - Grammar: RunAgent cmd [ret_value] - This option is to run resource agent. "cmd" is the parameter of the resource agent, such as "start, status, stop ...". The second parameter is optional. It will compare the actual returned value with the expected value when the script has run recourse agent. If differs, bugs will be found. cluster-agents-1.0.4/tools/ocft/Filesystem0000755000175000017500000000501511527003731021061 0ustar roaksoaxroaksoax# Filesystem # by dejan@suse.de on # Tue Feb 15 18:50:04 CET 2011 CONFIG #AgentRoot /usr/lib/ocf/resource.d/heartbeat HangTimeout 20 CASE-BLOCK tempvars Var OCFT_fs=/var/run/resource-agents/ocft-Filesystem-fs Var OCFT_loop=/dev/loop0 Var OCFT_dir=/var/run/resource-agents/ocft-Filesystem-mnt CASE-BLOCK required_args Include tempvars Var OCF_RESKEY_device=$OCFT_loop Var OCF_RESKEY_fstype=ext2 Var OCF_RESKEY_directory=$OCFT_dir SETUP-AGENT OCFT_fs=/var/run/resource-agents/ocft-Filesystem-fs OCFT_loop=/dev/loop0 OCFT_dir=/var/run/resource-agents/ocft-Filesystem-mnt rmdir $OCFT_dir 2>/dev/null || true losetup -d $OCFT_loop 2>/dev/null || true mkdir $OCFT_dir dd if=/dev/zero of=$OCFT_fs bs=1024k count=1 2>/dev/null losetup $OCFT_loop $OCFT_fs mke2fs -Fq $OCFT_loop CASE-BLOCK default_status AgentRun stop CASE-BLOCK prepare Include required_args Include default_status CASE "check base env" Include prepare AgentRun start OCF_SUCCESS CASE "check base env: invalid 'OCF_RESKEY_device'" Include prepare Var OCF_RESKEY_device=/dev/no_such_device AgentRun start OCF_ERR_INSTALLED CASE "check base env: unset 'OCF_RESKEY_device'" Include prepare Unvar OCF_RESKEY_device AgentRun start OCF_ERR_CONFIGURED CASE "normal start" Include prepare AgentRun start OCF_SUCCESS CASE "normal stop" Include prepare AgentRun start AgentRun stop OCF_SUCCESS CASE "double start" Include prepare AgentRun start AgentRun start OCF_SUCCESS CASE "double stop" Include prepare AgentRun stop OCF_SUCCESS CASE "monitor when running" Include prepare AgentRun start AgentRun monitor OCF_SUCCESS CASE "monitor when not running" Include prepare AgentRun monitor OCF_NOT_RUNNING CASE "monitor depth 10 when running" Include prepare AgentRun start Var OCF_CHECK_LEVEL=10 AgentRun monitor OCF_SUCCESS CASE "monitor depth 20 with running" Include prepare AgentRun start Var OCF_CHECK_LEVEL=20 AgentRun monitor OCF_SUCCESS CASE "start insert failure (remove device)" Include prepare Bash losetup -d $OCFT_loop BashAtExit losetup $OCFT_loop $OCFT_fs AgentRun start OCF_ERR_GENERIC CASE "monitor depth 20 insert failure (r/o fs)" Include prepare AgentRun start Bash mount -o remount,ro $OCFT_dir BashAtExit mount -o remount,rw $OCFT_dir Var OCF_CHECK_LEVEL=20 AgentRun monitor OCF_ERR_GENERIC CASE "unimplemented command" Include prepare AgentRun no_cmd OCF_ERR_UNIMPLEMENTED CASE "meta-data and cleanup" Include prepare Bash rmdir $OCFT_dir Bash rm $OCFT_fs Bash losetup -d $OCFT_loop AgentRun meta-data OCF_SUCCESS cluster-agents-1.0.4/tools/ocft/nfsserver0000644000175000017500000000316611527003731020754 0ustar roaksoaxroaksoax# nfsserver CONFIG #AgentRoot /usr/lib/ocf/resource.d/heartbeat InstallPackage nfs-kernel-server HangTimeout 20 CASE-BLOCK required_args Var OCF_RESKEY_nfs_init_script=/etc/init.d/nfsserver Var OCF_RESKEY_nfs_ip=127.0.0.1 Var OCF_RESKEY_nfs_shared_infodir=/var/lib/nfs Var OCF_RESKEY_nfs_notify_cmd=/usr/sbin/sm-notify CASE-BLOCK default_status AgentRun stop CASE-BLOCK prepare Include required_args Include default_status CASE "check base env" Include prepare AgentRun start OCF_SUCCESS CASE "check base env: invalid 'OCF_RESKEY_nfs_init_script'" Include prepare Var OCF_RESKEY_nfs_init_script=no_such_script AgentRun start OCF_ERR_INSTALLED CASE "check base env: unset 'OCF_RESKEY_nfs_ip'" Include prepare Unvar OCF_RESKEY_nfs_ip AgentRun start OCF_ERR_CONFIGURED CASE "check base env: unset 'OCF_RESKEY_nfs_shared_infodir'" Include prepare Unvar OCF_RESKEY_nfs_shared_infodir AgentRun start OCF_ERR_CONFIGURED CASE "check base env: invalid 'OCF_RESKEY_nfs_notify_cmd'" Include prepare Var OCF_RESKEY_nfs_notify_cmd=no_such_program AgentRun start OCF_ERR_INSTALLED CASE "normal start" Include prepare AgentRun start OCF_SUCCESS CASE "normal stop" Include prepare AgentRun start AgentRun stop OCF_SUCCESS CASE "double start" Include prepare AgentRun start AgentRun start OCF_SUCCESS CASE "double stop" Include prepare AgentRun stop OCF_SUCCESS CASE "monitor with running" Include prepare AgentRun start AgentRun monitor OCF_SUCCESS CASE "monitor with not running" Include prepare AgentRun monitor OCF_NOT_RUNNING CASE "unimplemented command" Include prepare AgentRun no_cmd OCF_ERR_UNIMPLEMENTED cluster-agents-1.0.4/tools/ocft/caselib.in0000644000175000017500000001536211527003731020747 0ustar roaksoaxroaksoax# # Copyright (c) 2010 Novell Inc, John Shi # All Rights Reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. quit() { local ret ret="$1" while [ $atexit_num -gt 0 ]; do atexit$atexit_num let atexit_num-- done rm -rf $fakebin exit $ret } agent_install() { local pkg if [ $# -eq 0 ]; then return 0 fi for pkg in "$@"; do if [ -e /etc/SuSE-release ]; then if ! rpm -ql "$pkg" >/dev/null 2>&1; then echo -n "${showhost}Installing $pkg ..." zypper -q install -y "$pkg" >/dev/null 2>&1 if ! rpm -ql "$pkg" >/dev/null 2>&1; then echo echo "${showhost}ERROR: Install '$pkg' failed, break this case." quit 2 fi echo "done" echo fi elif [ -e /etc/debian_version ]; then if ! dpkg -L "$pkg" >/dev/null 2>&1; then echo -n "${showhost}Installing $pkg ..." apt-get -y install "$pkg" >/dev/null 2>&1 if ! dpkg -L "$pkg" >/dev/null 2>&1; then echo echo "${showhost}ERROR: Install '$pkg' failed, break this case." quit 2 fi echo "done" echo fi elif [ -e /etc/redhat-release ]; then if ! rpm -ql "$pkg" >/dev/null 2>&1; then echo -n "${showhost}Installing $pkg ..." yum -y install "$pkg" >/dev/null 2>&1 if ! rpm -ql "$pkg" >/dev/null 2>&1; then echo echo "${showhost}ERROR: Install '$pkg' failed, break this case." quit 2 fi echo "done" echo fi else echo "${showhost}ERROR: Cannot detect your OS type, break this case." quit 2 fi done } agent_setup() { local agent agent="$1" if [ ! -e "/tmp/.${agent}_set" ]; then touch "/tmp/.${agent}_set" echo -n "${showhost}Initialing ${agent}..." bash >/dev/null echo "done" echo fi } agent_run() { local agent cmd timeout pid i ret aroot agent="$1" cmd="$2" timeout="$3" aroot=${MYROOT:-$AGENT_ROOT} setsid $aroot/$agent $cmd >/tmp/.ocft_runlog 2>&1 & pid=$! i=0 while [ $i -lt $timeout ]; do if [ ! -e /proc/$pid ]; then break fi sleep 1 let i++ done if [ $i -ge $timeout ]; then kill -SIGTERM -$pid >/dev/null 2>&1 sleep 3 kill -SIGKILL -$pid >/dev/null 2>&1 echo -n "${showhost}ERROR: The agent was hanging, killed it, " echo "maybe you damaged the agent or system's environment, break this CASE." echo quit 1 fi wait $pid } check_success() { local ret msg ret="$1" msg="$2" if [ $ret -ne 0 ]; then echo "${showhost}ERROR: '${msg}' failed, the return code is ${ret}, break this CASE." quit 1 fi } __maxfd() { (echo 0; ls -1 /proc/$$/fd) | sort -rn | head -1 } __getfd() { local host rw fd file host="$1" rw="$2" for fd in /proc/$$/fd/*; do file=$(basename "$(readlink $fd)") if [ "$file" = "${host}_$rw" ]; then basename $fd break fi done } backbash_start() { local host fd rfd wfd host="$1" if [ ! -d "$CASES_DIR" ]; then echo "${showhost}ERROR: Could not found Directory: ${CASES_DIR}." quit 1 fi if lsof $CASES_DIR/${host}_r $CASES_DIR/${host}_w >/dev/null 2>&1; then echo "${showhost}ERROR: Connection exist with $host, break this CASE." quit 1 fi if [ ! -p "$CASES_DIR/${host}_r" ] || [ ! -p "$CASES_DIR/${host}_w" ]; then rm -f $CASES_DIR/${host}_r $CASES_DIR/${host}_w if ! mkfifo $CASES_DIR/${host}_r $CASES_DIR/${host}_w >/dev/null 2>&1; then echo "${showhost}ERROR: Could not create pipe file: $CASES_DIR/${host}_*, break this CASE." quit 1 fi fi ssh root@$host '/bin/bash 2>&1 sed "s/00/001/g" /tmp/.backbash-log echo 000 echo 1' >$CASES_DIR/${host}_r <$CASES_DIR/${host}_w & fd=$(__maxfd) rfd=$(expr $fd + 1) wfd=$(expr $fd + 2) eval "exec ${rfd}<$CASES_DIR/${host}_r ${wfd}>$CASES_DIR/${host}_w" } backbash() { local host rfd wfd ret host="$1" rfd=$(__getfd $host r) wfd=$(__getfd $host w) if [ -z "$rfd" -o -z "$wfd" ]; then echo "${showhost}ERROR: Could not found connection with $host, break this CASE." fi cat >&$wfd <&$wfd cat >&$wfd <&/tmp/.backbash-log sed 's/00/001/g' /tmp/.backbash-log echo 000 echo 0 EOF if [ $? -ne 0 ]; then echo "${showhost}ERROR: Broken connection with $host, break this CASE." quit 1 fi awk -vlive=2 '{ if (sub(/000$/, "")) { if ($0 != "") { gsub("001", "00"); printf("%s", $0); } getline live; exit; } gsub("001", "00"); print; } END { exit(live); }' <&$rfd case $? in 1) quit 1 ;; 2) echo "${showhost}ERROR: Broken connection with $host, break this CASE." quit 1 ;; esac } backbash_stop() { local host rfd wfd host="$1" wfd=$(__getfd $host w) if [ -n "$wfd" ]; then cat >&$wfd <<<'quit 0' fi rm -f $CASES_DIR/${host}_r $CASES_DIR/${host}_w } export OCF_ROOT=@OCF_ROOT_DIR@ export OCF_LIB=@OCF_LIB_DIR@/heartbeat AGENT_ROOT=@OCF_RA_DIR@/heartbeat CASES_DIR=/var/lib/@PACKAGE_NAME@/ocft/cases atexit_num=0 if [ $EUID -ne 0 ]; then echo "${showhost}ERROR: '$0' needs to be run by root, break this CASE." quit 3 fi fakebin=./fakebin mkdir -p $fakebin >/dev/null 2>&1 && ln -sf /bin/true $fakebin/crm_master >/dev/null 2>&1 && ln -sf /bin/true $fakebin/crm_mon >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "${showhost}ERROR: initialize 'fakebin' failed, break this CASE." quit 3 fi export HA_SBIN_DIR=$fakebin . $OCF_LIB/ocf-returncodes || { echo "${showhost}ERROR: $OCF_LIB/ocf-returncodes not found, break this CASE." quit 3 } while read line; do if [ -n "$line" ]; then retn=${line%%=*} reti=$(eval echo \$$retn) retval[reti]=$retn fi done <<<"$(sed 's/#.*//' $OCF_LIB/ocf-returncodes)" cluster-agents-1.0.4/tools/ocft/apache0000644000175000017500000000237611527003731020162 0ustar roaksoaxroaksoax# apache CONFIG #AgentRoot /usr/lib/ocf/resource.d/heartbeat InstallPackage apache2 HangTimeout 20 SETUP-AGENT /etc/init.d/apache2 start /etc/init.d/apache2 stop CASE-BLOCK required_args Var OCF_RESKEY_statusurl=http://localhost/info2html.css Var OCF_RESKEY_testregex='This is' CASE-BLOCK default_status AgentRun stop CASE-BLOCK prepare Include required_args Include default_status CASE "check base env" Include prepare AgentRun start OCF_SUCCESS CASE "check base env: unset OCF_RESKEY_statusurl" Include prepare Unvar OCF_RESKEY_statusurl AgentRun start OCF_ERR_CONFIGURED CASE "check base env: unset OCF_RESKEY_testregex" Include prepare Unvar OCF_RESKEY_testregex AgentRun start OCF_ERR_CONFIGURED CASE "normal start" Include prepare AgentRun start OCF_SUCCESS CASE "normal stop" Include prepare AgentRun start AgentRun stop OCF_SUCCESS CASE "double start" Include prepare AgentRun start AgentRun start OCF_SUCCESS CASE "double stop" Include prepare AgentRun stop OCF_SUCCESS CASE "running monitor" Include prepare AgentRun start AgentRun monitor OCF_SUCCESS CASE "not running monitor" Include prepare AgentRun monitor OCF_NOT_RUNNING CASE "unimplemented command" Include prepare AgentRun no_cmd OCF_ERR_UNIMPLEMENTED cluster-agents-1.0.4/tools/ocft/db20000644000175000017500000001113711527003731017403 0ustar roaksoaxroaksoax# db2 # # This test assumes a db2 ESE instance with two partions and a database. # Default is instance=db2inst1, database=ocft # adapt this in set_testenv below # # Simple steps to generate a test environment (if you don't have one): # # A virtual machine with 1200MB RAM is sufficient # # - download an eval version of DB2 server from IBM # - create an user "db2inst1" in group "db2inst1" # # As root # - install DB2 software in some location # - create instance # cd /instance # ./db2icrt -s ese -u db2inst1 db2inst1 # - adapt profile of db2inst1 as instructed by db2icrt # # As db2inst1 # # allow to run with small memory footprint # db2set DB2_FCM_SETTINGS=FCM_MAXIMIZE_SET_SIZE:FALSE # db2start # db2start dbpartitionnum 1 add dbpartitionnum hostname $(uname -n) port 1 without tablespaces # db2stop # db2start # db2 create database ocft # Done # In order to install a real cluster refer to http://www.linux-ha.org/wiki/db2_(resource_agent) CONFIG HangTimeout 40 SETUP-AGENT # nothing CASE-BLOCK set_testenv Var OCFT_instance=db2inst Var OCFT_db=ocft CASE-BLOCK crm_setting Var OCF_RESKEY_instance=$OCFT_instance Var OCF_RESKEY_CRM_meta_timeout=30000 CASE-BLOCK default_status AgentRun stop CASE-BLOCK prepare Include set_testenv Include crm_setting Include default_status CASE "check base env" Include prepare AgentRun start OCF_SUCCESS CASE "check base env: invalid 'OCF_RESKEY_instance'" Include prepare Var OCF_RESKEY_instance=no_such AgentRun start OCF_ERR_INSTALLED CASE "invalid instance config" Include prepare Bash eval mv ~$OCFT_instance/sqllib ~$OCFT_instance/sqllib- BashAtExit eval mv ~$OCFT_instance/sqllib- ~$OCFT_instance/sqllib AgentRun start OCF_ERR_INSTALLED CASE "unimplemented command" Include prepare AgentRun no_cmd OCF_ERR_UNIMPLEMENTED CASE "normal start" Include prepare AgentRun start OCF_SUCCESS CASE "normal stop" Include prepare AgentRun start AgentRun stop OCF_SUCCESS CASE "double start" Include prepare AgentRun start AgentRun start OCF_SUCCESS CASE "double stop" Include prepare AgentRun stop OCF_SUCCESS CASE "started: monitor" Include prepare AgentRun start AgentRun monitor OCF_SUCCESS CASE "not started: monitor" Include prepare AgentRun monitor OCF_NOT_RUNNING CASE "killed instance: monitor" Include prepare AgentRun start OCF_SUCCESS AgentRun monitor OCF_SUCCESS BashAtExit rm /tmp/ocft-helper1 Bash echo "su $OCFT_instance -c '. ~$OCFT_instance/sqllib/db2profile; db2nkill 0 >/dev/null 2>&1'" > /tmp/ocft-helper1 Bash sh -x /tmp/ocft-helper1 AgentRun monitor OCF_NOT_RUNNING CASE "overload param instance by admin" Include prepare Var OCF_RESKEY_instance=no_such Var OCF_RESKEY_admin=$OCFT_instance AgentRun start OCF_SUCCESS CASE "check start really activates db" Include prepare AgentRun start OCF_SUCCESS BashAtExit rm /tmp/ocft-helper2 Bash echo "su $OCFT_instance -c '. ~$OCFT_instance/sqllib/db2profile; db2 get snapshot for database on $OCFT_db>/dev/null'" > /tmp/ocft-helper2 Bash sh -x /tmp/ocft-helper2 CASE "multipartion test" Include prepare AgentRun start OCF_SUCCESS AgentRun monitor OCF_SUCCESS # start does not start partion 1 Var OCF_RESKEY_dbpartitionnum=1 AgentRun monitor OCF_NOT_RUNNING # now start 1 AgentRun start OCF_SUCCESS AgentRun monitor OCF_SUCCESS # now stop 1 AgentRun stop OCF_SUCCESS AgentRun monitor OCF_NOT_RUNNING # does not affect 0 Var OCF_RESKEY_dbpartitionnum=0 AgentRun monitor OCF_SUCCESS # fault injection does not work on the 1.0.4 client due to a hardcoded path CASE "simulate hanging db2stop (not meaningful for 1.0.4 agent)" Include prepare AgentRun start OCF_SUCCESS Bash [ ! -f /usr/local/bin/db2stop ] BashAtExit rm /usr/local/bin/db2stop Bash echo -e "#!/bin/sh\necho fake db2stop\nsleep 10000" > /usr/local/bin/db2stop Bash chmod +x /usr/local/bin/db2stop AgentRun stop OCF_SUCCESS # fault injection does not work on the 1.0.4 client due to a hardcoded path CASE "simulate not stopping db2stop (not meaningful for 1.0.4 agent)" Include prepare AgentRun start OCF_SUCCESS Bash [ ! -f /usr/local/bin/db2stop ] BashAtExit rm /usr/local/bin/db2stop Bash echo -e "#!/bin/sh\necho fake db2stop\nexit 0" > /usr/local/bin/db2stop Bash chmod +x /usr/local/bin/db2stop AgentRun stop OCF_SUCCESS cluster-agents-1.0.4/tools/ocft/portblock0000644000175000017500000000246511527003731020737 0ustar roaksoaxroaksoax# portblock CONFIG #AgentRoot /usr/lib/ocf/resource.d/heartbeat InstallPackage iptables HangTimeout 15 CASE-BLOCK required_args Var OCF_RESKEY_protocol=tcp Var OCF_RESKEY_portno=80 Var OCF_RESKEY_action=block CASE-BLOCK default_status AgentRun stop CASE-BLOCK prepare Include required_args Include default_status CASE "check base env" Include prepare AgentRun start OCF_SUCCESS CASE "check base env: unset 'OCF_RESKEY_protocol'" Include prepare Unvar OCF_RESKEY_protocol AgentRun start OCF_ERR_CONFIGURED CASE "check base env: unset 'OCF_RESKEY_portno'" Include prepare Unvar OCF_RESKEY_portno AgentRun start OCF_ERR_CONFIGURED CASE "check base env: unset 'OCF_RESKEY_action'" Include prepare Unvar OCF_RESKEY_action AgentRun start OCF_ERR_CONFIGURED CASE "normal start" Include prepare AgentRun start OCF_SUCCESS CASE "normal stop" Include prepare AgentRun start AgentRun stop OCF_SUCCESS CASE "double start" Include prepare AgentRun start AgentRun start OCF_SUCCESS CASE "double stop" Include prepare AgentRun stop OCF_SUCCESS CASE "monitor with running" Include prepare AgentRun start AgentRun monitor OCF_SUCCESS CASE "monitor with not running" Include prepare AgentRun monitor OCF_NOT_RUNNING CASE "unimplemented command" Include prepare AgentRun no_cmd OCF_ERR_UNIMPLEMENTED cluster-agents-1.0.4/tools/ocft/IPaddr20000644000175000017500000000366011527003731020163 0ustar roaksoaxroaksoax# IPaddr2 CONFIG #AgentRoot /usr/lib/ocf/resource.d/heartbeat InstallPackage iproute2 HangTimeout 20 CASE-BLOCK required_args Var OCF_RESKEY_ip=127.0.0.3 CASE-BLOCK default_status AgentRun stop CASE-BLOCK prepare Include required_args Include default_status CASE "check base env" Include prepare AgentRun start OCF_SUCCESS CASE "check base env: unset 'OCF_RESKEY_ip'" Include prepare Unvar OCF_RESKEY_ip AgentRun start OCF_ERR_CONFIGURED CASE "check base env: set invalid 'OCF_RESKEY_ip'" Include prepare Var OCF_RESKEY_ip=not_ip_address AgentRun start OCF_ERR_CONFIGURED CASE "check base env: set 'OCF_RESKEY_cidr_netmask'" Include prepare Var OCF_RESKEY_cidr_netmask=8 AgentRun start OCF_SUCCESS CASE "check base env: set invalid 'OCF_RESKEY_cidr_netmask'" Include prepare Var OCF_RESKEY_cidr_netmask=not_netmask AgentRun start OCF_ERR_CONFIGURED CASE "check base env: set 'OCF_RESKEY_broadcast'" Include prepare Var OCF_RESKEY_broadcast=127.255.255.255 AgentRun start OCF_SUCCESS CASE "check base env: set invalid 'OCF_RESKEY_broadcast'" Include prepare Var OCF_RESKEY_broadcast=not_broadcast AgentRun start OCF_ERR_CONFIGURED CASE "check base env: set 'OCF_RESKEY_nic'" Include prepare Var OCF_RESKEY_nic=lo AgentRun start OCF_SUCCESS CASE "check base env: set invalid 'OCF_RESKEY_nic'" Include prepare Var OCF_RESKEY_nic=not_nic AgentRun start OCF_ERR_CONFIGURED CASE "normal start" Include prepare AgentRun start OCF_SUCCESS CASE "normal stop" Include prepare AgentRun start AgentRun stop OCF_SUCCESS CASE "double start" Include prepare AgentRun start AgentRun start OCF_SUCCESS CASE "double stop" Include prepare AgentRun stop OCF_SUCCESS CASE "monitor with running" Include prepare AgentRun start AgentRun monitor OCF_SUCCESS CASE "monitor with not running" Include prepare AgentRun monitor OCF_NOT_RUNNING CASE "unimplemented command" Include prepare AgentRun no_cmd OCF_ERR_UNIMPLEMENTED cluster-agents-1.0.4/tools/ocft/SendArp0000644000175000017500000000257411527003731020275 0ustar roaksoaxroaksoax# SendArp CONFIG #AgentRoot /usr/lib/ocf/resource.d/heartbeat InstallPackage resource-agents HangTimeout 15 CASE-BLOCK required_args Var OCF_RESKEY_ip=127.0.0.1 Var OCF_RESKEY_nic=lo CASE-BLOCK default_status AgentRun stop CASE-BLOCK prepare Include required_args Include default_status CASE "check base env" Include prepare AgentRun start OCF_SUCCESS CASE "check base env: unset 'OCF_RESKEY_ip'" Include prepare Unvar OCF_RESKEY_ip AgentRun start OCF_ERR_ARGS CASE "check base env: set worng 'OCF_RESKEY_ip'" Include prepare Var OCF_RESKEY_ip=not_ip_address AgentRun start OCF_ERR_ARGS CASE "check base env: unset 'OCF_RESKEY_nic'" Include prepare Unvar OCF_RESKEY_nic AgentRun start OCF_ERR_ARGS CASE "check base env: set worng 'OCF_RESKEY_nic'" Include prepare Var OCF_RESKEY_nic=not_nic AgentRun start OCF_ERR_ARGS CASE "normal start" Include prepare AgentRun start OCF_SUCCESS CASE "normal stop" Include prepare AgentRun start AgentRun stop OCF_SUCCESS CASE "double start" Include prepare AgentRun start AgentRun start OCF_SUCCESS CASE "double stop" Include prepare AgentRun stop OCF_SUCCESS CASE "monitor with running" Include prepare AgentRun start AgentRun monitor OCF_SUCCESS CASE "monitor with not running" Include prepare AgentRun monitor OCF_NOT_RUNNING CASE "unimplemented command" Include prepare AgentRun no_cmd OCF_ERR_UNIMPLEMENTED cluster-agents-1.0.4/tools/ocft/LVM0000755000175000017500000000364011527003731017375 0ustar roaksoaxroaksoax# LVM # by dejan@suse.de on # Wed Feb 16 13:15:01 CET 2011 CONFIG #AgentRoot /usr/lib/ocf/resource.d/heartbeat HangTimeout 20 CASE-BLOCK tempvars Var OCFT_pv=/var/run/resource-agents/ocft-LVM-pv Var OCFT_vg=ocft-vg Var OCFT_lv=ocft-lv Var OCFT_loop=/dev/loop0 CASE-BLOCK required_args Include tempvars Var OCF_RESKEY_volgrpname=$OCFT_vg SETUP-AGENT OCFT_pv=/var/run/resource-agents/ocft-LVM-pv OCFT_vg=ocft-vg OCFT_lv=ocft-lv OCFT_loop=/dev/loop0 losetup -d $OCFT_loop 2>/dev/null || true dd if=/dev/zero of=$OCFT_pv bs=1024k count=1 2>/dev/null losetup $OCFT_loop $OCFT_pv pvcreate $OCFT_loop vgcreate -s 4K $OCFT_vg $OCFT_loop lvcreate -n $OCFT_lv -L 600K $OCFT_vg CASE-BLOCK default_status AgentRun stop CASE-BLOCK prepare Include required_args Include default_status CASE "check base env" Include prepare AgentRun start OCF_SUCCESS CASE "check base env: invalid 'OCF_RESKEY_volgrpname'" Include prepare Var OCF_RESKEY_volgrpname=/dev/no_such_device AgentRun start OCF_ERR_GENERIC CASE "check base env: unset 'OCF_RESKEY_volgrpname'" Include prepare Unvar OCF_RESKEY_volgrpname AgentRun start OCF_ERR_CONFIGURED CASE "normal start" Include prepare AgentRun start OCF_SUCCESS CASE "normal stop" Include prepare AgentRun start AgentRun stop OCF_SUCCESS CASE "double start" Include prepare AgentRun start AgentRun start OCF_SUCCESS CASE "double stop" Include prepare AgentRun stop OCF_SUCCESS CASE "monitor when running" Include prepare AgentRun start AgentRun monitor OCF_SUCCESS CASE "monitor when not running" Include prepare AgentRun monitor OCF_NOT_RUNNING CASE "unimplemented command" Include prepare AgentRun no_cmd OCF_ERR_UNIMPLEMENTED CASE "meta-data and cleanup" Include prepare Bash vgchange -an $OCFT_vg Bash lvremove -f /dev/$OCFT_vg/$OCFT_lv Bash vgremove -f $OCFT_vg Bash losetup -d $OCFT_loop Bash rm $OCFT_pv Bash rm /tmp/.LVM_set AgentRun meta-data OCF_SUCCESS cluster-agents-1.0.4/tools/ocft/mysql0000644000175000017500000000261711527003731020104 0ustar roaksoaxroaksoax# mysql CONFIG #AgentRoot /usr/lib/ocf/resource.d/heartbeat InstallPackage mysql HangTimeout 20 SETUP-AGENT /etc/init.d/mysql start /etc/init.d/mysql stop CASE-BLOCK crm_setting Var OCF_RESKEY_CRM_meta_timeout=15000 CASE-BLOCK default_status AgentRun stop CASE-BLOCK prepare Include crm_setting Include default_status CASE "check base env" Include prepare AgentRun start OCF_SUCCESS CASE "check base env: invalid 'OCF_RESKEY_binary'" Include prepare Var OCF_RESKEY_binary=no_such AgentRun start OCF_ERR_INSTALLED CASE "normal start" Include prepare AgentRun start OCF_SUCCESS CASE "normal stop" Include prepare AgentRun start AgentRun stop OCF_SUCCESS CASE "double start" Include prepare AgentRun start AgentRun start OCF_SUCCESS CASE "double stop" Include prepare AgentRun stop OCF_SUCCESS CASE "running monitor" Include prepare AgentRun start AgentRun monitor OCF_SUCCESS CASE "not running monitor" Include prepare AgentRun monitor OCF_NOT_RUNNING CASE "check lib file" Include prepare Bash chmod u-w /var/lib/mysql BashAtExit chmod u+w /var/lib/mysql AgentRun start OCF_ERR_PERM CASE "unimplemented command" Include prepare AgentRun no_cmd OCF_ERR_UNIMPLEMENTED CASE "non-existent user" Include prepare Var OCF_RESKEY_user=no_user AgentRun start OCF_ERR_INSTALLED CASE "invalid user" Include prepare Var OCF_RESKEY_user=nobody AgentRun start OCF_ERR_PERM cluster-agents-1.0.4/tools/ocft/Makefile.am0000644000175000017500000000226211527003731021044 0ustar roaksoaxroaksoax# Author: John Shi # jshi@suse.de # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = $(ocftcfgs_DATA) $(ocft_DATA) sbin_SCRIPTS = ocft ocftcfgsdir = $(datadir)/$(PACKAGE_NAME)/ocft/configs ocftcfgs_DATA = apache \ IPaddr2 \ Filesystem \ LVM \ IPsrcaddr \ MailTo \ mysql \ db2 \ nfsserver \ portblock \ SendArp ocftdir = $(datadir)/$(PACKAGE_NAME)/ocft ocft_DATA = README \ README.zh_CN \ caselib cluster-agents-1.0.4/tools/ocft/MailTo0000644000175000017500000000177411527003731020127 0ustar roaksoaxroaksoax# MailTo CONFIG #AgentRoot /usr/lib/ocf/resource.d/heartbeat InstallPackage mailx HangTimeout 20 CASE-BLOCK required_args Var OCF_RESKEY_email=root@localhost CASE-BLOCK default_status AgentRun stop CASE-BLOCK prepare Include required_args Include default_status CASE "check base env" Include prepare AgentRun start OCF_SUCCESS CASE "check base env: unset 'OCF_RESKEY_email'" Include prepare Unvar OCF_RESKEY_email AgentRun start OCF_ERR_CONFIGURED CASE "normal start" Include prepare AgentRun start OCF_SUCCESS CASE "normal stop" Include prepare AgentRun start AgentRun stop OCF_SUCCESS CASE "double start" Include prepare AgentRun start AgentRun start OCF_SUCCESS CASE "double stop" Include prepare AgentRun stop OCF_SUCCESS CASE "monitor with running" Include prepare AgentRun start AgentRun monitor OCF_SUCCESS CASE "monitor with not running" Include prepare AgentRun monitor OCF_NOT_RUNNING CASE "unimplemented command" Include prepare AgentRun no_cmd OCF_ERR_UNIMPLEMENTED cluster-agents-1.0.4/tools/ocft/ChangeLog0000644000175000017500000000372211527003731020564 0ustar roaksoaxroaksoax0.42: - Fix a bug about agent installation. - The tests stop early if the basic functionality it not there. - Fix a bug about 'config parser'. - Keep the resource stat between the runs to avoid there are multiple stop/start per run. - Add a function with warning output. - Adjust the sequence of cases running. - Change the default timeout. - Replace the crm_master command in Agent scripts, it is no use in testing, because the agent runs in local when test. - Support drbd testing if the configuration is correct. - Fix a 'Include' bug. - Fix a bug about configuration of 'mysql'. 0.41: - Fixed a remote shell bug. - Improved 'Include' option, now it supports remote shell, invoked just like 'Include@ip_address'. - Show line number, if syntax error occurs in config file. - Add a 'AgentRoot' sub-option. - Fix a bug with config parsing. - Rename ACTION "run" to "test". - Add a simple output mode for "test" ACTION. - Add a verbose ouput mode for "help" ACTION. - The output will be recorded to log file. - Add double stop and double start to tests. - Exit with 1 if test script failed. - The output of agents will be exposed if the test fails. - Run zypper with -q - Use 127.0.0.x(and lo for interface) for agents IP* 0.4: - Add a 'CASE-BLOCK' top option, it can be included by 'CASE' - Add a 'SETUP-AGENT' top option, you can initialize agents before testing. - Add a 'Include' sub-option. - Add a 'Unvar' sub-option. - Rename 'GLOBAL' to 'CONFIG'. - Modify a part of syntax. - Imporve the 'InstallPackage' option, now it supports SUSE, Redhat and Debian. - Add README & README.zh_CN. 0.3: - All statements in CASE can be executed by remote shell. 0.2: - Implement options: BashAtExit, GLOBAL, HangTimeout, InstallPackage. - Delete option: Name. - Modify a part of syntax. - Fix some bugs. - Print testing message in human readable format. - Improve 'make' and 'run' option. - Add 'clean' option. 0.1: - Implement options: CASE, Bash, Run, Var, Name. cluster-agents-1.0.4/tools/ocft/ocft.in0000644000175000017500000004074411527003731020302 0ustar roaksoaxroaksoax#!/bin/bash # Copyright (c) 2010 Novell Inc, John Shi # All Rights Reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. die() { local str str="$1" echo "ERROR: $str" >&2 exit 1 } warn() { local str str="$1" echo "WARNING: $str" >&2 } parse_die() { local str str="$1" die "${agent}: line ${line_num}: ${str}" } # add quotes to string for Here Documents add_quotes() { local typ str a b typ="$1" str="$2" case "$typ" in 1) a=\'; b=\";; 2) a=\"; b=\';; esac echo "$str" | sed "s/$a/$a$b$a$b$a/g; 1 s/^/$a/; $ s/$/$a/" } # split strings explode() { local str str="$1" echo "$str" | awk -F'"' '{ if (NF > 0 && NF%2 == 0) exit(1); for (i=1; i<=NF; i++) { if (i%2 == 0) print $i; else { split($i, str, /[[:blank:]]+/); for (j=0; j>"$macro" else echo "$line$num" >>"$CASES_DIR/${agent}.preparse" fi done <"$opt_cfgsdir/$agent" rm -f $CASES_DIR/${agent}_macro.* } case_finish() { local host if [ -n "$sh" ]; then cat >>$sh <>$sh done echo "quit 0" >>$sh fi atexit_num=0 hosts= sh= } parse_cfg() { local agents i line stat sh trunk branch atexit_num host hosts if [ $# -eq 0 ]; then agents=($opt_cfgsdir/*) else agents=("$@") fi for agent in "${agents[@]}"; do i=0 agent="$(basename "$agent")" rm -f $CASES_DIR/*_${agent}.sh rm -f $CASES_DIR/${agent}_setup echo "Making '$agent': " preparse_cfg "$agent" while read -r line; do line_num="${line##* }" line="${line% *}" line2trunk # state switch case "${trunk[0]}" in CONFIG) case_finish stat=1 continue ;; SETUP-AGENT) case_finish stat=2 continue ;; CASE) case_finish trunk2branch sh="$CASES_DIR/${i}_${agent}.sh" echo " - case ${i}: ${branch[0]}" cat >$sh <>$sh <>$sh <>$sh <>$CASES_DIR/${agent}_setup ;; 3) host=$(echo ${trunk[0]} | awk -F@ '{print $2}') if [ -n "$host" ]; then if ! echo "$hosts" | grep -q "$host"; then hosts=$hosts$'\n'$host cat >>$sh <>$sh <>$sh <>$sh fi fi echo " # CASE statement: $line" >>$sh if [ -n "$host" ]; then echo "backbash $host <<'CMD'" >>$sh fi case "${trunk[0]}" in Var|Var@*) cat >>$sh <>$sh <>$sh <>$sh <>$sh <>$sh <>$sh <>$sh <>$sh <>$sh fi ;; *) parse_die "unimplemented statement: ${trunk[0]}" ;; esac done <$CASES_DIR/${agent}.preparse rm -f $CASES_DIR/${agent}.preparse rm -f $CASES_DIR/${agent}_setup case_finish done } start_test() { local sh shs agents line ret if ! cd $CASES_DIR >/dev/null 2>&1; then die "cases directory not found." fi export OCFT_VERBOSE=$opt_verbose if [ $# -eq 0 ]; then agents=($(ls -1 *.sh 2>/dev/null | sed 's/.*_\([^_]*\)\.sh$/\1/' | sort | uniq)) else agents=("$@") fi for shs in "${agents[@]}"; do for sh in $(ls -1 *_${shs}.sh 2>/dev/null | sort -n); do ./$sh ret=$? if [ $ret -eq 3 ]; then die "core function failed, break all tests." fi if [ $ret -eq 2 ]; then warn "core function failed, break all tests of '$shs'." break fi done done | while read -r line; do echo "$line" echo "$(date '+%F %T'): $line" | cat -A | sed -r 's/\^\[\[[0-9]+m|\^I|.$//g' >>ocft.log done } delete_cases() { local shs if [ $# -eq 0 ]; then rm -f $CASES_DIR/*.sh else for shs in "$@"; do rm -f $CASES_DIR/*_${shs}.sh done fi } usage() { cat < #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sfex.h" #include "sfex_lib.h" #if HAVE_GLUE_CONFIG_H #include /* for HA_LOG_FACILITY */ #endif static int sysrq_fd; static int lock_index = 1; /* default 1st lock */ static time_t collision_timeout = 1; /* default 1 sec */ static time_t lock_timeout = 60; /* default 60 sec */ time_t unlock_timeout = 60; static time_t monitor_interval = 10; static sfex_controldata cdata; static sfex_lockdata ldata; static sfex_lockdata ldata_new; static const char *device; const char *progname; char *nodename; static const char *rsc_id = "sfex"; static void usage(FILE *dist) { fprintf(dist, "usage: %s [-i ] [-c ] [-t ] \n", progname); } static void acquire_lock(void) { if (read_lockdata(&cdata, &ldata, lock_index) == -1) { cl_log(LOG_ERR, "read_lockdata failed in acquire_lock\n"); exit(EXIT_FAILURE); } if ((ldata.status == SFEX_STATUS_LOCK) && (strncmp(nodename, (const char*)(ldata.nodename), sizeof(ldata.nodename)))) { unsigned int t = lock_timeout; while (t > 0) t = sleep(t); read_lockdata(&cdata, &ldata_new, lock_index); if (ldata.count != ldata_new.count) { cl_log(LOG_ERR, "can\'t acquire lock: the lock's already hold by some other node.\n"); exit(2); } } /* The lock acquisition is possible because it was not updated. */ ldata.status = SFEX_STATUS_LOCK; ldata.count = SFEX_NEXT_COUNT(ldata.count); strncpy((char*)(ldata.nodename), nodename, sizeof(ldata.nodename)); if (write_lockdata(&cdata, &ldata, lock_index) == -1) { cl_log(LOG_ERR, "write_lockdata failed\n"); exit(EXIT_FAILURE); } /* detect the collision of lock */ /* The collision occurs when two or more nodes do the reservation processing of the lock at the same time. It waits for collision_timeout seconds to detect this,and whether the superscription of lock data by another node is done is checked. If the superscription was done by another node, the lock acquisition with the own node is given up. */ { unsigned int t = collision_timeout; while (t > 0) t = sleep(t); if (read_lockdata(&cdata, &ldata_new, lock_index) == -1) { cl_log(LOG_ERR, "read_lockdata failed in collision detection\n"); } if (strncmp((char*)(ldata.nodename), (const char*)(ldata_new.nodename), sizeof(ldata.nodename))) { cl_log(LOG_ERR, "can\'t acquire lock: collision detected in the air.\n"); exit(2); } } /* extension of lock */ /* Validly time of the lock is extended. It is because of spending at the collision_timeout seconds to detect the collision. */ ldata.count = SFEX_NEXT_COUNT(ldata.count); if (write_lockdata(&cdata, &ldata, lock_index) == -1) { cl_log(LOG_ERR, "write_lockdata failed in extension of lock\n"); exit(EXIT_FAILURE); } cl_log(LOG_INFO, "lock acquired\n"); } static void error_todo (void) { if (fork() == 0) { cl_log(LOG_INFO, "Execute \"crm_resource -F -r %s -H %s\" command\n", rsc_id, nodename); execl("/usr/sbin/crm_resource", "crm_resource", "-F", "-r", rsc_id, "-H", nodename, NULL); } else { exit(EXIT_FAILURE); } } static void failure_todo(void) { #ifdef SFEX_TESTING exit(EXIT_FAILURE); #else /*execl("/usr/sbin/crm_resource", "crm_resource", "-F", "-r", rsc_id, "-H", nodename, NULL); */ int ret; cl_log(LOG_INFO, "Force reboot node %s\n", nodename); ret = write(sysrq_fd, "b\n", 2); if (ret == -1) { cl_log(LOG_ERR, "%s\n", strerror(errno)); } close(sysrq_fd); exit(EXIT_FAILURE); #endif } static void update_lock(void) { /* read lock data */ if (read_lockdata(&cdata, &ldata, lock_index) == -1) { cl_log(LOG_ERR, "read_lockdata failed in update_lock\n"); error_todo(); exit(EXIT_FAILURE); } /* check current lock status */ /* if own node is not locking, lock update is failed */ if (ldata.status != SFEX_STATUS_LOCK || strncmp((const char*)(ldata.nodename), nodename, sizeof(ldata.nodename))) { cl_log(LOG_ERR, "can't update lock.\n"); failure_todo(); exit(EXIT_FAILURE); } /* lock update */ ldata.count = SFEX_NEXT_COUNT(ldata.count); if (write_lockdata(&cdata, &ldata, lock_index) == -1) { cl_log(LOG_ERR, "write_lockdata failed in update_lock\n"); error_todo(); exit(EXIT_FAILURE); } } static void release_lock(void) { /* The only thing I care about in release_lock(), is to terminate the process */ /* read lock data */ if (read_lockdata(&cdata, &ldata, lock_index) == -1) { cl_log(LOG_ERR, "read_lockdata failed in release_lock\n"); exit(EXIT_FAILURE); } /* check current lock status */ /* if own node is not locking, we judge that lock has been released already */ if (ldata.status != SFEX_STATUS_LOCK || strncmp((const char*)(ldata.nodename), nodename, sizeof(ldata.nodename))) { cl_log(LOG_ERR, "lock was already released.\n"); exit(EXIT_FAILURE); } /* lock release */ ldata.status = SFEX_STATUS_UNLOCK; if (write_lockdata(&cdata, &ldata, lock_index) == -1) { /*FIXME: We are going to self-stop */ cl_log(LOG_ERR, "write_lockdata failed in release_lock\n"); exit(EXIT_FAILURE); } cl_log(LOG_INFO, "lock released\n"); } static void quit_handler(int signo, siginfo_t *info, void *context) { cl_log(LOG_INFO, "quit_handler called. now releasing lock\n"); release_lock(); cl_log(LOG_INFO, "Shutdown sfex_daemon with EXIT_SUCCESS\n"); exit(EXIT_SUCCESS); } int main(int argc, char *argv[]) { int ret; progname = get_progname(argv[0]); nodename = get_nodename(); cl_log_set_entity(progname); cl_log_set_facility(HA_LOG_FACILITY); cl_inherit_logging_environment(0); /* read command line option */ opterr = 0; while (1) { int c = getopt(argc, argv, "hi:c:t:m:n:r:"); if (c == -1) break; switch (c) { case 'h': /* help*/ usage(stdout); exit(EXIT_SUCCESS); case 'i': /* -i */ { unsigned long l = strtoul(optarg, NULL, 10); if (l < SFEX_MIN_NUMLOCKS || l > SFEX_MAX_NUMLOCKS) { cl_log(LOG_ERR, "index %s is out of range or invalid. it must be integer value between %lu and %lu.\n", optarg, (unsigned long)SFEX_MIN_NUMLOCKS, (unsigned long)SFEX_MAX_NUMLOCKS); exit(4); } lock_index = l; } break; case 'c': /* -c */ { unsigned long l = strtoul(optarg, NULL, 10); if (l < 1 || l > INT_MAX) { cl_log(LOG_ERR, "collision_timeout %s is out of range or invalid. it must be integer value between %lu and %lu.\n", optarg, (unsigned long)1, (unsigned long)INT_MAX); exit(4); } collision_timeout = l; } break; case 'm': /* -m */ { unsigned long l = strtoul(optarg, NULL, 10); if (l < 1 || l > INT_MAX) { cl_log(LOG_ERR, "monitor_interval %s is out of range or invalid. it must be integer value between %lu and %lu.\n", optarg, (unsigned long)1, (unsigned long)INT_MAX); exit(4); } monitor_interval = l; } break; case 't': /* -t */ { unsigned long l = strtoul(optarg, NULL, 10); if (l < 1 || l > INT_MAX) { cl_log(LOG_ERR, "lock_timeout %s is out of range or invalid. it must be integer value between %lu and %lu.\n", optarg, (unsigned long)1, (unsigned long)INT_MAX); exit(4); } lock_timeout = l; } break; case 'n': { free(nodename); if (strlen(optarg) > SFEX_MAX_NODENAME) { cl_log(LOG_ERR, "nodename %s is too long. must be less than %d byte.\n", optarg, (unsigned int)SFEX_MAX_NODENAME); exit(EXIT_FAILURE); } nodename = strdup(optarg); } break; case 'r': { rsc_id = strdup(optarg); } break; case '?': /* error */ usage(stderr); exit(4); } } /* check parameter except the option */ if (optind >= argc) { cl_log(LOG_ERR, "no device specified.\n"); usage(stderr); exit(EXIT_FAILURE); } else if (optind + 1 < argc) { cl_log(LOG_ERR, "too many arguments.\n"); usage(stderr); exit(EXIT_FAILURE); } device = argv[optind]; prepare_lock(device); #if !SFEX_TESTING sysrq_fd = open("/proc/sysrq-trigger", O_WRONLY); if (sysrq_fd == -1) { cl_log(LOG_ERR, "failed to open /proc/sysrq-trigger due to %s\n", strerror(errno)); exit(EXIT_FAILURE); } #endif ret = lock_index_check(&cdata, lock_index); if (ret == -1) exit(EXIT_FAILURE); { struct sigaction sig_act; sigemptyset (&sig_act.sa_mask); sig_act.sa_flags = SA_SIGINFO; sig_act.sa_sigaction = quit_handler; ret = sigaction(SIGTERM, &sig_act, NULL); if (ret == -1) { cl_log(LOG_ERR, "sigaction failed\n"); exit(EXIT_FAILURE); } } cl_log(LOG_INFO, "Starting SFeX Daemon...\n"); /* acquire lock first.*/ acquire_lock(); if (daemon(0, 1) != 0) { cl_perror("%s::%d: daemon() failed.", __FUNCTION__, __LINE__); release_lock(); exit(EXIT_FAILURE); } cl_make_realtime(-1, -1, 128, 128); cl_log(LOG_INFO, "SFeX Daemon started.\n"); while (1) { sleep (monitor_interval); update_lock(); } } cluster-agents-1.0.4/tools/sfex_lib.h0000644000175000017500000000322511527003731020021 0ustar roaksoaxroaksoax/*------------------------------------------------------------------------- * * Shared Disk File EXclusiveness Control Program(SF-EX) * * sfex_lib.h --- Prototypes for lib.c. * * Copyright (c) 2007 NIPPON TELEGRAPH AND TELEPHONE CORPORATION * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * $Id$ * *-------------------------------------------------------------------------*/ #ifndef LIB_H #define LIB_H const char *get_progname(const char *argv0); char *get_nodename(void); void init_controldata(sfex_controldata *cdata, size_t blocksize, int numlocks); void init_lockdata(sfex_lockdata *ldata); void write_controldata(const sfex_controldata *cdata); int write_lockdata(const sfex_controldata *cdata, const sfex_lockdata *ldata, int index); int read_controldata(sfex_controldata *cdata); int read_lockdata(const sfex_controldata *cdata, sfex_lockdata *ldata, int index); int prepare_lock(const char *device); int lock_index_check(sfex_controldata * cdata, int index); #endif /* LIB_H */ cluster-agents-1.0.4/tools/send_arp.libnet.c0000644000175000017500000004172711527003731021301 0ustar roaksoaxroaksoax/* * send_arp * * This program sends out one ARP packet with source/target IP and Ethernet * hardware addresses suuplied by the user. It uses the libnet libary from * Packet Factory (http://www.packetfactory.net/libnet/ ). It has been tested * on Linux, FreeBSD, and on Solaris. * * This inspired by the sample application supplied by Packet Factory. * Matt Soffen * Copyright (C) 2001 Matt Soffen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* Needs to be defined before any other includes, otherwise some system * headers do not behave as expected! Major black magic... */ #undef _GNU_SOURCE /* in case it was defined on the command line */ #define _GNU_SOURCE #include #include #define USE_GNU #if defined(ANSI_ONLY) && !defined(inline) # define inline /* nothing */ #endif #include #include #include #include #include #include #ifdef HAVE_LIBNET_1_0_API # define LTYPE struct libnet_link_int #endif #ifdef HAVE_LIBNET_1_1_API # define LTYPE libnet_t #endif #define PIDDIR HA_VARRUNDIR "/" PACKAGE "/rsctmp/send_arp" #define PIDFILE_BASE PIDDIR "/send_arp-" static int send_arp(LTYPE* l, u_long ip, u_char *device, u_char mac[6] , u_char *broadcast, u_char *netmask, u_short arptype); static char print_usage[]={ "send_arp: sends out custom ARP packet.\n" " usage: send_arp [-i repeatinterval-ms] [-r repeatcount] [-p pidfile] \\\n" " device src_ip_addr src_hw_addr broadcast_ip_addr netmask\n" "\n" " where:\n" " repeatinterval-ms: timing, in milliseconds of sending arp packets\n" " For each ARP announcement requested, a pair of ARP packets is sent,\n" " an ARP request, and an ARP reply. This is becuse some systems\n" " ignore one or the other, and this combination gives the greatest\n" " chance of success.\n" "\n" " Each time an ARP is sent, if another ARP will be sent then\n" " the code sleeps for half of repeatinterval-ms.\n" "\n" " repeatcount: how many pairs of ARP packets to send.\n" " See above for why pairs are sent\n" "\n" " pidfile: pid file to use\n" "\n" " device: netowrk interace to use\n" "\n" " src_ip_addr: source ip address\n" "\n" " src_hw_addr: source hardware address.\n" " If \"auto\" then the address of device\n" "\n" " broadcast_ip_addr: ignored\n" "\n" " netmask: ignored\n" }; static const char * SENDARPNAME = "send_arp"; static void convert_macaddr (u_char *macaddr, u_char enet_src[6]); static int get_hw_addr(char *device, u_char mac[6]); int write_pid_file(const char *pidfilename); int create_pid_directory(const char *piddirectory); #define AUTO_MAC_ADDR "auto" #ifndef LIBNET_ERRBUF_SIZE # define LIBNET_ERRBUF_SIZE 256 #endif /* * For use logd, should keep identical with the same const variables defined * in heartbeat.h. */ #define ENV_PREFIX "HA_" #define KEY_LOGDAEMON "use_logd" static void byebye(int nsig) { (void)nsig; /* Avoid an "error exit" log message if we're killed */ exit(0); } int main(int argc, char *argv[]) { int c = -1; char errbuf[LIBNET_ERRBUF_SIZE]; char* device; char* ipaddr; char* macaddr; char* broadcast; char* netmask; u_long ip; u_char src_mac[6]; LTYPE* l; int repeatcount = 1; int j; long msinterval = 1000; int flag; char pidfilenamebuf[64]; char *pidfilename = NULL; CL_SIGNAL(SIGTERM, byebye); CL_SIGINTERRUPT(SIGTERM, 1); cl_log_set_entity(SENDARPNAME); cl_log_enable_stderr(TRUE); cl_log_set_facility(LOG_USER); cl_inherit_logging_environment(0); while ((flag = getopt(argc, argv, "i:r:p:")) != EOF) { switch(flag) { case 'i': msinterval= atol(optarg); break; case 'r': repeatcount= atoi(optarg); break; case 'p': pidfilename= optarg; break; default: fprintf(stderr, "%s\n\n", print_usage); return 1; break; } } if (argc-optind != 5) { fprintf(stderr, "%s\n\n", print_usage); return 1; } /* * argv[optind+1] DEVICE dc0,eth0:0,hme0:0, * argv[optind+2] IP 192.168.195.186 * argv[optind+3] MAC ADDR 00a0cc34a878 * argv[optind+4] BROADCAST 192.168.195.186 * argv[optind+5] NETMASK ffffffffffff */ device = argv[optind]; ipaddr = argv[optind+1]; macaddr = argv[optind+2]; broadcast = argv[optind+3]; netmask = argv[optind+4]; if (!pidfilename) { if (snprintf(pidfilenamebuf, sizeof(pidfilenamebuf), "%s%s", PIDFILE_BASE, ipaddr) >= (int)sizeof(pidfilenamebuf)) { cl_log(LOG_INFO, "Pid file truncated"); return EXIT_FAILURE; } pidfilename = pidfilenamebuf; } if(write_pid_file(pidfilename) < 0) { return EXIT_FAILURE; } #if defined(HAVE_LIBNET_1_0_API) #ifdef ON_DARWIN if ((ip = libnet_name_resolve((unsigned char*)ipaddr, 1)) == -1UL) { #else if ((ip = libnet_name_resolve(ipaddr, 1)) == -1UL) { #endif cl_log(LOG_ERR, "Cannot resolve IP address [%s]", ipaddr); unlink(pidfilename); return EXIT_FAILURE; } l = libnet_open_link_interface(device, errbuf); if (!l) { cl_log(LOG_ERR, "libnet_open_link_interface on %s: %s" , device, errbuf); unlink(pidfilename); return EXIT_FAILURE; } #elif defined(HAVE_LIBNET_1_1_API) if ((l=libnet_init(LIBNET_LINK, device, errbuf)) == NULL) { cl_log(LOG_ERR, "libnet_init failure on %s: %s", device, errbuf); unlink(pidfilename); return EXIT_FAILURE; } if ((signed)(ip = libnet_name2addr4(l, ipaddr, 1)) == -1) { cl_log(LOG_ERR, "Cannot resolve IP address [%s]", ipaddr); unlink(pidfilename); return EXIT_FAILURE; } #else # error "Must have LIBNET API version defined." #endif if (!strcasecmp(macaddr, AUTO_MAC_ADDR)) { if (get_hw_addr(device, src_mac) < 0) { cl_log(LOG_ERR, "Cannot find mac address for %s", device); unlink(pidfilename); return EXIT_FAILURE; } } else { convert_macaddr((unsigned char *)macaddr, src_mac); } /* * We need to send both a broadcast ARP request as well as the ARP response we * were already sending. All the interesting research work for this fix was * done by Masaki Hasegawa and his colleagues. */ for (j=0; j < repeatcount; ++j) { c = send_arp(l, ip, (unsigned char*)device, src_mac , (unsigned char*)broadcast, (unsigned char*)netmask , ARPOP_REQUEST); if (c < 0) { break; } mssleep(msinterval / 2); c = send_arp(l, ip, (unsigned char*)device, src_mac , (unsigned char *)broadcast , (unsigned char *)netmask, ARPOP_REPLY); if (c < 0) { break; } if (j != repeatcount-1) { mssleep(msinterval / 2); } } unlink(pidfilename); return c < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } void convert_macaddr (u_char *macaddr, u_char enet_src[6]) { int i, pos; u_char bits[3]; pos = 0; for (i = 0; i < 6; i++) { /* Inserted to allow old-style MAC addresses */ if (*macaddr == ':') { pos++; } bits[0] = macaddr[pos++]; bits[1] = macaddr[pos++]; bits[2] = '\0'; enet_src[i] = strtol((const char *)bits, (char **)NULL, 16); } } #ifdef HAVE_LIBNET_1_0_API int get_hw_addr(char *device, u_char mac[6]) { struct ether_addr *mac_address; struct libnet_link_int *network; char err_buf[LIBNET_ERRBUF_SIZE]; network = libnet_open_link_interface(device, err_buf); if (!network) { fprintf(stderr, "libnet_open_link_interface: %s\n", err_buf); return -1; } mac_address = libnet_get_hwaddr(network, device, err_buf); if (!mac_address) { fprintf(stderr, "libnet_get_hwaddr: %s\n", err_buf); return -1; } memcpy(mac, mac_address->ether_addr_octet, 6); return 0; } #endif #ifdef HAVE_LIBNET_1_1_API int get_hw_addr(char *device, u_char mac[6]) { struct libnet_ether_addr *mac_address; libnet_t *ln; char err_buf[LIBNET_ERRBUF_SIZE]; ln = libnet_init(LIBNET_LINK, device, err_buf); if (!ln) { fprintf(stderr, "libnet_open_link_interface: %s\n", err_buf); return -1; } mac_address = libnet_get_hwaddr(ln); if (!mac_address) { fprintf(stderr, "libnet_get_hwaddr: %s\n", err_buf); return -1; } memcpy(mac, mac_address->ether_addr_octet, 6); return 0; } #endif /* * Notes on send_arp() behaviour. Horms, 15th June 2004 * * 1. Target Hardware Address * (In the ARP portion of the packet) * * a) ARP Reply * * Set to the MAC address we want associated with the VIP, * as per RFC2002 (4.6). * * Previously set to ff:ff:ff:ff:ff:ff * * b) ARP Request * * Set to 00:00:00:00:00:00. According to RFC2002 (4.6) * this value is not used in an ARP request, so the value should * not matter. However, I observed that typically (always?) this value * is set to 00:00:00:00:00:00. It seems harmless enough to follow * this trend. * * Previously set to ff:ff:ff:ff:ff:ff * * 2. Source Hardware Address * (Ethernet Header, not in the ARP portion of the packet) * * Set to the MAC address of the interface that the packet is being * sent to. Actually, due to the way that send_arp is called this would * usually (always?) be the case anyway. Although this value should not * really matter, it seems sensible to set the source address to where * the packet is really coming from. The other obvious choice would be * the MAC address that is being associated for the VIP. Which was the * previous values. Again, these are typically the same thing. * * Previously set to MAC address being associated with the VIP */ #ifdef HAVE_LIBNET_1_0_API int send_arp(struct libnet_link_int *l, u_long ip, u_char *device, u_char *macaddr, u_char *broadcast, u_char *netmask, u_short arptype) { int n; u_char *buf; u_char *target_mac; u_char device_mac[6]; u_char bcast_mac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; u_char zero_mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; if (libnet_init_packet(LIBNET_ARP_H + LIBNET_ETH_H, &buf) == -1) { cl_log(LOG_ERR, "libnet_init_packet memory:"); return -1; } /* Convert ASCII Mac Address to 6 Hex Digits. */ /* Ethernet header */ if (get_hw_addr((char*)device, device_mac) < 0) { cl_log(LOG_ERR, "Cannot find mac address for %s", device); return -1; } if (libnet_build_ethernet(bcast_mac, device_mac, ETHERTYPE_ARP, NULL, 0 , buf) == -1) { cl_log(LOG_ERR, "libnet_build_ethernet failed:"); libnet_destroy_packet(&buf); return -1; } if (arptype == ARPOP_REQUEST) { target_mac = zero_mac; } else if (arptype == ARPOP_REPLY) { target_mac = macaddr; } else { cl_log(LOG_ERR, "unkonwn arptype:"); return -1; } /* * ARP header */ if (libnet_build_arp(ARPHRD_ETHER, /* Hardware address type */ ETHERTYPE_IP, /* Protocol address type */ 6, /* Hardware address length */ 4, /* Protocol address length */ arptype, /* ARP operation */ macaddr, /* Source hardware addr */ (u_char *)&ip, /* Target hardware addr */ target_mac, /* Destination hw addr */ (u_char *)&ip, /* Target protocol address */ NULL, /* Payload */ 0, /* Payload length */ buf + LIBNET_ETH_H) == -1) { cl_log(LOG_ERR, "libnet_build_arp failed:"); libnet_destroy_packet(&buf); return -1; } n = libnet_write_link_layer(l, (char*)device, buf, LIBNET_ARP_H + LIBNET_ETH_H); if (n == -1) { cl_log(LOG_ERR, "libnet_build_ethernet failed:"); } libnet_destroy_packet(&buf); return (n); } #endif /* HAVE_LIBNET_1_0_API */ #ifdef HAVE_LIBNET_1_1_API int send_arp(libnet_t* lntag, u_long ip, u_char *device, u_char macaddr[6], u_char *broadcast, u_char *netmask, u_short arptype) { int n; u_char *target_mac; u_char device_mac[6]; u_char bcast_mac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; u_char zero_mac[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; if (arptype == ARPOP_REQUEST) { target_mac = zero_mac; } else if (arptype == ARPOP_REPLY) { target_mac = macaddr; } else { cl_log(LOG_ERR, "unkonwn arptype:"); return -1; } /* * ARP header */ if (libnet_build_arp(ARPHRD_ETHER, /* hardware address type */ ETHERTYPE_IP, /* protocol address type */ 6, /* Hardware address length */ 4, /* protocol address length */ arptype, /* ARP operation type */ macaddr, /* sender Hardware address */ (u_char *)&ip, /* sender protocol address */ target_mac, /* target hardware address */ (u_char *)&ip, /* target protocol address */ NULL, /* Payload */ 0, /* Length of payload */ lntag, /* libnet context pointer */ 0 /* packet id */ ) == -1 ) { cl_log(LOG_ERR, "libnet_build_arp failed:"); return -1; } /* Ethernet header */ if (get_hw_addr((char *)device, device_mac) < 0) { cl_log(LOG_ERR, "Cannot find mac address for %s", device); return -1; } if (libnet_build_ethernet(bcast_mac, device_mac, ETHERTYPE_ARP, NULL, 0 , lntag, 0) == -1 ) { cl_log(LOG_ERR, "libnet_build_ethernet failed:"); return -1; } n = libnet_write(lntag); if (n == -1) { cl_log(LOG_ERR, "libnet_build_ethernet failed:"); } libnet_clear_packet(lntag); return (n); } #endif /* HAVE_LIBNET_1_1_API */ int create_pid_directory(const char *pidfilename) { int status; struct stat stat_buf; char *pidfilename_cpy; char *dir; pidfilename_cpy = strdup(pidfilename); if (!pidfilename_cpy) { cl_log(LOG_INFO, "Memory allocation failure: %s\n", strerror(errno)); return -1; } dir = dirname(pidfilename_cpy); status = stat(dir, &stat_buf); if (status < 0 && errno != ENOENT && errno != ENOTDIR) { cl_log(LOG_INFO, "Could not stat pid-file directory " "[%s]: %s", dir, strerror(errno)); free(pidfilename_cpy); return -1; } if (status >= 0) { if (S_ISDIR(stat_buf.st_mode)) { return 0; } cl_log(LOG_INFO, "Pid-File directory exists but is " "not a directory [%s]", dir); free(pidfilename_cpy); return -1; } if (mkdir(dir, S_IRUSR|S_IWUSR|S_IXUSR | S_IRGRP|S_IXGRP) < 0) { /* Did someone else make it while we were trying ? */ if (errno == EEXIST && stat(dir, &stat_buf) >= 0 && S_ISDIR(stat_buf.st_mode)) { return 0; } cl_log(LOG_INFO, "Could not create pid-file directory " "[%s]: %s", dir, strerror(errno)); free(pidfilename_cpy); return -1; } free(pidfilename_cpy); return 0; } int write_pid_file(const char *pidfilename) { int pidfilefd; char pidbuf[11]; unsigned long pid; ssize_t bytes; if (*pidfilename != '/') { cl_log(LOG_INFO, "Invalid pid-file name, must begin with a " "'/' [%s]\n", pidfilename); return -1; } if (create_pid_directory(pidfilename) < 0) { return -1; } while (1) { pidfilefd = open(pidfilename, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); if (pidfilefd < 0) { if (errno != EEXIST) { /* Old PID file */ cl_log(LOG_INFO, "Could not open pid-file " "[%s]: %s", pidfilename, strerror(errno)); return -1; } } else { break; } pidfilefd = open(pidfilename, O_RDONLY, S_IRUSR|S_IWUSR); if (pidfilefd < 0) { cl_log(LOG_INFO, "Could not open pid-file " "[%s]: %s", pidfilename, strerror(errno)); return -1; } while (1) { bytes = read(pidfilefd, pidbuf, sizeof(pidbuf)-1); if (bytes < 0) { if (errno == EINTR) { continue; } cl_log(LOG_INFO, "Could not read pid-file " "[%s]: %s", pidfilename, strerror(errno)); return -1; } pidbuf[bytes] = '\0'; break; } if(unlink(pidfilename) < 0) { cl_log(LOG_INFO, "Could not delete pid-file " "[%s]: %s", pidfilename, strerror(errno)); return -1; } if (!bytes) { cl_log(LOG_INFO, "Invalid pid in pid-file " "[%s]: %s", pidfilename, strerror(errno)); return -1; } close(pidfilefd); pid = strtoul(pidbuf, NULL, 10); if (pid == ULONG_MAX && errno == ERANGE) { cl_log(LOG_INFO, "Invalid pid in pid-file " "[%s]: %s", pidfilename, strerror(errno)); return -1; } if (kill(pid, SIGKILL) < 0 && errno != ESRCH) { cl_log(LOG_INFO, "Error killing old proccess [%lu] " "from pid-file [%s]: %s", pid, pidfilename, strerror(errno)); return -1; } cl_log(LOG_INFO, "Killed old send_arp process [%lu]\n", pid); } if (snprintf(pidbuf, sizeof(pidbuf), "%u" , getpid()) >= (int)sizeof(pidbuf)) { cl_log(LOG_INFO, "Pid too long for buffer [%u]", getpid()); return -1; } while (1) { bytes = write(pidfilefd, pidbuf, strlen(pidbuf)); if (bytes != (ssize_t)strlen(pidbuf)) { if (bytes < 0 && errno == EINTR) { continue; } cl_log(LOG_INFO, "Could not write pid-file " "[%s]: %s", pidfilename, strerror(errno)); return -1; } break; } close(pidfilefd); return 0; } cluster-agents-1.0.4/tools/Makefile.am0000644000175000017500000000415611527003731020115 0ustar roaksoaxroaksoax# # heartbeat: Linux-HA heartbeat code # # Copyright (C) 2001 Michael Moerz # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in SUBDIRS = ocft INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include halibdir = $(libdir)/heartbeat sbin_PROGRAMS = sbin_SCRIPTS = ocf-tester halib_PROGRAMS = findif if BUILD_SFEX halib_PROGRAMS += sfex_daemon sbin_PROGRAMS += sfex_init sfex_stat endif if USE_LIBNET halib_PROGRAMS += send_arp send_arp_SOURCES = send_arp.libnet.c send_arp_CFLAGS = @LIBNETDEFINES@ send_arp_LDADD = $(GLIBLIB) -lplumb @LIBNETLIBS@ else if SENDARP_LINUX halib_PROGRAMS += send_arp send_arp_SOURCES = send_arp.linux.c endif endif sfex_daemon_SOURCES = sfex_daemon.c sfex.h sfex_lib.c sfex_lib.h sfex_daemon_CFLAGS = -D_GNU_SOURCE sfex_daemon_LDADD = $(GLIBLIB) -lplumb -lplumbgpl sfex_init_SOURCES = sfex_init.c sfex.h sfex_lib.c sfex_lib.h sfex_init_CFLAGS = -D_GNU_SOURCE sfex_init_LDADD = $(GLIBLIB) -lplumb -lplumbgpl sfex_stat_SOURCES = sfex_stat.c sfex.h sfex_lib.c sfex_lib.h sfex_stat_CFLAGS = -D_GNU_SOURCE sfex_stat_LDADD = $(GLIBLIB) -lplumb -lplumbgpl findif_SOURCES = findif.c if BUILD_TICKLE halib_PROGRAMS += tickle_tcp tickle_tcp_SOURCES = tickle_tcp.c endif if BUILD_HELP man8_MANS = $(sbin_SCRIPTS:%=%.8) %.8: % echo Creating $@ chmod a+x $< help2man --output $@ --no-info --section 8 --name "Part of the Linux-HA project" $(top_builddir)/tools/$< endif .PHONY: install-exec-hook cluster-agents-1.0.4/tools/sfex_stat.c0000644000175000017500000001371411527003731020225 0ustar roaksoaxroaksoax/*------------------------------------------------------------------------- * * Shared Disk File EXclusiveness Control Program(SF-EX) * * sfex_stat.c --- Display lock status. This is a part of the SF-EX. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * Copyright (c) 2007 NIPPON TELEGRAPH AND TELEPHONE CORPORATION * * $Id$ * *------------------------------------------------------------------------- * * sfex_stat [-i ] * * -i --- The index is number of the resource that display the lock. * This number is specified by the integer of one or more. When two or more * resources are exclusively controlled by one meta-data, this option is used. * Default is 1. * * --- This is file path which stored meta-data. It is usually * expressed in "/dev/...", because it is partition on the shared disk. * * exit code --- 0 - Normal end. Own node is holding lock. 2 - Normal * end. Own node does not hold a lock. 3 - Error occurs while processing * it. The content of the error is displayed into stderr. 4 - The mistake * is found in the command line parameter. * *-------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #if HAVE_UNISTD_H # include #endif #include "sfex.h" #include "sfex_lib.h" const char *progname; char *nodename; void print_controldata(const sfex_controldata *cdata); void print_lockdata(const sfex_lockdata *ldata, int index); /* * print_controldata --- print sfex control data to the display * * print sfex_controldata to the display. * * cdata --- pointer for control data */ void print_controldata(const sfex_controldata *cdata) { printf("control data:\n"); printf(" magic: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", cdata->magic[0], cdata->magic[1], cdata->magic[2], cdata->magic[3]); printf(" version: %d\n", cdata->version); printf(" revision: %d\n", cdata->revision); printf(" blocksize: %d\n", (int)cdata->blocksize); printf(" numlocks: %d\n", cdata->numlocks); } /* * print_lockdata --- print sfex lock data to the display * * print sfex_lockdata to the display. * * ldata --- pointer for lock data * * index --- index number */ void print_lockdata(const sfex_lockdata *ldata, int index) { printf("lock data #%d:\n", index); printf(" status: %s\n", ldata->status == SFEX_STATUS_UNLOCK ? "unlock" : "lock"); printf(" count: %d\n", ldata->count); printf(" nodename: %s\n",ldata->nodename); } /* * usage --- display command line syntax * * display command line syntax. By the purpose, it can specify destination * stream. stdout or stderr is set usually. * * dist --- destination stream of the command line syntax, such as stderr. * * retrun value --- void */ static void usage(FILE *dist) { fprintf(dist, "usage: %s [-i ] \n", progname); } /* * main --- main function * * entry point of sfex_stat command. * * exit code --- 0 - Normal end. Own node is holding lock. 2 - Normal * end. Own node dose not hold a lock. 3 - Error occurs while processing * it. The content of the error is displayed into stderr. 4 - The mistake * is found in the command line parameter. */ int main(int argc, char *argv[]) { sfex_controldata cdata; sfex_lockdata ldata; int ret = 0; /* command line parameter */ int index = 1; /* default 1st lock */ const char *device; /* * startup process */ /* get a program name */ progname = get_progname(argv[0]); /* enable the cl_log output from the sfex library */ cl_log_set_entity(progname); /* The cl_log is output only to the standard error output */ cl_log_enable_stderr(TRUE); /* read command line option */ opterr = 0; while (1) { int c = getopt(argc, argv, "hi:"); if (c == -1) break; switch (c) { case 'h': /* help */ usage(stdout); exit(0); case 'i': /* -i */ { unsigned long l = strtoul(optarg, NULL, 10); if (l < SFEX_MIN_NUMLOCKS || l > SFEX_MAX_NUMLOCKS) { fprintf(stderr, "%s: ERROR: index %s is out of range or invalid. it must be integer value between %lu and %lu.\n", progname, optarg, (unsigned long)SFEX_MIN_NUMLOCKS, (unsigned long)SFEX_MAX_NUMLOCKS); exit(4); } index = l; } break; case '?': /* error */ usage(stderr); exit(4); } } /* check parameter except the option */ if (optind >= argc) { fprintf(stderr, "%s: ERROR: no device specified.\n", progname); usage(stderr); exit(4); } else if (optind + 1 < argc) { fprintf(stderr, "%s: ERROR: too many arguments.\n", progname); usage(stderr); exit(4); } device = argv[optind]; /* * main processes start */ /* get a node name */ nodename = get_nodename(); prepare_lock(device); ret = lock_index_check(&cdata, index); if (ret == -1) exit(EXIT_FAILURE); /* read lock data */ read_lockdata(&cdata, &ldata, index); /* display status */ print_controldata(&cdata); print_lockdata(&ldata, index); /* check current lock status */ if (ldata.status != SFEX_STATUS_LOCK || strcmp(ldata.nodename, nodename)) { fprintf(stdout, "status is UNLOCKED.\n"); exit(2); } else { fprintf(stdout, "status is LOCKED.\n"); exit(0); } } cluster-agents-1.0.4/tools/tickle_tcp.c0000644000175000017500000002201511527003731020340 0ustar roaksoaxroaksoax/* Tickle TCP connections tool Author: Jiaju Zhang Based on the code in CTDB http://ctdb.samba.org/ written by Andrew Tridgell and Ronnie Sahlberg This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define discard_const(ptr) ((void *)((intptr_t)(ptr))) typedef union { struct sockaddr sa; struct sockaddr_in ip; struct sockaddr_in6 ip6; } sock_addr; uint32_t uint16_checksum(uint16_t *data, size_t n); void set_nonblocking(int fd); void set_close_on_exec(int fd); static int parse_ipv4(const char *s, unsigned port, struct sockaddr_in *sin); static int parse_ipv6(const char *s, const char *iface, unsigned port, sock_addr *saddr); int parse_ip(const char *addr, const char *iface, unsigned port, sock_addr *saddr); int parse_ip_port(const char *addr, sock_addr *saddr); int send_tickle_ack(const sock_addr *dst, const sock_addr *src, uint32_t seq, uint32_t ack, int rst); static void usage(void); uint32_t uint16_checksum(uint16_t *data, size_t n) { uint32_t sum=0; while (n >= 2) { sum += (uint32_t)ntohs(*data); data++; n -= 2; } if (n == 1) { sum += (uint32_t)ntohs(*(uint8_t *)data); } return sum; } static uint16_t tcp_checksum(uint16_t *data, size_t n, struct iphdr *ip) { uint32_t sum = uint16_checksum(data, n); uint16_t sum2; sum += uint16_checksum((uint16_t *)(void *)&ip->saddr, sizeof(ip->saddr)); sum += uint16_checksum((uint16_t *)(void *)&ip->daddr, sizeof(ip->daddr)); sum += ip->protocol + n; sum = (sum & 0xFFFF) + (sum >> 16); sum = (sum & 0xFFFF) + (sum >> 16); sum2 = htons(sum); sum2 = ~sum2; if (sum2 == 0) { return 0xFFFF; } return sum2; } static uint16_t tcp_checksum6(uint16_t *data, size_t n, struct ip6_hdr *ip6) { uint32_t phdr[2]; uint32_t sum = 0; uint16_t sum2; sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_src, 16); sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_dst, 16); phdr[0] = htonl(n); phdr[1] = htonl(ip6->ip6_nxt); sum += uint16_checksum((uint16_t *)phdr, 8); sum += uint16_checksum(data, n); sum = (sum & 0xFFFF) + (sum >> 16); sum = (sum & 0xFFFF) + (sum >> 16); sum2 = htons(sum); sum2 = ~sum2; if (sum2 == 0) { return 0xFFFF; } return sum2; } void set_nonblocking(int fd) { unsigned v; v = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, v | O_NONBLOCK); } void set_close_on_exec(int fd) { unsigned v; v = fcntl(fd, F_GETFD, 0); fcntl(fd, F_SETFD, v | FD_CLOEXEC); } static int parse_ipv4(const char *s, unsigned port, struct sockaddr_in *sin) { sin->sin_family = AF_INET; sin->sin_port = htons(port); if (inet_pton(AF_INET, s, &sin->sin_addr) != 1) { fprintf(stderr, "Failed to translate %s into sin_addr\n", s); return -1; } return 0; } static int parse_ipv6(const char *s, const char *iface, unsigned port, sock_addr *saddr) { saddr->ip6.sin6_family = AF_INET6; saddr->ip6.sin6_port = htons(port); saddr->ip6.sin6_flowinfo = 0; saddr->ip6.sin6_scope_id = 0; if (inet_pton(AF_INET6, s, &saddr->ip6.sin6_addr) != 1) { fprintf(stderr, "Failed to translate %s into sin6_addr\n", s); return -1; } if (iface && IN6_IS_ADDR_LINKLOCAL(&saddr->ip6.sin6_addr)) { saddr->ip6.sin6_scope_id = if_nametoindex(iface); } return 0; } int parse_ip(const char *addr, const char *iface, unsigned port, sock_addr *saddr) { char *p; int ret; p = index(addr, ':'); if (!p) ret = parse_ipv4(addr, port, &saddr->ip); else ret = parse_ipv6(addr, iface, port, saddr); return ret; } int parse_ip_port(const char *addr, sock_addr *saddr) { char *s, *p; unsigned port; char *endp = NULL; int ret; s = strdup(addr); if (!s) { fprintf(stderr, "Failed strdup()\n"); return -1; } p = rindex(s, ':'); if (!p) { fprintf(stderr, "This addr: %s does not contain a port number\n", s); free(s); return -1; } port = strtoul(p+1, &endp, 10); if (!endp || *endp != 0) { fprintf(stderr, "Trailing garbage after the port in %s\n", s); free(s); return -1; } *p = 0; ret = parse_ip(s, NULL, port, saddr); free(s); return ret; } int send_tickle_ack(const sock_addr *dst, const sock_addr *src, uint32_t seq, uint32_t ack, int rst) { int s; int ret; uint32_t one = 1; uint16_t tmpport; sock_addr *tmpdest; struct { struct iphdr ip; struct tcphdr tcp; } ip4pkt; struct { struct ip6_hdr ip6; struct tcphdr tcp; } ip6pkt; switch (src->ip.sin_family) { case AF_INET: memset(&ip4pkt, 0, sizeof(ip4pkt)); ip4pkt.ip.version = 4; ip4pkt.ip.ihl = sizeof(ip4pkt.ip)/4; ip4pkt.ip.tot_len = htons(sizeof(ip4pkt)); ip4pkt.ip.ttl = 255; ip4pkt.ip.protocol = IPPROTO_TCP; ip4pkt.ip.saddr = src->ip.sin_addr.s_addr; ip4pkt.ip.daddr = dst->ip.sin_addr.s_addr; ip4pkt.ip.check = 0; ip4pkt.tcp.source = src->ip.sin_port; ip4pkt.tcp.dest = dst->ip.sin_port; ip4pkt.tcp.seq = seq; ip4pkt.tcp.ack_seq = ack; ip4pkt.tcp.ack = 1; if (rst) ip4pkt.tcp.rst = 1; ip4pkt.tcp.doff = sizeof(ip4pkt.tcp)/4; ip4pkt.tcp.window = htons(1234); ip4pkt.tcp.check = tcp_checksum((uint16_t *)&ip4pkt.tcp, sizeof(ip4pkt.tcp), &ip4pkt.ip); s = socket(AF_INET, SOCK_RAW, htons(IPPROTO_RAW)); if (s == -1) { fprintf(stderr, "Failed to open raw socket (%s)\n", strerror(errno)); return -1; } ret = setsockopt(s, SOL_IP, IP_HDRINCL, &one, sizeof(one)); if (ret != 0) { fprintf(stderr, "Failed to setup IP headers (%s)\n", strerror(errno)); close(s); return -1; } set_nonblocking(s); set_close_on_exec(s); ret = sendto(s, &ip4pkt, sizeof(ip4pkt), 0, (const struct sockaddr *)&dst->ip, sizeof(dst->ip)); close(s); if (ret != sizeof(ip4pkt)) { fprintf(stderr, "Failed sendto (%s)\n", strerror(errno)); return -1; } break; case AF_INET6: memset(&ip6pkt, 0, sizeof(ip6pkt)); ip6pkt.ip6.ip6_vfc = 0x60; ip6pkt.ip6.ip6_plen = htons(20); ip6pkt.ip6.ip6_nxt = IPPROTO_TCP; ip6pkt.ip6.ip6_hlim = 64; ip6pkt.ip6.ip6_src = src->ip6.sin6_addr; ip6pkt.ip6.ip6_dst = dst->ip6.sin6_addr; ip6pkt.tcp.source = src->ip6.sin6_port; ip6pkt.tcp.dest = dst->ip6.sin6_port; ip6pkt.tcp.seq = seq; ip6pkt.tcp.ack_seq = ack; ip6pkt.tcp.ack = 1; if (rst) ip6pkt.tcp.rst = 1; ip6pkt.tcp.doff = sizeof(ip6pkt.tcp)/4; ip6pkt.tcp.window = htons(1234); ip6pkt.tcp.check = tcp_checksum6((uint16_t *)&ip6pkt.tcp, sizeof(ip6pkt.tcp), &ip6pkt.ip6); s = socket(PF_INET6, SOCK_RAW, IPPROTO_RAW); if (s == -1) { fprintf(stderr, "Failed to open sending socket\n"); return -1; } tmpdest = discard_const(dst); tmpport = tmpdest->ip6.sin6_port; tmpdest->ip6.sin6_port = 0; ret = sendto(s, &ip6pkt, sizeof(ip6pkt), 0, (const struct sockaddr *)&dst->ip6, sizeof(dst->ip6)); tmpdest->ip6.sin6_port = tmpport; close(s); if (ret != sizeof(ip6pkt)) { fprintf(stderr, "Failed sendto (%s)\n", strerror(errno)); return -1; } break; default: fprintf(stderr, "Not an ipv4/v6 address\n"); return -1; } return 0; } static void usage(void) { printf("Usage: /usr/lib/heartbeat/tickle_tcp [ -n num ]\n"); printf("Please note that this program need to read the list of\n"); printf("{local_ip:port remote_ip:port} from stdin.\n"); exit(1); } #define OPTION_STRING "n:h" int main(int argc, char *argv[]) { int optchar, i, num = 1, cont = 1; sock_addr src, dst; char addrline[128], addr1[64], addr2[64]; while(cont) { optchar = getopt(argc, argv, OPTION_STRING); switch(optchar) { case 'n': num = atoi(optarg); break; case 'h': usage(); exit(EXIT_SUCCESS); break; case EOF: cont = 0; break; default: fprintf(stderr, "unknown option, please use '-h' for usage.\n"); exit(EXIT_FAILURE); break; }; } while(fgets(addrline, sizeof(addrline), stdin)) { sscanf(addrline, "%s %s", addr1, addr2); if (parse_ip_port(addr1, &src)) { fprintf(stderr, "Bad IP:port '%s'\n", addr1); return -1; } if (parse_ip_port(addr2, &dst)) { fprintf(stderr, "Bad IP:port '%s'\n", addr2); return -1; } for (i = 1; i <= num; i++) { if (send_tickle_ack(&dst, &src, 0, 0, 0)) { fprintf(stderr, "Error while sending tickle ack from '%s' to '%s'\n", addr1, addr2); return -1; } } } return 0; } cluster-agents-1.0.4/tools/ocf-tester.in0000755000175000017500000002456311527003731020473 0ustar roaksoaxroaksoax#!/bin/sh # # $Id: ocf-tester,v 1.2 2006/08/14 09:38:20 andrew Exp $ # # Copyright (c) 2006 Novell Inc, Andrew Beekhof # All Rights Reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # LRMD=@libdir@/heartbeat/lrmd LRMADMIN=@sbindir@/lrmadmin METADATA_LINT="xmllint --noout --valid -" # set some common meta attributes, which are expected to be # present by resource agents export OCF_RESKEY_CRM_meta_timeout=20000 # 20 seconds timeout export OCF_RESKEY_CRM_meta_interval=10000 # reset this for probes num_errors=0 usage() { echo "Tool for testing if a cluster resource is OCF compliant" echo "" echo "Usage: ocf-tester [-Lh] -n resource_name [-o name=value]* /full/path/to/resource/agent" echo "" echo "Options:" echo " -h,--help This text" echo " -n name Name of the resource" echo " -o name=value Name and value of any parameters required by the agent" echo " -L Use lrmadmin/lrmd for tests" exit $1 } assert() { rc=$1; shift target=$1; shift msg=$1; shift if [ $# = 0 ]; then exit_code=0 else exit_code=$1; shift fi if [ $rc -ne $target ]; then num_errors=`expr $num_errors + 1` echo "* rc=$rc: $msg" if [ $exit_code != 0 ]; then [ -n "$command_output" ] && cat</dev/null rc=$? if [ $rc -eq 3 ]; then lrmd_started=1 $LRMD & sleep 1 $LRMD -s 2>/dev/null else return $rc fi } add_resource() { $LRMADMIN -A $OCF_RESOURCE_INSTANCE \ ocf \ `basename $agent` \ $(basename `dirname $agent`) \ $lrm_ra_args > /dev/null } del_resource() { $LRMADMIN -D $OCF_RESOURCE_INSTANCE } parse_lrmadmin_output() { awk ' BEGIN{ rc=1; } /Waiting for lrmd to callback.../ { n=1; next; } n==1 && /----------------operation--------------/ { n++; next; } n==2 && /return code:/ { rc=$0; sub("return code: *","",rc); next } n==2 && /---------------------------------------/ { n++; next; } END{ if( n!=3 ) exit 1; else exit rc; } ' } exec_resource() { op="$1" args="$2" $LRMADMIN -E $OCF_RESOURCE_INSTANCE \ $op $lrmd_timeout $lrmd_interval \ $lrmd_target_rc \ $args | parse_lrmadmin_output } if [ "$use_lrmd" = 1 ]; then echo "Using lrmd/lrmadmin for all tests" start_lrmd || { echo "could not start lrmd" exit 1 } trap ' [ "$lrmd_started" = 1 ] && $LRMD -k ' EXIT add_resource || { echo "failed to add resource to lrmd" exit 1 } fi lrm_test_command() { action="$1" msg="$2" [ "$verbose" -eq 0 ] || echo "$msg" exec_resource $action "$lrm_ra_args" } test_permissions() { action=meta-data msg=${1:-"Testing permissions with uid nobody"} if [ $verbose -ne 0 ]; then echo $msg fi su nobody -s /bin/sh $agent $action > /dev/null } test_metadata() { action=meta-data msg=${1:-"Testing: $action"} if [ $verbose -ne 0 ]; then echo $msg fi bash $agent $action | (cd /usr/share/resource-agents && $METADATA_LINT) rc=$? #echo rc: $rc return $rc } test_command() { action=$1; shift export __OCF_ACTION=$action msg=${1:-"Testing: $action"} if [ "$use_lrmd" = 1 ]; then lrm_test_command $action "$msg" return $? fi #echo Running: "export $ra_args; bash $agent $action 2>&1 > /dev/null" if [ $verbose -eq 0 ]; then command_output=`bash $agent $action 2>&1` else echo $msg bash $agent $action fi rc=$? #echo rc: $rc return $rc } # Begin tests echo Beginning tests for $agent... if [ ! -f $agent ]; then assert 7 0 "Could not find file: $agent" fi if [ `id -u` = 0 ]; then test_permissions assert $? 0 "Your agent has too restrictive permissions: should be 755" else echo "WARN: Can't check agent's permissions because we're not root; they should be 755" fi test_metadata assert $? 0 "Your agent produces meta-data which does not conform to ra-api-1.dtd" OCF_TESTER_FAIL_HAVE_BINARY=1 export OCF_TESTER_FAIL_HAVE_BINARY test_command meta-data rc=$? if [ $rc -eq 3 ]; then assert $rc 0 "Your agent does not support the meta-data action" else assert $rc 0 "The meta-data action cannot fail and must return 0" fi unset OCF_TESTER_FAIL_HAVE_BINARY export $ra_args; test_command validate-all rc=$? if [ $rc -eq 3 ]; then assert $rc 0 "Your agent does not support the validate-all action" elif [ $rc -ne 0 ]; then assert $rc 0 "Validation failed. Did you supply enough options with -o ?" 1 usage $rc fi test_command monitor "Checking current state" rc=$? if [ $rc -eq 3 ]; then assert $rc 7 "Your agent does not support the monitor action" 1 elif [ $rc -eq 8 ]; then test_command demote "Cleanup, demote" assert $? 0 "Your agent was a master and could not be demoted" 1 test_command stop "Cleanup, stop" assert $? 0 "Your agent was a master and could not be stopped" 1 elif [ $rc -ne 7 ]; then test_command stop assert $? 0 "Your agent was active and could not be stopped" 1 fi test_command monitor assert $? $stopped_rc "Monitoring a stopped resource should return $stopped_rc" OCF_TESTER_FAIL_HAVE_BINARY=1 export OCF_TESTER_FAIL_HAVE_BINARY OCF_RESKEY_CRM_meta_interval=0 test_command monitor assert $? $stopped_rc "The initial probe for a stopped resource should return $stopped_rc even if all binaries are missing" unset OCF_TESTER_FAIL_HAVE_BINARY OCF_RESKEY_CRM_meta_interval=20000 test_command start assert $? 0 "Start failed. Did you supply enough options with -o ?" 1 test_command monitor assert $? 0 "Monitoring an active resource should return 0" test_command notify rc=$? if [ $rc -eq 3 ]; then echo "* Your agent does not support the notify action (optional)" else assert $rc 0 "The notify action cannot fail and must return 0" fi test_command demote "Checking for demote action" if [ $? -eq 3 ]; then has_demote=0 echo "* Your agent does not support the demote action (optional)" fi test_command promote "Checking for promote action" if [ $? -eq 3 ]; then has_promote=0 echo "* Your agent does not support the promote action (optional)" fi if [ $has_promote -eq 1 -a $has_demote -eq 1 ]; then test_command demote "Testing: demotion of started resource" assert $? 0 "Demoting a start resource should not fail" test_command promote assert $? 0 "Promote failed" test_command demote assert $? 0 "Demote failed" 1 test_command demote "Testing: demotion of demoted resource" assert $? 0 "Demoting a demoted resource should not fail" test_command promote "Promoting resource" assert $? 0 "Promote failed" 1 test_command promote "Testing: promotion of promoted resource" assert $? 0 "Promoting a promoted resource should not fail" test_command demote "Demoting resource" assert $? 0 "Demote failed" 1 elif [ $has_promote -eq 0 -a $has_demote -eq 0 ]; then echo "* Your agent does not support master/slave (optional)" else echo "* Your agent partially supports master/slave" num_errors=`expr $num_errors + 1` fi test_command stop assert $? 0 "Stop failed" 1 test_command monitor assert $? $stopped_rc "Monitoring a stopped resource should return $stopped_rc" test_command start "Restarting resource..." assert $? 0 "Start failed" 1 test_command monitor assert $? 0 "Monitoring an active resource should return 0" test_command start "Testing: starting a started resource" assert $? 0 "Starting a running resource is required to succeed" test_command monitor assert $? 0 "Monitoring an active resource should return 0" test_command stop "Stopping resource" assert $? 0 "Stop could not clean up after multiple starts" 1 test_command monitor assert $? $stopped_rc "Monitoring a stopped resource should return $stopped_rc" test_command stop "Testing: stopping a stopped resource" assert $? 0 "Stopping a stopped resource is required to succeed" test_command monitor assert $? $stopped_rc "Monitoring a stopped resource should return $stopped_rc" test_command migrate_to "Checking for migrate_to action" rc=$? if [ $rc -ne 3 ]; then test_command migrate_from "Checking for migrate_from action" fi if [ $? -eq 3 ]; then echo "* Your agent does not support the migrate action (optional)" fi test_command reload "Checking for reload action" if [ $? -eq 3 ]; then echo "* Your agent does not support the reload action (optional)" fi if [ $num_errors -gt 0 ]; then echo Tests failed: $agent failed $num_errors tests exit 1 else echo $agent passed all tests exit 0 fi # vim:et:ts=8:sw=4 cluster-agents-1.0.4/include/0000755000175000017500000000000011527051501016334 5ustar roaksoaxroaksoaxcluster-agents-1.0.4/include/agent_config.h.in0000644000175000017500000000010011527003731021526 0ustar roaksoaxroaksoax/* Where Resouce agents keep state files */ #undef HA_RSCTMPDIR cluster-agents-1.0.4/include/Makefile.am0000644000175000017500000000154311527003731020375 0ustar roaksoaxroaksoax# # Copyright (C) 2008 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in idir=$(includedir)/heartbeat i_HEADERS = agent_config.h noinst_HEADERS = config.h cluster-agents-1.0.4/.hgtags0000644000175000017500000000451111527003731016172 0ustar roaksoaxroaksoax19d388079befe5d069c73bccd043f1db4a95413a STABLE-2.0.5 1b9ca9365728029a9972b4c4f0102d8373bfcd57 sle11-rc7 1eeb9262052a95e31d3d2e2a1eed1009dae4464d obs-2.1.2-1 1eeb9262052a95e31d3d2e2a1eed1009dae4464d obs-2.1.2-2 24641938d44a65f7430ce734e8ffbd4ff43b8aa5 sle11-rc5 2bae9adf9c098126635e835b0c2f07695a56b820 Beta-0.4.9f 35135f93dd8695702d9bd1b44504788373bf73df STABLE-2.0.2 425aa3d2b818ae952d425e732942f18d8439f9ef STABLE-2.0.1 483281e0bacaade2c87ee43a5a28d49976eef47e STABLE-2.0.8 4ed7779c480c1db676b4d1af6d8db1622657c3f3 STABLE-2.0.0 52b6be2a2ef73fe262114a1e57a846ab946d3b0b Beta-0.4.9a 5ac6b8584d5bee0326ebf5a65a12d4c148b5dced STABLE-2.1.2 6c6dff3aa6132de8c8263904a6eb88ebeb6b93ec sle11-beta6 6e879c884437689d171cab5bded84c806c06db27 beta-2.99.2 7c19f502341b2e9758b23adcf3c98fc90384372b STABLE-2.0.6 7d996df373c5b6a9eb58590a5e0a5f8a2ad105e6 STABLE-0.4.9c 7ddf73677956f6233b7f6ec9fc4827f5a98985d3 sle11-rc3 936a8b0eb71a838e9e50b5fd66be7fce72e5ad24 STABLE-2.1.3 9c8d66dd24d7e71c581fe29016eab5a48c7373a1 STABLE-1.1.3 9fd049c9227bca051cc059e730a3ebc87bfaf42e sle11-rc2 a47a67cd81eac0fbbabb7dbf5a2d5d2f0589d227 STABLE-1.1.5 a6a918342b3428eba91f665b66e6f16e799cb554 obs-2.1.2-24 aba8153b997d328df4d6a7fce4ffc868137594dc SLE10-SP1 b50c26ac8a7e0ece683c9a4529da22d26f2f196e obs-2.1.2-4 b9347ad6361db4177fc0e638331f6ed057058c3e Series-Root-1.2 ba23f61326bfaf94b4fb942599bd0f3e912cb38c beta-2.99.1 c52a0034b36cffb5241d4a709e48ece432a6ee27 STABLE-2.1.1 ccb84a8849e1923a8a97e6e1da9cd03a91f96309 beta-2.99.0 cd07c521f1b9de268a5eff2e104d4a668be0acc7 sle11-rc9 ceead2d2c5dead2806db48dadf70d797d182f049 obs-2.1.2-15 db31ab54db1582b801e3661208ad9fb349266dda STABLE-2.0.4 e3a51620237fb51b365b1330bcaaba6bf2d40fc7 Series-Root-1.0 f20380d19c0f2f6ee18b7f30c7b748524836a9a8 STABLE-2.0.3 f840eaae3e1a1a24f2d80c8f1610a024cdf78975 STABLE-2.1.0 f8b0200f7de56073525cdba2c0f38f239598cb71 SLES10-GA-2.0.7-1.5 f8b0200f7de56073525cdba2c0f38f239598cb71 STABLE-2.0.7 fd0b7fa32b84489d1d55739732ee29fe2fba14f2 STABLE-0.4.9e bc085cb74dba47dd5fe1c3c21f8ad2fa268fc236 agents-1.0 1ab6e669bd5214bffbce024e70a72d7beaf70e2c agents-1.0.2-rc1 d1f026d175cf0b871957954fa1d024b284504b6c agents-1.0.2-rc2 61203c6ef710e35660f0d76047561877963d3a2f agents-1.0.2 548028f131c9311f4ea7fab8be9c370afe663b11 agents-1.0.3-rc1 5ae70412eec8099b25e352110596dd279d267a8a agents-1.0.3 526023023aec9e26994be47a92ebd73cc15f7b54 agents-1.0.4-rc cluster-agents-1.0.4/README0000644000175000017500000000000011527003731015561 0ustar roaksoaxroaksoaxcluster-agents-1.0.4/.hgignore0000644000175000017500000000134411527003731016520 0ustar roaksoaxroaksoaxsyntax: glob # Autofoo entries *.o *.la *.lo *.loT *.pyc .libs .deps *.cache *.upgrade.xml .cvsignore compile configure configure.status configure.lineno depcomp aclocal.m4 libtool ltmain.sh ltconfig libltdl mkinstalldirs install-sh missing py-compile autom4te* libtool.m4 ltdl.m4 libltdl.tar autoconf autoheader automake ylwrap # BEAM Entries *.beam parser-messages MISC_ERRORS cscope.files cscope.out patches updates logs # OS and Editor Artifacts .DS_Store .bomb *.rej *.bz2 *.sed *.diff *.patch *.gres *~ # Misc HTML TAGS GPATH GRTAGS GSYMS GTAGS .gres.* *.orig .gdb_history *~ \#* .changes pacemaker.tar.gz # Entries better done as regexp's to avoid matching too broadly syntax: regexp ^config\.* README$ Makefile$ Makefile.in$ cluster-agents-1.0.4/.hg_archival.txt0000644000175000017500000000013611527003731020001 0ustar roaksoaxroaksoaxrepo: 77f0cb65be3733f5dd89f7a4c5f6172c877286d3 node: 8a469febdcca558b097bb731141ce6f19ae66507 cluster-agents-1.0.4/Makefile.am0000755000175000017500000000210011527003731016743 0ustar roaksoaxroaksoax# # Copyright (C) 2008 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure DRF/config-h.in \ DRF/stamp-h.in libtool.m4 ltdl.m4 libltdl.tar SUBDIRS = include heartbeat tools ldirectord doc install-exec-local: $(INSTALL) -d -m 1755 $(DESTDIR)$(HA_RSCTMPDIR) dist-clean-local: rm -f autoconf automake autoheader $(TARFILE) .PHONY: cluster-agents-1.0.4/COPYING0000644000175000017500000004310311527003731015747 0ustar roaksoaxroaksoax GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. cluster-agents-1.0.4/ChangeLog0000644000175000017500000003042411527003731016470 0ustar roaksoaxroaksoax* Wed Feb 16 2011 Dejan Muhamedagic and others - stable release 1.0.4 - ocft: testcases for db2, LVM, and Filesystem * Fri Feb 11 2011 Dejan Muhamedagic and others - release candidate 1.0.4 - add GPLv3 license file (bnc#655700) - ocf-shellfuncs: allow ocf_run to return the actual exit code - ocf-shellfuncs: handle properly syslog facility set to none (bnc#621818) - ocf-shellfuncs: correctly identify root by id only (bnc#602312) - RA: add OCF_ROOT/lib/heartbeat directory (development) - RA: set the HA_RSCTMP directory to /var/run/resource-agents (lf#2378) - build: install jboss - conntrackd: new RA - exportfs: new RA - nginx: new RA - fio: new RA for IO load simulation - Filesystem: allow cloning of some filesystems as read-only (lf#2440) - Filesystem: add fast_stop parameter (lf#2402) - Filesystem: Clarify metadata and improve non-clone warning - Filesystem: new run_fsck parameter - LVM: add partial_activation parameter (lf#2490) - IPaddr2: fix reference to Infiniband arping binary (bnc#668447) - IPaddr2: optionally flush kernel routing table on interface stop - IPaddr2: exit with the right code when not properly configured - IPaddr2: exit early and with the right code if the ip parameter is not set - IPaddr2: unique_clone_address should work without CIP (lf#2442) - IPaddr: return the correct code if interface delete failed - IPv6addr: allow link-local addresses in case the interface name is provided - IPv6addr: interface index in /proc/net/if_inet6 may be longer than 2 chars (lf#2462) - IPsrcaddr: exit with the right code when not properly configured - IPsrcaddr: add the cidr_netmask parameter - Tools: findif: differentiate between error conditions - nfsserver: fix the default string for the notification parameter - nfsserver: don't use -v in the notify cmd with rpc.statd - iSCSITarget: fix race for target IDs when using IET (lf#2432) - iSCSITarget: follow changed IET access policy - Raid1: Support attempting to re-add mirrors on deep monitor action (bnc#619121) - Raid1: Fix graceful stop code path - Raid1: Handle stop for failed arrays properly (bnc#618775) - sfex: output log messages also to stderr in sfex_init - sfex: add the sfex_stat command - sfex: wait in the start and stop actions until sfex_daemon starts/exits - Xen: implement stop of a migrating domain (bnc#656227) - Xen: check the allow_mem_management boolean properly (bnc#637525) - Xen: Always run destroy in stop sequence. - Xen: use xen-list command for status check if available (bnc#628735) - Xen: use xen-destroy for stop, if available. - Xen: Allow node configurable attribute to specify which IP to use for live migration (bnc#628735) - VirtualDomain: fix spurious stop failures - VirtualDomain: don't timeout in stop before escalating to "forced stop" - ManageVE: add migration capability - MailTo: don't check if user exists for email address (might be an alias or remote) - CTDB: Remove hard-coded timeout on start op - CTDB: Don't manage Samba and Winbind by default - CTDB: Deprecate (and make optional) smb_private_dir param (bnc#623788) - tomcat: Ensure name of tomcat resource is only used on start operation and expose JAVA_OPTS variable for use - tomcat: Fix to ensure default OCF_RESKEY_xx values are observed - tomcat: Add CATALINA_BASE parameter, defaults to CATALINA_HOME, permits multiple tomcat instances - tomcat: Use Tomcat stop TIMEOUT -force to improve stop - Dummy: migrate_from/to: correct OCF_RESKEY_CRM_meta_migrate_xxx variable names - Dummy: make method reload work - anything: add the workdir parameter - mysql: clone and master-slave functionality - mysql: add replication monitoring - mysql: check for write permissions after creating pid and socket directory - mysql: make client binary path configurable - pgsql: cd to pgdata before running commands (fixes permission error) - pgsql: add optional username, password, and sqlcode parameters for monitor - pgsql: add new "config" parameter - pgsql: properly implement pghost parameter - pgsql: socketdir parameter to manage non-default UNIX socket directories - oracle: reduce output from sqlplus to the last line for queries (bnc#567815) - db2: Replace call to db2_local_ps with db2nps - db2: guard against a hanging db2stop by spawning this into the background. Use db2_kill after grace period. - db2: add multi partition support - db2: improve behaviour on probes - db2: support for v9.x instances (bnc#608952) - SAPDatabase,SAPInstance: improve LD_LIBRARY_PATH processing (bnc#640026) - SAPInstance: prevent premature expansion of [:upper:] [:lower:] when producing sidadm uid - SAPInstance: Moved testing of SAP profile directory and START profile to a later stage (only when needed), for more robustness - SAPInstance: fix return codes in probes - SAPInstance: New parameter: SHUTDOWN_METHOD - SAPInstance: ensure enqueue failover in monitor_clone on process failure - SAPInstance: don't rely on op target rc when monitoring clones (lf#2371) - SAPDatabase: prevent premature expansion of [:upper:] and [:lower:] when producing sidadm/orasid/db2sid uids - SAPdatabase: Changed Oracle recovery method from "recover automatic database" to "end backup" - SAPDatabase: Adapt process search pattern for DB/2 9.5 - SAPDatabase: start listener only if database processes are found - SAPDatabase: avoid continuous output to syslog in monitor with SAP 7.20 and J2EE_ONLY=1 - ldirectord: http: connect to server instead of protocol (Debian#594958) - ldirectord: add implicit support for submission RFC4409 - ldirectord: example configuration for a submission virtual service - ldirectord: Shutdown write-side of client connection after writing has finished - ldirectord: port number mismatch of imaps and pops - ldirectord: Oracle compatibility - ldirectord: don't exit on timeout in HTTP/HTTPS check - ldirectord: allow underscore in service name - ldirectord: use $1 instead of \1 in pattern replace (bnc#605086) - Tools: ocf-tester: Extend to cover initial probe (monitor_0) test. - Tools: ocf-tester: set and export some common meta variables (lf#2524) - Tools: ocf-tester: meta-data also should never be affected by missing binaries. - Tools: ocf-tester: show output from the agent in case of error * Tue Apr 13 2010 Dejan Muhamedagic and others - stable release 1.0.3 - meta-data: improve timeouts in most resource agents (reduce the number of warnings by the shell) - RA: log messages to stderr if attached to a terminal - ocf-shellfuncs: tests to check for clone/ms resources - ocf-shellfuncs: don't output to stderr if using syslog (prevents double logging from the RA and lrmd) - make sure that OCF_RESKEY_CRM_meta_interval is always defined (lf#2284) - ocft: new RA test suite - VirtualDomain: bail out early if config file can't be read during probe (nbc#593988) - VirtualDomain: spin on define until we definitely have a domain name - VirtualDomain: fix incorrect use of __OCF_ACTION (the stop operation may timeout otherwise) - Filesystem: prefer /proc/mounts to /etc/mtab for non-bind mounts (lf#2388) - IPaddr2: don't bring the interface down on stop (otherwise IPv6 addresses may be removed) - oracle/oralsnr: improve exit codes if the environment isn't valid - oracle/oralsnr: improve logging - Route: don't assume that OCF_RESKEY_CRM_meta_clone_node_max is set to a number (lf#2375) - Route: add route table parameter (lf#2335) - sfex: don't use pid file (lf#2363,bnc#585416) - SFEX daemon: fix logging - ldirectord: fix the configfile default (bnc#589457) - drbd: fix metadata (bnc#588684) - IPsrcaddr: modify the interface route (lf#2367) - ldirectord: Allow multiple email addresses (lf#2168) - vmware: fix set_environment() invocation (lf#2342) - vmware: updated to version 0.2 - apache: return the right exit code from monitor (bnc#578628) - iSCSILogicalUnit: fix monitor for STGT * Mon Feb 01 2010 Dejan Muhamedagic and others - stable release 1.0.2 - EvmsSCC, Evmsd, LinuxSCSI, drbd, pingd: marked as deprecated (lf#2244) - CTDB: new resource agent for clustered samba - postfix: new resource agent - proftpd: new resource agent - AoEtarget: new resource agent to export ATA-over-Ethernet (AoE) targets - Squid: new resource agent - VirtualDomain: new resource agent (manage virtual domains using libvirt/virsh) - anything: new resource agent for arbitrary daemons - mysql-proxy: new resource agent - iSCSITarget/iSCSILogicalUnit: two new resource agents - portblock: fast reconnect/tickle ACK (new feature) - IPv6addr: new nic and cidr_netmask parameters - mysql-proxy: log_level and keepalive parameters - Filesystem: implement deep monitor operation - apache: monitor operation of depth 10 for web applications (lf#2234) - SAPDatabase + SAPInstance: New versions from SAP - CTDB: auto-generate cluster-specific part of smb.conf (lf#2308) - ClusterMon: don't fail in stop if the process is missing (bnc#569957) - Filesystem: allow configuring smbfs mounts as clones - IPaddr2: CLUSTERIP/iptables rule not always inserted on failed monitor (lf#2281) - IPaddr2: behave if the interface is down (lf#2147) - IPaddr2: check binaries when it makes sense - IPaddr2: fix invalid default value for OCF_RESKEY_clusterip_hash (bnc#553753) - IPaddr2: include netmask in search for the right interface - IPaddr2: remove all colons from the mac address before passing it to send_arp (lf#2165) - IPsrcaddr: replace 0/0 with proper ip prefix - IPv6addr: recognize network masks properly - IPv6addr: supply checksum for ICMPv6 packets - IPv6addr: ifdef out the ip offset hack for libnet v1.1.4 (lf#2034) - IPv6addr: supply checksum for ICMPv6 packets - LVM: Make monitor operation quiet in logs (bnc#546353) - MailTo: Provide a default for MAILCMD (bnc#534803, bnc#556366) - MailTo: allow multiple word subject line - Raid1: improve monitor function (bnc#546551) - Route: improve validate (lf#2232) - Squid: make the regexp match more precisely output of netstat - VIParip: Pathname needed to be configurable (lf#1331) - VirtualDomain: avoid needlessly invoking "virsh define" - VirtualDomain: destroy domain shortly before timeout expiry - VirtualDomain: fix forceful stop (lf#2283) - VirtualDomain: loop on status if libvirtd is unreachable - Xen: Remove instance_attribute "allow_migrate" (bnc#539968) - apache: make sure that proxies are not used for monitor - iSCSILogicalUnit: add support for SCSI ID, SCSI SN, Vendor ID, and Product ID - iSCSILogicalUnit: add support for per-LU parameters - iSCSILogicalUnit: set default for SCSI SN, truncate SCSI ID default to 24 bytes - iSCSILogicalUnit: use a 16-byte default SCSI ID - iSCSITarget, iSCSILogicalUnit: add support for tgt - iSCSITarget: reintroduce "tid" parameter - iSCSITarget, iSCSILogicalUnit: identify targets by IQN, not by tid - iSCSITarget, iSCSILogicalUnit: support LIO - iSCSITarget: add support for CHAP authentication - iSCSITarget: add support for restricting target access - iSCSITarget: be more persistent deleting targets on stop - include ldirectord (formerly known as heartbeat-ldirectord) - iscsi: replace wrong variable reference (bnc#499291) - jboss: Added JBoss support - ldirectord: fix setting defaults for configfile and ldirectord (lf#2328) - ldirectord: fix various bugs in OCF RA (lf#1949) - mysql: escalate stop to KILL if regular shutdown doesn't work - mysql: handle monitor and stop properly on invalid environment - nfsserver: use default values (lf#2321) - nfsserver: validate should not check if nfs_shared_infodir exists (lf#2219) - nfsserver: use check_binary properly in validate (lf#2211) - nfsserver: exit properly in nfsserver_validate (lf#2173) - oracle/oralsnr: export variables properly - oracle: drop spurious output from sqlplus - pgsql: remove the previous backup_label if it exists - portblock: add per-IP filtering capability - portblock: fix invalid exit codes on monitor - postfix: fix double stop - scsi2reservation: fix wrong logic in check for scsi_reserve - vmware: make meta-data work and several cleanups (lf#2212) - shellfuncs: make the mktemp wrappers work - ocf-shellfuncs: add mercurial repository version information - ocf-shellfuncs: add ocf_is_probe function - doc: add resource agents' man pages including examples * Thu Oct 23 2008 Lars Marowsky-Bree and MANY others - beta release 2.99.2 - LVM: stop correctly in case vol group does not exist * Tue Sep 23 2008 Lars Marowsky-Bree and MANY others - beta release 2.99.1 * Tue Aug 19 2008 Andrew Beekhof and MANY others - beta release 2.99.0 cluster-agents-1.0.4/heartbeat/0000755000175000017500000000000011527051501016650 5ustar roaksoaxroaksoaxcluster-agents-1.0.4/heartbeat/conntrackd0000755000175000017500000002276411527003731020741 0ustar roaksoaxroaksoax#!/bin/bash # # # An OCF RA for conntrackd # http://conntrack-tools.netfilter.org/ # # Copyright (c) 2011 Dominik Klein # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # ####################################################################### # Initialization: : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs ####################################################################### OCF_RESKEY_binary_default=conntrackd OCF_RESKEY_config_default=/etc/conntrackd/conntrackd.conf : ${OCF_RESKEY_binary=${OCF_RESKEY_binary_default}} : ${OCF_RESKEY_config=${OCF_RESKEY_config_default}} meta_data() { cat < 1.1 Master/Slave OCF Resource Agent for conntrackd This resource agent manages conntrackd Name of the conntrackd executable. If conntrackd is installed and available in the default PATH, it is sufficient to configure the name of the binary For example "my-conntrackd-binary-version-0.9.14" If conntrackd is installed somewhere else, you may also give a full path For example "/packages/conntrackd-0.9.14/sbin/conntrackd" Name of the conntrackd executable Full path to the conntrackd.conf file. For example "/packages/conntrackd-0.9.14/etc/conntrackd/conntrackd.conf" Path to conntrackd.conf END } meta_expect() { local what=$1 whatvar=OCF_RESKEY_CRM_meta_${1//-/_} op=$2 expect=$3 local val=${!whatvar} if [[ -n $val ]]; then # [, not [[, or it won't work ;) [ $val $op $expect ] && return fi ocf_log err "meta parameter misconfigured, expected $what $op $expect, but found ${val:-unset}." exit $OCF_ERR_CONFIGURED } conntrackd_is_master() { # You can't query conntrackd whether it is master or slave. It can be both at the same time. # This RA creates a statefile during promote and enforces master-max=1 and clone-node-max=1 ha_pseudo_resource $statefile monitor } conntrackd_set_master_score() { ${HA_SBIN_DIR}/crm_master -Q -l reboot -v $1 } conntrackd_monitor() { rc=$OCF_NOT_RUNNING # It does not write a PID file, so check with pgrep pgrep -f $OCF_RESKEY_binary && rc=$OCF_SUCCESS if [ "$rc" -eq "$OCF_SUCCESS" ]; then # conntrackd is running # now see if it acceppts queries if ! $OCF_RESKEY_binary -C $OCF_RESKEY_config -s > /dev/null 2>&1; then rc=$OCF_ERR_GENERIC ocf_log err "conntrackd is running but not responding to queries" fi if conntrackd_is_master; then rc=$OCF_RUNNING_MASTER # Restore master setting on probes if [ $OCF_RESKEY_CRM_meta_interval -eq 0 ]; then conntrackd_set_master_score $master_score fi else # Restore master setting on probes if [ $OCF_RESKEY_CRM_meta_interval -eq 0 ]; then conntrackd_set_master_score $slave_score fi fi fi return $rc } conntrackd_start() { rc=$OCF_ERR_GENERIC # Keep trying to start the resource; # wait for the CRM to time us out if this fails while :; do conntrackd_monitor status=$? case "$status" in $OCF_SUCCESS) rc=$OCF_SUCCESS conntrackd_set_master_score $slave_score break ;; $OCF_NOT_RUNNING) ocf_log info "Starting conntrackd" $OCF_RESKEY_binary -C $OCF_RESKEY_config -d ;; $OCF_RUNNING_MASTER) ocf_log warn "conntrackd already in master mode, demoting." ha_pseudo_resource $statefile stop ;; $OCF_ERR_GENERIC) ocf_log err "conntrackd start failed" rc=$OCF_ERR_GENERIC break ;; esac done return $rc } conntrackd_stop() { rc=$OCF_ERR_GENERIC # Keep trying to bring down the resource; # wait for the CRM to time us out if this fails while :; do conntrackd_monitor status=$? case "$status" in $OCF_SUCCESS) ocf_log info "Stopping conntrackd" $OCF_RESKEY_binary -C $OCF_RESKEY_config -k ;; $OCF_NOT_RUNNING) rc=$OCF_SUCCESS break ;; $OCF_RUNNING_MASTER) ocf_log warn "conntrackd still master" ;; esac done return $rc } conntrackd_validate_all() { check_binary "$OCF_RESKEY_binary" if ! [ -e "$OCF_RESKEY_config" ]; then ocf_log err "Config FILE $OCF_RESKEY_config does not exist" return $OCF_ERR_INSTALLED fi meta_expect master-node-max = 1 meta_expect master-max = 1 meta_expect clone-node-max = 1 meta_expect clone-max = 2 return $OCF_SUCCESS } conntrackd_promote() { rc=$OCF_SUCCESS if ! conntrackd_is_master; then # -c = Commit the external cache to the kernel # -f = Flush internal and external cache # -R = resync with the kernel table # -B = send a bulk update on the line for parm in c f R B; do if ! $OCF_RESKEY_binary -C $OCF_RESKEY_config -$parm; then ocf_log err "$OCF_RESKEY_binary -C $OCF_RESKEY_config -$parm failed during promote." rc=$OCF_ERR_GENERIC break fi done ha_pseudo_resource $statefile start conntrackd_set_master_score $master_score fi return $rc } conntrackd_demote() { rc=$OCF_SUCCESS if conntrackd_is_master; then # -t = shorten kernel timers to remove zombies # -n = request a resync from the others for parm in t n; do if ! $OCF_RESKEY_binary -C $OCF_RESKEY_config -$parm; then ocf_log err "$OCF_RESKEY_binary -C $OCF_RESKEY_config -$parm failed during demote." rc=$OCF_ERR_GENERIC break fi done ha_pseudo_resource $statefile stop conntrackd_set_master_score $slave_score fi return $rc } conntrackd_notify() { hostname=$(hostname) # OCF_RESKEY_CRM_meta_notify_master_uname is a whitespace separated list of master hostnames for master in $OCF_RESKEY_CRM_meta_notify_master_uname; do # if we are the master and an instance was just started on another node: # send a bulk update to allow failback if [ "$hostname" = "$master" -a "$OCF_RESKEY_CRM_meta_notify_type" = "post" -a "$OCF_RESKEY_CRM_meta_notify_operation" = "start" -a "$OCF_RESKEY_CRM_meta_notify_start_uname" != "$hostname" ]; then ocf_log info "Sending bulk update in post start to peers to allow failback" $OCF_RESKEY_binary -C $OCF_RESKEY_config -B fi done for tobepromoted in $OCF_RESKEY_CRM_meta_notify_promote_uname; do # if there is a promote action to be executed on another node: # send a bulk update to allow failback if [ "$hostname" != "$tobepromoted" -a "$OCF_RESKEY_CRM_meta_notify_type" = "pre" -a "$OCF_RESKEY_CRM_meta_notify_operation" = "promote" ]; then ocf_log info "Sending bulk update in pre promote to peers to allow failback" $OCF_RESKEY_binary -C $OCF_RESKEY_config -B fi done } conntrackd_usage() { cat < 1.0 Resource script for Filesystem. It manages a Filesystem on a shared storage medium. The standard monitor operation of depth 0 (also known as probe) checks if the filesystem is mounted. If you want deeper tests, set OCF_CHECK_LEVEL to one of the following values: 10: read first 16 blocks of the device (raw read) This doesn't exercise the filesystem at all, but the device on which the filesystem lives. This is noop for non-block devices such as NFS, SMBFS, or bind mounts. 20: test if a status file can be written and read The status file must be writable by root. This is not always the case with an NFS mount, as NFS exports usually have the "root_squash" option set. In such a setup, you must either use read-only monitoring (depth=10), export with "no_root_squash" on your NFS server, or grant world write permissions on the directory where the status file is to be placed. Manages filesystem mounts The name of block device for the filesystem, or -U, -L options for mount, or NFS mount specification. block device The mount point for the filesystem. mount point The type of filesystem to be mounted. filesystem type Any extra options to be given as -o options to mount. For bind mounts, add "bind" here and set fstype to "none". We will do the right thing for options such as "bind,ro". options The prefix to be used for a status file for resource monitoring with depth 20. If you don't specify this parameter, all status files will be created in a separate directory. status file prefix Specify how to decide whether to run fsck or not. "auto" : decide to run fsck depending on the fstype(default) "force" : always run fsck regardless of the fstype "no" : do not run fsck ever. run_fsck Normally, we expect no users of the filesystem and the stop operation to finish quickly. If you cannot control the filesystem users easily and want to prevent the stop action from failing, then set this parameter to "no" and add an appropriate timeout for the stop operation. fast stop END } # # Make sure the kernel does the right thing with the FS buffers # This function should be called after unmounting and before mounting # It may not be necessary in 2.4 and later kernels, but it shouldn't hurt # anything either... # # It's really a bug that you have to do this at all... # flushbufs() { if have_binary $BLOCKDEV ; then if [ "$blockdevice" = "yes" ] ; then $BLOCKDEV --flushbufs $1 return $? fi fi return 0 } # Take advantage of /etc/mtab if present, use portable mount command # otherwise. Normalize format to "dev mountpoint fstype". is_bind_mount() { echo "$options" | grep -w bind >/dev/null 2>&1 } list_mounts() { local inpf="" if [ -e "/proc/mounts" ] && ! is_bind_mount; then inpf=/proc/mounts elif [ -f "/etc/mtab" -a -r "/etc/mtab" ]; then inpf=/etc/mtab fi if [ "$inpf" ]; then cut -d' ' -f1,2,3 < $inpf else $MOUNT | cut -d' ' -f1,3,5 fi } determine_blockdevice() { if [ $blockdevice = "yes" ]; then return fi # Get the current real device name, if possible. # (specified devname could be -L or -U...) case "$FSTYPE" in nfs|smbfs|cifs|none) ;; *) DEVICE=`list_mounts | grep " $MOUNTPOINT " | cut -d' ' -f1` if [ -b "$DEVICE" ]; then blockdevice=yes fi ;; esac } # Lists all filesystems potentially mounted under a given path, # excluding the path itself. list_submounts() { list_mounts | grep " $1/" | cut -d' ' -f2 | sort -r } ocfs2_del_cache() { if [ -e "$_OCFS2_uuid_cache" ]; then rm -f $_OCFS2_uuid_cache fi } ocfs2_cleanup() { # We'll never see the post-stop notification. We're gone now, # have unmounted, and thus should remove the membership. # # (Do so regardless of whether we were unmounted already, # because the admin might have manually unmounted but not # cleared up the membership directory. Bad admin, no cookie.) # if [ ! -d "$OCFS2_FS_ROOT" ]; then ocf_log info "$OCFS2_FS_ROOT: Filesystem membership already gone." else ocf_log info "$OCFS2_FS_ROOT: Removing membership directory." rm -rf $OCFS2_FS_ROOT/ fi ocfs2_del_cache } ocfs2_fetch_uuid() { mounted.ocfs2 -d $DEVICE|tail -1|awk '{print $3}'|tr -d -- -|tr '[a-z]' '[A-Z]' } ocfs2_set_uuid() { _OCFS2_uuid_cache="$HA_RSCTMP/Filesystem.ocfs2_uuid.$(echo $DEVICE|tr / .)" if [ "$OP" != "start" -a -e "$_OCFS2_uuid_cache" ]; then # Trust the cache. OCFS2_UUID=$(cat $_OCFS2_uuid_cache 2>/dev/null) return 0 fi OCFS2_UUID=$(ocfs2_fetch_uuid) if [ -n "$OCFS2_UUID" -a "$OCFS2_UUID" != "UUID" ]; then # UUID valid: echo $OCFS2_UUID > $_OCFS2_uuid_cache return 0 fi # Ok, no UUID still, but that's alright for stop, because it # very likely means we never got started - if [ "$OP" = "stop" ]; then ocf_log warn "$DEVICE: No UUID; assuming never started!" OCFS2_UUID="UUID_NOT_SET" return 0 fi # Everything else - wrong: ocf_log err "$DEVICE: Could not determine ocfs2 UUID for device." exit $OCF_ERR_GENERIC } ocfs2_init() { # Check & initialize the OCFS2 specific variables. # This check detects whether the special/legacy hooks to # integrate OCFS2 with user-space clustering on SLES10 need to # be activated. # Newer kernels >= 2.6.28, with OCFS2+openAIS+Pacemaker, do # not need this: OCFS2_SLES10="" if [ "X$HA_cluster_type" = "Xcman" ]; then return elif [ "X$HA_cluster_type" != "Xopenais" ]; then if grep -q "SUSE Linux Enterprise Server 10" /etc/SuSE-release >/dev/null 2>&1 ; then OCFS2_SLES10="yes" ocf_log info "$DEVICE: Enabling SLES10 compatibility mode for OCFS2." else ocf_log err "$DEVICE: ocfs2 is not compatible with your environment." exit $OCF_ERR_CONFIGURED fi else return fi if [ $OP != "stop" ]; then if [ -z "$OCF_RESKEY_CRM_meta_clone" ]; then ocf_log err "ocfs2 must be run as a clone." exit $OCF_ERR_GENERIC fi fi if [ $blockdevice = "no" ]; then ocf_log err "$DEVICE: ocfs2 needs a block device instead." exit $OCF_ERR_GENERIC fi for f in "$OCF_RESKEY_ocfs2_configfs" /sys/kernel/config/cluster /configfs/cluster ; do if [ -n "$f" -a -d "$f" ]; then OCFS2_CONFIGFS="$f" break fi done if [ ! -d "$OCFS2_CONFIGFS" ]; then ocf_log err "ocfs2 needs configfs mounted." exit $OCF_ERR_GENERIC fi ocfs2_set_uuid if [ -n "$OCF_RESKEY_ocfs2_cluster" ]; then OCFS2_CLUSTER=$(echo $OCF_RESKEY_ocfs2_cluster) else OCFS2_CLUSTER=$(find "$OCFS2_CONFIGFS" -maxdepth 1 -mindepth 1 -type d -printf %f 2>/dev/null) set -- $OCFS2_CLUSTER local n; n="$#" if [ $n -gt 1 ]; then ocf_log err "$OCFS2_CLUSTER: several clusters found." exit $OCF_ERR_GENERIC fi if [ $n -eq 0 ]; then ocf_log err "$OCFS2_CONFIGFS: no clusters found." exit $OCF_ERR_GENERIC fi fi OCFS2_CLUSTER_ROOT="$OCFS2_CONFIGFS/$OCFS2_CLUSTER" if [ ! -d "$OCFS2_CLUSTER_ROOT" ]; then ocf_log err "$OCFS2_CLUSTER: Cluster doesn't exist. Maybe o2cb hasn't been run?" exit $OCF_ERR_GENERIC fi OCFS2_FS_ROOT=$OCFS2_CLUSTER_ROOT/heartbeat/$OCFS2_UUID } # kernels < 2.6.26 can't handle bind remounts bind_kernel_check() { echo "$options" | grep -w ro >/dev/null 2>&1 || return uname -r | awk -F. ' $1==2 && $2==6 { sub("[^0-9].*","",$3); if ($3<26) exit(1); }' [ $? -ne 0 ] && ocf_log warn "kernel `uname -r` cannot handle read only bind mounts" } bind_mount() { if is_bind_mount && [ "$options" != "-o bind" ] then bind_kernel_check bind_opts=`echo $options | sed 's/bind/remount/'` $MOUNT $bind_opts $MOUNTPOINT else true # make sure to return OK fi } is_option() { echo $OCF_RESKEY_options | grep -w "$1" >/dev/null 2>&1 } is_fsck_needed() { case $OCF_RESKEY_run_fsck in force) true;; no) false;; ""|auto) case $FSTYPE in ext4|ext4dev|ext3|reiserfs|reiser4|nss|xfs|jfs|vfat|fat|nfs|cifs|smbfs|ocfs2|gfs2|none|lustre) false;; *) true;; esac;; *) ocf_log warn "Invalid parameter value for fsck: '$OCF_RESKEY_run_fsck'; setting to 'auto'" OCF_RESKEY_run_fsck="auto" is_fsck_needed;; esac } # # START: Start up the filesystem # Filesystem_start() { if [ -n "$OCFS2_SLES10" ]; then # "start" now has the notification data available; that # we're being started means we didn't get the # pre-notification, because we weren't running, so # process the information now first. ocf_log info "$OCFS2_UUID: Faking pre-notification on start." OCF_RESKEY_CRM_meta_notify_type="pre" OCF_RESKEY_CRM_meta_notify_operation="start" Filesystem_notify fi # See if the device is already mounted. if Filesystem_status >/dev/null 2>&1 ; then ocf_log info "Filesystem $MOUNTPOINT is already mounted." return $OCF_SUCCESS fi if [ "X${HOSTOS}" != "XOpenBSD" ];then # Insert SCSI module # TODO: This probably should go away. Why should the filesystem # RA magically load a kernel module? $MODPROBE scsi_hostadapter >/dev/null if [ -z "$FSTYPE" -o "$FSTYPE" = none ]; then : No FSTYPE specified, rely on the system has the right file-system support already else # Insert Filesystem module $MODPROBE $FSTYPE >/dev/null grep -e "$FSTYPE"'$' /proc/filesystems >/dev/null if [ $? -ne 0 ] ; then ocf_log err "Couldn't find filesystem $FSTYPE in /proc/filesystems" return $OCF_ERR_INSTALLED fi fi fi # Check the filesystem & auto repair. # NOTE: Some filesystem types don't need this step... Please modify # accordingly if [ $blockdevice = "yes" ]; then if [ "$DEVICE" != "/dev/null" -a ! -b "$DEVICE" ] ; then ocf_log err "Couldn't find device [$DEVICE]. Expected /dev/??? to exist" exit $OCF_ERR_INSTALLED fi if is_fsck_needed; then ocf_log info "Starting filesystem check on $DEVICE" if [ -z "$FSTYPE" ]; then $FSCK -p $DEVICE else $FSCK -t $FSTYPE -p $DEVICE fi # NOTE: if any errors at all are detected, it returns non-zero # if the error is >= 4 then there is a big problem if [ $? -ge 4 ]; then ocf_log err "Couldn't sucessfully fsck filesystem for $DEVICE" return $OCF_ERR_GENERIC fi fi fi if [ ! -d "$MOUNTPOINT" ] ; then ocf_log err "Couldn't find directory [$MOUNTPOINT] to use as a mount point" exit $OCF_ERR_INSTALLED fi flushbufs $DEVICE # Mount the filesystem. case "$FSTYPE" in none) $MOUNT $options $DEVICE $MOUNTPOINT && bind_mount ;; "") $MOUNT $options $DEVICE $MOUNTPOINT ;; *) $MOUNT -t $FSTYPE $options $DEVICE $MOUNTPOINT ;; esac if [ $? -ne 0 ]; then ocf_log err "Couldn't mount filesystem $DEVICE on $MOUNTPOINT" if [ -n "$OCFS2_SLES10" ]; then ocfs2_cleanup fi return $OCF_ERR_GENERIC fi return $OCF_SUCCESS } # end of Filesystem_start Filesystem_notify() { # Process notifications; this is the essential glue level for # giving user-space membership events to a cluster-aware # filesystem. Right now, only OCFS2 is supported. # # When we get a pre-start notification, we set up all the nodes # which will be active in our membership for the filesystem. # (For the resource to be started, this happens at the time of # the actual 'start' operation.) # # At a post-start, actually there's nothing to do for us really, # but no harm done in re-syncing either. # # pre-stop is meaningless; we can't remove any node yet, it # first needs to unmount. # # post-stop: the node is removed from the membership of the # other nodes. # # Note that this expects that the base cluster is already # active; ie o2cb has been started and populated # $OCFS2_CLUSTER_ROOT/node/ already. This can be achieved by # simply having o2cb run on all nodes by the CRM too. This # probably ought to be mentioned somewhere in the to be written # documentation. ;-) # if [ -z "$OCFS2_SLES10" ]; then # One of the cases which shouldn't occur; it should have # been caught much earlier. Still, you know ... ocf_log err "$DEVICE: Please only enable notifications for SLES10 OCFS2 mounts." # Yes, in theory this is a configuration error, but # simply discarding them allows users to switch from the # SLES10 stack to the new one w/o downtime. # Ignoring the notifications is harmless, afterall, and # they can simply disable them in their own time. return $OCF_SUCCESS fi local n_type; n_type="$OCF_RESKEY_CRM_meta_notify_type" local n_op; n_op="$OCF_RESKEY_CRM_meta_notify_operation" local n_active; n_active="$OCF_RESKEY_CRM_meta_notify_active_uname" local n_stop; n_stop="$OCF_RESKEY_CRM_meta_notify_stop_uname" local n_start; n_start="$OCF_RESKEY_CRM_meta_notify_start_uname" ocf_log info "$OCFS2_UUID: notify: $n_type for $n_op" ocf_log info "$OCFS2_UUID: notify active: $n_active" ocf_log info "$OCFS2_UUID: notify stop: $n_stop" ocf_log info "$OCFS2_UUID: notify start: $n_start" case "$n_type" in pre) case "$n_op" in stop) ocf_log info "$OCFS2_UUID: ignoring pre-notify for stop." return $OCF_SUCCESS ;; start) # These are about to become active; prepare to # communicate with them. # Duplicate removal - start can contain nodes # already on the active list, confusing the # script later on: for UNAME in $n_active; do n_start=`echo ${n_start} | sed s/$UNAME//` done # Merge pruned lists again: n_active="$n_active $n_start" ;; esac ;; post) case "$n_op" in stop) # remove unames from notify_stop_uname; these have been # stopped and can no longer be considered active. for UNAME in $n_stop; do n_active=`echo ${n_active} | sed s/$UNAME//` done ;; start) if [ "$n_op" = "start" ]; then ocf_log info "$OCFS2_UUID: ignoring post-notify for start." return $OCF_SUCCESS fi ;; esac ;; esac ocf_log info "$OCFS2_UUID: post-processed active: $n_active" local n_myself; n_myself=${HA_CURHOST:-$(uname -n | tr '[A-Z]' '[a-z]')} ocf_log info "$OCFS2_UUID: I am node $n_myself." case " $n_active " in *" $n_myself "*) ;; *) ocf_log err "$OCFS2_UUID: $n_myself (local) not on active list!" return $OCF_ERR_GENERIC ;; esac if [ -d "$OCFS2_FS_ROOT" ]; then entry_prefix=$OCFS2_FS_ROOT/ for entry in $OCFS2_FS_ROOT/* ; do n_fs="${entry##$entry_prefix}" # ocf_log info "$OCFS2_UUID: Found current node $n_fs" case " $n_active " in *" $n_fs "*) # Construct a list of nodes which are present # already in the membership. n_exists="$n_exists $n_fs" ocf_log info "$OCFS2_UUID: Keeping node: $n_fs" ;; *) # Node is in the membership currently, but not on our # active list. Must be removed. if [ "$n_op" = "start" ]; then ocf_log warn "$OCFS2_UUID: Removing nodes on start" fi ocf_log info "$OCFS2_UUID: Removing dead node: $n_fs" if ! rm -f $entry ; then ocf_log err "$OCFS2_UUID: Removal of $n_fs failed!" fi ;; esac done else ocf_log info "$OCFS2_UUID: heartbeat directory doesn't exist yet, creating." mkdir -p $OCFS2_FS_ROOT fi ocf_log info "$OCFS2_UUID: Existing node list: $n_exists" # (2) for entry in $n_active ; do # ocf_log info "$OCFS2_UUID: Expected active node: $entry" case " $n_exists " in *" $entry "*) ocf_log info "$OCFS2_UUID: Already active: $entry" ;; *) if [ "$n_op" = "stop" ]; then ocf_log warn "$OCFS2_UUID: Adding nodes on stop" fi ocf_log info "$OCFS2_UUID: Activating node: $entry" if ! ln -s $OCFS2_CLUSTER_ROOT/node/$entry $OCFS2_FS_ROOT/$entry ; then ocf_log err "$OCFS2_CLUSTER_ROOT/node/$entry: failed to link" fi ;; esac done } signal_processes() { local dir=$1 local sig=$2 # fuser returns a non-zero return code if none of the # specified files is accessed or in case of a fatal # error. if [ "X${HOSTOS}" = "XOpenBSD" ];then PIDS=`fstat | grep $dir | awk '{print $3}'` for PID in ${PIDS};do kill -s $sig ${PID} ocf_log info "Sent signal $sig to ${PID}" done else if $FUSER -$sig -m -k $dir ; then ocf_log info "Some processes on $dir were signalled" else ocf_log info "No processes on $dir were signalled" fi fi } try_umount() { local SUB=$1 $UMOUNT $umount_force $SUB list_mounts | grep -q " $SUB " >/dev/null 2>&1 || { ocf_log info "unmounted $SUB successfully" return $OCF_SUCCESS } return $OCF_ERR_GENERIC } fs_stop() { local SUB=$1 timeout=$2 sig cnt for sig in TERM KILL; do cnt=$((timeout/2)) # try half time with TERM while [ $cnt -gt 0 ]; do try_umount $SUB && return $OCF_SUCCESS ocf_log err "Couldn't unmount $SUB; trying cleanup with $sig" signal_processes $SUB $sig cnt=$((cnt-1)) sleep 1 done done return $OCF_ERR_GENERIC } # # STOP: Unmount the filesystem # Filesystem_stop() { # See if the device is currently mounted Filesystem_status >/dev/null 2>&1 if [ $? -eq $OCF_NOT_RUNNING ]; then # Already unmounted, wonderful. rc=$OCF_SUCCESS else # Wipe the status file, but continue with a warning if # removal fails -- the file system might be read only if [ -f "$STATUSFILE" ]; then rm -f ${STATUSFILE} if [ $? -ne 0 ]; then ocf_log warn "Failed to remove status file ${STATUSFILE}." fi fi # Determine the real blockdevice this is mounted on (if # possible) prior to unmounting. determine_blockdevice # For networked filesystems, there's merit in trying -f: case "$FSTYPE" in nfs|cifs|smbfs) umount_force="-f" ;; esac # Umount all sub-filesystems mounted under $MOUNTPOINT/ too. local timeout for SUB in `list_submounts $MOUNTPOINT` $MOUNTPOINT; do ocf_log info "Trying to unmount $SUB" if ocf_is_true "$FAST_STOP"; then timeout=6 else timeout=${OCF_RESKEY_CRM_meta_timeout:="20000"} timeout=$((timeout/1000)) fi fs_stop $SUB $timeout rc=$? if [ $rc -ne $OCF_SUCCESS ]; then ocf_log err "Couldn't unmount $SUB, giving up!" fi done fi flushbufs $DEVICE # Yes I know the next blob is ugly, sorry. if [ $rc -eq $OCF_SUCCESS ]; then if [ "$FSTYPE" = "ocfs2" ]; then ocfs2_init if [ -n "$OCFS2_SLES10" ]; then ocfs2_cleanup fi fi fi return $rc } # end of Filesystem_stop # # STATUS: is the filesystem mounted or not? # Filesystem_status() { if list_mounts | grep -q " $MOUNTPOINT " >/dev/null 2>&1; then rc=$OCF_SUCCESS msg="$MOUNTPOINT is mounted (running)" else rc=$OCF_NOT_RUNNING msg="$MOUNTPOINT is unmounted (stopped)" fi # TODO: For ocfs2, or other cluster filesystems, should we be # checking connectivity to other nodes here, or the IO path to # the storage? # Special case "monitor" to check whether the UUID cached and # on-disk still match? case "$OP" in status) ocf_log info "$msg";; esac return $rc } # end of Filesystem_status # Note: the read/write tests below will stall in case the # underlying block device (or in the case of a NAS mount, the # NAS server) has gone away. In that case, if I/O does not # return to normal in time, the operation hits its timeout # and it is up to the CRM to initiate appropriate recovery # actions (such as fencing the node). # # MONITOR 10: read the device # Filesystem_monitor_10() { if [ "$blockdevice" = "no" ] ; then ocf_log warn "$DEVICE is not a block device, monitor 10 is noop" return $OCF_SUCCESS fi dd_opts="iflag=direct bs=512 count=16" err_output=`dd if=$DEVICE $dd_opts 2>&1 >/dev/null` if [ $? -ne 0 ]; then ocf_log err "Failed to read device $DEVICE" ocf_log err "dd said: $err_output" return $OCF_ERR_GENERIC fi return $OCF_SUCCESS } # # MONITOR 20: write and read a status file # Filesystem_monitor_20() { # Writing to the device in O_DIRECT mode is imperative # to bypass caches. dd_opts="oflag=direct,sync bs=512 conv=fsync,sync" status_dir=`dirname $STATUSFILE` [ -d "$status_dir" ] || mkdir -p "$status_dir" err_output=` echo "${OCF_RESOURCE_INSTANCE}" | dd of=${STATUSFILE} $dd_opts 2>&1` if [ $? -ne 0 ]; then ocf_log err "Failed to write status file ${STATUSFILE}" ocf_log err "dd said: $err_output" return $OCF_ERR_GENERIC fi test -f ${STATUSFILE} if [ $? -ne 0 ]; then ocf_log err "Cannot stat the status file ${STATUSFILE}" return $OCF_ERR_GENERIC fi cat ${STATUSFILE} > /dev/null if [ $? -ne 0 ]; then ocf_log err "Cannot read the status file ${STATUSFILE}" return $OCF_ERR_GENERIC fi return $OCF_SUCCESS } Filesystem_monitor() { Filesystem_status rc=$? if [ $rc -ne $OCF_SUCCESS ]; then return $rc fi if [ $rc -eq $OCF_SUCCESS -a $OCF_CHECK_LEVEL -gt 0 ]; then case "$OCF_CHECK_LEVEL" in 10) Filesystem_monitor_10; rc=$?;; 20) Filesystem_monitor_20; rc=$?;; *) ocf_log err "unsupported monitor level $OCF_CHECK_LEVEL" rc=$OCF_ERR_CONFIGURED ;; esac fi return $rc } # end of Filesystem_monitor # # VALIDATE_ALL: Are the instance parameters valid? # FIXME!! The only part that's useful is the return code. # This code always returns $OCF_SUCCESS (!) # Filesystem_validate_all() { if [ -n $MOUNTPOINT -a ! -d $MOUNTPOINT ]; then ocf_log warn "Mountpoint $MOUNTPOINT does not exist" fi # Check if the $FSTYPE is workable # NOTE: Without inserting the $FSTYPE module, this step may be imprecise # TODO: This is Linux specific crap. if [ ! -z "$FSTYPE" -a "$FSTYPE" != none ]; then cut -f2 /proc/filesystems |grep -q ^$FSTYPE$ if [ $? -ne 0 ]; then modpath=/lib/modules/`uname -r` moddep=$modpath/modules.dep # Do we have $FSTYPE in modules.dep? cut -d' ' -f1 $moddep |grep -q "^$modpath.*$FSTYPE\.k\?o:$" if [ $? -ne 0 ]; then ocf_log info "It seems we do not have $FSTYPE support" fi fi fi # If we are supposed to do monitoring with status files, then # we need a utility to write in O_DIRECT mode. if [ $OCF_CHECK_LEVEL -gt 0 ]; then check_binary dd # Note: really old coreutils version do not support # the "oflag" option for dd. We don't check for that # here. In case dd does not support oflag, monitor is # bound to fail, with dd spewing an error message to # the logs. On such systems, we must do without status # file monitoring. fi #TODO: How to check the $options ? return $OCF_SUCCESS } # Check the arguments passed to this script if [ $# -ne 1 ]; then usage exit $OCF_ERR_ARGS fi # Check the OCF_RESKEY_ environment variables... DEVICE=$OCF_RESKEY_device FSTYPE=$OCF_RESKEY_fstype if [ ! -z "$OCF_RESKEY_options" ]; then options="-o $OCF_RESKEY_options" fi FAST_STOP={$OCF_RESKEY_fast_stop:="yes"} OP=$1 # These operations do not require instance parameters case $OP in meta-data) meta_data exit $OCF_SUCCESS ;; usage) usage exit $OCF_SUCCESS ;; esac blockdevice=no case $DEVICE in "") ocf_log err "Please set OCF_RESKEY_device to the device to be managed" exit $OCF_ERR_CONFIGURED ;; -*) # Oh... An option to mount instead... Typically -U or -L ;; [!/]*:/*) # An NFS filesystem specification... ;; //[!/]*/*) # An SMB filesystem specification... ;; /dev/null) # Special case for BSC blockdevice=yes ;; *) if [ ! -b "$DEVICE" -a ! -d "$DEVICE" -a "X$OP" != Xstart ] ; then ocf_log warn "Couldn't find device [$DEVICE]. Expected /dev/??? to exist" fi if [ ! -d "$DEVICE" ];then blockdevice=yes fi ;; esac # Normalize instance parameters: # It is possible that OCF_RESKEY_directory has one or even multiple trailing "/". # But the output of `mount` and /proc/mounts do not. if [ -z "$OCF_RESKEY_directory" ]; then if [ X$OP = "Xstart" -o $blockdevice = "no" ]; then ocf_log err "Please specify the directory" exit $OCF_ERR_CONFIGURED fi else MOUNTPOINT=$(echo $OCF_RESKEY_directory | sed 's/\/*$//') : ${MOUNTPOINT:=/} # At this stage, $MOUNTPOINT does not contain trailing "/" unless it is "/" # TODO: / mounted via Filesystem sounds dangerous. On stop, we'll # kill the whole system. Is that a good idea? fi # Check to make sure the utilites are found if [ "X${HOSTOS}" != "XOpenBSD" ];then check_binary $MODPROBE check_binary $FUSER fi check_binary $FSCK check_binary $MOUNT check_binary $UMOUNT if [ "$OP" != "monitor" ]; then ocf_log info "Running $OP for $DEVICE on $MOUNTPOINT" fi # These operations do not require the clone checking + OCFS2 # initialization. case $OP in status) Filesystem_status exit $? ;; monitor) Filesystem_monitor exit $? ;; validate-all) Filesystem_validate_all exit $? ;; stop) Filesystem_stop exit $? ;; esac CLUSTERSAFE=0 is_option "ro" && CLUSTERSAFE=2 case $FSTYPE in ocfs2) ocfs2_init CLUSTERSAFE=1 ;; nfs|smbfs|none|gfs2) CLUSTERSAFE=1 # this is kind of safe too ;; # add here CLUSTERSAFE=0 for all filesystems which are not # cluster aware and which, even if when mounted read-only, # could still modify parts of it such as journal/metadata ext4|ext4dev|ext3|reiserfs|reiser4|xfs|jfs) CLUSTERSAFE=0 # these are not allowed ;; esac if [ -n "$OCF_RESKEY_CRM_meta_clone" ]; then case $CLUSTERSAFE in 0) ocf_log err "DANGER! $FSTYPE on $DEVICE is NOT cluster-aware!" ocf_log err "DO NOT RUN IT AS A CLONE!" ocf_log err "Politely refusing to proceed to avoid data corruption." exit $OCF_ERR_CONFIGURED ;; 2) ocf_log warn "$FSTYPE on $DEVICE is NOT cluster-aware!" ocf_log warn "But we'll let it run because it is mounted read-only." ocf_log warn "Please make sure that it's meta data is read-only too!" ;; esac fi case $OP in start) Filesystem_start ;; notify) Filesystem_notify ;; *) usage exit $OCF_ERR_UNIMPLEMENTED ;; esac exit $? cluster-agents-1.0.4/heartbeat/nfsserver0000755000175000017500000001361211527003731020620 0ustar roaksoaxroaksoax#!/bin/sh # nfsserver # # Description: Manages nfs server as OCF resource # by hxinwei@gmail.com # License: GNU General Public License v2 (GPLv2) and later if [ -n "$OCF_DEBUG_LIBRARY" ]; then . $OCF_DEBUG_LIBRARY else : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs fi DEFAULT_INIT_SCRIPT="/etc/init.d/nfsserver" DEFAULT_NOTIFY_CMD="/sbin/sm-notify" nfsserver_meta_data() { cat < 1.0 Nfsserver helps to manage the Linux nfs server as a failover-able resource in Linux-HA. It depends on Linux specific NFS implementation details, so is considered not portable to other platforms yet. Manages an NFS server The default init script shipped with the Linux distro. The nfsserver resource agent offloads the start/stop/monitor work to the init script because the procedure to start/stop/monitor nfsserver varies on different Linux distro. Init script for nfsserver The tool to send out NSM reboot notification. Failover of nfsserver can be considered as rebooting to different machines. The nfsserver resource agent use this command to notify all clients about the happening of failover. The tool to send out notification. The nfsserver resource agent will save nfs related information in this specific directory. And this directory must be able to fail-over before nfsserver itself. Directory to store nfs server related information. The floating IP address used to access the nfs service IP address. END return $OCF_SUCCESS } nfsserver_usage() { cat < $fn 2>&1 rc=$? ocf_log debug `cat $fn` rm -f $fn #Adapte LSB status code to OCF return code if [ $rc -eq 0 ]; then return $OCF_SUCCESS elif [ $rc -eq 3 ]; then return $OCF_NOT_RUNNING else return $OCF_ERR_GENERIC fi } prepare_directory () { [ -d "$fp" ] || mkdir -p $fp [ -d "$fp/rpc_pipefs" ] || mkdir -p $fp/rpc_pipefs [ -d "$fp/sm" ] || mkdir -p $fp/sm [ -d "$fp/sm.ha" ] || mkdir -p $fp/sm.ha [ -d "$fp/sm.bak" ] || mkdir -p $fp/sm.bak [ -d "$fp/v4recovery" ] || mkdir -p $fp/v4recovery } is_bound () { mount | grep -q "$1 on $2 type none (.*bind)" return $? } bind_tree () { if is_bound $fp /var/lib/nfs; then ocf_log debug "$fp is already bound to /var/lib/nfs" return 0 fi mount --bind $fp /var/lib/nfs } unbind_tree () { if `mount | grep -q "rpc_pipefs on /var/lib/nfs/rpc_pipefs"`; then umount /var/lib/nfs/rpc_pipefs fi if is_bound $fp /var/lib/nfs; then umount /var/lib/nfs fi } nfsserver_start () { prepare_directory bind_tree rm -f /var/lib/nfs/sm.ha/* > /dev/null 2>&1 cp -f /var/lib/nfs/sm/* /var/lib/nfs/sm.ha > /dev/null 2>&1 ocf_log info "Starting NFS server ..." fn=`mktemp` ${OCF_RESKEY_nfs_init_script} start > $fn 2>&1 rc=$? ocf_log debug `cat $fn` rm -f $fn if [ $rc -ne 0 ]; then ocf_log err "Failed to start NFS server" return $rc fi #Notify the nfs server has been moved or rebooted #The init script do that already, but with the hostname, which may be ignored by client #we have to do it again with the nfs_ip local opts="-v" echo $OCF_RESKEY_nfs_notify_cmd | grep -qws rpc.statd && opts="" ${OCF_RESKEY_nfs_notify_cmd} $opts $OCF_RESKEY_nfs_ip -P /var/lib/nfs/sm.ha ocf_log info "NFS server started" return $OCF_SUCCESS } nfsserver_stop () { ocf_log info "Stopping NFS server ..." fn=`mktemp` ${OCF_RESKEY_nfs_init_script} stop > $fn 2>&1 rc=$? ocf_log debug `cat $fn` rm -f $fn if [ $rc -eq 0 ]; then unbind_tree ocf_log info "NFS server stopped" return $OCF_SUCCESS fi ocf_log err "Failed to stop NFS server" return $rc } nfsserver_validate () { check_binary ${OCF_RESKEY_nfs_init_script} check_binary ${OCF_RESKEY_nfs_notify_cmd} if [ -z ${OCF_RESKEY_nfs_ip} ]; then ocf_log err "nfs_ip not set" exit $OCF_ERR_CONFIGURED fi if [ x = "x$OCF_RESKEY_nfs_shared_infodir" ]; then ocf_log err "nfs_shared_infodir not set" exit $OCF_ERR_CONFIGURED fi return $OCF_SUCCESS } if [ -n "$OCF_RESKEY_CRM_meta_clone" ]; then ocf_log err "THIS RA DO NOT SUPPORT CLONE MODE!" exit $OCF_ERR_CONFIGURED fi nfsserver_validate case $__OCF_ACTION in start) nfsserver_start ;; stop) nfsserver_stop ;; monitor) nfsserver_monitor ;; validate-all) nfsserver_validate ;; *) nfsserver_usage exit $OCF_ERR_UNIMPLEMENTED ;; esac cluster-agents-1.0.4/heartbeat/fio0000755000175000017500000001040011527003731017350 0ustar roaksoaxroaksoax#!/bin/bash # # fio RA # # Copyright (c) 2010 SUSE Linux Products GmbH, Lars Marowsky-Brée # All Rights Reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of version 2 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it would be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # # Further, this software is distributed without any warranty that it is # free of the rightful claim of any third person regarding infringement # or the like. Any license provided herein, whether implied or # otherwise, applies only to this software file. Patent licenses, if # any, provided herein do not apply to combinations of this program with # other software, or any other product whatsoever. # # You should have received a copy of the GNU General Public License # along with this program; if not, write the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. # ####################################################################### # Initialization: : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs ####################################################################### meta_data() { cat < 1.0 fio is a generic I/O load generator. This RA allows start/stop of fio instances to simulate load on a cluster without configuring complex services. fio IO load generator Arguments to the fio client. Minimally, this should be a (list of) job descriptions to run. fio arguments END } ####################################################################### fio_usage() { cat </dev/null 2>&1 ${fio_state_file} ocf_log info "fio started as pid=$fio_pid" exit $OCF_SUCCESS } fio_stop() { for sig in SIGINT SIGTERM SIGKILL ; do fio_monitor ; rc=$? case $rc in $OCF_NOT_RUNNING) ocf_log info "fio already stopped." exit $OCF_SUCCESS ;; $OCF_ERR_GENERIC) rm $fio_state_file ocf_log "fio stopped and cleaned up." exit $OCF_SUCCESS ;; $OCF_SUCCESS) if [ -n "$fio_pid" ]; then ocf_log "Sending $sig to fio (pid=$fio_pid)" kill -$sig $fio_pid sleep 3 continue fi ocf_log err "Internal logic failure in fio RA." ;; *) ocf_log err "Internal logic failure in fio RA." ;; esac done ocf_log err "fio did not stop! Perhaps hung on IO?" exit $OCF_ERR_GENERIC } fio_monitor() { fio_state_file="${HA_RSCTMP}/fio-${OCF_RESOURCE_INSTANCE}.state" if [ ! -e $fio_state_file ]; then return $OCF_NOT_RUNNING fi fio_pid=`cat $fio_state_file` if [ -z "$fio_pid" ]; then ocf_log err "State file found, but empty. Assuming stopped." return $OCF_NOT_RUNNING fi ps=`ps h -o comm $fio_pid 2>&1` if [ "$ps" != "fio" ]; then fio_pid="" return $OCF_ERR_GENERIC fi return $OCF_SUCCESS } fio_validate() { return $OCF_SUCCESS } case $__OCF_ACTION in meta-data) meta_data exit $OCF_SUCCESS ;; validate-all) fio_validate;; usage|help) fio_usage exit $OCF_SUCCESS ;; esac ocf_is_probe || check_binary fio case $__OCF_ACTION in start) fio_start;; stop) fio_stop;; monitor) fio_monitor;; *) fio_usage exit $OCF_ERR_UNIMPLEMENTED ;; esac cluster-agents-1.0.4/heartbeat/WAS60000755000175000017500000003062011527003731017321 0ustar roaksoaxroaksoax#!/bin/sh # WAS6 # # Description: Manages a Websphere Application Server as an HA resource # # # Author: Ru Xiang Min # Support: linux-ha@lists.linux-ha.org # License: GNU General Public License (GPL) # Copyright: (C) 2006 International Business Machines China, Ltd., Inc. # # # An example usage in /etc/ha.d/haresources: # node1 10.0.0.170 WAS::/opt/IBM/WebSphere/AppServer/profiles/default/config/cells/Node01Cell/nodes/Node01/serverindex.xml # # See usage() function below for more details... # # OCF parameters are as below: # OCF_RESKEY_profile # (WAS profile name, used for the single server edition of WAS6) ####################################################################### # Initialization: : ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat} . ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs ####################################################################### WAS_DIR=/opt/IBM/WebSphere/AppServer if [ ! -d $WAS_DIR ] then WAS_DIR=/usr/IBM/WebSphere/AppServer fi STARTTIME=300 # 5 minutes DEFAULT_WASPORTS="9080" # # WAS_BIN=$WAS_DIR/bin DEFAULT=default # # Print usage message # usage() { methods=`WAS_methods | grep -v methods` methods=`echo $methods | tr ' ' '|'` cat <<-END usage: $0 ($methods) For the single server edition of WAS6, you have to set the following enviroment virable: OCF_RESKEY_profile (WAS profile name) $0 manages a Websphere Application Server 6(WAS6) as an HA resource The 'start' operation starts WAS6. The 'stop' operation stops WAS6. The 'status' operation reports whether WAS6 is running The 'monitor' operation reports whether the WAS6 seems to be working (httpd also needs to be working for this case) The 'validate-all' operation reports whether the OCF instance parameter (OCF_RESKEY_profileName ) is valid The 'methods' operation reports on the methods $0 supports This is known to work with the Single Server edition of Websphere. The default profile name for the single server edition is: $DEFAULT The start and stop operations must be run as root. The status operation will report a pid of "-" for the WAS root process using unless it is run as root. If you don't have xmllint on your system, parsing of WAS configuration files is very primitive. We run servlet/snoop on the seventh transport port listed in the config file for the "monitor" operation. END } meta_data() { cat < 1.0 Resource script for WAS6. It manages a Websphere Application Server (WAS6) as an HA resource. Manages a WebSphere Application Server 6 instance The WAS profile name. profile name END } # # Reformat the XML document in a sort of canonical form # if we can. If we don't have xmllint, we just cat it out # and hope for the best ;-) # xmlcat() { if [ "X$XMLcat" = X ] then XMLcat=`which xmllint 2>/dev/null` if [ "X${XMLcat}" = X -o ! -x "${XMLcat}" ] then XMLcat=cat else XMLcat="$XMLcat --recover --format" fi fi for j in "$@" do ${XMLcat} "$j" done } # #This is a bit skanky, but it works anyway... # # It's not really skanky if we can find xmllint on the system, because it # reformats tags so they are all on one line, which is all we we need... # # # Get the numbers of the ports WAS should be listening on... # # If we don't have xmllint around, then the applicationserver and the # port= specification have to be on the same line in the XML config file. # GetWASPorts() { case $1 in [0-9]*) echo "$1" | tr ',' '\012';; *) xmlcat ${WAS_DIR}/profiles/${WAS_PROFILE_NAME}/config/cells/${WAS_CELL}/nodes/${WAS_NODE}/serverindex.xml | grep port= | sed -e 's%.*port= *"* *%%' \ -e 's%[^0-9][^0-9]*.*$%%' # Delete up to port=, throw away optional quote and optional # white space. # Throw away everything after the first non-digit. # This should leave us the port number all by itself... esac } # # We assume that the seventh port listed in the serverindex.xml # is the one we should run servlet/snoop on. # GetWASSnoopPort() { GetWASPorts "$@" | sed -n '7p' } # # Return information on the processname/id for the WAS ports # # pid/java is the expected output. Several lines, one per port... # # WASPortInfo() { pat="" once=yes PortCount=0 for j in $* do case $pat in "") pat="$j";; *) pat="$pat|$j";; esac PortCount=`expr $PortCount + 1` done netstat -ltnp 2>/dev/null| egrep -i "($pat) .*LISTEN" | sed 's%.*LISTEN *%%' } # # Return the number of WAS ports which are open # CheckWASPortsInUse() { count=`WASPortInfo "$@" | wc -l` echo $count } # # Return the pid(s) of the processes that have WAS ports open # WASPIDs() { WASPortInfo "$@" | sort -u | cut -f1 -d/ } # # The version of ps that returns all processes and their (long) args # It's only used by WAS_procs, which isn't used for anything ;-) # ps_long() { ps axww } # # The total set of WAS processes (single server only) # WAS_procs() { ps_long | grep -i "config=$1" | grep -i java | cut -d' ' -f1 } # # methods: What methods/operations do we support? # WAS_methods() { cat <<-! start stop status methods validate-all meta-data usage ! if have_binary $WGET then echo " monitor" fi } # # Return WAS status (silently) # WAS_status() { WASPorts=`GetWASPorts $1` PortsInUse=`CheckWASPortsInUse $WASPorts` case $PortsInUse in 0) false;; *) true;; esac } # # Report on WAS status to stdout... # WAS_report_status() { WASPorts=`GetWASPorts $1` PortCount=`echo $WASPorts | wc -w` PortCount=`echo $PortCount` PortsInUse=`CheckWASPortsInUse $WASPorts` case $PortsInUse in 0) ocf_log debug "WAS: server $1 is stopped."; return $OCF_NOT_RUNNING;; *) pids=`WASPIDs $WASPorts` if [ $PortsInUse -ge $PortCount ] then ocf_log debug "WAS: server $1 is running (pid" $pids "et al)." else ocf_log debug "WAS: server $1 is running (pid $pids et al) but not listening on all ports." fi return $OCF_SUCCESS;; esac } # # Monitor WAS - does it really seem to be working? # # For this we invoke the snoop applet via wget. # # This is actually faster than WAS_status above... # WAS_monitor() { trap '[ -z "$tmpfile" || rmtempfile "$tmpfile"' 0 tmpfile=`maketempfile` || exit 1 SnoopPort=`GetWASSnoopPort $1` output=`$WGET -nv -O$tmpfile http://localhost:$SnoopPort/snoop 2>&1` rc=$? if [ $rc -eq 0 ] then if grep -i 'user-agent.*Wget' $tmpfile >/dev/null then : OK else ocf_log "err" "WAS: $1: no user-agent from snoop application" rc=$OCF_ERR_GENERIC fi else ocf_log "err" "WAS: $1: wget failure: $output" rc=$OCF_ERR_GENERIC fi return $rc } # # Start WAS instance # WAS_start() { # Launch Arguments: # -nowait # -quiet # -logfile # -replacelog # -trace # -script [