diff --git a/.hgignore b/.hgignore new file mode 100644 index 000000000..dc5c5c106 --- /dev/null +++ b/.hgignore @@ -0,0 +1,79 @@ +syntax: 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$ diff --git a/Makefile.am b/Makefile.am index e47277e08..4ef013572 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,32 +1,32 @@ # # 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 = heartbeat +SUBDIRS = heartbeat tools tgz: rm -f $(TARFILE) hg archive -t tgz $(TARFILE) echo Rebuilt $(TARFILE) on `date` dist-clean-local: rm -f autoconf automake autoheader $(TARFILE) .PHONY: diff --git a/configure.in b/configure.in index 55656568f..3bb140aca 100644 --- a/configure.in +++ b/configure.in @@ -1,763 +1,777 @@ dnl 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(glue, 1.0.0, pacemaker@oss.clusterlabs.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 ]) 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 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 Home-grown variables eval INITDIR="${INITDIR}" eval docdir="`eval echo ${docdir}`" 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 dnl Eventually remove this dnl CFLAGS="$CFLAGS -I${prefix}/include/heartbeat" 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(glue_config.h) if test "X$OCF_ROOT_DIR" = X; then if test "$ac_cv_header_glue_config_h" != "yes"; then AC_MSG_FAILURE(The cluster-glue development headers were not found) fi OCF_ROOT_DIR=`extract_header_define glue_config.h OCF_ROOT_DIR` 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) AC_SUBST(HA_VARRUNDIR) 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) 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 -itb` 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(HTML2TXT, lynx w3m) AC_PATH_PROGS(HELP2MAN, help2man) AC_PATH_PROGS(POD2MAN, pod2man, pod2man) 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(RPM, rpm) 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) AC_PATH_PROGS(EGREP, egrep) AC_PATH_PROGS(PKGCONFIG, pkg-config) AC_PATH_PROGS(XML2CONFIG, xml2-config) AC_PATH_PROGS(VALGRIND_BIN, valgrind, /usr/bin/valgrind) AC_DEFINE_UNQUOTED(VALGRIND_BIN, "$VALGRIND_BIN", Valgrind command) AC_SUBST(MAILCMD) AC_SUBST(EGREP) AC_SUBST(SHELL) AC_SUBST(PING) AC_SUBST(TEST) AC_SUBST(RPM) 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"") if test x"${HELP2MAN}" != x""; then PKG_FEATURES="$PKG_FEATURES manpages" fi 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 dnl ======================================================================== dnl Functions dnl ======================================================================== 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) dnl ======================================================================== dnl Functions dnl ======================================================================== AC_CHECK_FUNCS(getopt, AC_DEFINE(HAVE_DECL_GETOPT, 1, [Have getopt function])) dnl ======================================================================== dnl libnet dnl ======================================================================== AC_ARG_ENABLE([libnet], [ --enable-libnet Use libnet for ARP based funcationality, [default=try]], [enable_libnet=$withval], [enable_libnet=try]) 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 +sendarp_linux=0 +case $host_os in + *Linux*|*linux*) sendarp_linux=1;; +esac + +AC_SUBST(LIBNETLIBS) +AC_SUBST(LIBNETDEFINES) +AC_DEFINE(HAVE_LIBNET_API, test "x$libnet_version" != "xnone", Libnet API) + +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 -a "$new_libnet" = yes ) dnl ======================================================================== dnl checks for library functions to replace them dnl dnl NoSuchFunctionName: dnl is a dummy function which no system supplies. It is here to make dnl the system compile semi-correctly on OpenBSD which doesn't know dnl how to create an empty archive dnl dnl scandir: Only on BSD. dnl System-V systems may have it, but hidden and/or deprecated. dnl A replacement function is supplied for it. dnl dnl setenv: is some bsdish function that should also be avoided (use dnl putenv instead) dnl On the other hand, putenv doesn't provide the right API for the dnl code and has memory leaks designed in (sigh...) Fortunately this dnl A replacement function is supplied for it. dnl dnl strerror: returns a string that corresponds to an errno. dnl A replacement function is supplied for it. dnl dnl unsetenv: is some bsdish function that should also be avoided (No dnl replacement) dnl A replacement function is supplied for it. dnl dnl strnlen: is a gnu function similar to strlen, but safer. dnl We wrote a tolearably-fast replacement function for it. dnl dnl strndup: is a gnu function similar to strdup, but safer. dnl We wrote a tolearably-fast replacement function for it. dnl dnl daemon: is a GNU function. The daemon() function is for programs wishing to dnl detach themselves from the controlling terminal and run in the dnl background as system daemon dnl A replacement function is supplied for it. AC_REPLACE_FUNCS(alphasort inet_pton NoSuchFunctionName scandir setenv strerror unsetenv strnlen strndup daemon strlcpy strlcat) 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 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 EXTRA_FLAGS="-fgnu89-inline -fstack-protector-all -Wall -Waggregate-return -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 \ heartbeat/Makefile \ heartbeat/.ocf-binaries \ heartbeat/.ocf-directories \ heartbeat/.ocf-shellfuncs \ heartbeat/ocf-returncodes \ heartbeat/ocf-shellfuncs \ +tools/Makefile \ + tools/ocf-tester \ ) 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([ State information = ${localstatedir}]) AC_MSG_RESULT([ System configuration = ${sysconfdir}]) 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}]) diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 000000000..8030ffb57 --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,53 @@ +# +# 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 + +INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include + +halibdir = $(libdir)/@HB_PKG@ + +sbin_SCRIPTS = ocf-tester + +halib_PROGRAMS = sfex_daemon +sbin_PROGRAMS = sfex_init + +if USE_LIBNET +halib_PROGRAMS += send_arp +send_arp_SOURCES = send_arp.libnet.c +send_arp_CFLAGS = @LIBNETDEFINES@ +sfex_daemon_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 + +.PHONY: install-exec-hook diff --git a/tools/README.sfex b/tools/README.sfex new file mode 100644 index 000000000..ff850d1f4 --- /dev/null +++ b/tools/README.sfex @@ -0,0 +1,336 @@ +Shared 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. + diff --git a/tools/ocf-tester.in b/tools/ocf-tester.in new file mode 100644 index 000000000..88fd30ca6 --- /dev/null +++ b/tools/ocf-tester.in @@ -0,0 +1,376 @@ +#!/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 -" + +num_errors=0 + +usage() { + echo "ocf-tester [-Lh] -n resource_name [-o name=value]* /full/path/to/resource" + echo "" + 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 + echo "Aborting tests" + exit $exit_code + fi + fi +} + +done=0 +ra_args="" +verbose=0 +while test "$done" = "0"; do + case "$1" in + -n) OCF_RESOURCE_INSTANCE=$2; ra_args="$ra_args OCF_RESOURCE_INSTANCE=$2"; shift; shift;; + -o) name=${2%%=*}; value=${2#*=}; + lrm_ra_args="$lrm_ra_args $2"; + ra_args="$ra_args OCF_RESKEY_$name=$value"; shift; shift;; + -L) use_lrmd=1; shift;; + -v) verbose=1; shift;; + -?) usage 0;; + -*) echo "unknown option: $1"; usage 1;; + *) done=1;; + esac +done + +if [ "x" = "x$OCF_ROOT" ]; then + if [ -d /usr/lib/ocf ]; then + export OCF_ROOT=/usr/lib/ocf + else + echo "You must supply the location of OCF_ROOT (common location is /usr/lib/ocf)" + usage 1 + fi +fi + +if [ "x" = "x$OCF_RESOURCE_INSTANCE" ]; then + echo "You must give your resource a name, set OCF_RESOURCE_INSTANCE" + usage 1 +fi + +agent=$1 +if [ ! -e $agent ]; then + echo "You must provide the full path to your resource agent" + usage 1 +fi +stopped_rc=7 +has_demote=1 +has_promote=1 + +start_lrmd() { + lrmd_timeout=0 + lrmd_interval=0 + lrmd_target_rc=EVERYTIME + lrmd_started="" + $LRMD -s 2>/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 @HA_NOARCHDATAHBDIR@ && $METADATA_LINT) + rc=$? + #echo rc: $rc + return $rc +} + +test_command() { + action=$1; shift + 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 + bash $agent $action >/dev/null 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" + +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 + +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" + +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 diff --git a/tools/send_arp.libnet.c b/tools/send_arp.libnet.c new file mode 100644 index 000000000..bb3311bd5 --- /dev/null +++ b/tools/send_arp.libnet.c @@ -0,0 +1,691 @@ +/* + * 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 + */ + +#include + +#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; +} + + diff --git a/tools/send_arp.linux.c b/tools/send_arp.linux.c new file mode 100644 index 000000000..39dd245cd --- /dev/null +++ b/tools/send_arp.linux.c @@ -0,0 +1,557 @@ +/* + * 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, unsolicited, advert; +static int quiet; +static int count = -1; +static int timeout; +static int unicasting; +static int s; +static int broadcast_only; + +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) + exit(!!received); + if (unsolicited) + exit(0); + 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; +} + +int +main(int argc, char **argv) +{ + int socket_errno; + int ch; + uid_t uid = getuid(); + int hb_mode = 0; + + 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 + */ + + 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); + } +} + + diff --git a/tools/sfex.h b/tools/sfex.h new file mode 100644 index 000000000..652df96e3 --- /dev/null +++ b/tools/sfex.h @@ -0,0 +1,184 @@ +/*------------------------------------------------------------------------- + * + * 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 + +#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; + +#define SFEX_LOG_ERR(args...) cl_log(LOG_ERR, args) +#define SFEX_LOG_INFO(args...) cl_log(LOG_INFO, args) +#if 0 +#define SFEX_LOG_ERR(args...) do {fprintf(stderr, args);} while (0) +#define SFEX_LOG_INFO(args...) do {fprintf(stderr, args);} while (0) +#endif + +#endif /* SFEX_H */ diff --git a/tools/sfex_daemon.c b/tools/sfex_daemon.c new file mode 100644 index 000000000..8c7cba700 --- /dev/null +++ b/tools/sfex_daemon.c @@ -0,0 +1,376 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sfex.h" +#include "sfex_lib.h" + +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 const char *rscpidfile; + +static void usage(FILE *dist) { + fprintf(dist, "usage: %s [-i ] [-c ] [-t ] \n", progname); +} + +static int lock_index_check(void) +{ + if (read_controldata(&cdata) == -1) { + SFEX_LOG_ERR("%s\n", "read_controldata failed in lock_index_check"); + return -1; + } +#ifdef SFEX_DEBUG + SFEX_LOG_INFO("version: %d\n", cdata.version); + SFEX_LOG_INFO("revision: %d\n", cdata.revision); + SFEX_LOG_INFO("blocksize: %d\n", cdata.blocksize); + SFEX_LOG_INFO("numlocks: %d\n", cdata.numlocks); +#endif + + if (lock_index > cdata.numlocks) { + SFEX_LOG_ERR("%s: ERROR: index %d is too large. %d locks are stored.\n", + progname, lock_index, cdata.numlocks); + return -1; + //exit(EXIT_FAILURE); + } + + if (cdata.blocksize != sector_size) { + SFEX_LOG_ERR("%s: ERROR: sector_size is not the same as the blocksize.\n", progname); + return -1; + } + return 0; +} + +static void acquire_lock(void) +{ + if (read_lockdata(&cdata, &ldata, lock_index) == -1) { + SFEX_LOG_ERR("%s\n", "read_lockdata failed in acquire_lock"); + 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) { + SFEX_LOG_ERR("%s", "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) { + SFEX_LOG_ERR("%s", "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) { + SFEX_LOG_ERR("%s", "read_lockdata failed\n"); + } + if (strncmp((char*)(ldata.nodename), (const char*)(ldata_new.nodename), sizeof(ldata.nodename))) { + SFEX_LOG_ERR("%s", "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) { + SFEX_LOG_ERR("%s\n", "write_lockdata failed"); + exit(EXIT_FAILURE); + } + SFEX_LOG_ERR("%s", "lock acquired\n"); +} + +static void error_todo (void) +{ + if (fork() == 0) { + 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; + ret = write(sysrq_fd, "b\n", 2); + if (ret == -1) { + SFEX_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) { + 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))) { + SFEX_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) { + 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) { + 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))) { + SFEX_LOG_ERR("lock was already released.\n"); + exit(1); + } + + /* lock release */ + ldata.status = SFEX_STATUS_UNLOCK; + if (write_lockdata(&cdata, &ldata, lock_index) == -1) { + //FIXME: We are going to self-stop + exit(EXIT_FAILURE); + } + SFEX_LOG_INFO("lock released\n"); +} + +static void quit_handler(int signo, siginfo_t *info, void *context) +{ + SFEX_LOG_INFO("quit_handler\n"); + release_lock(); + exit(EXIT_SUCCESS); +} + +int main(int argc, char *argv[]) +{ + + int ret; + + progname = get_progname(argv[0]); + nodename = get_nodename(); + + +#if 0 + openlog("SFex Daemon", LOG_PID|LOG_CONS|LOG_NDELAY, LOG_USER); +#endif + + /* read command line option */ + opterr = 0; + while (1) { + int c = getopt(argc, argv, "hi:c:t:m:n:r:d:"); + 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) { + SFEX_LOG_ERR( + "%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); + } + lock_index = l; + } + break; + case 'c': /* -c */ + { + unsigned long l = strtoul(optarg, NULL, 10); + if (l < 1 || l > INT_MAX) { + SFEX_LOG_ERR( + "%s: ERROR: collision_timeout %s is out of range or invalid. it must be integer value between %lu and %lu.\n", + progname, 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) { + SFEX_LOG_ERR( + "%s: ERROR: monitor_interval %s is out of range or invalid. it must be integer value between %lu and %lu.\n", + progname, 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) { + SFEX_LOG_ERR( + "%s: ERROR: lock_timeout %s is out of range or invalid. it must be integer value between %lu and %lu.\n", + progname, optarg, + (unsigned long)1, + (unsigned long)INT_MAX); + exit(4); + } + lock_timeout = l; + } + break; + case 'n': + { + free(nodename); + if (strlen(optarg) > SFEX_MAX_NODENAME) { + SFEX_LOG_ERR("%s: ERROR: nodename %s is too long. must be less than %d byte.\n", + progname, optarg, + (unsigned int)SFEX_MAX_NODENAME); + exit(EXIT_FAILURE); + } + nodename = strdup(optarg); + } + break; + case 'r': + { + rsc_id = strdup(optarg); + } + break; + case 'd': + { + rscpidfile = strdup(optarg); + } + break; + case '?': /* error */ + usage(stderr); + exit(4); + } + } + /* check parameter except the option */ + if (optind >= argc) { + SFEX_LOG_ERR("%s: ERROR: no device specified.\n", progname); + usage(stderr); + exit(EXIT_FAILURE); + } else if (optind + 1 < argc) { + SFEX_LOG_ERR("%s: ERROR: too many arguments.\n", progname); + usage(stderr); + exit(EXIT_FAILURE); + } + device = argv[optind]; + + if (rscpidfile == NULL) { + SFEX_LOG_ERR("%s: ERROR: Directory for saving pid file is not specified.\n", progname); + exit(EXIT_FAILURE); + } + + prepare_lock(device); +#if !SFEX_TESTING + sysrq_fd = open("/proc/sysrq-trigger", O_WRONLY); + if (sysrq_fd == -1) { + SFEX_LOG_ERR("failed to open /proc/sysrq-trigger due to %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } +#endif + + ret = lock_index_check(); + 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) { + SFEX_LOG_ERR("sigaction failed\n"); + exit(EXIT_FAILURE); + } + } + + SFEX_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); + } + if (cl_lock_pidfile(rscpidfile) < 0) { + SFEX_LOG_ERR("Creating pidfile failed."); + release_lock(); + exit(EXIT_FAILURE); + } + + cl_make_realtime(-1, -1, 128, 128); + + SFEX_LOG_INFO("SFeX Daemon started.\n"); + while (1) { + sleep (monitor_interval); + update_lock(); + } +} diff --git a/tools/sfex_init.c b/tools/sfex_init.c new file mode 100644 index 000000000..bf81ff45d --- /dev/null +++ b/tools/sfex_init.c @@ -0,0 +1,173 @@ +/*------------------------------------------------------------------------- + * + * 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 + +#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 + */ + +/* + openlog("SFex Init", LOG_PID|LOG_CONS, LOG_USER); +*/ + + /* get a program name */ + progname = get_progname(argv[0]); + + /* 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); +} diff --git a/tools/sfex_lib.c b/tools/sfex_lib.c new file mode 100644 index 000000000..920975622 --- /dev/null +++ b/tools/sfex_lib.c @@ -0,0 +1,436 @@ +/*------------------------------------------------------------------------- + * + * Shared Disk File EXclusiveness Control Program(SF-EX) + * + * 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; + SFEX_LOG_ERR ("%s: ERROR: can't open device %s: %s\n", + progname, device, strerror (errno)); + exit (3); + } + break; + } + while (1); + + ioctl(dev_fd, BLKSSZGET, §or_size); + if (sector_size == 0) { + SFEX_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) { + SFEX_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)) { + SFEX_LOG_ERR ("%s: ERROR: %s\n", progname, strerror (errno)); + exit (3); + } + if (strlen (u.nodename) > SFEX_MAX_NODENAME) { + SFEX_LOG_ERR + ("%s: ERROR: nodename %s is too long. must be less than %lu byte.\n", + progname, u.nodename, (unsigned long)SFEX_MAX_NODENAME); + exit (3); + } + n = strdup (&u.nodename[0]); + if (!n) { + SFEX_LOG_ERR ("%s: ERROR: %s\n", progname, 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) { + SFEX_LOG_ERR ("%s: ERROR: can't seek file pointer: %s\n", progname, + 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; + SFEX_LOG_ERR ("%s: ERROR: can't write meta-data: %s\n", + progname, 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) { + SFEX_LOG_ERR ("%s: ERROR: can't seek file pointer: %s\n", progname, + 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; + SFEX_LOG_ERR ("%s: ERROR: can't write meta-data: %s\n", + progname, strerror (errno)); + return -1; + } + else if (s != cdata->blocksize) { + /* if writing atomically failed, this process is error */ + SFEX_LOG_ERR ("%s: ERROR: can't write meta-data atomically.\n", + progname); + 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) { + SFEX_LOG_ERR ("%s: ERROR: can't seek file pointer: %s\n", progname, + 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; + SFEX_LOG_ERR + ("%s: ERROR: can't read controldata meta-data: %s\n", + progname, 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))) { + SFEX_LOG_ERR ("%s: ERROR: magic number mismatched. %c%c%c%c <-> %s\n", progname, 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]) { + SFEX_LOG_ERR ("%s: ERROR: control data format error.\n", progname); + return -1; + } + cdata->version = atoi ((char *) (block->version)); + if (cdata->version != SFEX_VERSION) { + SFEX_LOG_ERR + ("%s: ERROR: version number mismatched. program is %d, data is %d.\n", + progname, 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) { + SFEX_LOG_ERR ("%s: ERROR: can't seek file pointer: %s\n", progname, + 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; + SFEX_LOG_ERR ("%s: ERROR: can't read lockdata meta-data: %s\n", + progname, strerror (errno)); + return -1; + } + else if (s != cdata->blocksize) { + SFEX_LOG_ERR ("%s: ERROR: can't read meta-data atomically.\n", + progname); + 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]) { + SFEX_LOG_ERR ("%s: ERROR: lock data format error.\n", progname); + return -1; + } + ldata->status = block->status; + if (ldata->status != SFEX_STATUS_UNLOCK + && ldata->status != SFEX_STATUS_LOCK) { + SFEX_LOG_ERR ("%s: ERROR: lock data format error.\n", progname); + return -1; + } + ldata->count = atoi ((char *) (block->count)); + strncpy ((char *) (ldata->nodename), (const char *) (block->nodename), sizeof(block->nodename)); + +#ifdef SFEX_DEBUG + SFEX_LOG_INFO ("status: %c\n", ldata->status); + SFEX_LOG_INFO ("count: %d\n", ldata->count); + SFEX_LOG_INFO ("nodename: %s\n", ldata->nodename); +#endif + return 0; +} diff --git a/tools/sfex_lib.h b/tools/sfex_lib.h new file mode 100644 index 000000000..2e9943b75 --- /dev/null +++ b/tools/sfex_lib.h @@ -0,0 +1,41 @@ +/*------------------------------------------------------------------------- + * + * Shared Disk File EXclusiveness Control Program(SF-EX) + * + * 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); + +#endif /* LIB_H */