diff --git a/configure.ac b/configure.ac index 14c0e175..a27ba95a 100644 --- a/configure.ac +++ b/configure.ac @@ -1,750 +1,751 @@ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. # bootstrap / init AC_PREREQ([2.61]) AC_INIT([corosync], m4_esyscmd([build-aux/git-version-gen .tarball-version .gitarchivever]), [users@clusterlabs.org]) AC_USE_SYSTEM_EXTENSIONS AM_INIT_AUTOMAKE([foreign 1.11]) LT_PREREQ([2.2.6]) LT_INIT AM_SILENT_RULES([yes]) AC_CONFIG_SRCDIR([lib/cpg.c]) AC_CONFIG_HEADER([include/corosync/config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CANONICAL_HOST AC_LANG([C]) AC_SUBST(WITH_LIST, [""]) #Enable inter-library dependencies AC_ARG_ENABLE(interlib-deps, [AC_HELP_STRING([--disable-interlib-deps ],[disable inter-library dependencies (might break builds)])], [enable_interlib_deps="$enableval"], [enable_interlib_deps="yes"]) AC_MSG_NOTICE([enable inter-library dependencies: $enable_interlib_deps]) if test "${enable_interlib_deps}" == "yes"; then link_all_deplibs=yes link_all_deplibs_CXX=yes else link_all_deplibs=no link_all_deplibs_CXX=no fi dnl Fix default variables - "prefix" variable if not specified if test "$prefix" = "NONE"; then prefix="/usr" dnl Fix "localstatedir" variable if not specified if test "$localstatedir" = "\${prefix}/var"; then localstatedir="/var" fi dnl Fix "sysconfdir" variable if not specified if test "$sysconfdir" = "\${prefix}/etc"; then sysconfdir="/etc" fi dnl Fix "libdir" variable if not specified if test "$libdir" = "\${exec_prefix}/lib"; then if test -e /usr/lib64; then libdir="/usr/lib64" else libdir="/usr/lib" fi fi fi if test "$srcdir" = "."; then AC_MSG_NOTICE([building in place srcdir:$srcdir]) AC_DEFINE([BUILDING_IN_PLACE], 1, [building in place]) else AC_MSG_NOTICE([building out of tree srcdir:$srcdir]) fi # Checks for programs. # check stolen from gnulib/m4/gnu-make.m4 if ! ${MAKE-make} --version /cannot/make/this >/dev/null 2>&1; then AC_MSG_ERROR([you don't seem to have GNU make; it is required]) fi sinclude(corosync-default.m4) AC_PROG_CC AC_PROG_CC_C99 if test "x$ac_cv_prog_cc_c99" = "xno"; then AC_MSG_ERROR(["C99 support is required"]) fi AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_SED AC_CHECK_PROGS([GROFF], [groff]) AC_CHECK_PROGS([PKGCONFIG], [pkg-config]) AC_CHECK_PROGS([AUGTOOL], [augtool]) AC_CHECK_PROGS([DOT], [dot]) AC_CHECK_PROGS([DOXYGEN], [doxygen]) AC_CHECK_PROGS([AWK], [awk]) AC_PATH_PROG([BASHPATH], [bash]) # Checks for compiler characteristics. AC_PROG_GCC_TRADITIONAL AC_C_CONST AC_C_INLINE AC_C_VOLATILE # Checks for header files. AC_HEADER_DIRENT AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h netdb.h netinet/in.h stdint.h \ stdlib.h string.h sys/ioctl.h sys/param.h sys/socket.h \ sys/time.h syslog.h unistd.h sys/types.h getopt.h malloc.h \ utmpx.h ifaddrs.h stddef.h sys/file.h sys/uio.h]) # Check entries in specific structs AC_CHECK_MEMBER([struct sockaddr_in.sin_len], [AC_DEFINE_UNQUOTED([HAVE_SOCK_SIN_LEN], [1], [sockaddr_in needs sin_len])], [], [[#include ]]) AC_CHECK_MEMBER([struct sockaddr_in6.sin6_len], [AC_DEFINE_UNQUOTED([HAVE_SOCK_SIN6_LEN], [1], [sockaddr_in6 needs sin6_len])], [], [[#include ]]) AC_CHECK_MEMBER([struct msghdr.msg_control], [AC_DEFINE_UNQUOTED([HAVE_MSGHDR_CONTROL], [1], [msghdr has msg_control])], [], [[#include ]]) AC_CHECK_MEMBER([struct msghdr.msg_controllen], [AC_DEFINE_UNQUOTED([HAVE_MSGHDR_CONTROLLEN], [1], [msghdr has msg_controllen])], [], [[#include ]]) AC_CHECK_MEMBER([struct msghdr.msg_flags], [AC_DEFINE_UNQUOTED([HAVE_MSGHDR_FLAGS], [1], [msghdr has msg_flags])], [], [[#include ]]) AC_CHECK_MEMBER([struct msghdr.msg_accrights], [AC_DEFINE_UNQUOTED([HAVE_MSGHDR_ACCRIGHTS], [1], [msghdr has msg_accrights])], [], [[#include ]]) AC_CHECK_MEMBER([struct msghdr.msg_accrightslen], [AC_DEFINE_UNQUOTED([HAVE_MSGHDR_ACCRIGHTSLEN], [1], [msghdr has msg_accrightslen])], [], [[#include ]]) # Checks for typedefs. AC_TYPE_UID_T AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_INT64_T AC_TYPE_INT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_TYPE_UINT8_T AC_TYPE_SIZE_T AC_TYPE_SSIZE_T # Checks for libraries. SAVE_CPPFLAGS="$CPPFLAGS" SAVE_LIBS="$LIBS" PKG_CHECK_MODULES([LIBQB], [libqb]) CPPFLAGS="$CPPFLAGS $LIBQB_CFLAGS" LIBS="$LIBS $LIBQB_LIBS" AC_CHECK_LIB([qb], [qb_log_thread_priority_set], \ have_qb_log_thread_priority_set="yes", \ have_qb_log_thread_priority_set="no") if test "x${have_qb_log_thread_priority_set}" = xyes; then AC_DEFINE_UNQUOTED([HAVE_QB_LOG_THREAD_PRIORITY_SET], 1, [have qb_log_thread_priority_set]) fi AC_CHECK_LIB([qb], [qb_log_file_reopen], \ have_qb_log_file_reopen="yes", \ have_qb_log_file_reopen="no") if test "x${have_qb_log_file_reopen}" = xyes; then AC_DEFINE_UNQUOTED([HAVE_QB_LOG_FILE_REOPEN], 1, [have qb_log_file_reopen]) fi AM_CONDITIONAL(HAVE_QB_LOG_FILE_REOPEN, test x$have_qb_log_file_reopen = xyes) CPPFLAGS="$SAVE_CPPFLAGS" LIBS="$SAVE_LIBS" AC_CHECK_LIB([pthread], [pthread_create]) AC_CHECK_LIB([socket], [socket]) PKG_CHECK_MODULES([knet],[libknet]) AC_CHECK_LIB([nsl], [t_open]) AC_CHECK_LIB([rt], [sched_getscheduler]) AC_CHECK_LIB([z], [crc32], AM_CONDITIONAL([HAVE_CRC32], true), AM_CONDITIONAL([HAVE_CRC32], false)) # Checks for library functions. AC_FUNC_ALLOCA AC_FUNC_CLOSEDIR_VOID AC_FUNC_ERROR_AT_LINE AC_FUNC_FORK AC_FUNC_MALLOC AC_FUNC_MEMCMP AC_FUNC_MMAP AC_FUNC_REALLOC AC_FUNC_SELECT_ARGTYPES AC_FUNC_VPRINTF AC_CHECK_FUNCS([alarm alphasort atexit bzero dup2 endgrent endpwent fdatasync \ fcntl getcwd getpeerucred getpeereid gettimeofday inet_ntoa \ memmove memset mkdir scandir select socket strcasecmp strchr \ strdup strerror strrchr strspn strstr pthread_setschedparam \ sched_get_priority_max sched_setscheduler getifaddrs \ clock_gettime ftruncate gethostname localtime_r munmap strtol]) AC_CONFIG_FILES([Makefile exec/Makefile include/Makefile init/Makefile lib/Makefile common_lib/Makefile man/Makefile pkgconfig/Makefile test/Makefile tools/Makefile conf/Makefile vqsim/Makefile Doxyfile conf/logrotate/Makefile]) ### Local business dnl =============================================== dnl Functions / global M4 variables dnl =============================================== dnl Global list of LIB names m4_define([local_soname_list], [])dnl dnl Upcase parameter m4_define([local_upcase], [translit([$*], [a-z], [A-Z])])dnl dnl M4 macro for include lib/lib$1.soname and subst that m4_define([LIB_SONAME_IMPORT],[dnl m4_define([local_libname], local_upcase($1)[_SONAME])dnl m4_define([local_soname], translit(m4_sinclude(lib/lib$1.verso), [ ], []))dnl local_libname="local_soname"dnl m4_define([local_soname_list], m4_defn([local_soname_list])[,]local_libname[,]local_upcase($1))dnl AC_SUBST(local_libname)dnl ])dnl dnl M4 macro for print padspaces (used in LIB_MSG_RESULT). It takes 2 arguments, length of string to pad and desired dnl (padded) length m4_define([m4_printpadspace],[ifelse(m4_eval([$2 - $1 < 1]),[1],,[ ][m4_printpadspace([$1],m4_eval([$2 - 1]))])])dnl dnl Show AC_MSG_RESULT for specific libraries m4_define([LIB_MSG_RESULT], [ifelse([$#], [1], ,[dnl AC_MSG_RESULT([ $2 Library SONAME m4_printpadspace(len($2),8) = ${$1}]) LIB_MSG_RESULT(m4_shift(m4_shift($@)))dnl ])])dnl # =============================================== # Helpers # =============================================== ## check if the compiler supports -Werror -Wunknown-warning-option AC_MSG_CHECKING([whether $CC supports -Wunknown-warning-option -Werror]) BACKUP="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -Werror -Wunknown-warning-option" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], [unknown_warnings_as_errors='-Wunknown-warning-option -Werror'; AC_MSG_RESULT([yes])], [unknown_warnings_as_errors=''; AC_MSG_RESULT([no])]) CPPFLAGS="$BACKUP" ## helper for CC stuff cc_supports_flag() { BACKUP="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $@ $unknown_warnings_as_errors" AC_MSG_CHECKING([whether $CC supports "$@"]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([])], [RC=0; AC_MSG_RESULT([yes])], [RC=1; AC_MSG_RESULT([no])]) CPPFLAGS="$BACKUP" return $RC } ## cleanup AC_MSG_NOTICE(Sanitizing prefix: ${prefix}) case $prefix in NONE) prefix=/usr/local;; esac AC_MSG_NOTICE(Sanitizing exec_prefix: ${exec_prefix}) case $exec_prefix in dnl For consistency with Corosync, map NONE->$prefix NONE) exec_prefix=$prefix;; prefix) exec_prefix=$prefix;; esac ## local defines PACKAGE_FEATURES="" LINT_FLAGS="-weak -unrecog +posixlib +ignoresigns -fcnuse \ -badflag -D__gnuc_va_list=va_list -D__attribute\(x\)=" # default libraries SONAME SOMAJOR="5" SOMINOR="0" SOMICRO="0" SONAME="${SOMAJOR}.${SOMINOR}.${SOMICRO}" # specific libraries SONAME LIB_SONAME_IMPORT([cfg]) LIB_SONAME_IMPORT([cpg]) LIB_SONAME_IMPORT([quorum]) LIB_SONAME_IMPORT([sam]) LIB_SONAME_IMPORT([votequorum]) LIB_SONAME_IMPORT([cmap]) # local options AC_ARG_ENABLE([ansi], [ --enable-ansi : force to build with ANSI standards. ], [ default="no" ]) AC_ARG_ENABLE([fatal-warnings], [ --enable-fatal-warnings : enable fatal warnings. ], [ default="no" ]) AC_ARG_ENABLE([debug], [ --enable-debug : enable debug build. ], [ default="no" ]) AC_ARG_ENABLE([secure-build], [ --enable-secure-build : enable PIE/RELRO build. ], [], [enable_secure_build="yes"]) AC_ARG_ENABLE([user-flags], [ --enable-user-flags : rely on user environment. ], [ default="no" ]) AC_ARG_ENABLE([coverage], [ --enable-coverage : coverage analysis of the codebase. ], [ default="no" ]) AC_ARG_ENABLE([small-memory-footprint], [ --enable-small-memory-footprint : Use small message queues and small messages sizes. ], [ default="no" ]) AC_ARG_ENABLE([dbus], [ --enable-dbus : dbus events. ],, [ enable_dbus="no" ]) AC_ARG_ENABLE([monitoring], [ --enable-monitoring : resource monitoring ],, [ default="no" ]) AM_CONDITIONAL(BUILD_MONITORING, test x$enable_monitoring = xyes) AC_ARG_ENABLE([watchdog], [ --enable-watchdog : Watchdog support ],, [ default="no" ]) AM_CONDITIONAL(BUILD_WATCHDOG, test x$enable_watchdog = xyes) AC_ARG_ENABLE([augeas], [ --enable-augeas : Install the augeas lens for corosync.conf ],, [ enable_augeas="no" ]) AM_CONDITIONAL(INSTALL_AUGEAS, test x$enable_augeas = xyes) AC_ARG_ENABLE([systemd], [ --enable-systemd : Install systemd service files],, [ enable_systemd="no" ]) AM_CONDITIONAL(INSTALL_SYSTEMD, test x$enable_systemd = xyes) AC_ARG_WITH([initconfigdir], [AS_HELP_STRING([--with-initconfigdir=DIR], [configuration directory @<:@SYSCONFDIR/sysconfig@:>@])], [INITCONFIGDIR="$withval"], [INITCONFIGDIR='${sysconfdir}/sysconfig']) AC_SUBST([INITCONFIGDIR]) AC_ARG_WITH([initddir], [ --with-initddir=DIR : path to init script directory. ], [ INITDDIR="$withval" ], [ INITDDIR="$sysconfdir/init.d" ]) AC_ARG_WITH([systemddir], [ --with-systemddir=DIR : path to systemd unit files directory. ], [ SYSTEMDDIR="$withval" ], [ SYSTEMDDIR="/lib/systemd/system" ]) AC_ARG_WITH([logdir], [ --with-logdir=DIR : the base directory for corosync logging files. ], [ LOGDIR="$withval" ], [ LOGDIR="$localstatedir/log/cluster" ]) AC_ARG_WITH([logrotatedir], [ --with-logrotatedir=DIR : the base directory for logrorate.d files. ], [ LOGROTATEDIR="$withval" ], [ LOGROTATEDIR="$sysconfdir/logrotate.d" ]) AC_ARG_ENABLE([snmp], [ --enable-snmp : SNMP protocol support ], [ default="no" ]) AC_ARG_ENABLE([xmlconf], [ --enable-xmlconf : XML configuration support ],, [ enable_xmlconf="no" ]) AM_CONDITIONAL(INSTALL_XMLCONF, test x$enable_xmlconf = xyes) AC_ARG_ENABLE([vqsim], [ --enable-vqsim : Quorum simulator support ],, [ enable_vqsim="no" ]) AM_CONDITIONAL(BUILD_VQSIM, test x$enable_vqsim = xyes) AC_ARG_ENABLE([nozzle], [ --enable-nozzle : Support for nozzle ],, [ enable_nozzle="no" ]) # *FLAGS handling goes here ENV_CFLAGS="$CFLAGS" ENV_CPPFLAGS="$CPPFLAGS" ENV_LDFLAGS="$LDFLAGS" # debug build stuff if test "x${enable_debug}" = xyes; then AC_DEFINE_UNQUOTED([DEBUG], [1], [Compiling Debugging code]) OPT_CFLAGS="-O0" PACKAGE_FEATURES="$PACKAGE_FEATURES debug" else OPT_CFLAGS="-O3" fi # gdb flags if test "x${GCC}" = xyes; then GDB_FLAGS="-ggdb3" else GDB_FLAGS="-g" fi # Look for dbus-1 if test "x${enable_dbus}" = xyes; then PKG_CHECK_MODULES([DBUS],[dbus-1]) AC_DEFINE_UNQUOTED([HAVE_DBUS], 1, [have dbus]) PACKAGE_FEATURES="$PACKAGE_FEATURES dbus" WITH_LIST="$WITH_LIST --with dbus" fi if test "x${enable_monitoring}" = xyes; then PKG_CHECK_MODULES([statgrab], [libstatgrab]) PKG_CHECK_MODULES([statgrabge090], [libstatgrab >= 0.90], AC_DEFINE_UNQUOTED([HAVE_LIBSTATGRAB_GE_090], 1, [have libstatgrab >= 0.90]), TMP_VARIABLE=1) AC_DEFINE_UNQUOTED([HAVE_MONITORING], 1, [have resource monitoring]) PACKAGE_FEATURES="$PACKAGE_FEATURES monitoring" WITH_LIST="$WITH_LIST --with monitoring" fi if test "x${enable_watchdog}" = xyes; then AC_CHECK_HEADER([linux/watchdog.h], [], [AC_MSG_ERROR([watchdog requires linux/watchdog.h])]) AC_CHECK_HEADER([linux/reboot.h], [], [AC_MSG_ERROR([watchdog requires linux/reboot.h])]) AC_DEFINE_UNQUOTED([HAVE_WATCHDOG], 1, [have watchdog]) PACKAGE_FEATURES="$PACKAGE_FEATURES watchdog" WITH_LIST="$WITH_LIST --with watchdog" fi if test "x${enable_augeas}" = xyes; then PACKAGE_FEATURES="$PACKAGE_FEATURES augeas" fi if test "x${enable_systemd}" = xyes; then PKG_CHECK_MODULES([libsystemd], [libsystemd]) AC_DEFINE([HAVE_LIBSYSTEMD], [1], [have systemd interface library]) PACKAGE_FEATURES="$PACKAGE_FEATURES systemd" WITH_LIST="$WITH_LIST --with systemd" fi if test "x${enable_xmlconf}" = xyes; then PACKAGE_FEATURES="$PACKAGE_FEATURES xmlconf" WITH_LIST="$WITH_LIST --with xmlconf" fi if test "x${enable_vqsim}" = xyes; then vqsim_readline=no AC_CHECK_HEADERS([readline/readline.h readline/history.h], [], AC_MSG_WARN([vqsim will lack readline support])) PACKAGE_FEATURES="$PACKAGE_FEATURES vqsim" + WITH_LIST="$WITH_LIST --with vqsim" fi AM_CONDITIONAL(VQSIM_READLINE, [test "x${ac_cv_header_readline_readline_h}" = xyes]) # Look for nozzle if test "x${enable_nozzle}" = xyes; then PKG_CHECK_MODULES([nozzle],[libnozzle]) AC_DEFINE_UNQUOTED([HAVE_LIBNOZZLE], 1, [have nozzle]) PACKAGE_FEATURES="$PACKAGE_FEATURES nozzle" WITH_LIST="$WITH_LIST --with nozzle" fi do_snmp=0 if test "x${enable_snmp}" = xyes; then AC_PATH_PROGS([SNMPCONFIG], [net-snmp-config]) if test "x${SNMPCONFIG}" != "x"; then AC_MSG_CHECKING([for snmp includes]) SNMP_PREFIX=`$SNMPCONFIG --prefix` SNMP_INCLUDES="-I$SNMP_PREFIX/include" AC_MSG_RESULT([$SNMP_INCLUDES]) AC_MSG_CHECKING([for snmp libraries]) SNMP_LIBS=`$SNMPCONFIG --libs` AC_MSG_RESULT([$SNMP_LIBS]) AC_SUBST([SNMP_LIBS]) saveCFLAGS="$CFLAGS" CFLAGS="$CFLAGS $SNMP_INCLUDES" AC_CHECK_HEADERS([net-snmp/net-snmp-config.h]) CFLAGS="$saveCFLAGS" if test "x${ac_cv_header_net_snmp_net_snmp_config_h}" != "xyes"; then AC_MSG_ERROR([Unable to use net-snmp/net-snmp-config.h]) fi savedLibs=$LIBS LIBS="$LIBS $SNMP_LIBS" AC_CHECK_FUNCS([netsnmp_transport_open_client]) if test $ac_cv_func_netsnmp_transport_open_client != yes; then AC_CHECK_FUNCS([netsnmp_tdomain_transport]) if test $ac_cv_func_netsnmp_tdomain_transport != yes; then AC_MSG_ERROR([No usable SNMP client transport implementation found]) fi else AC_DEFINE_UNQUOTED([NETSNMPV54], $NETSNMP_NEW_SUPPORT, [have net-snmp5.4 over]) fi LIBS=$savedLibs do_snmp=1 PACKAGE_FEATURES="$PACKAGE_FEATURES snmp" WITH_LIST="$WITH_LIST --with snmp" AC_DEFINE_UNQUOTED([ENABLE_SNMP], $do_snmp, [Build in support for sending SNMP traps]) else AC_MSG_ERROR([You need the net_snmp development package to continue.]) fi fi AM_CONDITIONAL(BUILD_SNMP, test "${do_snmp}" = "1") # extra warnings EXTRA_WARNINGS="" WARNLIST=" all shadow missing-prototypes missing-declarations strict-prototypes pointer-arith write-strings cast-align bad-function-cast missing-format-attribute format=2 format-security format-nonliteral no-long-long unsigned-char no-strict-aliasing " for j in $WARNLIST; do if cc_supports_flag -W$j; then EXTRA_WARNINGS="$EXTRA_WARNINGS -W$j"; fi done if test "x${enable_coverage}" = xyes && \ cc_supports_flag -ftest-coverage && \ cc_supports_flag -fprofile-arcs ; then AC_MSG_NOTICE([Enabling Coverage (enable -O0 by default)]) OPT_CFLAGS="-O0" COVERAGE_CFLAGS="-ftest-coverage -fprofile-arcs" COVERAGE_LDFLAGS="-ftest-coverage -fprofile-arcs" PACKAGE_FEATURES="$PACKAGE_FEATURES coverage" else COVERAGE_CFLAGS="" COVERAGE_LDFLAGS="" fi if test "x${enable_small_memory_footprint}" = xyes ; then AC_DEFINE_UNQUOTED([HAVE_SMALL_MEMORY_FOOTPRINT], 1, [have small_memory_footprint]) PACKAGE_FEATURES="$PACKAGE_FEATURES small-memory-footprint" fi if test "x${enable_ansi}" = xyes && \ cc_supports_flag -std=iso9899:199409 ; then AC_MSG_NOTICE([Enabling ANSI Compatibility]) ANSI_CPPFLAGS="-ansi -DANSI_ONLY" PACKAGE_FEATURES="$PACKAGE_FEATURES ansi" else ANSI_CPPFLAGS="" fi if test "x${enable_fatal_warnings}" = xyes && \ cc_supports_flag -Werror ; then AC_MSG_NOTICE([Enabling Fatal Warnings (-Werror)]) WERROR_CFLAGS="-Werror" PACKAGE_FEATURES="$PACKAGE_FEATURES fatal-warnings" else WERROR_CFLAGS="" fi # don't add addtional cflags if test "x${enable_user_flags}" = xyes; then OPT_CFLAGS="" GDB_FLAGS="" EXTRA_WARNINGS="" fi if test "x${enable_secure_build}" = xyes; then # stolen from apache configure snippet AC_CACHE_CHECK([whether $CC accepts PIE flags], [ap_cv_cc_pie], [ save_CFLAGS=$CFLAGS save_LDFLAGS=$LDFLAGS CFLAGS="$CFLAGS -fPIE" LDFLAGS="$LDFLAGS -pie" AC_TRY_RUN([static int foo[30000]; int main () { return 0; }], [ap_cv_cc_pie=yes], [ap_cv_cc_pie=no], [ap_cv_cc_pie=yes]) CFLAGS=$save_CFLAGS LDFLAGS=$save_LDFLAGS ]) if test "$ap_cv_cc_pie" = "yes"; then SEC_FLAGS="$SEC_FLAGS -fPIE" SEC_LDFLAGS="$SEC_LDFLAGS -pie" PACKAGE_FEATURES="$PACKAGE_FEATURES pie" fi # similar to above AC_CACHE_CHECK([whether $CC accepts RELRO flags], [ap_cv_cc_relro], [ save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,-z,relro" AC_TRY_RUN([static int foo[30000]; int main () { return 0; }], [ap_cv_cc_relro=yes], [ap_cv_cc_relro=no], [ap_cv_cc_relro=yes]) LDFLAGS=$save_LDFLAGS ]) if test "$ap_cv_cc_relro" = "yes"; then SEC_LDFLAGS="$SEC_LDFLAGS -Wl,-z,relro" PACKAGE_FEATURES="$PACKAGE_FEATURES relro" fi AC_CACHE_CHECK([whether $CC accepts BINDNOW flags], [ap_cv_cc_bindnow], [ save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,-z,now" AC_TRY_RUN([static int foo[30000]; int main () { return 0; }], [ap_cv_cc_bindnow=yes], [ap_cv_cc_bindnow=no], [ap_cv_cc_bindnow=yes]) LDFLAGS=$save_LDFLAGS ]) if test "$ap_cv_cc_bindnow" = "yes"; then SEC_LDFLAGS="$SEC_LDFLAGS -Wl,-z,now" PACKAGE_FEATURES="$PACKAGE_FEATURES bindnow" fi fi AC_CACHE_CHECK([whether $CC accepts "--as-needed"], [ap_cv_cc_as_needed], [ save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,--as-needed" AC_TRY_RUN([static int foo[30000]; int main () { return 0; }], [ap_cv_cc_as_needed=yes], [ap_cv_cc_as_needed=no], [ap_cv_cc_as_needed=yes]) LDFLAGS=$save_LDFLAGS ]) AC_CACHE_CHECK([whether $CC accepts "--version-script"], [ap_cv_cc_version_script], [ save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -Wl,--version-script=conftest.versions" echo "CONFTEST { };" >conftest.versions AC_TRY_RUN([static int foo[30000]; int main () { return 0; }], [ap_cv_cc_version_script=yes], [ap_cv_cc_version_script=no], [ap_cv_cc_version_script=yes]) rm -f conftest.versions LDFLAGS=$save_LDFLAGS ]) if test "$ap_cv_cc_version_script" = "yes"; then AC_SUBST(VERSCRIPT_LDFLAGS, ["-Wl,--version-script=\$(srcdir)/lib\$(call get_libname,\$<).versions"]) else AC_SUBST(VERSCRIPT_LDFLAGS, [""]) fi # define global include dirs INCLUDE_DIRS="$INCLUDE_DIRS -I\$(top_builddir)/include -I\$(top_srcdir)/include" INCLUDE_DIRS="$INCLUDE_DIRS -I\$(top_builddir)/include/corosync -I\$(top_srcdir)/include/corosync" # final build of *FLAGS CFLAGS="$ENV_CFLAGS $lt_prog_compiler_pic $SEC_FLAGS $OPT_CFLAGS $GDB_FLAGS \ $COVERAGE_CFLAGS $EXTRA_WARNINGS \ $WERROR_CFLAGS $LIBQB_CFLAGS \ $SNMP_INCLUDES" CPPFLAGS="$ENV_CPPFLAGS $ANSI_CPPFLAGS $INCLUDE_DIRS" LDFLAGS="$ENV_LDFLAGS $lt_prog_compiler_pic $SEC_LDFLAGS $COVERAGE_LDFLAGS" if test "$ap_cv_cc_as_needed" = "yes"; then LDFLAGS="$LDFLAGS -Wl,--as-needed" fi # substitute what we need: AC_SUBST([BASHPATH]) AC_SUBST([INITDDIR]) AC_SUBST([SYSTEMDDIR]) AC_SUBST([LOGDIR]) AC_SUBST([LOGROTATEDIR]) AC_SUBST([SOMAJOR]) AC_SUBST([SOMINOR]) AC_SUBST([SOMICRO]) AC_SUBST([SONAME]) AM_CONDITIONAL(INSTALL_MIB, test "${do_snmp}" = "1") AM_CONDITIONAL(INSTALL_DBUSCONF, test "${enable_dbus}" = "yes") AM_CONDITIONAL(AUGTOOL, test -n "${AUGTOOL}") AM_CONDITIONAL(BUILD_HTML_DOCS, test -n "${GROFF}") AC_SUBST([LINT_FLAGS]) AC_DEFINE_UNQUOTED([LOCALSTATEDIR], "$(eval echo ${localstatedir})", [localstate directory]) COROSYSCONFDIR=${sysconfdir}/corosync AC_SUBST([COROSYSCONFDIR]) AC_DEFINE_UNQUOTED([COROSYSCONFDIR], "$(eval echo ${COROSYSCONFDIR})", [corosync config directory]) AC_DEFINE_UNQUOTED([PACKAGE_FEATURES], "${PACKAGE_FEATURES}", [corosync built-in features]) AC_OUTPUT AC_MSG_RESULT([]) AC_MSG_RESULT([$PACKAGE configuration:]) AC_MSG_RESULT([ Version = ${VERSION}]) AC_MSG_RESULT([ Prefix = ${prefix}]) AC_MSG_RESULT([ Executables = ${sbindir}]) AC_MSG_RESULT([ Man pages = ${mandir}]) AC_MSG_RESULT([ Doc dir = ${docdir}]) 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([ System init.d directory = ${INITDDIR}]) AC_MSG_RESULT([ System systemd directory = ${SYSTEMDDIR}]) AC_MSG_RESULT([ Log directory = ${LOGDIR}]) AC_MSG_RESULT([ Log rotate directory = ${LOGROTATEDIR}]) AC_MSG_RESULT([ corosync config dir = ${COROSYSCONFDIR}]) AC_MSG_RESULT([ init config directory = ${INITCONFIGDIR}]) AC_MSG_RESULT([ Features =${PACKAGE_FEATURES}]) AC_MSG_RESULT([]) AC_MSG_RESULT([$PACKAGE build info:]) AC_MSG_RESULT([ Library SONAME = ${SONAME}]) LIB_MSG_RESULT(m4_shift(local_soname_list))dnl AC_MSG_RESULT([ Default optimization = ${OPT_CFLAGS}]) AC_MSG_RESULT([ Default debug options = ${GDB_CFLAGS}]) AC_MSG_RESULT([ Extra compiler warnings = ${EXTRA_WARNING}]) AC_MSG_RESULT([ Env. defined CFLAG = ${ENV_CFLAGS}]) AC_MSG_RESULT([ Env. defined CPPFLAGS = ${ENV_CPPFLAGS}]) AC_MSG_RESULT([ Env. defined LDFLAGS = ${ENV_LDFLAGS}]) AC_MSG_RESULT([ ANSI defined CPPFLAGS = ${ANSI_CPPFLAGS}]) AC_MSG_RESULT([ Coverage CFLAGS = ${COVERAGE_CFLAGS}]) AC_MSG_RESULT([ Coverage LDFLAGS = ${COVERAGE_LDFLAGS}]) AC_MSG_RESULT([ Fatal War. CFLAGS = ${WERROR_CFLAGS}]) AC_MSG_RESULT([ Final CFLAGS = ${CFLAGS}]) AC_MSG_RESULT([ Final CPPFLAGS = ${CPPFLAGS}]) AC_MSG_RESULT([ Final LDFLAGS = ${LDFLAGS}]) diff --git a/corosync.spec.in b/corosync.spec.in index 07c004c3..8af686e0 100644 --- a/corosync.spec.in +++ b/corosync.spec.in @@ -1,271 +1,297 @@ @ALPHATAG@ @NUMCOMM@ @DIRTY@ # Conditionals # Invoke "rpmbuild --without " or "rpmbuild --with " # to disable or enable specific features %bcond_with watchdog %bcond_with monitoring %bcond_with snmp %bcond_with dbus %bcond_with systemd %bcond_with xmlconf %bcond_with nozzle +%bcond_with vqsim %bcond_with runautogen %global gitver %{?numcomm:.%{numcomm}}%{?alphatag:.%{alphatag}}%{?dirty:.%{dirty}} %global gittarver %{?numcomm:.%{numcomm}}%{?alphatag:-%{alphatag}}%{?dirty:-%{dirty}} Name: corosync Summary: The Corosync Cluster Engine and Application Programming Interfaces Version: @version@ Release: 1%{?gitver}%{?dist} License: BSD URL: http://corosync.github.io/corosync/ Source0: http://build.clusterlabs.org/corosync/releases/%{name}-%{version}%{?gittarver}.tar.gz # Runtime bits # The automatic dependency overridden in favor of explicit version lock Requires: corosynclib%{?_isa} = %{version}-%{release} # Build bits BuildRequires: gcc %if 0%{?suse_version} BuildRequires: groff-full %else BuildRequires: groff %endif BuildRequires: libqb-devel BuildRequires: libknet1-devel BuildRequires: zlib-devel %if %{with runautogen} BuildRequires: autoconf automake libtool %endif %if %{with monitoring} BuildRequires: libstatgrab-devel %endif %if %{with snmp} BuildRequires: net-snmp-devel %endif %if %{with dbus} %if 0%{?suse_version} BuildRequires: dbus-1-devel %else BuildRequires: dbus-devel %endif %endif %if %{with nozzle} BuildRequires: libnozzle1-devel %endif %if %{with systemd} %{?systemd_requires} BuildRequires: systemd BuildRequires: systemd-devel %else Requires(post): /sbin/chkconfig Requires(preun): /sbin/chkconfig %endif %if %{with xmlconf} Requires: libxslt %endif +%if %{with vqsim} +BuildRequires: readline-devel +%endif %prep %setup -q -n %{name}-%{version}%{?gittarver} %build %if %{with runautogen} ./autogen.sh %endif %{configure} \ %if %{with watchdog} --enable-watchdog \ %endif %if %{with monitoring} --enable-monitoring \ %endif %if %{with snmp} --enable-snmp \ %endif %if %{with dbus} --enable-dbus \ %endif %if %{with systemd} --enable-systemd \ %endif %if %{with xmlconf} --enable-xmlconf \ %endif %if %{with nozzle} --enable-nozzle \ +%endif +%if %{with vqsim} + --enable-vqsim \ %endif --with-initddir=%{_initrddir} \ --with-systemddir=%{_unitdir} \ --docdir=%{_docdir} make %{_smp_mflags} %install make install DESTDIR=%{buildroot} %if %{with dbus} mkdir -p -m 0700 %{buildroot}/%{_sysconfdir}/dbus-1/system.d install -m 644 %{_builddir}/%{name}-%{version}%{?gittarver}/conf/corosync-signals.conf %{buildroot}/%{_sysconfdir}/dbus-1/system.d/corosync-signals.conf %endif ## tree fixup # drop static libs rm -f %{buildroot}%{_libdir}/*.a rm -f %{buildroot}%{_libdir}/*.la # drop docs and html docs for now rm -rf %{buildroot}%{_docdir}/* # /etc/sysconfig/corosync-notifyd mkdir -p %{buildroot}%{_sysconfdir}/sysconfig install -m 644 tools/corosync-notifyd.sysconfig.example \ %{buildroot}%{_sysconfdir}/sysconfig/corosync-notifyd # /etc/sysconfig/corosync install -m 644 init/corosync.sysconfig.example \ %{buildroot}%{_sysconfdir}/sysconfig/corosync %description This package contains the Corosync Cluster Engine Executive, several default APIs and libraries, default configuration files, and an init script. %post %if %{with systemd} && 0%{?systemd_post:1} %systemd_post corosync.service %else if [ $1 -eq 1 ]; then /sbin/chkconfig --add corosync || : fi %endif %preun %if %{with systemd} && 0%{?systemd_preun:1} %systemd_preun corosync.service %else if [ $1 -eq 0 ]; then /sbin/service corosync stop &>/dev/null || : /sbin/chkconfig --del corosync || : fi %endif %postun %if %{with systemd} && 0%{?systemd_postun:1} %systemd_postun corosync.service %endif %files %doc LICENSE %{_sbindir}/corosync %{_sbindir}/corosync-keygen %{_sbindir}/corosync-cmapctl %{_sbindir}/corosync-cfgtool %{_sbindir}/corosync-cpgtool %{_sbindir}/corosync-quorumtool %{_sbindir}/corosync-notifyd %{_bindir}/corosync-blackbox %if %{with xmlconf} %{_bindir}/corosync-xmlproc %dir %{_datadir}/corosync %{_datadir}/corosync/xml2conf.xsl %{_mandir}/man8/corosync-xmlproc.8* %{_mandir}/man5/corosync.xml.5* %endif %dir %{_sysconfdir}/corosync %dir %{_sysconfdir}/corosync/uidgid.d %config(noreplace) %{_sysconfdir}/corosync/corosync.conf.example %config(noreplace) %{_sysconfdir}/sysconfig/corosync-notifyd %config(noreplace) %{_sysconfdir}/sysconfig/corosync %config(noreplace) %{_sysconfdir}/logrotate.d/corosync %if %{with dbus} %{_sysconfdir}/dbus-1/system.d/corosync-signals.conf %endif %if %{with snmp} %{_datadir}/snmp/mibs/COROSYNC-MIB.txt %endif %if %{with systemd} %{_unitdir}/corosync.service %{_unitdir}/corosync-notifyd.service %else %{_initrddir}/corosync %{_initrddir}/corosync-notifyd %endif %dir %{_localstatedir}/lib/corosync %dir %{_localstatedir}/log/cluster %{_mandir}/man7/corosync_overview.7* %{_mandir}/man8/corosync.8* %{_mandir}/man8/corosync-blackbox.8* %{_mandir}/man8/corosync-cmapctl.8* %{_mandir}/man8/corosync-keygen.8* %{_mandir}/man8/corosync-cfgtool.8* %{_mandir}/man8/corosync-cpgtool.8* %{_mandir}/man8/corosync-notifyd.8* %{_mandir}/man8/corosync-quorumtool.8* %{_mandir}/man5/corosync.conf.5* %{_mandir}/man5/votequorum.5* %{_mandir}/man8/cmap_keys.8* # library # %package -n corosynclib Summary: The Corosync Cluster Engine Libraries %description -n corosynclib This package contains corosync libraries. %files -n corosynclib %doc LICENSE %{_libdir}/libcfg.so.* %{_libdir}/libcpg.so.* %{_libdir}/libcmap.so.* %{_libdir}/libquorum.so.* %{_libdir}/libvotequorum.so.* %{_libdir}/libsam.so.* %{_libdir}/libcorosync_common.so.* %post -n corosynclib -p /sbin/ldconfig %postun -n corosynclib -p /sbin/ldconfig %package -n corosynclib-devel Summary: The Corosync Cluster Engine Development Kit Requires: corosynclib%{?_isa} = %{version}-%{release} Requires: pkgconfig Provides: corosync-devel = %{version} %description -n corosynclib-devel This package contains include files and man pages used to develop using The Corosync Cluster Engine APIs. %files -n corosynclib-devel %doc LICENSE %dir %{_includedir}/corosync/ %{_includedir}/corosync/corodefs.h %{_includedir}/corosync/cfg.h %{_includedir}/corosync/cmap.h %{_includedir}/corosync/corotypes.h %{_includedir}/corosync/cpg.h %{_includedir}/corosync/hdb.h %{_includedir}/corosync/sam.h %{_includedir}/corosync/quorum.h %{_includedir}/corosync/votequorum.h %{_libdir}/libcfg.so %{_libdir}/libcpg.so %{_libdir}/libcmap.so %{_libdir}/libquorum.so %{_libdir}/libvotequorum.so %{_libdir}/libsam.so %{_libdir}/libcorosync_common.so %{_libdir}/pkgconfig/*.pc %{_mandir}/man3/cpg_*3* %{_mandir}/man3/quorum_*3* %{_mandir}/man3/votequorum_*3* %{_mandir}/man3/sam_*3* %{_mandir}/man3/cmap_*3* +%if %{with vqsim} +%package -n corosync-vqsim +Summary: The Corosync Cluster Engine - Votequorum Simulator +Requires: corosynclib%{?_isa} = %{version}-%{release} +Requires: pkgconfig + +%description -n corosync-vqsim +A command-line simulator for the corosync votequorum subsystem. +It uses the same code as the corosync quorum system but forks +them into subprocesses to simulate nodes. +Nodes can be added and removed as well as partitioned (to simulate +network splits) + +%files -n corosync-vqsim +%doc LICENSE +%{_bindir}/corosync-vqsim +%{_mandir}/man8/corosync-vqsim.8* +%endif + %changelog * @date@ Autotools generated version - @version@-1-@numcomm@.@alphatag@.@dirty@ - Autotools generated version diff --git a/man/Makefile.am b/man/Makefile.am index d1b7f94e..8e4eda3d 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -1,181 +1,185 @@ # Copyright (c) 2004 MontaVista Software, Inc. # Copyright (c) 2009, 2012, 2014 Red Hat, Inc. # # Authors: Steven Dake (sdake@redhat.com) # Fabio M. Di Nitto (fdinitto@redhat.com) # # All rights reserved. # # This software licensed under BSD license, the text of which follows: # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # - Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # - Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # - Neither the name of the MontaVista Software, Inc. nor the names of its # contributors may be used to endorse or promote products derived from this # software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. MAINTAINERCLEANFILES = Makefile.in xml_man = corosync-xmlproc.8 \ corosync.xml.5 INDEX_HTML = index.html autogen_man = cpg_context_get.3 \ cpg_context_set.3 \ cpg_dispatch.3 \ cpg_fd_get.3 \ cpg_finalize.3 \ cpg_initialize.3 \ cpg_join.3 \ cpg_leave.3 \ cpg_local_get.3 \ cpg_mcast_joined.3 \ cpg_model_initialize.3 \ cpg_zcb_mcast_joined.3 \ cpg_zcb_alloc.3 \ cpg_zcb_free.3 \ cpg_membership_get.3 \ cpg_iteration_finalize.3 \ cpg_iteration_initialize.3 \ cpg_iteration_next.3 \ quorum_initialize.3 \ quorum_finalize.3 \ quorum_fd_get.3 \ quorum_dispatch.3 \ quorum_context_get.3 \ quorum_context_set.3 \ quorum_getquorate.3 \ quorum_trackstart.3 \ quorum_trackstop.3 \ votequorum_dispatch.3 \ votequorum_fd_get.3 \ votequorum_context_get.3 \ votequorum_context_set.3 \ votequorum_finalize.3 \ votequorum_getinfo.3 \ votequorum_initialize.3 \ votequorum_setexpected.3 \ votequorum_setvotes.3 \ votequorum_trackstart.3 \ votequorum_trackstop.3 \ votequorum_qdevice_register.3 \ votequorum_qdevice_unregister.3 \ votequorum_qdevice_update.3 \ votequorum_qdevice_master_wins.3 \ votequorum_qdevice_poll.3 \ sam_data_getsize.3 \ sam_data_restore.3 \ sam_data_store.3 \ sam_finalize.3 \ sam_hc_callback_register.3 \ sam_hc_send.3 \ sam_initialize.3 \ sam_mark_failed.3 \ sam_register.3 \ sam_start.3 \ sam_stop.3 \ sam_warn_signal_set.3 \ cmap_context_get.3 \ cmap_dec.3 \ cmap_iter_init.3 \ cmap_get.3 \ cmap_inc.3 \ cmap_set.3 \ cmap_iter_next.3 \ cmap_delete.3 \ cmap_iter_finalize.3 \ cmap_finalize.3 \ cmap_dispatch.3 \ cmap_initialize.3 \ cmap_initialize_map.3 \ cmap_track_add.3 \ cmap_context_set.3 \ cmap_fd_get.3 \ cmap_track_delete.3 autogen_common = ipc_common.sh.errors EXTRA_DIST = $(INDEX_HTML) \ $(xml_man) \ $(autogen_man:%=%.in) \ $(autogen_common) man_MANS = $(autogen_man) dist_man_MANS = corosync.conf.5 \ votequorum.5 \ corosync.8 \ corosync-cmapctl.8 \ corosync-blackbox.8 \ corosync-keygen.8 \ corosync-cfgtool.8 \ corosync-cpgtool.8 \ corosync-notifyd.8 \ corosync-quorumtool.8 \ corosync_overview.7 \ cpg_overview.3 \ quorum_overview.3 \ votequorum_overview.3 \ sam_overview.3 \ cmap_overview.3 \ cmap_keys.8 +if BUILD_VQSIM +dist_man_MANS += corosync-vqsim.8 +endif + if INSTALL_XMLCONF dist_man_MANS += $(xml_man) endif HTML_DOCS = $(dist_man_MANS:%=%.html) $(man_MANS:%=%.html) # developer man page generation %.3: %.3.in $(autogen_common) @echo Generating $@ man page && \ rm -f $@-t-t $@-t $@ && \ DATE_FMT="%Y-%m-%d" && \ SOURCE_DATE_EPOCH="$${SOURCE_DATE_EPOCH:-$$(date +%s)}" && \ date="$$(date -u -d "@$$SOURCE_DATE_EPOCH" "+$$DATE_FMT" 2>/dev/null || date -u -r "$$SOURCE_DATE_EPOCH" "+$$DATE_FMT" 2>/dev/null || date -u "+$$DATE_FMT")" && \ $(AWK) "{print}(\$$1 ~ /@COMMONIPCERRORS@/){exit 0}" ${top_srcdir}/man/$@.in > $@-t-t && \ cat ${top_srcdir}/man/$(autogen_common) >> $@-t-t && \ $(AWK) -v p=0 "(\$$1 ~ /@COMMONIPCERRORS@/){p = 1} {if(p==1)print}" ${top_srcdir}/man/$@.in >> $@-t-t && \ cat $@-t-t | \ $(SED) -e 's#@BUILDDATE@#'$$date'#g' \ -e 's#@COMMONIPCERRORS@##g' \ > $@-t && \ rm -f $@-t-t && \ mv $@-t $@ clean-local: rm -rf $(HTML_DOCS) $(autogen_man) if BUILD_HTML_DOCS %.html: % $(GROFF) -mandoc -Thtml $^ > $@ install-data-local: $(INSTALL) -d $(DESTDIR)/${docdir}/html $(INSTALL) -m 644 ${srcdir}/$(INDEX_HTML) $(HTML_DOCS) $(DESTDIR)/${docdir}/html/ uninstall-local: cd $(DESTDIR)/${docdir}/html && rm -f $(INDEX_HTML) $(HTML_DOCS) rmdir $(DESTDIR)/${docdir}/html 2> /dev/null || : all-local: $(HTML_DOCS) endif diff --git a/man/corosync-vqsim.8 b/man/corosync-vqsim.8 new file mode 100644 index 00000000..26a6468e --- /dev/null +++ b/man/corosync-vqsim.8 @@ -0,0 +1,94 @@ +.\"/* +.\" * Copyright (C) 2019 Red Hat, Inc. +.\" * +.\" * All rights reserved. +.\" * +.\" * Author: Christine Caulfield +.\" * +.\" * This software licensed under BSD license, the text of which follows: +.\" * +.\" * Redistribution and use in source and binary forms, with or without +.\" * modification, are permitted provided that the following conditions are met: +.\" * +.\" * - Redistributions of source code must retain the above copyright notice, +.\" * this list of conditions and the following disclaimer. +.\" * - Redistributions in binary form must reproduce the above copyright notice, +.\" * this list of conditions and the following disclaimer in the documentation +.\" * and/or other materials provided with the distribution. +.\" * - Neither the name of the MontaVista Software, Inc. nor the names of its +.\" * contributors may be used to endorse or promote products derived from this +.\" * software without specific prior written permission. +.\" * +.\" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +.\" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +.\" * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +.\" * THE POSSIBILITY OF SUCH DAMAGE. +.\" */ +.TH COROSYNC-VQSIM 8 2019-05-10 +.SH NAME +corosync-vqsim \- The votequorum simulator +.SH SYNOPSIS +.B "corosync-vqsim [\-c config_file] [\-o output file] [\-n] [\-h]" +.SH DESCRIPTION +.B corosync-vqsim +simulates the quorum functions of corosync in a single program. it can simulate +multiple nodes, network splits and a basic quorum device. + +By default vqsim will build a virtual cluster of all the nodes in the corosync.conf file, +each 'node' running in a forked subprocess (and thus asynchronously). It then provides a +command-line interface to add (up) or remove (down) nodes, and cause network splits and +rejoins. After each event it shows the new quorum status for all nodes. + +Nodes in vqsim are always referred to by their nodeid (the IP address is meaningless) and +optionally by a 'partition' which precedes the nodeid with a colon. By default all nodes +are in partition 0. Nodes can be moved between partitions using the split and join commands. +Multiple nodes can be split and joined at the same time. + +To script vqsim you must send input to it via a pipe rather than just redirecting STDIN. This +is because it runs asynchronously to enable the virtual 'nodes' to report status when needed. +(eg if you kill a subprocess using the 'kill(1)' command it gets removed from the cluster). + +By default vqsim will wait for all nodes in all partitions to reach the same +ring sequence number before returning a prompt, +there is a timeout associated with this in case of a 'node' failure and exceeding this timeout +can (optionally) quit the program signalling an error. + +You can disable waiting using the 'sync off' command or the -n command-line option. This can easily +cause unexpected behaviour so use it with care. + +The number of votes per node is read from corosync.conf. New nodes added using the 'up' command +will copy their number of votes from the first node in corosync.conf. This may not be what you +expect and I might fix it in future. As most clusters have only 1 vote per node (and this is +strongly recommended) then this should rarely be a problem. + +Once you have the 'vqsim> ' prompt you can type 'help' and get a list of sub-commands. + +.SH OPTIONS +.TP +.B -c +This specifies the fully qualified path to the corosync configuration file. + +The default is /etc/corosync/corosync.conf. +.TP +.B -o +Specifies the output destination. STDOUT by default. +.TP +.B -n +Don't pause after each command, come straight back to a prompt. Use with care! + +.TP +.B -h +Display a brief help message +.SH SEE ALSO +.BR corosync (9), +.BR corosync.conf (5), +.SH AUTHOR +Christine Caulfield +.PP diff --git a/vqsim/Makefile.am b/vqsim/Makefile.am index 2a765440..9a7fbf6f 100644 --- a/vqsim/Makefile.am +++ b/vqsim/Makefile.am @@ -1,52 +1,54 @@ # # Copyright (c) 2009 Red Hat, Inc. # # Authors: Andrew Beekhof # Steven Dake (sdake@redhat.com) # # This software licensed under BSD license, the text of which follows: # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # - Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # - Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # - Neither the name of the MontaVista Software, Inc. nor the names of its # contributors may be used to endorse or promote products derived from this # software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. -MAINTAINERCLEANFILES = Makefile.in +MAINTAINERCLEANFILES = Makefile.in if BUILD_VQSIM -noinst_PROGRAMS = vqsim +noinst_HEADERS = vqsim.h -vqsim_LDADD = $(top_builddir)/common_lib/libcorosync_common.la \ - ../exec/corosync-votequorum.o ../exec/corosync-icmap.o ../exec/corosync-logsys.o \ - ../exec/corosync-coroparse.o ../exec/corosync-logconfig.o \ - ../exec/corosync-util.o \ - $(LIBQB_LIBS) +bin_PROGRAMS = corosync-vqsim + +corosync_vqsim_LDADD = $(top_builddir)/common_lib/libcorosync_common.la \ + ../exec/corosync-votequorum.o ../exec/corosync-icmap.o \ + ../exec/corosync-coroparse.o ../exec/corosync-logconfig.o \ + ../exec/corosync-util.o ../exec/corosync-logsys.o \ + $(LIBQB_LIBS) if VQSIM_READLINE -vqsim_LDADD += -lreadline +corosync_vqsim_LDADD += -lreadline endif -vqsim_DEPENDENCIES = $(top_builddir)/common_lib/libcorosync_common.la +corosync_vqsim_DEPENDENCIES = $(top_builddir)/common_lib/libcorosync_common.la -vqsim_SOURCES = vqmain.c parser.c vq_object.c vqsim_vq_engine.c +corosync_vqsim_SOURCES = vqmain.c parser.c vq_object.c vqsim_vq_engine.c endif diff --git a/vqsim/parser.c b/vqsim/parser.c index 2fc10431..d13e6780 100644 --- a/vqsim/parser.c +++ b/vqsim/parser.c @@ -1,331 +1,418 @@ /* Parses the interactive commands */ #include #include #include #include #include #ifdef HAVE_READLINE_HISTORY_H #include #endif #include #include "vqsim.h" static void do_usage(void) { printf(" All node IDs in the cluster are unique and belong to a numbered 'partition' (default=0)\n"); printf("\n"); printf("up [:][[,] ...] [[:][...]] [...]\n"); printf(" bring node(s) online in the specified partition(s)\n"); printf("down ,[...]\n"); printf(" send nodes offline (shut them down)\n"); printf("move/split [:][[,] ...] [[:][...]] [...]\n"); printf(" Move nodes from one partition to another (netsplit)\n"); printf(" here is the partition to move the nodes to\n"); printf("join [] ... \n"); printf(" Join partitions together (reverse of a netsplit)\n"); printf("qdevice on|off [:][[,] ...] [[:][...]] [...]\n"); printf(" Enable quorum device in specified nodes\n"); printf("autofence on|off\n"); printf(" automatically 'down' nodes on inquorate side on netsplit\n"); + printf("timeout (default 250)\n"); + printf(" Wait a maximum of milli-seconds for the next command to complete.\n"); + printf("sync on|off (default on)\n"); + printf(" enable/disable synchronous execution of commands (wait for completion)\n"); + printf("assert on|off (default off)\n"); + printf(" Abort the simulation run if a timeout expires\n"); printf("show Show current nodes status\n"); printf("exit\n\n"); } -typedef void (*cmd_routine_t)(int argc, char **argv); +/* Commands return 0 if they return immediately, >1 if we are waiting for replies from nodes */ +typedef int (*cmd_routine_t)(int argc, char **argv); -static void run_up_cmd(int argc, char **argv); -static void run_down_cmd(int argc, char **argv); -static void run_join_cmd(int argc, char **argv); -static void run_move_cmd(int argc, char **argv); -static void run_exit_cmd(int argc, char **argv); -static void run_show_cmd(int argc, char **argv); -static void run_autofence_cmd(int argc, char **argv); -static void run_qdevice_cmd(int argc, char **argv); +static int run_up_cmd(int argc, char **argv); +static int run_down_cmd(int argc, char **argv); +static int run_join_cmd(int argc, char **argv); +static int run_move_cmd(int argc, char **argv); +static int run_exit_cmd(int argc, char **argv); +static int run_show_cmd(int argc, char **argv); +static int run_timeout_cmd(int argc, char **argv); +static int run_assert_cmd(int argc, char **argv); +static int run_autofence_cmd(int argc, char **argv); +static int run_qdevice_cmd(int argc, char **argv); +static int run_sync_cmd(int argc, char **argv); static struct cmd_list_struct { const char *cmd; int min_args; cmd_routine_t cmd_runner; } cmd_list[] = { { "up", 1, run_up_cmd}, { "down", 1, run_down_cmd}, { "move", 2, run_move_cmd}, { "split", 2, run_move_cmd}, { "join", 2, run_join_cmd}, { "autofence", 1, run_autofence_cmd}, { "qdevice", 1, run_qdevice_cmd}, { "show", 0, run_show_cmd}, + { "timeout", 1, run_timeout_cmd}, + { "sync", 1, run_sync_cmd}, + { "assert", 1, run_assert_cmd}, { "exit", 0, run_exit_cmd}, { "quit", 0, run_exit_cmd}, { "q", 0, run_exit_cmd}, }; static int num_cmds = (sizeof(cmd_list)) / sizeof(struct cmd_list_struct); #define MAX_ARGS 1024 /* Takes a :[[,]...] list and return it as a partition and a list of nodes. Returns 0 if successful, -1 if not */ static int parse_partition_nodelist(char *string, int *partition, int *num_nodes, int **retnodes) { int i; int nodecount; int len; int last_comma; char *nodeptr; int *nodes; char *colonptr = strchr(string, ':'); if (colonptr) { *colonptr = '\0'; nodeptr = colonptr+1; *partition = atoi(string); } else { /* Default to partition 0 */ *partition = 0; nodeptr = string; } /* Count the number of commas and allocate space for the nodes */ nodecount = 0; for (i=0; i MAX_NODES) { return -1; } nodes = malloc(sizeof(int) * nodecount); if (!nodes) { return -1; } nodecount = 0; last_comma = 0; len = strlen(nodeptr); for (i=0; i<=len; i++) { if (nodeptr[i] == ',' || nodeptr[i] == '\0') { nodeptr[i] = '\0'; nodes[nodecount++] = atoi(&nodeptr[last_comma]); last_comma = i+1; } } *num_nodes = nodecount; *retnodes = nodes; return 0; } void parse_input_command(char *rl_cmd) { int i; int argc = 0; int valid_cmd = 0; char *argv[MAX_ARGS]; int last_arg_start = 0; int last_was_space = 0; int len; + int ret = 0; char *cmd; /* ^D quits */ if (rl_cmd == NULL) { - run_exit_cmd(0, NULL); + (void)run_exit_cmd(0, NULL); + } + /* '#' starts a comment */ + if (rl_cmd[0] == '#') { + return; } cmd = strdup(rl_cmd); /* Split cmd up into args * destroying the original string mwahahahaha */ len = strlen(cmd); /* Span leading spaces */ for (i=0; cmd[i] == ' '; i++) ; last_arg_start = i; for (; i<=len; i++) { if (cmd[i] == ' ' || cmd[i] == '\0') { /* Allow multiple spaces */ if (last_was_space) { continue; } cmd[i] = '\0'; last_was_space = 1; argv[argc] = &cmd[last_arg_start]; argc++; } else { if (last_was_space) { last_arg_start = i; } last_was_space = 0; } } /* Ignore null commands */ if (strlen(argv[0]) == 0) { free(cmd); - return; + resume_kb_input(0); + return; } #ifdef HAVE_READLINE_HISTORY_H add_history(rl_cmd); #endif /* Dispatch command */ for (i=0; i #include #include #include #include #include #include #include #include #ifdef HAVE_READLINE_READLINE_H #include #else #include /* isatty */ #endif #include "../exec/votequorum.h" #include "../exec/service.h" #include #include #include "icmap.h" #include "vqsim.h" /* Easier than including the config file with a ton of conflicting dependencies */ extern int coroparse_configparse (icmap_map_t config_map, const char **error_string); extern int corosync_log_config_read (const char **error_string); +static int stdin_read_fn(int32_t fd, int32_t revents, void *data); /* 'Keep the compiler happy' time */ const char *corosync_get_config_file(void); /* One of these per partition */ struct vq_partition { TAILQ_HEAD(, vq_node) nodelist; struct memb_ring_id ring_id; int num; }; /* One of these per node */ struct vq_node { vq_object_t instance; unsigned int nodeid; int fd; struct vq_partition *partition; TAILQ_ENTRY(vq_node) entries; /* Last status */ int last_quorate; struct memb_ring_id last_ring_id; int last_view_list[MAX_NODES]; int last_view_list_entries; }; static struct vq_partition partitions[MAX_PARTITIONS]; static qb_loop_t *poll_loop; static int autofence; static int check_for_quorum; static FILE *output_file; -static int nosync; +static int sync_cmds = 1; static qb_loop_timer_handle kb_timer; -static ssize_t wait_count; -static ssize_t wait_count_to_unblock; +static int waiting_for_sync = 0; +static int is_tty; +static int assert_on_timeout; +static uint64_t command_timeout = 250000000L; static struct vq_node *find_by_pid(pid_t pid); static void send_partition_to_nodes(struct vq_partition *partition, int newring); -static void start_kb_input(void); static void start_kb_input_timeout(void *data); +static void finish_wait_timeout(void *data); #ifndef HAVE_READLINE_READLINE_H #define INPUT_BUF_SIZE 1024 static char input_buf[INPUT_BUF_SIZE]; static size_t input_buf_term = 0; -static int is_tty; #endif /* 'Keep the compiler happy' time */ static char corosync_config_file[PATH_MAX + 1] = COROSYSCONFDIR "/corosync.conf"; const char *corosync_get_config_file(void) { - return (corosync_config_file); } /* Tell all non-quorate nodes to quit */ static void force_fence(void) { int i; struct vq_node *vqn; for (i=0; iinstance); } } } /* Save quorum state from the incoming message */ static void save_quorum_state(struct vq_node *node, struct vqsim_quorum_msg *qmsg) { node->last_quorate = qmsg->quorate; memcpy(&node->last_ring_id, &qmsg->ring_id, sizeof(struct memb_ring_id)); memcpy(node->last_view_list, qmsg->view_list, sizeof(int) * qmsg->view_list_entries); node->last_view_list_entries = qmsg->view_list_entries; /* If at least one node is quorate and autofence is enabled, then fence everyone who is not quorate */ if (check_for_quorum && qmsg->quorate & autofence) { check_for_quorum = 0; force_fence(); } } /* Print current node state */ static void print_quorum_state(struct vq_node *node) { int i; if (node->last_quorate < 0) { fprintf(output_file, "%d:%02d: q=UNINITIALIZED\n", node->partition->num, node->nodeid); return; } fprintf(output_file, "%d:%02d: q=%d ring=[%d/%lld] ", node->partition->num, node->nodeid, node->last_quorate, node->last_ring_id.nodeid, node->last_ring_id.seq); fprintf(output_file, "nodes=["); for (i = 0; i < node->last_view_list_entries; i++) { if (i) { fprintf(output_file, " "); } fprintf(output_file, "%d", node->last_view_list[i]); } fprintf(output_file, "]\n"); } static void propogate_vq_message(struct vq_node *vqn, const char *msg, int len) { struct vq_node *other_vqn; /* Send it to everyone in that node's partition (including itself) */ TAILQ_FOREACH(other_vqn, &vqn->partition->nodelist, entries) { write(other_vqn->fd, msg, len); } } + +static void cmd_show_prompt_if_needed(void) +{ + qb_loop_timer_del(poll_loop, kb_timer); + if (is_tty) { + printf("vqsim> "); + fflush(stdout); + } else { + printf("#vqsim> "); + fflush(stdout); + } + +} + +void resume_kb_input(int show_status) +{ + /* If running synchronously, we don't display + the quorum messages as they come in. So run 'show' commamnd + */ + if (show_status && waiting_for_sync) { + cmd_show_node_states(); + } + + waiting_for_sync = 0; + + if (qb_loop_poll_add(poll_loop, + QB_LOOP_MED, + STDIN_FILENO, + POLLIN | POLLERR, + NULL, + stdin_read_fn)) { + if (errno != EEXIST) { + perror("qb_loop_poll_add1 returned error"); + } + } + /* Always shows the prompt here, cos we cleared waiting_for_sync */ + cmd_show_prompt_if_needed(); +} + +/* Return true (1) if all nodes in each partition have the same ring id, false(0) otherwise */ +static int all_nodes_consistent(void) +{ + int i; + struct vq_node *vqn; + struct memb_ring_id last_ring_id; + + for (i=0; ilast_ring_id.seq) { + return 0; + } + last_ring_id.seq = vqn->last_ring_id.seq; + } + } + return 1; +} + static int vq_parent_read_fn(int32_t fd, int32_t revents, void *data) { char msgbuf[8192]; int msglen; struct vqsim_msg_header *msg; struct vqsim_quorum_msg *qmsg; struct vq_node *vqn = data; if (revents == POLLIN) { msglen = read(fd, msgbuf, sizeof(msgbuf)); if (msglen < 0) { perror("read failed"); } if (msglen > 0) { msg = (void*)msgbuf; switch (msg->type) { case VQMSG_QUORUM: - if (!nosync && --wait_count_to_unblock <= 0) - qb_loop_timer_del(poll_loop, kb_timer); qmsg = (void*)msgbuf; save_quorum_state(vqn, qmsg); - print_quorum_state(vqn); - if (!nosync && wait_count_to_unblock <= 0) - start_kb_input(); + if (!sync_cmds) { + print_quorum_state(vqn); + } + + /* Have the partitions stabilised? */ + if (sync_cmds && waiting_for_sync && + all_nodes_consistent()) { + qb_loop_timer_del(poll_loop, kb_timer); + resume_kb_input(sync_cmds); + } break; case VQMSG_EXEC: /* Message from votequorum, pass around the partition */ propogate_vq_message(vqn, msgbuf, msglen); break; case VQMSG_QUIT: case VQMSG_SYNC: case VQMSG_QDEVICE: case VQMSG_QUORUMQUIT: /* not used here */ break; } } } if (revents == POLLERR) { fprintf(stderr, "pollerr on %d\n", vqn->nodeid); } return 0; } static int read_corosync_conf(void) { int res; const char *error_string; int err = icmap_init(); if (!err) { fprintf(stderr, "icmap_init failed\n"); } /* Load corosync.conf */ logsys_format_set(NULL); res = coroparse_configparse(icmap_get_global_map(), &error_string); if (res == -1) { - log_printf (LOGSYS_LEVEL_INFO, "Error loading corosyc.conf %s", error_string); + log_printf (LOGSYS_LEVEL_INFO, "Error loading corosync.conf %s", error_string); return -1; } else { res = corosync_log_config_read (&error_string); if (res < 0) { log_printf (LOGSYS_LEVEL_INFO, "error reading log config %s", error_string); syslog (LOGSYS_LEVEL_INFO, "error reading log config %s", error_string); } else { logsys_config_apply(); } } if (logsys_thread_start() != 0) { log_printf (LOGSYS_LEVEL_ERROR, "Can't initialize log thread"); return -1; } return 0; } static void remove_node(struct vq_node *node) { struct vq_partition *part; part = node->partition; /* Remove from partition list */ TAILQ_REMOVE(&part->nodelist, node, entries); free(node); - wait_count--; - /* Rebuild quorum */ send_partition_to_nodes(part, 1); } static int32_t sigchld_handler(int32_t sig, void *data) { pid_t pid; int status; struct vq_node *vqn; const char *exit_status=""; char text[132]; pid = wait(&status); if (WIFEXITED(status)) { vqn = find_by_pid(pid); if (vqn) { switch (WEXITSTATUS(status)) { case 0: exit_status = "(on request)"; break; case 1: exit_status = "(autofenced)"; break; default: sprintf(text, "(exit code %d)", WEXITSTATUS(status)); break; } - printf("%d:%02d Quit %s\n", vqn->partition->num, vqn->nodeid, exit_status); + printf("%d:%02d: Quit %s\n", vqn->partition->num, vqn->nodeid, exit_status); remove_node(vqn); } else { fprintf(stderr, "Unknown child %d exited with status %d\n", pid, WEXITSTATUS(status)); } } if (WIFSIGNALED(status)) { vqn = find_by_pid(pid); if (vqn) { printf("%d:%02d exited on signal %d%s\n", vqn->partition->num, vqn->nodeid, WTERMSIG(status), WCOREDUMP(status)?" (core dumped)":""); remove_node(vqn); } else { fprintf(stderr, "Unknown child %d exited with status %d%s\n", pid, WTERMSIG(status), WCOREDUMP(status)?" (core dumped)":""); } } return 0; } static void send_partition_to_nodes(struct vq_partition *partition, int newring) { struct vq_node *vqn; int nodelist[MAX_NODES]; int nodes = 0; int first = 1; if (newring) { /* Simulate corosync incrementing the seq by 4 for added authenticity */ partition->ring_id.seq += 4; } /* Build the node list */ TAILQ_FOREACH(vqn, &partition->nodelist, entries) { nodelist[nodes++] = vqn->nodeid; if (first) { partition->ring_id.nodeid = vqn->nodeid; first = 0; } } TAILQ_FOREACH(vqn, &partition->nodelist, entries) { vq_set_nodelist(vqn->instance, &partition->ring_id, nodelist, nodes); } } static void init_partitions(void) { int i; for (i=0; ilast_quorate = -1; /* mark "uninitialized" */ newvq->instance = vq_create_instance(poll_loop, nodeid); if (!newvq->instance) { fprintf(stderr, "ERR: could not create vq instance nodeid %d\n", nodeid); return (pid_t) -1; } newvq->partition = &partitions[partno]; newvq->nodeid = nodeid; newvq->fd = vq_get_parent_fd(newvq->instance); TAILQ_INSERT_TAIL(&partitions[partno].nodelist, newvq, entries); if (qb_loop_poll_add(poll_loop, QB_LOOP_MED, newvq->fd, POLLIN | POLLERR, newvq, vq_parent_read_fn)) { perror("qb_loop_poll_add returned error"); return (pid_t) -1; } /* Send sync with all the nodes so far in it. */ send_partition_to_nodes(&partitions[partno], 1); return vq_get_pid(newvq->instance); } return (pid_t) -1; } static size_t create_nodes_from_config(void) { icmap_iter_t iter; char tmp_key[ICMAP_KEYNAME_MAXLEN]; uint32_t node_pos; uint32_t nodeid; const char *iter_key; int res; pid_t pid; size_t ret = 0; init_partitions(); iter = icmap_iter_init("nodelist.node."); while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) { res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, tmp_key); if (res != 2) { continue; } if (strcmp(tmp_key, "ring0_addr") != 0) { continue; } snprintf(tmp_key, ICMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos); if (icmap_get_uint32(tmp_key, &nodeid) == CS_OK) { pid = create_node(nodeid, 0); if (pid == (pid_t) -1) { fprintf(stderr, "ERR: nodeid %d could not be spawned\n", nodeid); exit(1); } ret++; } } icmap_iter_finalize(iter); return ret; } static struct vq_node *find_node(int nodeid) { int i; struct vq_node *vqn; for (i=0; inodeid == nodeid) { return vqn; } } } return NULL; } static struct vq_node *find_by_pid(pid_t pid) { int i; struct vq_node *vqn; for (i=0; iinstance) == pid) { return vqn; } } } return NULL; } /* Routines called from the parser */ -void cmd_start_new_node(int nodeid, int partition) + + +/* + * The parser calls this before running a command where + * we might have to wait for a result to come back. + */ +void cmd_start_sync_command() +{ + if (sync_cmds) { + qb_loop_poll_del(poll_loop, STDIN_FILENO); + qb_loop_timer_add(poll_loop, + QB_LOOP_MED, + command_timeout, + NULL, + finish_wait_timeout, + &kb_timer); + waiting_for_sync = 1; + } +} + +int cmd_start_new_node(int nodeid, int partition) { struct vq_node *node; node = find_node(nodeid); if (node) { fprintf(stderr, "ERR: nodeid %d already exists in partition %d\n", nodeid, node->partition->num); - return; + return -1; } - qb_loop_poll_del(poll_loop, STDIN_FILENO); - create_node(nodeid, partition); - if (!nosync) { - /* Delay kb input handling by 0.25 second when we've just - added a node; expect that the delay will be cancelled - substantially earlier once it has reported its quorum info - (the delay is in fact a failsafe input enabler here) */ - qb_loop_timer_add(poll_loop, - QB_LOOP_MED, - 250000000, - NULL, - start_kb_input_timeout, - &kb_timer); + if (create_node(nodeid, partition) == -1) { + return -1; } + return 0; } void cmd_stop_all_nodes() { int i; struct vq_node *vqn; for (i=0; iinstance); } } } void cmd_show_node_states() { int i; struct vq_node *vqn; for (i=0; iinstance); /* Node will be removed when the child process exits */ + return 0; } /* Move all nodes in 'nodelist' into partition 'partition' */ void cmd_move_nodes(int partition, int num_nodes, int *nodelist) { int i; struct vq_node *node; + struct vq_node *vqn; + int total_nodes = num_nodes; + + /* Work out the number of nodes affected */ + TAILQ_FOREACH(vqn, &partitions[partition].nodelist, entries) { + total_nodes++; + } for (i=0; ipartition->nodelist, node, entries); /* Add it to the new partition */ TAILQ_INSERT_TAIL(&partitions[partition].nodelist, node, entries); node->partition = &partitions[partition]; } else { printf("ERR: node %d does not exist\n", nodelist[i]); } } } /* Take all the nodes in part2 and join them to part1 */ void cmd_join_partitions(int part1, int part2) { struct vq_node *vqn; + int total_nodes=0; + + /* Work out the number of nodes affected */ + total_nodes += nodes_in_partition(part1); + total_nodes += nodes_in_partition(part2); /* TAILQ_FOREACH is not delete safe *sigh* */ retry: TAILQ_FOREACH(vqn, &partitions[part2].nodelist, entries) { TAILQ_REMOVE(&vqn->partition->nodelist, vqn, entries); TAILQ_INSERT_TAIL(&partitions[part1].nodelist, vqn, entries); vqn->partition = &partitions[part1]; goto retry; } } void cmd_set_autofence(int onoff) { autofence = onoff; fprintf(output_file, "#autofence: %s\n", onoff?"on":"off"); } +void cmd_set_sync(int onoff) +{ + autofence = onoff; + fprintf(output_file, "#sync: %s\n", onoff?"on":"off"); + sync_cmds = onoff; +} + +void cmd_set_assert(int onoff) +{ + assert_on_timeout = onoff; +} + void cmd_update_all_partitions(int newring) { int i; check_for_quorum = 1; for (i=0; iinstance, &node->partition->ring_id, onoff); } } +/* If we get called then a command has timed-out */ +static void finish_wait_timeout(void *data) +{ + if (command_timeout) { + fprintf(stderr, "ERR: Partition(s) not stable within timeout\n"); + if (assert_on_timeout) { + exit(2); + } + } + + resume_kb_input(sync_cmds); +} + +void cmd_set_timeout(uint64_t seconds) +{ + command_timeout = seconds * QB_TIME_NS_IN_MSEC; +} + /* ---------------------------------- */ #ifndef HAVE_READLINE_READLINE_H static void dummy_read_char(void); static void dummy_read_char() { int c, flush = 0; while (!flush) { c = getchar(); if (++input_buf_term >= INPUT_BUF_SIZE) { if (c != '\n' && c != EOF) fprintf(stderr, "User input overflows the limit: %zu\n", (size_t) INPUT_BUF_SIZE); input_buf[INPUT_BUF_SIZE - 1] = '\0'; flush = 1; } else if (c == '\n' || c == EOF) { input_buf[input_buf_term - 1] = '\0'; flush = 1; } else { input_buf[input_buf_term - 1] = c; } } parse_input_command((c == EOF) ? NULL : input_buf); input_buf_term = 0; - - if (is_tty) { - printf("vqsim> "); - fflush(stdout); - } } #endif static int stdin_read_fn(int32_t fd, int32_t revents, void *data) { #ifdef HAVE_READLINE_READLINE_H /* Send it to readline */ rl_callback_read_char(); #else dummy_read_char(); #endif return 0; } -static void start_kb_input(void) -{ - wait_count_to_unblock = 0; - -#ifdef HAVE_READLINE_READLINE_H - /* Readline will deal with completed lines when they arrive */ - rl_callback_handler_install("vqsim> ", parse_input_command); -#else - if (is_tty) { - printf("vqsim> "); - fflush(stdout); - } -#endif - - /* Send stdin to readline */ - if (qb_loop_poll_add(poll_loop, - QB_LOOP_MED, - STDIN_FILENO, - POLLIN | POLLERR, - NULL, - stdin_read_fn)) { - if (errno != EEXIST) { - perror("qb_loop_poll_add1 returned error"); - } - } -} static void start_kb_input_timeout(void *data) { -// fprintf(stderr, "Waiting for nodes to report status timed out\n"); - start_kb_input(); + resume_kb_input(1); } static void usage(char *program) { printf("Usage:\n"); printf("\n"); - printf("%s [-f ] [-o ]\n", program); + printf("%s [-c ] [-o ]\n", program); printf("\n"); - printf(" -f config file. defaults to /etc/corosync/corosync.conf\n"); + printf(" -c config file. defaults to /etc/corosync/corosync.conf\n"); printf(" -o output file. defaults to stdout\n"); printf(" -n no synchronization (on adding a node)\n"); printf(" -h display this help text\n"); printf("\n"); + printf("%s always takes input from STDIN, but cannot use a file.\n", program); + printf("If you want to script it then use\n cat | %s\n", program); + printf("\n"); } int main(int argc, char **argv) { qb_loop_signal_handle sigchld_qb_handle; int ch; char *output_file_name = NULL; - while ((ch = getopt (argc, argv, "f:o:nh")) != EOF) { + while ((ch = getopt (argc, argv, "c:o:nh")) != EOF) { switch (ch) { - case 'f': + case 'c': strncpy(corosync_config_file, optarg, sizeof(corosync_config_file)); break; case 'o': output_file_name = optarg; break; case 'n': - nosync = 1; + sync_cmds = 0; break; default: usage(argv[0]); exit(0); } } if (output_file_name) { output_file = fopen(output_file_name, "w"); if (!output_file) { fprintf(stderr, "Unable to open %s for output: %s\n", output_file_name, strerror(errno)); exit(-1); } } else { output_file = stdout; } -#ifndef HAVE_READLINE_READLINE_H + is_tty = isatty(STDIN_FILENO); -#endif qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FUNCTION, "*", LOG_DEBUG); qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE); qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FUNCTION, "*", LOG_DEBUG); poll_loop = qb_loop_create(); /* SIGCHLD handler to reap sub-processes and reconfigure the cluster */ qb_loop_signal_add(poll_loop, QB_LOOP_MED, SIGCHLD, NULL, sigchld_handler, &sigchld_qb_handle); - /* Create a full cluster of nodes from corosync.conf */ + +#ifdef HAVE_READLINE_READLINE_H + /* Readline will deal with completed lines when they arrive */ + /* + * For scripting add '#' to the start of the prompt so that + * parsers can ignore input lines + */ + rl_already_prompted = 1; + if (is_tty) { + rl_callback_handler_install("vqsim> ", parse_input_command); + } else { + rl_callback_handler_install("#vqsim> ", parse_input_command); + } +#endif + + + +/* Create a full cluster of nodes from corosync.conf */ read_corosync_conf(); - if (create_nodes_from_config() && !nosync) { + if (create_nodes_from_config() && sync_cmds) { /* Delay kb input handling by 1 second when we've just added the nodes from corosync.conf; expect that the delay will be cancelled substantially earlier once they all have reported their quorum info (the delay is in fact a failsafe input enabler here) */ qb_loop_timer_add(poll_loop, QB_LOOP_MED, 1000000000, NULL, start_kb_input_timeout, &kb_timer); + waiting_for_sync = 1; } else { - start_kb_input(); + resume_kb_input(0); } qb_loop_run(poll_loop); return 0; } diff --git a/vqsim/vqsim.h b/vqsim/vqsim.h index 0c999d74..0c4c9738 100644 --- a/vqsim/vqsim.h +++ b/vqsim/vqsim.h @@ -1,77 +1,82 @@ typedef enum {VQMSG_QUIT=1, VQMSG_SYNC, /* set nodelist */ VQMSG_QUORUM, /* quorum state of this 'node' */ VQMSG_EXEC, /* message for exec_handler */ VQMSG_QDEVICE, /* quorum device enable/disable */ VQMSG_QUORUMQUIT, /* quit if you don't have quorum */ } vqsim_msg_type_t; typedef struct vq_instance *vq_object_t; struct vqsim_msg_header { vqsim_msg_type_t type; int from_nodeid; int param; }; /* This is the sync sent from the controller process */ struct vqsim_sync_msg { struct vqsim_msg_header header; struct memb_ring_id ring_id; size_t view_list_entries; unsigned int view_list[]; }; /* This is just info sent from each VQ instance */ struct vqsim_quorum_msg { struct vqsim_msg_header header; int quorate; struct memb_ring_id ring_id; size_t view_list_entries; unsigned int view_list[]; }; struct vqsim_exec_msg { struct vqsim_msg_header header; char execmsg[]; }; struct vqsim_lib_msg { struct vqsim_msg_header header; char libmsg[]; }; #define MAX_NODES 1024 #define MAX_PARTITIONS 16 /* In vq_object.c */ vq_object_t vq_create_instance(qb_loop_t *poll_loop, int nodeid); void vq_quit(vq_object_t instance); int vq_set_nodelist(vq_object_t instance, struct memb_ring_id *ring_id, int *nodeids, int nodeids_entries); int vq_get_parent_fd(vq_object_t instance); int vq_set_qdevice(vq_object_t instance, struct memb_ring_id *ring_id, int onoff); int vq_quit_if_inquorate(vq_object_t instance); pid_t vq_get_pid(vq_object_t instance); /* in vqsim_vq_engine.c - effectively the constructor */ int fork_new_instance(int nodeid, int *vq_sock, pid_t *child_pid); /* In parser.c */ void parse_input_command(char *cmd); /* These are in vqmain.c */ -void cmd_stop_node(int nodeid); +int cmd_stop_node(int nodeid); void cmd_stop_all_nodes(void); -void cmd_start_new_node(int nodeid, int partition); +int cmd_start_new_node(int nodeid, int partition); void cmd_set_autofence(int onoff); +void cmd_set_sync(int onoff); +void cmd_set_assert(int onoff); void cmd_move_nodes(int partition, int num_nodes, int *nodelist); void cmd_join_partitions(int part1, int part2); void cmd_update_all_partitions(int newring); void cmd_qdevice_poll(int nodeid, int onoff); void cmd_show_node_states(void); +void cmd_set_timeout(uint64_t seconds); +void cmd_start_sync_command(void); +void resume_kb_input(int show_state); diff --git a/vqsim/vqsim_vq_engine.c b/vqsim/vqsim_vq_engine.c index cbe1d471..eb35d356 100644 --- a/vqsim/vqsim_vq_engine.c +++ b/vqsim/vqsim_vq_engine.c @@ -1,471 +1,477 @@ /* This is the bit of VQSIM that runs in the forked process. It represents a single votequorum instance or, if you like, a 'node' in the cluster. */ #include #include #include #include #include #include #include #include #include "../exec/votequorum.h" #include "../exec/service.h" #include "../include/corosync/corotypes.h" #include "../include/corosync/votequorum.h" #include "../include/corosync/ipc_votequorum.h" #include #include #include "icmap.h" #include "vqsim.h" #define QDEVICE_NAME "VQsim_qdevice" /* Static variables here are per-instance because we are forked */ static struct corosync_service_engine *engine; static int parent_socket; /* Our end of the socket */ static char buffer[8192]; static int our_nodeid; static char *private_data; static qb_loop_t *poll_loop; static qb_loop_timer_handle sync_timer; static qb_loop_timer_handle qdevice_timer; static int we_are_quorate; static void *fake_conn = (void*)1; static cs_error_t last_lib_error; static struct memb_ring_id current_ring_id; static int qdevice_registered; static unsigned int qdevice_timeout = VOTEQUORUM_QDEVICE_DEFAULT_TIMEOUT; /* 'Keep the compiler happy' time */ char *get_run_dir(void); int api_timer_add_duration ( unsigned long long nanosec_duration, void *data, void (*timer_fn) (void *data), corosync_timer_handle_t *handle); static void api_error_memory_failure(void) __attribute__((noreturn)); static void api_error_memory_failure() { fprintf(stderr, "Out of memory error\n"); exit(-1); } static void api_timer_delete(corosync_timer_handle_t th) { qb_loop_timer_del(poll_loop, th); } int api_timer_add_duration ( unsigned long long nanosec_duration, void *data, void (*timer_fn) (void *data), corosync_timer_handle_t *handle) { return qb_loop_timer_add(poll_loop, QB_LOOP_MED, nanosec_duration, data, timer_fn, handle); } static unsigned int api_totem_nodeid_get(void) { return our_nodeid; } static int api_totem_mcast(const struct iovec *iov, unsigned int iovlen, unsigned int type) { struct vqsim_msg_header header; struct iovec iovec[iovlen+1]; int total = sizeof(header); int res; int i; header.type = VQMSG_EXEC; header.from_nodeid = our_nodeid; header.param = 0; iovec[0].iov_base = &header; iovec[0].iov_len = sizeof(header); for (i=0; ierror; return 0; } static struct corosync_api_v1 corosync_api = { .error_memory_failure = api_error_memory_failure, .timer_delete = api_timer_delete, .timer_add_duration = api_timer_add_duration, .totem_nodeid_get = api_totem_nodeid_get, .totem_mcast = api_totem_mcast, .ipc_private_data_get = api_ipc_private_data_get, .ipc_response_send = api_ipc_response_send, }; /* -------------------- Above is all for providing the corosync_api support routines --------------------------------------------*/ /* They need to be in the same file as the engine as they use the local 'poll_loop' variable which is per-process */ static void start_qdevice_poll(int longwait); static void start_sync_timer(void); /* Callback from Votequorum to tell us about the quorum state */ static void quorum_fn(const unsigned int *view_list, size_t view_list_entries, int quorate, struct memb_ring_id *ring_id) { char msgbuf[8192]; int len; struct vqsim_quorum_msg *quorum_msg = (void*) msgbuf; we_are_quorate = quorate; /* Send back to parent */ quorum_msg->header.type = VQMSG_QUORUM; quorum_msg->header.from_nodeid = our_nodeid; quorum_msg->header.param = 0; quorum_msg->quorate = quorate; memcpy(&quorum_msg->ring_id, ring_id, sizeof(*ring_id)); quorum_msg->view_list_entries = view_list_entries; memcpy(quorum_msg->view_list, view_list, sizeof(unsigned int)*view_list_entries); if ( (len=write(parent_socket, msgbuf, sizeof(*quorum_msg) + sizeof(unsigned int)*view_list_entries)) <= 0) { perror("write (view list to parent) failed"); } memcpy(¤t_ring_id, ring_id, sizeof(*ring_id)); } char *corosync_service_link_and_init(struct corosync_api_v1 *api, struct default_service *service_engine) { /* dummy */ return NULL; } /* For votequorum */ char *get_run_dir() { static char cwd_buffer[PATH_MAX]; return getcwd(cwd_buffer, PATH_MAX); } /* This is different to the one in totemconfig.c in that we already * know the 'local' node ID, so we can just search for that. * It needs to be here rather than at main config read time as it's * (obviously) going to be different for each instance. */ static void set_local_node_pos(struct corosync_api_v1 *api) { icmap_iter_t iter; uint32_t node_pos; char name_str[ICMAP_KEYNAME_MAXLEN]; uint32_t nodeid; const char *iter_key; int res; + int found = 0; iter = icmap_iter_init("nodelist.node."); while ((iter_key = icmap_iter_next(iter, NULL, NULL)) != NULL) { res = sscanf(iter_key, "nodelist.node.%u.%s", &node_pos, name_str); if (res != 2) { continue; } if (strcmp(name_str, "nodeid")) { continue; } res = icmap_get_uint32(iter_key, &nodeid); if (res == CS_OK) { if (nodeid == our_nodeid) { + found = 1; res = icmap_set_uint32("nodelist.local_node_pos", node_pos); - if (res != CS_OK) { - fprintf(stderr, "Failed to find node %d in corosync.conf. Quorum calculations may not be correct:\n", our_nodeid); - } } } } + if (!found) { + /* This probably indicates a dynamically-added node + * set the pos to zero and use the votes of the + * first node in corosync.conf + */ + res = icmap_set_uint32("nodelist.local_node_pos", 0); + } } static int load_quorum_instance(struct corosync_api_v1 *api) { const char *error_string; int res; error_string = votequorum_init(api, quorum_fn); if (error_string) { fprintf(stderr, "Votequorum init failed: %s\n", error_string); return -1; } engine = votequorum_get_service_engine_ver0(); error_string = engine->exec_init_fn(api); if (error_string) { fprintf(stderr, "votequorum exec init failed: %s\n", error_string); return -1; } private_data = malloc(engine->private_data_size); if (!private_data) { perror("Malloc in child failed"); return -1; } res = engine->lib_init_fn(fake_conn); return res; } static void sync_dispatch_fn(void *data) { if (engine->sync_process()) { start_sync_timer(); } else { engine->sync_activate(); } } static void start_sync_timer() { qb_loop_timer_add(poll_loop, QB_LOOP_MED, 10000000, NULL, sync_dispatch_fn, &sync_timer); } static void send_sync(char *buf, int len) { struct vqsim_sync_msg *msg = (void*)buf; /* Votequorum doesn't use the transitional node list :-) */ engine->sync_init(NULL, 0, msg->view_list, msg->view_list_entries, &msg->ring_id); start_sync_timer(); } static void send_exec_msg(char *buf, int len) { struct vqsim_exec_msg *execmsg = (void*)buf; struct qb_ipc_request_header *qb_header = (void*)execmsg->execmsg; engine->exec_engine[qb_header->id & 0xFFFF].exec_handler_fn(execmsg->execmsg, execmsg->header.from_nodeid); } static int send_lib_msg(int type, void *msg) { /* Clear this as not all lib functions return a response immediately */ last_lib_error = CS_OK; engine->lib_engine[type].lib_handler_fn(fake_conn, msg); return last_lib_error; } static int poll_qdevice(int onoff) { struct req_lib_votequorum_qdevice_poll pollmsg; int res; pollmsg.cast_vote = onoff; pollmsg.ring_id.nodeid = current_ring_id.nodeid; pollmsg.ring_id.seq = current_ring_id.seq; strcpy(pollmsg.name, QDEVICE_NAME); res = send_lib_msg(MESSAGE_REQ_VOTEQUORUM_QDEVICE_POLL, &pollmsg); if (res != CS_OK) { fprintf(stderr, "%d: qdevice poll failed: %d\n", our_nodeid, res); } return res; } static void qdevice_dispatch_fn(void *data) { if (poll_qdevice(1) == CS_OK) { start_qdevice_poll(0); } } static void start_qdevice_poll(int longwait) { unsigned long long timeout; timeout = (unsigned long long)qdevice_timeout*500000; /* Half the corosync timeout */ if (longwait) { timeout *= 2; } qb_loop_timer_add(poll_loop, QB_LOOP_MED, timeout, NULL, qdevice_dispatch_fn, &qdevice_timer); } static void stop_qdevice_poll(void) { qb_loop_timer_del(poll_loop, qdevice_timer); qdevice_timer = 0; } static void do_qdevice(int onoff) { int res; if (onoff) { if (!qdevice_registered) { struct req_lib_votequorum_qdevice_register regmsg; strcpy(regmsg.name, QDEVICE_NAME); if ( (res=send_lib_msg(MESSAGE_REQ_VOTEQUORUM_QDEVICE_REGISTER, ®msg)) == CS_OK) { qdevice_registered = 1; start_qdevice_poll(1); } else { fprintf(stderr, "%d: qdevice registration failed: %d\n", our_nodeid, res); } } else { if (!qdevice_timer) { start_qdevice_poll(0); } } } else { poll_qdevice(0); stop_qdevice_poll(); } } /* From controller */ static int parent_pipe_read_fn(int32_t fd, int32_t revents, void *data) { struct vqsim_msg_header *header = (void*)buffer; int len; len = read(fd, buffer, sizeof(buffer)); if (len > 0) { /* Check header and route */ switch (header->type) { case VQMSG_QUIT: exit(0); break; case VQMSG_EXEC: /* For votequorum exec messages */ send_exec_msg(buffer, len); break; case VQMSG_SYNC: send_sync(buffer, len); break; case VQMSG_QDEVICE: do_qdevice(header->param); break; case VQMSG_QUORUMQUIT: if (!we_are_quorate) { exit(1); } break; case VQMSG_QUORUM: /* not used here */ break; } } return 0; } static void initial_sync(int nodeid) { unsigned int trans_list[1] = {nodeid}; unsigned int member_list[1] = {nodeid}; struct memb_ring_id ring_id; ring_id.nodeid = our_nodeid; ring_id.seq = 1; /* cluster with just us in it */ engine->sync_init(trans_list, 1, member_list, 1, &ring_id); start_sync_timer(); } /* Return pipe FDs & child PID if sucessful */ int fork_new_instance(int nodeid, int *vq_sock, pid_t *childpid) { int pipes[2]; pid_t pid; if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, pipes)) { return -1; } parent_socket = pipes[0]; switch ( (pid=fork()) ) { case -1: perror("fork failed"); return -1; case 0: /* child process - continue below */ break; default: /* parent process */ *vq_sock = pipes[1]; *childpid = pid; return 0; } our_nodeid = nodeid; poll_loop = qb_loop_create(); if (icmap_get_uint32("quorum.device.timeout", &qdevice_timeout) != CS_OK) { qdevice_timeout = VOTEQUORUM_QDEVICE_DEFAULT_TIMEOUT; } set_local_node_pos(&corosync_api); load_quorum_instance(&corosync_api); qb_loop_poll_add(poll_loop, QB_LOOP_MED, parent_socket, POLLIN, NULL, parent_pipe_read_fn); /* Start it up! */ initial_sync(nodeid); qb_loop_run(poll_loop); return 0; }