Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/configure.ac b/configure.ac
index 219a97a5fb..cf379a8855 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,2190 +1,2190 @@
dnl
dnl autoconf for Pacemaker
dnl
dnl Copyright 2009-2024 the Pacemaker project contributors
dnl
dnl The version control history for this file may have further details.
dnl
dnl This source code is licensed under the GNU General Public License version 2
dnl or later (GPLv2+) WITHOUT ANY WARRANTY.
dnl ==============================================
dnl Bootstrap autotools
dnl ==============================================
# Require a minimum version of autoconf itself
AC_PREREQ(2.64)
dnl AC_CONFIG_MACRO_DIR is deprecated as of autoconf 2.70 (2020-12-08).
dnl Once we can require that version, we can simplify this, and no longer
dnl need ACLOCAL_AMFLAGS in Makefile.am.
m4_ifdef([AC_CONFIG_MACRO_DIRS],
[AC_CONFIG_MACRO_DIRS([m4])],
[AC_CONFIG_MACRO_DIR([m4])])
m4_include([m4/version.m4])
AC_INIT([pacemaker], VERSION_NUMBER, [users@clusterlabs.org], [pacemaker],
PCMK_URL)
LT_CONFIG_LTDL_DIR([libltdl])
AC_CONFIG_AUX_DIR([libltdl/config])
dnl Where #defines that autoconf makes (e.g. HAVE_whatever) go
dnl
dnl include/config.h
dnl - Internal API
dnl - Contains all defines
dnl - include/config.h.in is generated automatically by autoheader
dnl - Not to be included in any header files except crm_internal.h
dnl (which is also not to be included in any other header files)
dnl
dnl include/crm_config.h
dnl - External API
dnl - Contains a subset of defines
dnl - include/crm_config.h.in is manually edited to select the subset
dnl - Should not include HAVE_* defines
dnl - Safe to include anywhere
AC_CONFIG_HEADERS([include/config.h include/crm_config.h])
dnl 1.13: minimum automake version required
dnl foreign: don't require GNU-standard top-level files
dnl tar-ustar: use (older) POSIX variant of generated tar rather than v7
dnl subdir-objects: keep .o's with their .c's (no-op in 2.0+)
AM_INIT_AUTOMAKE([1.13 foreign tar-ustar subdir-objects])
dnl Require minimum version of pkg-config
PKG_PROG_PKG_CONFIG(0.28)
AS_IF([test x"${PKG_CONFIG}" != x""], [],
[AC_MSG_FAILURE([Could not find required build tool pkg-config (0.28 or later)])])
PKG_INSTALLDIR
PKG_NOARCH_INSTALLDIR
dnl ==============================================
dnl Compiler checks and helpers
dnl ==============================================
dnl A particular compiler can be forced by setting the CC environment variable
AC_PROG_CC
dnl C++ is needed only to run maintainer utilities, not to build
AC_PROG_CXX
dnl Use at least C99 if possible (automatic for autoconf >= 2.70)
m4_version_prereq([2.70], [:], [AC_PROG_CC_STDC])
# cc_supports_flag <compiler-flag>
# Return success if the C compiler supports the given flag
cc_supports_flag() {
local CFLAGS="-Werror $@"
AC_MSG_CHECKING([whether $CC supports $@])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]], [[ ]])],
[RC=0; AC_MSG_RESULT([yes])],
[RC=1; AC_MSG_RESULT([no])])
return $RC
}
# cc_temp_flags <compiler-flags>
# Use the given flags for subsequent C compilation. These can be reverted to
# what was used previously with cc_restore_flags. This allows certain tests to
# use specific flags without affecting anything else.
cc_temp_flags() {
ac_save_CFLAGS="$CFLAGS"
CFLAGS="$*"
}
# cc_restore_flags
# Restore C compiler flags to what they were before the last cc_temp_flags
# call.
cc_restore_flags() {
CFLAGS=$ac_save_CFLAGS
}
# Check for fatal warning support
AS_IF([test $enable_fatal_warnings -ne $DISABLED dnl
&& test x"$GCC" = x"yes" && cc_supports_flag -Werror],
[WERROR="-Werror"],
[
WERROR=""
AS_CASE([$enable_fatal_warnings],
[$REQUIRED], [AC_MSG_ERROR([Compiler does not support fatal warnings])],
[$OPTIONAL], [enable_fatal_warnings=$DISABLED])
])
dnl ==============================================
dnl Linker checks
dnl ==============================================
# Check whether linker supports --enable-new-dtags to use RUNPATH instead of
# RPATH. It is necessary to do this before libtool does linker detection.
# See also: https://github.com/kronosnet/kronosnet/issues/107
AX_CHECK_LINK_FLAG([-Wl,--enable-new-dtags],
[AM_LDFLAGS=-Wl,--enable-new-dtags],
[AC_MSG_ERROR(["Linker support for --enable-new-dtags is required"])])
AC_SUBST([AM_LDFLAGS])
saved_LDFLAGS="$LDFLAGS"
LDFLAGS="$AM_LDFLAGS $LDFLAGS"
LT_INIT([dlopen])
LDFLAGS="$saved_LDFLAGS"
LTDL_INIT([convenience])
dnl ==============================================
dnl Define configure options
dnl ==============================================
# yes_no_try <user-response> <default>
# Map a yes/no/try user selection to $REQUIRED for yes, $DISABLED for no, and
# $OPTIONAL for try.
DISABLED=0
REQUIRED=1
OPTIONAL=2
yes_no_try() {
local value
AS_IF([test x"$1" = x""], [value="$2"], [value="$1"])
AS_CASE(["`echo "$value" | tr '[A-Z]' '[a-z]'`"],
[0|no|false|disable], [return $DISABLED],
[1|yes|true|enable], [return $REQUIRED],
[try|check], [return $OPTIONAL]
)
AC_MSG_ERROR([Invalid option value "$value"])
}
#
# Fix the defaults of certain built-in variables so they can be used in the
# defaults for our custom arguments
#
AC_MSG_NOTICE([Sanitizing prefix: ${prefix}])
AS_IF([test x"$prefix" = x"NONE"],
[
prefix=/usr
dnl Fix default variables - "prefix" variable if not specified
AS_IF([test x"$localstatedir" = x"\${prefix}/var"],
[localstatedir="/var"])
AS_IF([test x"$sysconfdir" = x"\${prefix}/etc"],
[sysconfdir="/etc"])
])
AC_MSG_NOTICE([Sanitizing exec_prefix: ${exec_prefix}])
AS_CASE([$exec_prefix],
[prefix|NONE], [exec_prefix=$prefix])
AC_MSG_NOTICE([Sanitizing libdir: ${libdir}])
AS_CASE([$libdir],
[prefix|NONE], [
AC_MSG_CHECKING([which lib directory to use])
for aDir in lib64 lib
do
trydir="${exec_prefix}/${aDir}"
AS_IF([test -d ${trydir}],
[
libdir=${trydir}
break
])
done
AC_MSG_RESULT([$libdir])
])
# Start a list of optional features this build supports
PCMK_FEATURES=""
dnl This section should include only the definition of configure script
dnl options and determining their values. Processing should be done later when
dnl possible, other than what's needed to determine values and defaults.
dnl Per the autoconf docs, --enable-*/--disable-* options should control
dnl features inherent to Pacemaker, while --with-*/--without-* options should
dnl control the use of external software. However, --enable-*/--disable-* may
dnl implicitly require additional external dependencies, and
dnl --with-*/--without-* may implicitly enable or disable features, so the
dnl line is blurry.
dnl
dnl We also use --with-* options for custom file, directory, and path
dnl locations, since autoconf does not provide an option type for those.
dnl --enable-* options: build process
AC_ARG_ENABLE([quiet],
[AS_HELP_STRING([--enable-quiet],
[suppress make output unless there is an error @<:@no@:>@])]
)
yes_no_try "$enable_quiet" "no"
enable_quiet=$?
AC_ARG_ENABLE([fatal-warnings],
[AS_HELP_STRING([--enable-fatal-warnings],
[enable pedantic and fatal warnings for gcc @<:@try@:>@])],
)
yes_no_try "$enable_fatal_warnings" "try"
enable_fatal_warnings=$?
AC_ARG_ENABLE([hardening],
[AS_HELP_STRING([--enable-hardening],
[harden the resulting executables/libraries @<:@try@:>@])]
)
yes_no_try "$enable_hardening" "try"
enable_hardening=$?
dnl --enable-* options: features
AC_ARG_ENABLE([systemd],
[AS_HELP_STRING([--enable-systemd],
[enable support for managing resources via systemd @<:@try@:>@])]
)
yes_no_try "$enable_systemd" "try"
enable_systemd=$?
AC_ARG_ENABLE([upstart],
[AS_HELP_STRING([--enable-upstart],
[enable support for managing resources via Upstart (deprecated) @<:@try@:>@])]
)
yes_no_try "$enable_upstart" "try"
enable_upstart=$?
dnl --enable-* options: features inherent to Pacemaker
# AM_GNU_GETTEXT calls AM_NLS which defines the nls option, but it defaults
# to enabled. We override the definition of AM_NLS to flip the default and mark
# it as experimental in the help text.
AC_DEFUN([AM_NLS],
[AC_MSG_CHECKING([whether NLS is requested])
AC_ARG_ENABLE([nls],
[AS_HELP_STRING([--enable-nls],
[use Native Language Support (experimental)])],
USE_NLS=$enableval, USE_NLS=no)
AC_MSG_RESULT([$USE_NLS])
AC_SUBST([USE_NLS])]
)
AM_GNU_GETTEXT([external])
AM_GNU_GETTEXT_VERSION([0.18])
dnl --with-* options: external software support, and custom locations
dnl This argument is defined via an M4 macro so default can be a variable
AC_DEFUN([VERSION_ARG],
[AC_ARG_WITH([version],
[AS_HELP_STRING([--with-version=VERSION],
[override package version @<:@$1@:>@])],
[ PACEMAKER_VERSION="$withval" ],
[ PACEMAKER_VERSION="$PACKAGE_VERSION" ])]
)
VERSION_ARG(VERSION_NUMBER)
CRM_DAEMON_USER=""
AC_ARG_WITH([daemon-user],
[AS_HELP_STRING([--with-daemon-user=USER],
[user to run unprivileged Pacemaker daemons as (advanced option: changing this may break other cluster components unless similarly configured) @<:@hacluster@:>@])],
[ CRM_DAEMON_USER="$withval" ]
)
AS_IF([test x"${CRM_DAEMON_USER}" = x""],
[CRM_DAEMON_USER="hacluster"])
CRM_DAEMON_GROUP=""
AC_ARG_WITH([daemon-group],
[AS_HELP_STRING([--with-daemon-group=GROUP],
[group to run unprivileged Pacemaker daemons as (advanced option: changing this may break other cluster components unless similarly configured) @<:@haclient@:>@])],
[ CRM_DAEMON_GROUP="$withval" ]
)
AS_IF([test x"${CRM_DAEMON_GROUP}" = x""],
[CRM_DAEMON_GROUP="haclient"])
BUG_URL=""
AC_ARG_WITH([bug-url],
[AS_HELP_STRING([--with-bug-url=DIR], m4_normalize([
address where users should submit bug reports
@<:@https://bugs.clusterlabs.org/enter_bug.cgi?product=Pacemaker@:>@]))],
[ BUG_URL="$withval" ]
)
AS_IF([test x"${BUG_URL}" = x""],
[BUG_URL="https://bugs.clusterlabs.org/enter_bug.cgi?product=Pacemaker"])
dnl --with-* options: features
AC_ARG_WITH([cibsecrets],
[AS_HELP_STRING([--with-cibsecrets],
[support separate file for CIB secrets @<:@no@:>@])]
)
yes_no_try "$with_cibsecrets" "no"
with_cibsecrets=$?
-PCMK_GNUTLS_PRIORITIES="NORMAL"
+PCMK__GNUTLS_PRIORITIES="NORMAL"
AC_ARG_WITH([gnutls-priorities],
[AS_HELP_STRING([--with-gnutls-priorities],
[default GnuTLS cipher priorities @<:@NORMAL@:>@])],
- [ test x"$withval" = x"no" || PCMK_GNUTLS_PRIORITIES="$withval" ]
+ [ test x"$withval" = x"no" || PCMK__GNUTLS_PRIORITIES="$withval" ]
)
AC_ARG_WITH([concurrent-fencing-default],
[AS_HELP_STRING([--with-concurrent-fencing-default],
[default value for concurrent-fencing cluster option @<:@false@:>@])],
)
AS_CASE([$with_concurrent_fencing_default],
[""], [with_concurrent_fencing_default="false"],
[false], [],
[true], [PCMK_FEATURES="$PCMK_FEATURES default-concurrent-fencing"],
[AC_MSG_ERROR([Invalid value "$with_concurrent_fencing_default" for --with-concurrent-fencing-default])]
)
AC_ARG_WITH([sbd-sync-default],
[AS_HELP_STRING([--with-sbd-sync-default], m4_normalize([
default value used by sbd if SBD_SYNC_RESOURCE_STARTUP
environment variable is not set @<:@false@:>@]))],
)
AS_CASE([$with_sbd_sync_default],
[""], [with_sbd_sync_default=false],
[false], [],
[true], [PCMK_FEATURES="$PCMK_FEATURES default-sbd-sync"],
[AC_MSG_ERROR([Invalid value "$with_sbd_sync_default" for --with-sbd-sync-default])]
)
AC_ARG_WITH([resource-stickiness-default],
[AS_HELP_STRING([--with-resource-stickiness-default],
[If positive, value to add to new CIBs as explicit resource default for resource-stickiness @<:@0@:>@])],
)
errmsg="Invalid value \"$with_resource_stickiness_default\" for --with-resource-stickiness-default"
AS_CASE([$with_resource_stickiness_default],
[0|""], [with_resource_stickiness_default="0"],
[*[[!0-9]]*], [AC_MSG_ERROR([$errmsg])],
[PCMK_FEATURES="$PCMK_FEATURES default-resource-stickiness"]
)
AC_ARG_WITH([corosync],
[AS_HELP_STRING([--with-corosync],
[support the Corosync messaging and membership layer @<:@try@:>@])]
)
yes_no_try "$with_corosync" "try"
with_corosync=$?
dnl Get default from Corosync if possible
PKG_CHECK_VAR([PCMK__COROSYNC_CONF], [corosync], [corosysconfdir],
[PCMK__COROSYNC_CONF="$PCMK__COROSYNC_CONF/corosync.conf"],
[PCMK__COROSYNC_CONF="${sysconfdir}/corosync/corosync.conf"])
AC_ARG_WITH([corosync-conf],
[AS_HELP_STRING([--with-corosync-conf], m4_normalize([
location of Corosync configuration file
@<:@value from Corosync package if available otherwise
SYSCONFDIR/corosync/corosync.conf@:>@]))],
[ PCMK__COROSYNC_CONF="$withval" ]
)
AC_ARG_WITH([nagios],
[AS_HELP_STRING([--with-nagios], [support nagios resources (deprecated)])]
)
yes_no_try "$with_nagios" "try"
with_nagios=$?
dnl --with-* options: directory locations
AC_ARG_WITH([nagios-plugin-dir],
[AS_HELP_STRING([--with-nagios-plugin-dir=DIR],
[directory for nagios plugins (deprecated) @<:@LIBEXECDIR/nagios/plugins@:>@])],
[ NAGIOS_PLUGIN_DIR="$withval" ]
)
AC_ARG_WITH([nagios-metadata-dir],
[AS_HELP_STRING([--with-nagios-metadata-dir=DIR],
[directory for nagios plugins metadata (deprecated) @<:@DATADIR/nagios/plugins-metadata@:>@])],
[ NAGIOS_METADATA_DIR="$withval" ]
)
INITDIR=""
AC_ARG_WITH([initdir],
[AS_HELP_STRING([--with-initdir=DIR],
[directory for init (rc) scripts])],
[ INITDIR="$withval" ]
)
systemdsystemunitdir="${systemdsystemunitdir-}"
AC_ARG_WITH([systemdsystemunitdir],
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR],
[directory for systemd unit files (advanced option: must match what systemd uses)])],
[ systemdsystemunitdir="$withval" ]
)
CONFIGDIR=""
AC_ARG_WITH([configdir],
[AS_HELP_STRING([--with-configdir=DIR],
[directory for Pacemaker configuration file @<:@SYSCONFDIR/sysconfig@:>@])],
[ CONFIGDIR="$withval" ]
)
dnl --runstatedir is available as of autoconf 2.70 (2020-12-08). When users
dnl have an older version, they can use our --with-runstatedir.
pcmk_runstatedir=""
AC_ARG_WITH([runstatedir],
[AS_HELP_STRING([--with-runstatedir=DIR],
[modifiable per-process data @<:@LOCALSTATEDIR/run@:>@ (ignored if --runstatedir is available)])],
[ pcmk_runstatedir="$withval" ]
)
CRM_LOG_DIR=""
AC_ARG_WITH([logdir],
[AS_HELP_STRING([--with-logdir=DIR],
[directory for Pacemaker log file @<:@LOCALSTATEDIR/log/pacemaker@:>@])],
[ CRM_LOG_DIR="$withval" ]
)
CRM_BUNDLE_DIR=""
AC_ARG_WITH([bundledir],
[AS_HELP_STRING([--with-bundledir=DIR],
[directory for Pacemaker bundle logs @<:@LOCALSTATEDIR/log/pacemaker/bundles@:>@])],
[ CRM_BUNDLE_DIR="$withval" ]
)
dnl Get default from resource-agents if possible. Otherwise, the default uses
dnl /usr/lib rather than libdir because it's determined by the OCF project and
dnl not Pacemaker. Even if a user wants to install Pacemaker to /usr/local or
dnl such, the OCF agents will be expected in their usual location. However, we
dnl do give the user the option to override it.
PKG_CHECK_VAR([OCF_ROOT_DIR], [resource-agents], [ocfrootdir], [],
[OCF_ROOT_DIR="/usr/lib/ocf"])
AC_ARG_WITH([ocfdir],
[AS_HELP_STRING([--with-ocfdir=DIR], m4_normalize([
OCF resource agent root directory (advanced option: changing this
may break other cluster components unless similarly configured)
@<:@value from resource-agents package if available otherwise
/usr/lib/ocf@:>@]))],
[ OCF_ROOT_DIR="$withval" ]
)
dnl Get default from resource-agents if possible
PKG_CHECK_VAR([OCF_RA_PATH], [resource-agents], [ocfrapath], [],
[OCF_RA_PATH="$OCF_ROOT_DIR/resource.d"])
AC_ARG_WITH([ocfrapath],
[AS_HELP_STRING([--with-ocfrapath=DIR], m4_normalize([
OCF resource agent directories (colon-separated) to search
@<:@value from resource-agents package if available otherwise
OCFDIR/resource.d@:>@]))],
[ OCF_RA_PATH="$withval" ]
)
OCF_RA_INSTALL_DIR="$OCF_ROOT_DIR/resource.d"
AC_ARG_WITH([ocfrainstalldir],
[AS_HELP_STRING([--with-ocfrainstalldir=DIR], m4_normalize([
OCF installation directory for Pacemakers resource agents
@<:@OCFDIR/resource.d@:>@]))],
[ OCF_RA_INSTALL_DIR="$withval" ]
)
dnl Get default from fence-agents if available
PKG_CHECK_VAR([FA_PREFIX], [fence-agents], [prefix],
[PCMK__FENCE_BINDIR="${FA_PREFIX}/sbin"],
[PCMK__FENCE_BINDIR="$sbindir"])
AC_ARG_WITH([fence-bindir],
[AS_HELP_STRING([--with-fence-bindir=DIR], m4_normalize([
directory for executable fence agents @<:@value from fence-agents
package if available otherwise SBINDIR@:>@]))],
[ PCMK__FENCE_BINDIR="$withval" ]
)
dnl --with-* options: non-production testing
AC_ARG_WITH([profiling],
[AS_HELP_STRING([--with-profiling],
[disable optimizations, for effective profiling @<:@no@:>@])]
)
yes_no_try "$with_profiling" "no"
with_profiling=$?
AC_ARG_WITH([coverage],
[AS_HELP_STRING([--with-coverage],
[disable optimizations, for effective profiling and coverage testing @<:@no@:>@])]
)
yes_no_try "$with_coverage" "no"
with_coverage=$?
AC_DEFINE_UNQUOTED([PCMK__WITH_COVERAGE], [$with_coverage], [Build with code coverage])
AM_CONDITIONAL([BUILD_COVERAGE], [test $with_coverage -ne $DISABLED])
AC_ARG_WITH([sanitizers],
[AS_HELP_STRING([--with-sanitizers=...,...],
[enable SANitizer build, do *NOT* use for production. Only ASAN/UBSAN/TSAN are currently supported])],
[ SANITIZERS="$withval" ],
[ SANITIZERS="" ])
dnl Environment variable options
AC_ARG_VAR([CFLAGS_HARDENED_LIB], [extra C compiler flags for hardened libraries])
AC_ARG_VAR([LDFLAGS_HARDENED_LIB], [extra linker flags for hardened libraries])
AC_ARG_VAR([CFLAGS_HARDENED_EXE], [extra C compiler flags for hardened executables])
AC_ARG_VAR([LDFLAGS_HARDENED_EXE], [extra linker flags for hardened executables])
dnl ==============================================
dnl Locate essential tools
dnl ==============================================
PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin"
export PATH
dnl Pacemaker's executable python scripts will invoke the python specified by
dnl configure's PYTHON variable. If not specified, AM_PATH_PYTHON will check a
dnl built-in list with (unversioned) "python" having precedence. To configure
dnl Pacemaker to use a specific python interpreter version, define PYTHON
dnl when calling configure, for example: ./configure PYTHON=/usr/bin/python3.6
dnl If PYTHON was specified, ensure it is an absolute path
AS_IF([test x"${PYTHON}" != x""], [AC_PATH_PROG([PYTHON], [$PYTHON])])
dnl Require a minimum Python version
AM_PATH_PYTHON([3.6])
AC_PROG_LN_S
AC_PROG_MKDIR_P
AC_PATH_PROG([GIT], [git], [false])
dnl Bash is needed for building man pages and running regression tests.
dnl We set "BASH_PATH" because "BASH" is already an environment variable.
REQUIRE_PROG([BASH_PATH], [bash])
AC_PATH_PROGS(VALGRIND_BIN, valgrind, /usr/bin/valgrind)
AC_DEFINE_UNQUOTED(VALGRIND_BIN, "$VALGRIND_BIN", Valgrind command)
dnl ==============================================
dnl Package and schema versioning
dnl ==============================================
# Redefine PACKAGE_VERSION and VERSION according to PACEMAKER_VERSION in case
# the user used --with-version. Unfortunately, this can only affect the
# substitution variables and later uses in this file, not the config.h
# constants, so we have to be careful to use only PACEMAKER_VERSION in C code.
PACKAGE_VERSION=$PACEMAKER_VERSION
VERSION=$PACEMAKER_VERSION
AC_DEFINE_UNQUOTED(PACEMAKER_VERSION, "$VERSION",
[Version number of this Pacemaker build])
AC_MSG_CHECKING([build version])
AS_IF([test "$GIT" != "false" && test -d .git],
[
BUILD_VERSION=`"$GIT" log --pretty="format:%h" -n 1`
AC_MSG_RESULT([$BUILD_VERSION (git hash)])
],
[
# The current directory name make a reasonable default
# Most generated archives will include the hash or tag
BASE=`basename $PWD`
BUILD_VERSION=`echo $BASE | sed s:.*[[Pp]]acemaker-::`
AC_MSG_RESULT([$BUILD_VERSION (directory name)])
])
AC_DEFINE_UNQUOTED(BUILD_VERSION, "$BUILD_VERSION", Build version)
AC_SUBST(BUILD_VERSION)
# schema_files <schema-dir>
# List all manually edited RNG schemas (as opposed to auto-generated via make)
# in the given directory. Use git if available to list managed RNGs, in case
# there are leftover schema files from an earlier build of a different
# version. Otherwise, check all RNGs.
schema_files() {
local files="$("$GIT" ls-files "$1"/*.rng 2>/dev/null)"
AS_IF([test x"$files" = x""],
[
files="$(ls -1 "$1"/*.rng | grep -E -v \
'/(pacemaker|api-result|crm_mon|versions)[^/]*\.rng')"
])
echo "$files"
}
# latest_schema_version <schema-dir>
# Determine highest RNG version in the given schema directory.
latest_schema_version() {
schema_files "$1" | sed -n -e 's/^.*-\([[0-9]][[0-9.]]*\).rng$/\1/p' dnl
| sort -V | tail -1
}
# schemas_for_make <schema-dir>
# Like schema_files, but suitable for use in make variables.
schemas_for_make() {
local file
for file in $(schema_files "$1"); do
AS_ECHO_N(["\$(top_srcdir)/$file "])
done
}
# Detect highest API schema version
API_VERSION=$(latest_schema_version "xml/api")
AC_DEFINE_UNQUOTED([PCMK__API_VERSION], ["$API_VERSION"],
[Highest API schema version])
# Detect highest CIB schema version
CIB_VERSION=$(latest_schema_version "xml")
AC_SUBST(CIB_VERSION)
# Re-run configure at next make if schema files change, to re-detect versions
cib_schemas="$(schemas_for_make "xml")"
api_schemas="$(schemas_for_make "xml/api")"
CONFIG_STATUS_DEPENDENCIES="$cib_schemas $api_schemas"
AC_SUBST(CONFIG_STATUS_DEPENDENCIES)
dnl ==============================================
dnl Process simple options
dnl ==============================================
AS_IF([test x"$enable_nls" = x"yes"], [PCMK_FEATURES="$PCMK_FEATURES nls"])
AC_DEFINE_UNQUOTED([PCMK__CONCURRENT_FENCING_DEFAULT],
["$with_concurrent_fencing_default"],
[Default value for concurrent-fencing cluster option])
AC_DEFINE_UNQUOTED([PCMK__SBD_SYNC_DEFAULT],
[$with_sbd_sync_default],
[Default value for SBD_SYNC_RESOURCE_STARTUP environment variable])
AC_DEFINE_UNQUOTED([PCMK__RESOURCE_STICKINESS_DEFAULT],
[$with_resource_stickiness_default],
[Default value for resource-stickiness resource meta-attribute])
-AS_IF([test x"${PCMK_GNUTLS_PRIORITIES}" != x""], [],
+AS_IF([test x"${PCMK__GNUTLS_PRIORITIES}" != x""], [],
[AC_MSG_ERROR([--with-gnutls-priorities value must not be empty])])
-AC_DEFINE_UNQUOTED([PCMK_GNUTLS_PRIORITIES], ["$PCMK_GNUTLS_PRIORITIES"],
+AC_DEFINE_UNQUOTED([PCMK__GNUTLS_PRIORITIES], ["$PCMK__GNUTLS_PRIORITIES"],
[GnuTLS cipher priorities])
-AC_SUBST(PCMK_GNUTLS_PRIORITIES)
+AC_SUBST(PCMK__GNUTLS_PRIORITIES)
AC_SUBST(BUG_URL)
AC_DEFINE_UNQUOTED([PCMK__BUG_URL], ["$BUG_URL"],
[Where bugs should be reported])
AC_DEFINE_UNQUOTED([CRM_DAEMON_USER], ["$CRM_DAEMON_USER"],
[User to run Pacemaker daemons as])
AC_SUBST(CRM_DAEMON_USER)
AC_DEFINE_UNQUOTED([CRM_DAEMON_GROUP], ["$CRM_DAEMON_GROUP"],
[Group to run Pacemaker daemons as])
AC_SUBST(CRM_DAEMON_GROUP)
dnl ==============================================
dnl Process file paths
dnl ==============================================
# expand_path_option <path-variable-name> [<default>]
# Given the name of a file path variable, expand any variable references
# inside it, use the specified default if it is not specified, and ensure it
# is a full path.
expand_path_option() {
# The first argument is the variable *name* (not value)
ac_path_varname="$1"
# Get the original value of the variable
ac_path_value=$(eval echo "\${${ac_path_varname}}")
# Expand any literal variable expressions in the value so that we don't
# end up with something like '${prefix}' in #defines etc.
#
# Autoconf deliberately leaves values unexpanded to allow overriding
# the configure script choices in make commands (for example,
# "make exec_prefix=/foo install"). No longer being able to do this seems
# like no great loss.
eval ac_path_value=$(eval echo "${ac_path_value}")
# Use (expanded) default if necessary
AS_IF([test x"${ac_path_value}" = x""],
[eval ac_path_value=$(eval echo "$2")])
# Require a full path
AS_CASE(["$ac_path_value"],
[/*], [eval ${ac_path_varname}="$ac_path_value"],
[*], [AC_MSG_ERROR([$ac_path_varname value "$ac_path_value" is not a full path])]
)
}
AC_MSG_NOTICE([Sanitizing INITDIR: ${INITDIR}])
AS_CASE([$INITDIR],
[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
AS_IF([test -d $initdir],
[
INITDIR=$initdir
break
])
done
AC_MSG_RESULT([$INITDIR])
])
AC_SUBST(INITDIR)
dnl Expand values of autoconf-provided directory options
expand_path_option prefix
expand_path_option exec_prefix
expand_path_option bindir
expand_path_option sbindir
expand_path_option libexecdir
expand_path_option datarootdir
expand_path_option datadir
expand_path_option sysconfdir
expand_path_option sharedstatedir
expand_path_option localstatedir
expand_path_option libdir
expand_path_option includedir
expand_path_option oldincludedir
expand_path_option infodir
expand_path_option mandir
AC_DEFUN([AC_DATAROOTDIR_CHECKED])
dnl Expand values of custom directory options
expand_path_option localedir "${datadir}/locale"
AC_DEFINE_UNQUOTED([PCMK__LOCALE_DIR],["$localedir"],
[Base directory for message catalogs])
AS_IF([test x"${runstatedir}" = x""], [runstatedir="${pcmk_runstatedir}"])
expand_path_option runstatedir "${localstatedir}/run"
AC_DEFINE_UNQUOTED([PCMK_RUN_DIR], ["$runstatedir"],
[Location for modifiable per-process data])
AC_SUBST(runstatedir)
expand_path_option INITDIR
AC_DEFINE_UNQUOTED([PCMK__LSB_INIT_DIR], ["$INITDIR"],
[Location for LSB init scripts])
expand_path_option docdir "${datadir}/doc/${PACKAGE}-${VERSION}"
AC_SUBST(docdir)
expand_path_option CONFIGDIR "${sysconfdir}/sysconfig"
AC_SUBST(CONFIGDIR)
expand_path_option PCMK__COROSYNC_CONF "${sysconfdir}/corosync/corosync.conf"
AC_SUBST(PCMK__COROSYNC_CONF)
expand_path_option CRM_LOG_DIR "${localstatedir}/log/pacemaker"
AC_DEFINE_UNQUOTED([CRM_LOG_DIR], ["$CRM_LOG_DIR"],
[Location for Pacemaker log file])
AC_SUBST(CRM_LOG_DIR)
expand_path_option CRM_BUNDLE_DIR "${localstatedir}/log/pacemaker/bundles"
AC_DEFINE_UNQUOTED([CRM_BUNDLE_DIR], ["$CRM_BUNDLE_DIR"],
[Location for Pacemaker bundle logs])
AC_SUBST(CRM_BUNDLE_DIR)
expand_path_option PCMK__FENCE_BINDIR
AC_SUBST(PCMK__FENCE_BINDIR)
AC_DEFINE_UNQUOTED([PCMK__FENCE_BINDIR], ["$PCMK__FENCE_BINDIR"],
[Location for executable fence agents])
expand_path_option OCF_ROOT_DIR
AC_SUBST(OCF_ROOT_DIR)
AC_DEFINE_UNQUOTED([OCF_ROOT_DIR], ["$OCF_ROOT_DIR"],
[OCF root directory for resource agents and libraries])
expand_path_option OCF_RA_PATH
AC_SUBST(OCF_RA_PATH)
AC_DEFINE_UNQUOTED([OCF_RA_PATH], ["$OCF_RA_PATH"],
[OCF directories to search for resource agents ])
expand_path_option OCF_RA_INSTALL_DIR
AC_SUBST(OCF_RA_INSTALL_DIR)
# Derived paths
CRM_SCHEMA_DIRECTORY="${datadir}/pacemaker"
AC_DEFINE_UNQUOTED([CRM_SCHEMA_DIRECTORY], ["$CRM_SCHEMA_DIRECTORY"],
[Location for the Pacemaker Relax-NG Schema])
AC_SUBST(CRM_SCHEMA_DIRECTORY)
PCMK__REMOTE_SCHEMA_DIR="${localstatedir}/lib/pacemaker/schemas"
AC_DEFINE_UNQUOTED([PCMK__REMOTE_SCHEMA_DIR], ["$PCMK__REMOTE_SCHEMA_DIR"],
[Location to store Relax-NG Schema files on remote nodes])
AC_SUBST(PCMK__REMOTE_SCHEMA_DIR)
CRM_CORE_DIR="${localstatedir}/lib/pacemaker/cores"
AC_DEFINE_UNQUOTED([CRM_CORE_DIR], ["$CRM_CORE_DIR"],
[Directory Pacemaker daemons should change to (without systemd, core files will go here)])
AC_SUBST(CRM_CORE_DIR)
CRM_PACEMAKER_DIR="${localstatedir}/lib/pacemaker"
AC_DEFINE_UNQUOTED([CRM_PACEMAKER_DIR], ["$CRM_PACEMAKER_DIR"],
[Location to store directory produced by Pacemaker daemons])
AC_SUBST(CRM_PACEMAKER_DIR)
CRM_BLACKBOX_DIR="${localstatedir}/lib/pacemaker/blackbox"
AC_DEFINE_UNQUOTED([CRM_BLACKBOX_DIR], ["$CRM_BLACKBOX_DIR"],
[Where to keep blackbox dumps])
AC_SUBST(CRM_BLACKBOX_DIR)
PE_STATE_DIR="${localstatedir}/lib/pacemaker/pengine"
AC_DEFINE_UNQUOTED([PE_STATE_DIR], ["$PE_STATE_DIR"],
[Where to keep scheduler outputs])
AC_SUBST(PE_STATE_DIR)
CRM_CONFIG_DIR="${localstatedir}/lib/pacemaker/cib"
AC_DEFINE_UNQUOTED([CRM_CONFIG_DIR], ["$CRM_CONFIG_DIR"],
[Where to keep configuration files])
AC_SUBST(CRM_CONFIG_DIR)
CRM_DAEMON_DIR="${libexecdir}/pacemaker"
AC_DEFINE_UNQUOTED([CRM_DAEMON_DIR], ["$CRM_DAEMON_DIR"],
[Location for Pacemaker daemons])
AC_SUBST(CRM_DAEMON_DIR)
CRM_STATE_DIR="${runstatedir}/crm"
AC_DEFINE_UNQUOTED([CRM_STATE_DIR], ["$CRM_STATE_DIR"],
[Where to keep state files and sockets])
AC_SUBST(CRM_STATE_DIR)
CRM_RSCTMP_DIR="${runstatedir}/resource-agents"
AC_DEFINE_UNQUOTED([CRM_RSCTMP_DIR], ["$CRM_RSCTMP_DIR"],
[Where resource agents should keep state files])
AC_SUBST(CRM_RSCTMP_DIR)
PACEMAKER_CONFIG_DIR="${sysconfdir}/pacemaker"
AC_DEFINE_UNQUOTED([PACEMAKER_CONFIG_DIR], ["$PACEMAKER_CONFIG_DIR"],
[Where to keep configuration files like authkey])
AC_SUBST(PACEMAKER_CONFIG_DIR)
AC_DEFINE_UNQUOTED([SBIN_DIR], ["$sbindir"], [Location for system binaries])
# Warn about any directories that don't exist (which may be OK)
for j in prefix exec_prefix bindir sbindir libexecdir datadir sysconfdir \
sharedstatedir localstatedir libdir includedir oldincludedir infodir \
mandir INITDIR docdir CONFIGDIR localedir
do
dirname=`eval echo '${'${j}'}'`
AS_IF([test ! -d "$dirname"],
[AC_MSG_WARN([$j directory ($dirname) does not exist (yet)])])
done
dnl ===============================================
dnl General Processing
dnl ===============================================
us_auth=
AC_CHECK_HEADER([sys/socket.h], [
AC_CHECK_DECL([SO_PEERCRED], [
# Linux
AC_CHECK_TYPE([struct ucred], [
us_auth=peercred_ucred;
AC_DEFINE([HAVE_UCRED], [1],
[Define if Unix socket auth method is
getsockopt(s, SO_PEERCRED, &ucred, ...)])
], [
# OpenBSD
AC_CHECK_TYPE([struct sockpeercred], [
us_auth=localpeercred_sockepeercred;
AC_DEFINE([HAVE_SOCKPEERCRED], [1],
[Define if Unix socket auth method is
getsockopt(s, SO_PEERCRED, &sockpeercred, ...)])
], [], [[#include <sys/socket.h>]])
], [[#define _GNU_SOURCE
#include <sys/socket.h>]])
], [], [[#include <sys/socket.h>]])
])
AS_IF([test -z "${us_auth}"], [
# FreeBSD
AC_CHECK_DECL([getpeereid], [
us_auth=getpeereid;
AC_DEFINE([HAVE_GETPEEREID], [1],
[Define if Unix socket auth method is
getpeereid(s, &uid, &gid)])
], [
# Solaris/OpenIndiana
AC_CHECK_DECL([getpeerucred], [
us_auth=getpeerucred;
AC_DEFINE([HAVE_GETPEERUCRED], [1],
[Define if Unix socket auth method is
getpeercred(s, &ucred)])
], [
AC_MSG_FAILURE([No way to authenticate a Unix socket peer])
], [[#include <ucred.h>]])
])
])
dnl OS-based decision-making is poor autotools practice; feature-based
dnl mechanisms are strongly preferred. Keep this section to a bare minimum;
dnl regard as a "necessary evil".
dnl Set host_os and host_cpu
AC_CANONICAL_HOST
INIT_EXT=""
PROCFS=0
dnl Solaris and some *BSD versions support procfs but not files we need
AS_CASE(["$host_os"],
[*bsd*], [INIT_EXT=".sh"],
[*linux*], [PROCFS=1],
[darwin*], [
LIBS="$LIBS -L${prefix}/lib"
CFLAGS="$CFLAGS -I${prefix}/include"
])
AC_SUBST(INIT_EXT)
AM_CONDITIONAL([SUPPORT_PROCFS], [test $PROCFS -eq 1])
AC_DEFINE_UNQUOTED([HAVE_LINUX_PROCFS], [$PROCFS],
[Define to 1 if procfs is supported])
AS_CASE(["$host_cpu"],
[ppc64|powerpc64], [
AS_CASE([$CFLAGS],
[*powerpc64*], [],
[*], [AS_IF([test x"$GCC" = x"yes"], [CFLAGS="$CFLAGS -m64"])
])
])
dnl ==============================================
dnl Documentation build dependencies and checks
dnl ==============================================
AC_PATH_PROGS([ASCIIDOC_CONV], [asciidoc asciidoctor])
AC_PATH_PROG([HELP2MAN], [help2man])
AC_PATH_PROG([SPHINX], [sphinx-build])
AC_PATH_PROG([INKSCAPE], [inkscape])
AC_PATH_PROG([XSLTPROC], [xsltproc])
AC_PATH_PROG([XMLCATALOG], [xmlcatalog])
AM_CONDITIONAL(BUILD_HELP, test x"${HELP2MAN}" != x"")
AS_IF([test x"${HELP2MAN}" != x""],
[PCMK_FEATURES="$PCMK_FEATURES generated-manpages"])
MANPAGE_XSLT=""
AS_IF([test x"${XSLTPROC}" != x""],
[
AC_MSG_CHECKING([for DocBook-to-manpage transform])
# first try to figure out correct template using xmlcatalog query,
# resort to extensive (semi-deterministic) file search if that fails
DOCBOOK_XSL_URI='http://docbook.sourceforge.net/release/xsl/current'
DOCBOOK_XSL_PATH='manpages/docbook.xsl'
MANPAGE_XSLT=$(${XMLCATALOG} "" ${DOCBOOK_XSL_URI}/${DOCBOOK_XSL_PATH} \
| sed -n 's|^file://||p;q')
AS_IF([test x"${MANPAGE_XSLT}" = x""],
[
DIRS=$(find "${datadir}" -name $(basename $(dirname ${DOCBOOK_XSL_PATH})) \
-type d 2>/dev/null | LC_ALL=C sort)
XSLT=$(basename ${DOCBOOK_XSL_PATH})
for d in ${DIRS}
do
AS_IF([test -f "${d}/${XSLT}"],
[
MANPAGE_XSLT="${d}/${XSLT}"
break
])
done
])
])
AC_MSG_RESULT([$MANPAGE_XSLT])
AC_SUBST(MANPAGE_XSLT)
AM_CONDITIONAL(BUILD_XML_HELP, test x"${MANPAGE_XSLT}" != x"")
AS_IF([test x"${MANPAGE_XSLT}" != x""],
[PCMK_FEATURES="$PCMK_FEATURES agent-manpages"])
AM_CONDITIONAL([IS_ASCIIDOC], [echo "${ASCIIDOC_CONV}" | grep -Eq 'asciidoc$'])
AM_CONDITIONAL([BUILD_ASCIIDOC], [test "x${ASCIIDOC_CONV}" != x])
AS_IF([test x"${ASCIIDOC_CONV}" != x""],
[PCMK_FEATURES="$PCMK_FEATURES ascii-docs"])
AM_CONDITIONAL([BUILD_SPHINX_DOCS],
[test x"${SPHINX}" != x"" && test x"${INKSCAPE}" != x""])
AM_COND_IF([BUILD_SPHINX_DOCS], [PCMK_FEATURES="$PCMK_FEATURES books"])
dnl Pacemaker's shell scripts (and thus man page builders) rely on GNU getopt
AC_MSG_CHECKING([for GNU-compatible getopt])
IFS_orig=$IFS
IFS=:
for PATH_DIR in $PATH
do
IFS=$IFS_orig
GETOPT_PATH="${PATH_DIR}/getopt"
AS_IF([test -f "$GETOPT_PATH" && test -x "$GETOPT_PATH"],
[
$GETOPT_PATH -T >/dev/null 2>/dev/null
AS_IF([test $? -eq 4], [break])
])
GETOPT_PATH=""
done
IFS=$IFS_orig
AS_IF([test -n "$GETOPT_PATH"], [AC_MSG_RESULT([$GETOPT_PATH])],
[
AC_MSG_RESULT([no])
AC_MSG_ERROR([Could not find required build tool GNU-compatible getopt])
])
AC_SUBST([GETOPT_PATH])
dnl ===============================================
dnl Libraries
dnl ===============================================
AC_CHECK_LIB(socket, socket) dnl -lsocket
AC_CHECK_LIB(c, dlopen) dnl if dlopen is in libc...
AC_CHECK_LIB(dl, dlopen) dnl -ldl (for Linux)
AC_CHECK_LIB(rt, sched_getscheduler) dnl -lrt (for Tru64)
AC_CHECK_LIB(gnugetopt, getopt_long) dnl -lgnugetopt ( if available )
AC_CHECK_LIB(pam, pam_start) dnl -lpam (if available)
PKG_CHECK_MODULES([UUID], [uuid],
[CPPFLAGS="${CPPFLAGS} ${UUID_CFLAGS}"
LIBS="${LIBS} ${UUID_LIBS}"])
AC_CHECK_FUNCS([sched_setscheduler])
AS_IF([test x"$ac_cv_func_sched_setscheduler" != x"yes"],
[PC_LIBS_RT=""],
[PC_LIBS_RT="-lrt"])
AC_SUBST(PC_LIBS_RT)
# Require minimum glib version
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.42.0],
[CPPFLAGS="${CPPFLAGS} ${GLIB_CFLAGS}"
LIBS="${LIBS} ${GLIB_LIBS}"])
# Check whether high-resolution sleep function is available
AC_CHECK_FUNCS([nanosleep usleep])
#
# Where is dlopen?
#
AS_IF([test x"$ac_cv_lib_c_dlopen" = x"yes"],
[LIBADD_DL=""],
[test x"$ac_cv_lib_dl_dlopen" = x"yes"],
[LIBADD_DL=-ldl],
[LIBADD_DL=${lt_cv_dlopen_libs}])
PKG_CHECK_MODULES(LIBXML2, [libxml-2.0 >= 2.9.2],
[CPPFLAGS="${CPPFLAGS} ${LIBXML2_CFLAGS}"
LIBS="${LIBS} ${LIBXML2_LIBS}"])
AC_PATH_PROGS(XMLLINT_PATH, xmllint, /usr/bin/xmllint)
AC_DEFINE_UNQUOTED(XMLLINT_PATH, "$XMLLINT_PATH", xmllint command)
REQUIRE_LIB([xslt], [xsltApplyStylesheet])
AC_MSG_CHECKING([whether __progname and __progname_full are available])
AC_LINK_IFELSE([AC_LANG_PROGRAM([[extern char *__progname, *__progname_full;]],
[[__progname = "foo";
__progname_full = "foo bar";]])],
[
have_progname="yes"
AC_DEFINE(HAVE_PROGNAME, 1,
[Define to 1 if processes can change their name])
],
[have_progname="no"])
AC_MSG_RESULT([$have_progname])
dnl ========================================================================
dnl Headers
dnl ========================================================================
# Some distributions insert #warnings into deprecated headers. If we will
# enable fatal warnings for the build, then enable them for the header checks
# as well, otherwise the build could fail even though the header check
# succeeds. (We should probably be doing this in more places.)
cc_temp_flags "$CFLAGS $WERROR"
# Optional headers (inclusion of these should be conditional in C code)
AC_CHECK_HEADERS([linux/swab.h])
AC_CHECK_HEADERS([stddef.h])
AC_CHECK_HEADERS([sys/signalfd.h])
AC_CHECK_HEADERS([uuid/uuid.h])
AC_CHECK_HEADERS([security/pam_appl.h pam/pam_appl.h])
# Required headers
REQUIRE_HEADER([arpa/inet.h])
REQUIRE_HEADER([ctype.h])
REQUIRE_HEADER([dirent.h])
REQUIRE_HEADER([dlfcn.h])
REQUIRE_HEADER([errno.h])
REQUIRE_HEADER([fcntl.h])
REQUIRE_HEADER([float.h])
REQUIRE_HEADER([glib.h])
REQUIRE_HEADER([grp.h])
REQUIRE_HEADER([inttypes.h])
REQUIRE_HEADER([libgen.h])
REQUIRE_HEADER([limits.h])
REQUIRE_HEADER([locale.h])
REQUIRE_HEADER([netdb.h])
REQUIRE_HEADER([netinet/in.h])
REQUIRE_HEADER([netinet/ip.h], [
#include <sys/types.h>
#include <netinet/in.h>
])
REQUIRE_HEADER([netinet/tcp.h])
REQUIRE_HEADER([pwd.h])
REQUIRE_HEADER([regex.h])
REQUIRE_HEADER([sched.h])
REQUIRE_HEADER([signal.h])
REQUIRE_HEADER([stdarg.h])
REQUIRE_HEADER([stdbool.h])
REQUIRE_HEADER([stdint.h])
REQUIRE_HEADER([stdio.h])
REQUIRE_HEADER([stdlib.h])
REQUIRE_HEADER([string.h])
REQUIRE_HEADER([strings.h])
REQUIRE_HEADER([sys/ioctl.h])
REQUIRE_HEADER([sys/param.h])
REQUIRE_HEADER([sys/reboot.h])
REQUIRE_HEADER([sys/resource.h])
REQUIRE_HEADER([sys/socket.h])
REQUIRE_HEADER([sys/stat.h])
REQUIRE_HEADER([sys/time.h])
REQUIRE_HEADER([sys/types.h])
REQUIRE_HEADER([sys/uio.h])
REQUIRE_HEADER([sys/utsname.h])
REQUIRE_HEADER([sys/wait.h])
REQUIRE_HEADER([termios.h])
REQUIRE_HEADER([time.h])
REQUIRE_HEADER([unistd.h])
REQUIRE_HEADER([libxml/xpath.h])
REQUIRE_HEADER([libxslt/xslt.h])
cc_restore_flags
dnl ========================================================================
dnl Generic declarations
dnl ========================================================================
AC_CHECK_DECLS([CLOCK_MONOTONIC], [PCMK_FEATURES="$PCMK_FEATURES monotonic"], [], [[
#include <time.h>
]])
dnl ========================================================================
dnl Unit test declarations
dnl ========================================================================
AC_CHECK_DECLS([assert_float_equal], [], [], [[
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
]])
dnl ========================================================================
dnl Byte size
dnl ========================================================================
# Compile-time assert hack
# https://jonjagger.blogspot.com/2017/07/compile-time-assertions-in-c.html
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <limits.h>]],
[[
switch (0) {
case 0:
case (CHAR_BIT == 8):
break;
}
]])],
[],
[AC_MSG_FAILURE(m4_normalize([Pacemaker is not supported on
platforms where char is not 8
bits]))])
dnl ========================================================================
dnl Structures
dnl ========================================================================
AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[[#include <time.h>]])
AC_CHECK_MEMBER([struct dirent.d_type],
AC_DEFINE(HAVE_STRUCT_DIRENT_D_TYPE,1,[Define this if struct dirent has d_type]),,
[#include <dirent.h>])
dnl ========================================================================
dnl Functions
dnl ========================================================================
REQUIRE_FUNC([alphasort])
REQUIRE_FUNC([getopt])
REQUIRE_FUNC([scandir])
REQUIRE_FUNC([setenv])
REQUIRE_FUNC([strndup])
REQUIRE_FUNC([strnlen])
REQUIRE_FUNC([unsetenv])
REQUIRE_FUNC([uuid_unparse])
REQUIRE_FUNC([vasprintf])
AC_CHECK_FUNCS([strchrnul])
AC_CHECK_FUNCS([fopen64])
AM_CONDITIONAL([WRAPPABLE_FOPEN64], [test x"$ac_cv_func_fopen64" = x"yes"])
AC_MSG_CHECKING([whether strerror always returns non-NULL])
AC_RUN_IFELSE([AC_LANG_PROGRAM([[
#include <stdio.h>
#include <string.h>
]], [[
return strerror(-1) == NULL;
]])],
[AC_MSG_RESULT([yes])],
[AC_MSG_ERROR([strerror() is not C99-compliant])],
[AC_MSG_ERROR([strerror() is not C99-compliant])])
AC_RUN_IFELSE([AC_LANG_PROGRAM([[#include <stdio.h>]], [[
const char *s = "some-command-line-arg";
char *name = NULL;
int n = sscanf(s, "%ms", &name);
return n != 1;
]])],
[have_sscanf_m="yes"],
[have_sscanf_m="no"],
[have_sscanf_m="no"])
AS_IF([test x"$have_sscanf_m" = x"yes"],
[AC_DEFINE([HAVE_SSCANF_M], [1],
[Define to 1 if sscanf %m modifier is available])])
dnl ========================================================================
dnl bzip2
dnl ========================================================================
REQUIRE_HEADER([bzlib.h])
REQUIRE_LIB([bz2], [BZ2_bzBuffToBuffCompress])
dnl ========================================================================
dnl sighandler_t is missing from Illumos, Solaris11 systems
dnl ========================================================================
AC_MSG_CHECKING([for sighandler_t])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <signal.h>]], [[sighandler_t *f;]])],
[
AC_MSG_RESULT([yes])
AC_DEFINE([HAVE_SIGHANDLER_T], [1],
[Define to 1 if sighandler_t is available])
],
[AC_MSG_RESULT([no])])
dnl ========================================================================
dnl ncurses
dnl ========================================================================
dnl
dnl A few OSes (e.g. Linux) deliver a default "ncurses" alongside "curses".
dnl Many non-Linux deliver "curses"; sites may add "ncurses".
dnl
dnl However, the source-code recommendation for both is to #include "curses.h"
dnl (i.e. "ncurses" still wants the include to be simple, no-'n', "curses.h").
dnl
dnl ncurses takes precedence.
dnl
AC_CHECK_HEADERS([curses.h curses/curses.h ncurses.h ncurses/ncurses.h])
dnl Although n-library is preferred, only look for it if the n-header was found.
CURSESLIBS=''
PC_NAME_CURSES=""
PC_LIBS_CURSES=""
AS_IF([test x"$ac_cv_header_ncurses_h" = x"yes"], [
AC_CHECK_LIB(ncurses, printw,
[AC_DEFINE(HAVE_LIBNCURSES,1, have ncurses library)])
CURSESLIBS=`$PKG_CONFIG --libs ncurses` || CURSESLIBS='-lncurses'
PC_NAME_CURSES="ncurses"
])
AS_IF([test x"$ac_cv_header_ncurses_ncurses_h" = x"yes"], [
AC_CHECK_LIB(ncurses, printw,
[AC_DEFINE(HAVE_LIBNCURSES,1, have ncurses library)])
CURSESLIBS=`$PKG_CONFIG --libs ncurses` || CURSESLIBS='-lncurses'
PC_NAME_CURSES="ncurses"
])
dnl Only look for non-n-library if there was no n-library.
AS_IF([test x"$CURSESLIBS" = x"" && test x"$ac_cv_header_curses_h" = x"yes"], [
AC_CHECK_LIB(curses, printw,
[CURSESLIBS='-lcurses'; AC_DEFINE(HAVE_LIBCURSES,1, have curses library)])
PC_LIBS_CURSES="$CURSESLIBS"
])
dnl Only look for non-n-library if there was no n-library.
AS_IF([test x"$CURSESLIBS" = x"" && test x"$ac_cv_header_curses_curses_h" = x"yes"], [
AC_CHECK_LIB(curses, printw,
[CURSESLIBS='-lcurses'; AC_DEFINE(HAVE_LIBCURSES,1, have curses library)])
PC_LIBS_CURSES="$CURSESLIBS"
])
AS_IF([test x"$CURSESLIBS" != x""],
[PCMK_FEATURES="$PCMK_FEATURES ncurses"])
dnl Check for printw() prototype compatibility
AS_IF([test x"$CURSESLIBS" != x"" && cc_supports_flag -Wcast-qual], [
ac_save_LIBS=$LIBS
LIBS="$CURSESLIBS"
# avoid broken test because of hardened build environment in Fedora 23+
# - https://fedoraproject.org/wiki/Changes/Harden_All_Packages
# - https://bugzilla.redhat.com/1297985
AS_IF([cc_supports_flag -fPIC],
[cc_temp_flags "-Wcast-qual $WERROR -fPIC"],
[cc_temp_flags "-Wcast-qual $WERROR"])
AC_MSG_CHECKING([whether curses library is compatible])
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([
#if defined(HAVE_NCURSES_H)
# include <ncurses.h>
#elif defined(HAVE_NCURSES_NCURSES_H)
# include <ncurses/ncurses.h>
#elif defined(HAVE_CURSES_H)
# include <curses.h>
#endif
],
[printw((const char *)"Test");]
)],
[AC_MSG_RESULT([yes])],
[
AC_MSG_RESULT([no])
AC_MSG_WARN(m4_normalize([Disabling curses because the printw()
function of your (n)curses library is old.
If you wish to enable curses, update to a
newer version (ncurses 5.4 or later is
recommended, available from
https://invisible-island.net/ncurses/)
]))
AC_DEFINE([HAVE_INCOMPATIBLE_PRINTW], [1],
[Define to 1 if curses library has incompatible printw()])
]
)
LIBS=$ac_save_LIBS
cc_restore_flags
])
AC_SUBST(CURSESLIBS)
AC_SUBST(PC_NAME_CURSES)
AC_SUBST(PC_LIBS_CURSES)
dnl ========================================================================
dnl Profiling and GProf
dnl ========================================================================
CFLAGS_ORIG="$CFLAGS"
AS_IF([test $with_coverage -ne $DISABLED],
[
with_profiling=$REQUIRED
PCMK_FEATURES="$PCMK_FEATURES coverage"
CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage"
dnl During linking, make sure to specify -lgcov or -coverage
]
)
AS_IF([test $with_profiling -ne $DISABLED],
[
with_profiling=$REQUIRED
PCMK_FEATURES="$PCMK_FEATURES profile"
dnl Disable various compiler optimizations
CFLAGS="$CFLAGS -fno-omit-frame-pointer -fno-inline -fno-builtin"
dnl CFLAGS="$CFLAGS -fno-inline-functions"
dnl CFLAGS="$CFLAGS -fno-default-inline"
dnl CFLAGS="$CFLAGS -fno-inline-functions-called-once"
dnl CFLAGS="$CFLAGS -fno-optimize-sibling-calls"
dnl Turn off optimization so tools can get accurate line numbers
CFLAGS=`echo $CFLAGS | sed \
-e 's/-O.\ //g' \
-e 's/-Wp,-D_FORTIFY_SOURCE=.\ //g' \
-e 's/-D_FORTIFY_SOURCE=.\ //g'`
CFLAGS="$CFLAGS -O0 -g3 -gdwarf-2"
AC_MSG_NOTICE([CFLAGS before adding profiling options: $CFLAGS_ORIG])
AC_MSG_NOTICE([CFLAGS after: $CFLAGS])
]
)
AM_CONDITIONAL([BUILD_PROFILING], [test "$with_profiling" = "$REQUIRED"])
dnl ========================================================================
dnl Cluster infrastructure - LibQB
dnl ========================================================================
PKG_CHECK_MODULES([libqb], [libqb >= 1.0.1])
CPPFLAGS="$libqb_CFLAGS $CPPFLAGS"
LIBS="$libqb_LIBS $LIBS"
dnl libqb 2.0.5+ (2022-03)
AC_CHECK_FUNCS([qb_ipcc_connect_async])
dnl libqb 2.0.2+ (2020-10)
AC_CHECK_FUNCS([qb_ipcc_auth_get])
dnl libqb 2.0.0+ (2020-05)
dnl also defines QB_FEATURE_LOG_HIRES_TIMESTAMPS
CHECK_ENUM_VALUE([qb/qblog.h],[qb_log_conf],[QB_LOG_CONF_MAX_LINE_LEN])
CHECK_ENUM_VALUE([qb/qblog.h],[qb_log_conf],[QB_LOG_CONF_ELLIPSIS])
dnl Support Linux-HA fence agents if available
AS_IF([test x"$cross_compiling" != x"yes"],
[CPPFLAGS="$CPPFLAGS -I${prefix}/include/heartbeat"])
AC_CHECK_HEADERS([stonith/stonith.h],
[
AC_CHECK_LIB([pils], [PILLoadPlugin])
AC_CHECK_LIB([plumb], [G_main_add_IPC_Channel])
PCMK_FEATURES="$PCMK_FEATURES lha"
])
AM_CONDITIONAL([BUILD_LHA_SUPPORT], [test x"$ac_cv_header_stonith_stonith_h" = x"yes"])
dnl ===============================================
dnl Detect DBus, systemd, and Upstart support
dnl ===============================================
HAVE_dbus=0
PC_NAME_DBUS=""
PKG_CHECK_MODULES([DBUS],[dbus-1 >= 1.5.12],
[
HAVE_dbus=1
PC_NAME_DBUS="dbus-1"
CPPFLAGS="${CPPFLAGS} ${DBUS_CFLAGS}"
],[])
AC_DEFINE_UNQUOTED(HAVE_DBUS, $HAVE_dbus, Support dbus)
AM_CONDITIONAL(BUILD_DBUS, test $HAVE_dbus = 1)
AC_SUBST(PC_NAME_DBUS)
check_systemdsystemunitdir() {
AC_MSG_CHECKING([which system unit file directory to use])
PKG_CHECK_VAR([systemdsystemunitdir], [systemd], [systemdsystemunitdir])
AC_MSG_RESULT([${systemdsystemunitdir}])
test x"$systemdsystemunitdir" != x""
return $?
}
AS_CASE([$enable_systemd],
[$REQUIRED], [
AS_IF([test $HAVE_dbus = 0],
[AC_MSG_FAILURE([Cannot support systemd resources without DBus])])
AS_IF([test "$ac_cv_have_decl_CLOCK_MONOTONIC" = "no"],
[AC_MSG_FAILURE([Cannot support systemd resources without monotonic clock])])
AS_IF([check_systemdsystemunitdir], [],
[AC_MSG_FAILURE([Cannot support systemd resources without systemdsystemunitdir])])
],
[$OPTIONAL], [
AS_IF([test $HAVE_dbus = 0 \
|| test x"$ac_cv_have_decl_CLOCK_MONOTONIC" = x"no"],
[enable_systemd=$DISABLED],
[
AC_MSG_CHECKING([for systemd version (using dbus-send)])
ret=$({ dbus-send --system --print-reply \
--dest=org.freedesktop.systemd1 \
/org/freedesktop/systemd1 \
org.freedesktop.DBus.Properties.Get \
string:org.freedesktop.systemd1.Manager \
string:Version 2>/dev/null \
|| echo "version unavailable"; } | tail -n1)
# sanitize output a bit (interested just in value, not type),
# ret is intentionally unenquoted so as to normalize whitespace
ret=$(echo ${ret} | cut -d' ' -f2-)
AC_MSG_RESULT([${ret}])
AS_IF([test x"$ret" != x"unavailable" \
|| systemctl --version 2>/dev/null | grep -q systemd],
[
AS_IF([check_systemdsystemunitdir],
[enable_systemd=$REQUIRED],
[enable_systemd=$DISABLED])
],
[enable_systemd=$DISABLED]
)
])
],
)
AC_MSG_CHECKING([whether to enable support for managing resources via systemd])
AS_IF([test $enable_systemd -eq $DISABLED], [AC_MSG_RESULT([no])],
[
AC_MSG_RESULT([yes])
PCMK_FEATURES="$PCMK_FEATURES systemd"
]
)
AC_SUBST([systemdsystemunitdir])
AC_DEFINE_UNQUOTED([SUPPORT_SYSTEMD], [$enable_systemd],
[Support systemd resources])
AM_CONDITIONAL([BUILD_SYSTEMD], [test $enable_systemd = $REQUIRED])
AC_SUBST(SUPPORT_SYSTEMD)
AS_CASE([$enable_upstart],
[$REQUIRED], [
AS_IF([test $HAVE_dbus = 0],
[AC_MSG_FAILURE([Cannot support Upstart resources without DBus])])
],
[$OPTIONAL], [
AS_IF([test $HAVE_dbus = 0], [enable_upstart=$DISABLED],
[
AC_MSG_CHECKING([for Upstart version (using dbus-send)])
ret=$({ dbus-send --system --print-reply \
--dest=com.ubuntu.Upstart \
/com/ubuntu/Upstart org.freedesktop.DBus.Properties.Get \
string:com.ubuntu.Upstart0_6 string:version 2>/dev/null \
|| echo "version unavailable"; } | tail -n1)
# sanitize output a bit (interested just in value, not type),
# ret is intentionally unenquoted so as to normalize whitespace
ret=$(echo ${ret} | cut -d' ' -f2-)
AC_MSG_RESULT([${ret}])
AS_IF([test x"$ret" != x"unavailable" \
|| initctl --version 2>/dev/null | grep -q upstart],
[enable_upstart=$REQUIRED],
[enable_upstart=$DISABLED]
)
])
],
)
AC_MSG_CHECKING([whether to enable support for managing resources via Upstart])
AS_IF([test $enable_upstart -eq $DISABLED], [AC_MSG_RESULT([no])],
[
AC_MSG_RESULT([yes])
PCMK_FEATURES="$PCMK_FEATURES upstart"
]
)
AC_DEFINE_UNQUOTED([SUPPORT_UPSTART], [$enable_upstart],
[Support Upstart resources])
AM_CONDITIONAL([BUILD_UPSTART], [test $enable_upstart -eq $REQUIRED])
AC_SUBST(SUPPORT_UPSTART)
dnl ========================================================================
dnl Detect Nagios support
dnl ========================================================================
AS_CASE([$with_nagios],
[$REQUIRED], [
AS_IF([test x"$ac_cv_have_decl_CLOCK_MONOTONIC" = x"no"],
[AC_MSG_FAILURE([Cannot support nagios resources without monotonic clock])])
],
[$OPTIONAL], [
AS_IF([test x"$ac_cv_have_decl_CLOCK_MONOTONIC" = x"no"],
[with_nagios=$DISABLED], [with_nagios=$REQUIRED])
]
)
AS_IF([test $with_nagios -eq $REQUIRED], [PCMK_FEATURES="$PCMK_FEATURES nagios"])
AC_DEFINE_UNQUOTED([SUPPORT_NAGIOS], [$with_nagios], [Support nagios plugins])
AM_CONDITIONAL([BUILD_NAGIOS], [test $with_nagios -eq $REQUIRED])
AS_IF([test x"$NAGIOS_PLUGIN_DIR" = x""],
[NAGIOS_PLUGIN_DIR="${libexecdir}/nagios/plugins"])
AC_DEFINE_UNQUOTED(NAGIOS_PLUGIN_DIR, "$NAGIOS_PLUGIN_DIR", Directory for nagios plugins)
AC_SUBST(NAGIOS_PLUGIN_DIR)
AS_IF([test x"$NAGIOS_METADATA_DIR" = x""],
[NAGIOS_METADATA_DIR="${datadir}/nagios/plugins-metadata"])
AC_DEFINE_UNQUOTED(NAGIOS_METADATA_DIR, "$NAGIOS_METADATA_DIR", Directory for nagios plugins metadata)
AC_SUBST(NAGIOS_METADATA_DIR)
STACKS=""
CLUSTERLIBS=""
PC_NAME_CLUSTER=""
dnl ========================================================================
dnl Cluster stack - Corosync
dnl ========================================================================
COROSYNC_LIBS=""
AS_CASE([$with_corosync],
[$REQUIRED], [
# These will be fatal if unavailable
PKG_CHECK_MODULES([cpg], [libcpg])
PKG_CHECK_MODULES([cfg], [libcfg])
PKG_CHECK_MODULES([cmap], [libcmap])
PKG_CHECK_MODULES([quorum], [libquorum])
PKG_CHECK_MODULES([libcorosync_common], [libcorosync_common])
]
[$OPTIONAL], [
PKG_CHECK_MODULES([cpg], [libcpg], [], [with_corosync=$DISABLED])
PKG_CHECK_MODULES([cfg], [libcfg], [], [with_corosync=$DISABLED])
PKG_CHECK_MODULES([cmap], [libcmap], [], [with_corosync=$DISABLED])
PKG_CHECK_MODULES([quorum], [libquorum], [], [with_corosync=$DISABLED])
PKG_CHECK_MODULES([libcorosync_common], [libcorosync_common], [], [with_corosync=$DISABLED])
AS_IF([test $with_corosync -ne $DISABLED], [with_corosync=$REQUIRED])
]
)
AS_IF([test $with_corosync -ne $DISABLED],
[
AC_MSG_CHECKING([for Corosync 2 or later])
AC_MSG_RESULT([yes])
CFLAGS="$CFLAGS $libqb_CFLAGS $cpg_CFLAGS $cfg_CFLAGS $cmap_CFLAGS $quorum_CFLAGS $libcorosync_common_CFLAGS"
CPPFLAGS="$CPPFLAGS `$PKG_CONFIG --cflags-only-I corosync`"
COROSYNC_LIBS="$COROSYNC_LIBS $cpg_LIBS $cfg_LIBS $cmap_LIBS $quorum_LIBS $libcorosync_common_LIBS"
CLUSTERLIBS="$CLUSTERLIBS $COROSYNC_LIBS"
PC_NAME_CLUSTER="$PC_CLUSTER_NAME libcfg libcmap libcorosync_common libcpg libquorum"
STACKS="$STACKS corosync-ge-2"
dnl Shutdown tracking added (back) to corosync Jan 2021
saved_LIBS="$LIBS"
LIBS="$LIBS $COROSYNC_LIBS"
AC_CHECK_FUNCS([corosync_cfg_trackstart])
LIBS="$saved_LIBS"
]
)
AC_DEFINE_UNQUOTED([SUPPORT_COROSYNC], [$with_corosync],
[Support the Corosync messaging and membership layer])
AM_CONDITIONAL([BUILD_CS_SUPPORT], [test $with_corosync -eq $REQUIRED])
AC_SUBST([SUPPORT_COROSYNC])
dnl
dnl Cluster stack - Sanity
dnl
AS_IF([test x"$STACKS" != x""], [AC_MSG_NOTICE([Supported stacks:${STACKS}])],
[AC_MSG_FAILURE([At least one cluster stack must be supported])])
PCMK_FEATURES="${PCMK_FEATURES}${STACKS}"
AC_SUBST(CLUSTERLIBS)
AC_SUBST(PC_NAME_CLUSTER)
dnl ========================================================================
dnl CIB secrets
dnl ========================================================================
AS_IF([test $with_cibsecrets -ne $DISABLED],
[
with_cibsecrets=$REQUIRED
PCMK_FEATURES="$PCMK_FEATURES cibsecrets"
LRM_CIBSECRETS_DIR="${localstatedir}/lib/pacemaker/lrm/secrets"
AC_DEFINE_UNQUOTED([LRM_CIBSECRETS_DIR], ["$LRM_CIBSECRETS_DIR"],
[Location for CIB secrets])
AC_SUBST([LRM_CIBSECRETS_DIR])
]
)
AC_DEFINE_UNQUOTED([SUPPORT_CIBSECRETS], [$with_cibsecrets], [Support CIB secrets])
AM_CONDITIONAL([BUILD_CIBSECRETS], [test $with_cibsecrets -eq $REQUIRED])
dnl ========================================================================
dnl GnuTLS
dnl ========================================================================
PKG_CHECK_MODULES(GNUTLS, [gnutls >= 3.1.7],
[CPPFLAGS="${CPPFLAGS} ${GNUTLS_CFLAGS}"
LIBS="${LIBS} ${GNUTLS_LIBS}"])
# --- ASAN/UBSAN/TSAN (see man gcc) ---
# when using SANitizers, we need to pass the -fsanitize..
# to both CFLAGS and LDFLAGS. The CFLAGS/LDFLAGS must be
# specified as first in the list or there will be runtime
# issues (for example user has to LD_PRELOAD asan for it to work
# properly).
AS_IF([test -n "${SANITIZERS}"], [
SANITIZERS=$(echo $SANITIZERS | sed -e 's/,/ /g')
for SANITIZER in $SANITIZERS
do
AS_CASE([$SANITIZER],
[asan|ASAN], [
SANITIZERS_CFLAGS="$SANITIZERS_CFLAGS -fsanitize=address"
SANITIZERS_LDFLAGS="$SANITIZERS_LDFLAGS -fsanitize=address -lasan"
PCMK_FEATURES="$PCMK_FEATURES asan"
REQUIRE_LIB([asan],[main])
],
[ubsan|UBSAN], [
SANITIZERS_CFLAGS="$SANITIZERS_CFLAGS -fsanitize=undefined"
SANITIZERS_LDFLAGS="$SANITIZERS_LDFLAGS -fsanitize=undefined -lubsan"
PCMK_FEATURES="$PCMK_FEATURES ubsan"
REQUIRE_LIB([ubsan],[main])
],
[tsan|TSAN], [
SANITIZERS_CFLAGS="$SANITIZERS_CFLAGS -fsanitize=thread"
SANITIZERS_LDFLAGS="$SANITIZERS_LDFLAGS -fsanitize=thread -ltsan"
PCMK_FEATURES="$PCMK_FEATURES tsan"
REQUIRE_LIB([tsan],[main])
])
done
])
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.
AS_IF([export | fgrep " CFLAGS=" > /dev/null],
[
SAVED_CFLAGS="$CFLAGS"
unset CFLAGS
CFLAGS="$SAVED_CFLAGS"
unset SAVED_CFLAGS
])
CC_EXTRAS=""
AS_IF([test x"$GCC" != x"yes"], [CFLAGS="$CFLAGS -g"], [
CFLAGS="$CFLAGS -ggdb"
dnl When we don't have diagnostic push / pull, we can't explicitly disable
dnl checking for nonliteral formats in the places where they occur on purpose
dnl thus we disable nonliteral format checking globally as we are aborting
dnl on warnings.
dnl what makes the things really ugly is that nonliteral format checking is
dnl obviously available as an extra switch in very modern gcc but for older
dnl gcc this is part of -Wformat=2
dnl so if we have push/pull we can enable -Wformat=2 -Wformat-nonliteral
dnl if we don't have push/pull but -Wformat-nonliteral we can enable -Wformat=2
dnl otherwise none of both
gcc_diagnostic_push_pull=no
cc_temp_flags "$CFLAGS $WERROR"
AC_MSG_CHECKING([for gcc diagnostic push / pull])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#pragma GCC diagnostic push
#pragma GCC diagnostic pop
]])],
[
AC_MSG_RESULT([yes])
gcc_diagnostic_push_pull=yes
], AC_MSG_RESULT([no]))
cc_restore_flags
AS_IF([cc_supports_flag "-Wformat-nonliteral"],
[gcc_format_nonliteral=yes],
[gcc_format_nonliteral=no])
# We had to eliminate -Wnested-externs because of libtool changes
# Make sure to order options so that the former stand for prerequisites
# of the latter (e.g., -Wformat-nonliteral requires -Wformat).
EXTRA_FLAGS="-fgnu89-inline"
EXTRA_FLAGS="$EXTRA_FLAGS -Wall"
EXTRA_FLAGS="$EXTRA_FLAGS -Waggregate-return"
EXTRA_FLAGS="$EXTRA_FLAGS -Wbad-function-cast"
EXTRA_FLAGS="$EXTRA_FLAGS -Wcast-align"
EXTRA_FLAGS="$EXTRA_FLAGS -Wdeclaration-after-statement"
EXTRA_FLAGS="$EXTRA_FLAGS -Wendif-labels"
EXTRA_FLAGS="$EXTRA_FLAGS -Wfloat-equal"
EXTRA_FLAGS="$EXTRA_FLAGS -Wformat-security"
EXTRA_FLAGS="$EXTRA_FLAGS -Wimplicit-fallthrough"
EXTRA_FLAGS="$EXTRA_FLAGS -Wmissing-prototypes"
EXTRA_FLAGS="$EXTRA_FLAGS -Wmissing-declarations"
EXTRA_FLAGS="$EXTRA_FLAGS -Wnested-externs"
EXTRA_FLAGS="$EXTRA_FLAGS -Wno-long-long"
EXTRA_FLAGS="$EXTRA_FLAGS -Wno-strict-aliasing"
EXTRA_FLAGS="$EXTRA_FLAGS -Wpointer-arith"
EXTRA_FLAGS="$EXTRA_FLAGS -Wstrict-prototypes"
EXTRA_FLAGS="$EXTRA_FLAGS -Wwrite-strings"
EXTRA_FLAGS="$EXTRA_FLAGS -Wunused-but-set-variable"
EXTRA_FLAGS="$EXTRA_FLAGS -Wunsigned-char"
AS_IF([test x"$gcc_diagnostic_push_pull" = x"yes"],
[
AC_DEFINE([HAVE_FORMAT_NONLITERAL], [],
[gcc can complain about nonliterals in format])
EXTRA_FLAGS="$EXTRA_FLAGS -Wformat=2 -Wformat-nonliteral"
],
[test x"$gcc_format_nonliteral" = x"yes"],
[EXTRA_FLAGS="$EXTRA_FLAGS -Wformat=2"])
# Additional warnings it might be nice to enable one day
# -Wshadow
# -Wunreachable-code
for j in $EXTRA_FLAGS
do
AS_IF([cc_supports_flag $CC_EXTRAS $j], [CC_EXTRAS="$CC_EXTRAS $j"])
done
AC_MSG_NOTICE([Using additional gcc flags: ${CC_EXTRAS}])
])
dnl
dnl Hardening flags
dnl
dnl The prime control of whether to apply (targeted) hardening build flags and
dnl which ones is --{enable,disable}-hardening option passed to ./configure:
dnl
dnl --enable-hardening=try (default):
dnl depending on whether any of CFLAGS_HARDENED_EXE, LDFLAGS_HARDENED_EXE,
dnl CFLAGS_HARDENED_LIB or LDFLAGS_HARDENED_LIB environment variables
dnl (see below) is set and non-null, all these custom flags (even if not
dnl set) are used as are, otherwise the best effort is made to offer
dnl reasonably strong hardening in several categories (RELRO, PIE,
dnl "bind now", stack protector) according to what the selected toolchain
dnl can offer
dnl
dnl --enable-hardening:
dnl same effect as --enable-hardening=try when the environment variables
dnl in question are suppressed
dnl
dnl --disable-hardening:
dnl do not apply any targeted hardening measures at all
dnl
dnl The user-injected environment variables that regulate the hardening in
dnl default case are as follows:
dnl
dnl * CFLAGS_HARDENED_EXE, LDFLAGS_HARDENED_EXE
dnl compiler and linker flags (respectively) for daemon programs
dnl (pacemakerd, pacemaker-attrd, pacemaker-controld, pacemaker-execd,
dnl pacemaker-based, pacemaker-fenced, pacemaker-remoted,
dnl pacemaker-schedulerd)
dnl
dnl * CFLAGS_HARDENED_LIB, LDFLAGS_HARDENED_LIB
dnl compiler and linker flags (respectively) for libraries linked
dnl with the daemon programs
dnl
dnl Note that these are purposedly targeted variables (addressing particular
dnl targets all over the scattered Makefiles) and have no effect outside of
dnl the predestined scope (e.g., CLI utilities). For a global reach,
dnl use CFLAGS, LDFLAGS, etc. as usual.
dnl
dnl For guidance on the suitable flags consult, for instance:
dnl https://fedoraproject.org/wiki/Changes/Harden_All_Packages#Detailed_Harden_Flags_Description
dnl https://owasp.org/index.php/C-Based_Toolchain_Hardening#GCC.2FBinutils
dnl
AS_IF([test $enable_hardening -eq $OPTIONAL],
[
AS_IF([test "$(env | grep -Ec '^(C|LD)FLAGS_HARDENED_(EXE|LIB)=.')" = 0],
[enable_hardening=$REQUIRED],
[AC_MSG_NOTICE([Hardening: using custom flags from environment])]
)
],
[
unset CFLAGS_HARDENED_EXE
unset CFLAGS_HARDENED_LIB
unset LDFLAGS_HARDENED_EXE
unset LDFLAGS_HARDENED_LIB
]
)
AS_CASE([$enable_hardening],
[$DISABLED], [AC_MSG_NOTICE([Hardening: explicitly disabled])],
[$REQUIRED], [
CFLAGS_HARDENED_EXE=
CFLAGS_HARDENED_LIB=
LDFLAGS_HARDENED_EXE=
LDFLAGS_HARDENED_LIB=
relro=0
pie=0
bindnow=0
stackprot="none"
# daemons incl. libs: partial RELRO
flag="-Wl,-z,relro"
CC_CHECK_LDFLAGS(["${flag}"],
[
LDFLAGS_HARDENED_EXE="${LDFLAGS_HARDENED_EXE} ${flag}"
LDFLAGS_HARDENED_LIB="${LDFLAGS_HARDENED_LIB} ${flag}"
relro=1
])
# daemons: PIE for both CFLAGS and LDFLAGS
AS_IF([cc_supports_flag -fPIE],
[
flag="-pie"
CC_CHECK_LDFLAGS(["${flag}"],
[
CFLAGS_HARDENED_EXE="${CFLAGS_HARDENED_EXE} -fPIE"
LDFLAGS_HARDENED_EXE="${LDFLAGS_HARDENED_EXE} ${flag}"
pie=1
])
]
)
# daemons incl. libs: full RELRO if sensible + as-needed linking
# so as to possibly mitigate startup performance
# hit caused by excessive linking with unneeded
# libraries
AS_IF([test "${relro}" = 1 && test "${pie}" = 1],
[
flag="-Wl,-z,now"
CC_CHECK_LDFLAGS(["${flag}"],
[
LDFLAGS_HARDENED_EXE="${LDFLAGS_HARDENED_EXE} ${flag}"
LDFLAGS_HARDENED_LIB="${LDFLAGS_HARDENED_LIB} ${flag}"
bindnow=1
])
]
)
AS_IF([test "${bindnow}" = 1],
[
flag="-Wl,--as-needed"
CC_CHECK_LDFLAGS(["${flag}"],
[
LDFLAGS_HARDENED_EXE="${LDFLAGS_HARDENED_EXE} ${flag}"
LDFLAGS_HARDENED_LIB="${LDFLAGS_HARDENED_LIB} ${flag}"
])
])
# universal: prefer strong > all > default stack protector if possible
flag=
AS_IF([cc_supports_flag -fstack-protector-strong],
[
flag="-fstack-protector-strong"
stackprot="strong"
],
[cc_supports_flag -fstack-protector-all],
[
flag="-fstack-protector-all"
stackprot="all"
],
[cc_supports_flag -fstack-protector],
[
flag="-fstack-protector"
stackprot="default"
]
)
AS_IF([test -n "${flag}"], [CC_EXTRAS="${CC_EXTRAS} ${flag}"])
# universal: enable stack clash protection if possible
AS_IF([cc_supports_flag -fstack-clash-protection],
[
CC_EXTRAS="${CC_EXTRAS} -fstack-clash-protection"
AS_IF([test "${stackprot}" = "none"],
[stackprot="clash-only"],
[stackprot="${stackprot}+clash"]
)
]
)
# Log a summary
AS_IF([test "${relro}" = 1 || test "${pie}" = 1 || test x"${stackprot}" != x"none"],
[AC_MSG_NOTICE(m4_normalize([Hardening:
relro=${relro}
pie=${pie}
bindnow=${bindnow}
stackprot=${stackprot}]))
],
[AC_MSG_WARN([Hardening: no suitable features in the toolchain detected])]
)
],
)
CFLAGS="$SANITIZERS_CFLAGS $CFLAGS $CC_EXTRAS"
LDFLAGS="$SANITIZERS_LDFLAGS $LDFLAGS"
CFLAGS_HARDENED_EXE="$SANITIZERS_CFLAGS $CFLAGS_HARDENED_EXE"
LDFLAGS_HARDENED_EXE="$SANITIZERS_LDFLAGS $LDFLAGS_HARDENED_EXE"
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
AS_IF([test $enable_fatal_warnings -ne $DISABLED], [
AC_MSG_NOTICE([Enabling fatal compiler warnings])
CFLAGS="$CFLAGS $WERROR"
])
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(LIBADD_DL) dnl extra flags for dynamic linking libraries
AC_SUBST(LOCALE)
dnl Options for cleaning up the compiler output
AS_IF([test $enable_quiet -ne $DISABLED],
[
AC_MSG_NOTICE([Suppressing make details])
QUIET_LIBTOOL_OPTS="--silent"
QUIET_MAKE_OPTS="-s" # POSIX compliant
],
[
QUIET_LIBTOOL_OPTS=""
QUIET_MAKE_OPTS=""
]
)
dnl Put the above variables to use
LIBTOOL="${LIBTOOL} --tag=CC \$(QUIET_LIBTOOL_OPTS)"
MAKEFLAGS="${MAKEFLAGS} ${QUIET_MAKE_OPTS}"
# Make features list available (sorted alphabetically, without leading space)
PCMK_FEATURES=`echo "$PCMK_FEATURES" | sed -e 's/^ //' -e 's/ /\n/g' | sort | xargs`
AC_DEFINE_UNQUOTED(CRM_FEATURES, "$PCMK_FEATURES", Set of enabled features)
AC_SUBST(PCMK_FEATURES)
AC_SUBST(CC)
AC_SUBST(MAKEFLAGS)
AC_SUBST(LIBTOOL)
AC_SUBST(QUIET_LIBTOOL_OPTS)
dnl Files we output that need to be executable
CONFIG_FILES_EXEC([agents/ocf/ClusterMon],
[agents/ocf/Dummy],
[agents/ocf/HealthCPU],
[agents/ocf/HealthIOWait],
[agents/ocf/HealthSMART],
[agents/ocf/Stateful],
[agents/ocf/SysInfo],
[agents/ocf/attribute],
[agents/ocf/controld],
[agents/ocf/ifspeed],
[agents/ocf/ping],
[agents/ocf/remote],
[agents/stonith/fence_legacy],
[agents/stonith/fence_watchdog],
[cts/cluster_test],
[cts/cts],
[cts/cts-attrd],
[cts/cts-cli],
[cts/cts-exec],
[cts/cts-fencing],
[cts/cts-lab],
[cts/cts-regression],
[cts/cts-scheduler],
[cts/cts-schemas],
[cts/benchmark/clubench],
[cts/support/LSBDummy],
[cts/support/cts-support],
[cts/support/fence_dummy],
[cts/support/pacemaker-cts-dummyd],
[doc/abi-check],
[maint/bumplibs],
[tools/cluster-clean],
[tools/cluster-helper],
[tools/crm_failcount],
[tools/crm_master],
[tools/crm_report],
[tools/crm_standby],
[tools/cibsecret],
[tools/pcmk_simtimes],
[xml/rng-helper])
dnl Other files we output
AC_CONFIG_FILES(Makefile \
agents/Makefile \
agents/alerts/Makefile \
agents/ocf/Makefile \
agents/stonith/Makefile \
cts/Makefile \
cts/benchmark/Makefile \
cts/scheduler/Makefile \
cts/scheduler/dot/Makefile \
cts/scheduler/exp/Makefile \
cts/scheduler/scores/Makefile \
cts/scheduler/stderr/Makefile \
cts/scheduler/summary/Makefile \
cts/scheduler/xml/Makefile \
cts/support/Makefile \
cts/support/pacemaker-cts-dummyd@.service \
daemons/Makefile \
daemons/attrd/Makefile \
daemons/based/Makefile \
daemons/controld/Makefile \
daemons/execd/Makefile \
daemons/execd/pacemaker_remote \
daemons/execd/pacemaker_remote.service \
daemons/fenced/Makefile \
daemons/pacemakerd/Makefile \
daemons/pacemakerd/pacemaker.combined.upstart \
daemons/pacemakerd/pacemaker.service \
daemons/pacemakerd/pacemaker.upstart \
daemons/schedulerd/Makefile \
devel/Makefile \
doc/Doxyfile \
doc/Makefile \
doc/sphinx/Makefile \
etc/Makefile \
etc/init.d/pacemaker \
etc/logrotate.d/pacemaker \
etc/sysconfig/pacemaker \
include/Makefile \
include/crm/Makefile \
include/crm/cib/Makefile \
include/crm/common/Makefile \
include/crm/cluster/Makefile \
include/crm/fencing/Makefile \
include/crm/pengine/Makefile \
include/pcmki/Makefile \
lib/Makefile \
lib/cib/Makefile \
lib/cluster/Makefile \
lib/cluster/tests/Makefile \
lib/cluster/tests/cluster/Makefile \
lib/cluster/tests/cpg/Makefile \
lib/common/Makefile \
lib/common/tests/Makefile \
lib/common/tests/acl/Makefile \
lib/common/tests/actions/Makefile \
lib/common/tests/agents/Makefile \
lib/common/tests/cmdline/Makefile \
lib/common/tests/digest/Makefile \
lib/common/tests/flags/Makefile \
lib/common/tests/health/Makefile \
lib/common/tests/io/Makefile \
lib/common/tests/iso8601/Makefile \
lib/common/tests/lists/Makefile \
lib/common/tests/messages/Makefile \
lib/common/tests/nodes/Makefile \
lib/common/tests/nvpair/Makefile \
lib/common/tests/options/Makefile \
lib/common/tests/output/Makefile \
lib/common/tests/probes/Makefile \
lib/common/tests/procfs/Makefile \
lib/common/tests/resources/Makefile \
lib/common/tests/results/Makefile \
lib/common/tests/rules/Makefile \
lib/common/tests/scheduler/Makefile \
lib/common/tests/schemas/Makefile \
lib/common/tests/scores/Makefile \
lib/common/tests/strings/Makefile \
lib/common/tests/utils/Makefile \
lib/common/tests/xml/Makefile \
lib/common/tests/xpath/Makefile \
lib/fencing/Makefile \
lib/libpacemaker.pc \
lib/lrmd/Makefile \
lib/pacemaker/Makefile \
lib/pacemaker/tests/Makefile \
lib/pacemaker/tests/pcmk_resource/Makefile \
lib/pacemaker/tests/pcmk_ticket/Makefile \
lib/pacemaker.pc \
lib/pacemaker-cib.pc \
lib/pacemaker-cluster.pc \
lib/pacemaker-fencing.pc \
lib/pacemaker-lrmd.pc \
lib/pacemaker-service.pc \
lib/pacemaker-pe_rules.pc \
lib/pacemaker-pe_status.pc \
lib/pengine/Makefile \
lib/pengine/tests/Makefile \
lib/pengine/tests/native/Makefile \
lib/pengine/tests/status/Makefile \
lib/pengine/tests/unpack/Makefile \
lib/pengine/tests/utils/Makefile \
lib/services/Makefile \
maint/Makefile \
po/Makefile.in \
python/Makefile \
python/setup.py \
python/pacemaker/Makefile \
python/pacemaker/_cts/Makefile \
python/pacemaker/_cts/tests/Makefile \
python/pacemaker/buildoptions.py \
python/tests/Makefile \
rpm/Makefile \
tests/Makefile \
tools/Makefile \
tools/crm_mon.service \
tools/crm_mon.upstart \
tools/report.collector \
tools/report.common \
xml/Makefile \
xml/pacemaker-schemas.pc \
)
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_NOTICE([])
AC_MSG_NOTICE([$PACKAGE configuration:])
AC_MSG_NOTICE([ Version = ${VERSION} (Build: $BUILD_VERSION)])
AC_MSG_NOTICE([ Features = ${PCMK_FEATURES}])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([ Prefix = ${prefix}])
AC_MSG_NOTICE([ Executables = ${sbindir}])
AC_MSG_NOTICE([ Man pages = ${mandir}])
AC_MSG_NOTICE([ Libraries = ${libdir}])
AC_MSG_NOTICE([ Header files = ${includedir}])
AC_MSG_NOTICE([ Arch-independent files = ${datadir}])
AC_MSG_NOTICE([ State information = ${localstatedir}])
AC_MSG_NOTICE([ System configuration = ${sysconfdir}])
AC_MSG_NOTICE([ OCF agents = ${OCF_ROOT_DIR}])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([ HA group name = ${CRM_DAEMON_GROUP}])
AC_MSG_NOTICE([ HA user name = ${CRM_DAEMON_USER}])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([ CFLAGS = ${CFLAGS}])
AC_MSG_NOTICE([ CFLAGS_HARDENED_EXE = ${CFLAGS_HARDENED_EXE}])
AC_MSG_NOTICE([ CFLAGS_HARDENED_LIB = ${CFLAGS_HARDENED_LIB}])
AC_MSG_NOTICE([ LDFLAGS_HARDENED_EXE = ${LDFLAGS_HARDENED_EXE}])
AC_MSG_NOTICE([ LDFLAGS_HARDENED_LIB = ${LDFLAGS_HARDENED_LIB}])
AC_MSG_NOTICE([ Libraries = ${LIBS}])
AC_MSG_NOTICE([ Stack Libraries = ${CLUSTERLIBS}])
AC_MSG_NOTICE([ Unix socket auth method = ${us_auth}])
diff --git a/doc/sphinx/Makefile.am b/doc/sphinx/Makefile.am
index b95f47b959..d5826733e9 100644
--- a/doc/sphinx/Makefile.am
+++ b/doc/sphinx/Makefile.am
@@ -1,233 +1,233 @@
#
# Copyright 2003-2024 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
# This source code is licensed under the GNU General Public License version 2
# or later (GPLv2+) WITHOUT ANY WARRANTY.
#
include $(top_srcdir)/mk/common.mk
# Define release-related variables
include $(top_srcdir)/mk/release.mk
# Things you might want to override on the command line
# Books to generate
BOOKS ?= Clusters_from_Scratch \
Pacemaker_Administration \
Pacemaker_Development \
Pacemaker_Explained \
Pacemaker_Python_API \
Pacemaker_Remote
# Output formats to generate. Possible values:
# html (multiple HTML files)
# dirhtml (HTML files named index.html in multiple directories)
# singlehtml (a single large HTML file)
# text
# pdf
# epub
# latex
# linkcheck (not actually a format; check validity of external links)
#
# The results will end up in <book>/_build/<format>
BOOK_FORMATS ?= singlehtml
# Set to "a4paper" or "letterpaper" if building latex format
PAPER ?= letterpaper
# Additional options for sphinx-build
SPHINXFLAGS ?=
# toplevel rsync destination for www targets (without trailing slash)
RSYNC_DEST ?= root@www.clusterlabs.org:/var/www/html
# End of useful overrides
# Example scheduler transition graphs
# @TODO The original CIB XML for these is long lost. Ideally, we would recreate
# something similar and keep those here instead of the DOTs (or use a couple of
# scheduler regression test inputs instead), then regenerate the SVG
# equivalents using crm_simulate and dot when making a release.
DOTS = $(wildcard shared/images/*.dot)
# Vector sources for generated PNGs (including SVG equivalents of DOTS, created
# manually using dot)
SVGS = $(wildcard shared/images/pcmk-*.svg) \
$(DOTS:%.dot=%.svg)
# PNG images generated from SVGS
#
# These will not be accessible in a VPATH build, which will generate warnings
# when building the documentation, but the make will still succeed. It is
# nontrivial to get them working for VPATH builds and not worth the effort.
PNGS_GENERATED = $(SVGS:%.svg=%.png)
# Original PNG image sources
PNGS_Clusters_from_Scratch = $(wildcard Clusters_from_Scratch/images/*.png)
PNGS_Pacemaker_Explained = $(wildcard Pacemaker_Explained/images/*.png)
PNGS_Pacemaker_Remote = $(wildcard Pacemaker_Remote/images/*.png)
STATIC_FILES = $(wildcard _static/*.css)
EXTRA_DIST = $(wildcard */*.rst) $(DOTS) $(SVGS) \
$(PNGS_Clusters_from_Scratch) \
$(PNGS_Pacemaker_Explained) \
$(PNGS_Pacemaker_Remote) \
$(wildcard Pacemaker_Python_API/_templates/*rst) \
$(STATIC_FILES) \
conf.py.in
# recursive, preserve symlinks/permissions/times, verbose, compress,
# don't cross filesystems, sparse, show progress
RSYNC_OPTS = -rlptvzxS --progress
PACKAGE_SERIES=$(shell echo "$(VERSION)" | awk -F. '{ print $$1"."$$2 }')
BOOK_RSYNC_DEST = $(RSYNC_DEST)/$(PACKAGE)/doc/$(PACKAGE_SERIES)
BOOK = none
DEPS_intro = shared/pacemaker-intro.rst \
$(PNGS_GENERATED)
DEPS_Clusters_from_Scratch = $(DEPS_intro) \
$(PNGS_Clusters_from_Scratch)
DEPS_Pacemaker_Administration = $(DEPS_intro)
DEPS_Pacemaker_Development =
DEPS_Pacemaker_Explained = $(DEPS_intro) \
$(PNGS_Pacemaker_Explained)
DEPS_Pacemaker_Python_API = ../../python
DEPS_Pacemaker_Remote = $(PNGS_Pacemaker_Remote)
if BUILD_SPHINX_DOCS
INKSCAPE_CMD = $(INKSCAPE) --export-dpi=90 -C
# Pattern rule to generate PNGs from SVGs
# (--export-png works with Inkscape <1.0, --export-filename with >=1.0;
# create the destination directory in case this is a VPATH build)
%.png: %.svg
$(AM_V_at)-$(MKDIR_P) "$(shell dirname "$@")"
$(AM_V_GEN) { \
$(INKSCAPE_CMD) --export-png="$@" "$<" 2>/dev/null \
|| $(INKSCAPE_CMD) --export-filename="$@" "$<"; \
} $(PCMK_quiet)
# Create a book's Sphinx configuration.
# Create the book directory in case this is a VPATH build.
$(BOOKS:%=%/conf.py): conf.py.in
$(AM_V_at)-$(MKDIR_P) "$(@:%/conf.py=%)"
$(AM_V_GEN)sed \
-e 's/%VERSION%/$(VERSION)/g' \
-e 's/%BOOK_ID%/$(@:%/conf.py=%)/g' \
-e 's/%BOOK_TITLE%/$(subst _, ,$(@:%/conf.py=%))/g' \
-e 's#%SRC_DIR%#$(abs_srcdir)#g' \
-e 's#%ABS_TOP_SRCDIR%#$(abs_top_srcdir)#g' \
-e 's#%CONFIGDIR%#@CONFIGDIR@#g' \
-e 's#%CRM_BLACKBOX_DIR%#@CRM_BLACKBOX_DIR@#g' \
-e 's#%CRM_CONFIG_DIR%#@CRM_CONFIG_DIR@#g' \
-e 's#%CRM_DAEMON_GROUP%#@CRM_DAEMON_GROUP@#g' \
-e 's#%CRM_DAEMON_USER%#@CRM_DAEMON_USER@#g' \
-e 's#%CRM_LOG_DIR%#@CRM_LOG_DIR@#g' \
-e 's#%CRM_SCHEMA_DIRECTORY%#@CRM_SCHEMA_DIRECTORY@#g' \
-e 's#%PACEMAKER_CONFIG_DIR%#@PACEMAKER_CONFIG_DIR@#g' \
- -e 's#%PCMK_GNUTLS_PRIORITIES%#@PCMK_GNUTLS_PRIORITIES@#g' \
+ -e 's#%PCMK__GNUTLS_PRIORITIES%#@PCMK__GNUTLS_PRIORITIES@#g' \
-e 's#%PCMK__REMOTE_SCHEMA_DIR%#@PCMK__REMOTE_SCHEMA_DIR@#g' \
$(<) > "$@"
$(BOOK)/_build: $(STATIC_FILES) $(BOOK)/conf.py $(DEPS_$(BOOK)) $(wildcard $(srcdir)/$(BOOK)/*.rst)
@echo 'Building "$(subst _, ,$(BOOK))" because of $?' $(PCMK_quiet)
$(AM_V_at)rm -rf "$@"
$(AM_V_BOOK)for format in $(BOOK_FORMATS); do \
echo -e "\n * Building $$format" $(PCMK_quiet); \
doctrees="doctrees"; \
real_format="$$format"; \
case "$$format" in \
pdf) real_format="latex" ;; \
gettext) doctrees="gettext-doctrees" ;; \
esac; \
$(SPHINX) -b "$$real_format" -d "$@/$$doctrees" \
-c "$(builddir)/$(BOOK)" \
-D latex_elements.papersize=$(PAPER) \
$(SPHINXFLAGS) \
"$(srcdir)/$(BOOK)" "$@/$$format" \
$(PCMK_quiet); \
if [ "$$format" = "pdf" ]; then \
$(MAKE) $(AM_MAKEFLAGS) -C "$@/$$format" \
all-pdf; \
fi; \
done
endif
build-$(PACKAGE_SERIES).txt: all
$(AM_V_GEN)echo "Generated on `date --utc` from version $(TAG)" > "$@"
.PHONY: books-upload
books-upload: all build-$(PACKAGE_SERIES).txt
if BUILD_SPHINX_DOCS
@echo "Uploading $(PACKAGE_SERIES) documentation set"
@for book in $(BOOKS); do \
echo " * $$book"; \
rsync $(RSYNC_OPTS) $(BOOK_FORMATS:%=$$book/_build/%) \
"$(BOOK_RSYNC_DEST)/$$book/"; \
done
@rsync $(RSYNC_OPTS) "$(builddir)/build-$(PACKAGE_SERIES).txt" \
"$(RSYNC_DEST)/$(PACKAGE)/doc"
endif
.PHONY: vars
vars:
@echo "BOOK_FORMATS='$(BOOK_FORMATS)'"
@echo "PAPER='$(PAPER)'"
@echo "SPHINXFLAGS='$(SPHINXFLAGS)'"
@echo "RSYNC_DEST='$(RSYNC_DEST)'"
@echo "VERSION='$(VERSION)'"
@echo "PACKAGE_SERIES='$(PACKAGE_SERIES)'"
.PHONY: all-local
all-local:
if BUILD_SPHINX_DOCS
@for book in $(BOOKS); do \
$(MAKE) $(AM_MAKEFLAGS) BOOK=$$book \
PAPER="$(PAPER)" SPHINXFLAGS="$(SPHINXFLAGS)" \
BOOK_FORMATS="$(BOOK_FORMATS)" $$book/_build; \
done
endif
.PHONY: install-data-local
install-data-local: all-local
if BUILD_SPHINX_DOCS
$(AM_V_at)for book in $(BOOKS); do \
for format in $(BOOK_FORMATS); do \
formatdir="$$book/_build/$$format"; \
for f in `find "$$formatdir" -print`; do \
dname="`echo $$f | sed s:_build/::`"; \
dloc="$(DESTDIR)/$(docdir)/$$dname"; \
if [ -d "$$f" ]; then \
$(INSTALL) -d -m 755 "$$dloc"; \
else \
$(INSTALL_DATA) "$$f" "$$dloc"; \
fi \
done; \
done; \
done
endif
.PHONY: uninstall-local
uninstall-local:
if BUILD_SPHINX_DOCS
$(AM_V_at)for book in $(BOOKS); do \
rm -rf "$(DESTDIR)/$(docdir)/$$book"; \
done
endif
.PHONY: clean-local
clean-local:
$(AM_V_at)-rm -rf \
$(BOOKS:%="$(builddir)/%/_build") \
$(BOOKS:%="$(builddir)/%/conf.py") \
$(BOOKS:%="$(builddir)/%/generated") \
$(PNGS_GENERATED)
diff --git a/doc/sphinx/Pacemaker_Explained/local-options.rst b/doc/sphinx/Pacemaker_Explained/local-options.rst
index 5d55c4ae2a..dedbdd7ee9 100644
--- a/doc/sphinx/Pacemaker_Explained/local-options.rst
+++ b/doc/sphinx/Pacemaker_Explained/local-options.rst
@@ -1,739 +1,739 @@
Host-Local Configuration
------------------------
.. index::
pair: XML element; configuration
.. note:: Directory and file paths below may differ on your system depending on
your Pacemaker build settings. Check your Pacemaker configuration
file to find the correct paths.
Configuration Value Types
#########################
Throughout this document, configuration values will be designated as having one
of the following types:
.. list-table:: **Configuration Value Types**
:class: longtable
:widths: 1 3
:header-rows: 1
* - Type
- Description
* - .. _boolean:
.. index::
pair: type; boolean
boolean
- Case-insensitive text value where ``1``, ``yes``, ``y``, ``on``,
and ``true`` evaluate as true and ``0``, ``no``, ``n``, ``off``,
``false``, and unset evaluate as false
* - .. _date_time:
.. index::
pair: type; date/time
date/time
- Textual timestamp like ``Sat Dec 21 11:47:45 2013``
* - .. _duration:
.. index::
pair: type; duration
duration
- A time duration, specified either like a :ref:`timeout <timeout>` or an
`ISO 8601 duration <https://en.wikipedia.org/wiki/ISO_8601#Durations>`_.
A duration may be up to approximately 49 days but is intended for much
smaller time periods.
* - .. _enumeration:
.. index::
pair: type; enumeration
enumeration
- Text that must be one of a set of defined values (which will be listed
in the description)
* - .. _epoch_time:
.. index::
pair: type; epoch_time
epoch_time
- Time as the integer number of seconds since the Unix epoch,
``1970-01-01 00:00:00 +0000 (UTC)``.
* - .. _id:
.. index::
pair: type; id
id
- A text string starting with a letter or underbar, followed by any
combination of letters, numbers, dashes, dots, and/or underbars; when
used for a property named ``id``, the string must be unique across all
``id`` properties in the CIB
* - .. _integer:
.. index::
pair: type; integer
integer
- 32-bit signed integer value (-2,147,483,648 to 2,147,483,647)
* - .. _iso8601:
.. index::
pair: type; iso8601
ISO 8601
- An `ISO 8601 <https://en.wikipedia.org/wiki/ISO_8601>`_ date/time.
* - .. _nonnegative_integer:
.. index::
pair: type; nonnegative integer
nonnegative integer
- 32-bit nonnegative integer value (0 to 2,147,483,647)
* - .. _percentage:
.. index::
pair: type; percentage
percentage
- Floating-point number followed by an optional percent sign ('%')
* - .. _port:
.. index::
pair: type; port
port
- Integer TCP port number (0 to 65535)
* - .. _range:
.. index::
pair: type; range
range
- A range may be a single nonnegative integer or a dash-separated range of
nonnegative integers. Either the first or last value may be omitted to
leave the range open-ended. Examples: ``0``, ``3-``, ``-5``, ``4-6``.
* - .. _score:
.. index::
pair: type; score
score
- A Pacemaker score can be an integer between -1,000,000 and 1,000,000, or
a string alias: ``INFINITY`` or ``+INFINITY`` is equivalent to
1,000,000, ``-INFINITY`` is equivalent to -1,000,000, and ``red``,
``yellow``, and ``green`` are equivalent to integers as described in
:ref:`node-health`.
* - .. _text:
.. index::
pair: type; text
text
- A text string
* - .. _timeout:
.. index::
pair: type; timeout
timeout
- A time duration, specified as a bare number (in which case it is
considered to be in seconds) or a number with a unit (``ms`` or ``msec``
for milliseconds, ``us`` or ``usec`` for microseconds, ``s`` or ``sec``
for seconds, ``m`` or ``min`` for minutes, ``h`` or ``hr`` for hours)
optionally with whitespace before and/or after the number.
* - .. _version:
.. index::
pair: type; version
version
- Version number (any combination of alphanumeric characters, dots, and
dashes, starting with a number).
Scores
______
Scores are integral to how Pacemaker works. Practically everything from moving
a resource to deciding which resource to stop in a degraded cluster is achieved
by manipulating scores in some way.
Scores are calculated per resource and node. Any node with a negative score for
a resource can't run that resource. The cluster places a resource on the node
with the highest score for it.
Score addition and subtraction follow these rules:
* Any value (including ``INFINITY``) - ``INFINITY`` = ``-INFINITY``
* ``INFINITY`` + any value other than ``-INFINITY`` = ``INFINITY``
.. note::
What if you want to use a score higher than 1,000,000? Typically this possibility
arises when someone wants to base the score on some external metric that might
go above 1,000,000.
The short answer is you can't.
The long answer is it is sometimes possible work around this limitation
creatively. You may be able to set the score to some computed value based on
the external metric rather than use the metric directly. For nodes, you can
store the metric as a node attribute, and query the attribute when computing
the score (possibly as part of a custom resource agent).
Local Options
#############
Pacemaker supports several host-local configuration options. These options can
be configured on each node in the main Pacemaker configuration file
(|PCMK_CONFIG_FILE|) in the format ``<NAME>="<VALUE>"``. They work by setting
environment variables when Pacemaker daemons start up.
.. list-table:: **Local Options**
:class: longtable
:widths: 2 2 2 5
:header-rows: 1
* - Name
- Type
- Default
- Description
* - .. _cib_pam_service:
.. index::
pair: node option; CIB_pam_service
CIB_pam_service
- :ref:`text <text>`
- login
- PAM service to use for remote CIB client authentication (passed to
``pam_start``).
* - .. _pcmk_logfacility:
.. index::
pair: node option; PCMK_logfacility
PCMK_logfacility
- :ref:`enumeration <enumeration>`
- daemon
- Enable logging via the system log or journal, using the specified log
facility. Messages sent here are of value to all Pacemaker
administrators. This can be disabled using ``none``, but that is not
recommended. Allowed values:
* ``none``
* ``daemon``
* ``user``
* ``local0``
* ``local1``
* ``local2``
* ``local3``
* ``local4``
* ``local5``
* ``local6``
* ``local7``
* - .. _pcmk_logpriority:
.. index::
pair: node option; PCMK_logpriority
PCMK_logpriority
- :ref:`enumeration <enumeration>`
- notice
- Unless system logging is disabled using ``PCMK_logfacility=none``,
messages of the specified log severity and higher will be sent to the
system log. The default is appropriate for most installations. Allowed
values:
* ``emerg``
* ``alert``
* ``crit``
* ``error``
* ``warning``
* ``notice``
* ``info``
* ``debug``
* - .. _pcmk_logfile:
.. index::
pair: node option; PCMK_logfile
PCMK_logfile
- :ref:`text <text>`
- |PCMK_LOG_FILE|
- Unless set to ``none``, more detailed log messages will be sent to the
specified file (in addition to the system log, if enabled). These
messages may have extended information, and will include messages of info
severity. This log is of more use to developers and advanced system
administrators, and when reporting problems. Note: The default is
|PCMK_CONTAINER_LOG_FILE| (inside the container) for bundled container
nodes; this would typically be mapped to a different path on the host
running the container.
* - .. _pcmk_logfile_mode:
.. index::
pair: node option; PCMK_logfile_mode
PCMK_logfile_mode
- :ref:`text <text>`
- 0660
- Pacemaker will set the permissions on the detail log to this value (see
``chmod(1)``).
* - .. _pcmk_debug:
.. index::
pair: node option; PCMK_debug
PCMK_debug
- :ref:`enumeration <enumeration>`
- no
- Whether to send debug severity messages to the detail log. This may be
set for all subsystems (``yes`` or ``no``) or for specific (comma-
separated) subsystems. Allowed subsystems are:
* ``pacemakerd``
* ``pacemaker-attrd``
* ``pacemaker-based``
* ``pacemaker-controld``
* ``pacemaker-execd``
* ``pacemaker-fenced``
* ``pacemaker-schedulerd``
Example: ``PCMK_debug="pacemakerd,pacemaker-execd"``
* - .. _pcmk_stderr:
.. index::
pair: node option; PCMK_stderr
PCMK_stderr
- :ref:`boolean <boolean>`
- no
- *Advanced Use Only:* Whether to send daemon log messages to stderr. This
would be useful only during troubleshooting, when starting Pacemaker
manually on the command line.
Setting this option in the configuration file is pointless, since the
file is not read when starting Pacemaker manually. However, it can be set
directly as an environment variable on the command line.
* - .. _pcmk_trace_functions:
.. index::
pair: node option; PCMK_trace_functions
PCMK_trace_functions
- :ref:`text <text>`
-
- *Advanced Use Only:* Send debug and trace severity messages from these
(comma-separated) source code functions to the detail log.
Example:
``PCMK_trace_functions="func1,func2"``
* - .. _pcmk_trace_files:
.. index::
pair: node option; PCMK_trace_files
PCMK_trace_files
- :ref:`text <text>`
-
- *Advanced Use Only:* Send debug and trace severity messages from all
functions in these (comma-separated) source file names to the detail log.
Example: ``PCMK_trace_files="file1.c,file2.c"``
* - .. _pcmk_trace_formats:
.. index::
pair: node option; PCMK_trace_formats
PCMK_trace_formats
- :ref:`text <text>`
-
- *Advanced Use Only:* Send trace severity messages that are generated by
these (comma-separated) format strings in the source code to the detail
log.
Example: ``PCMK_trace_formats="Error: %s (%d)"``
* - .. _pcmk_trace_tags:
.. index::
pair: node option; PCMK_trace_tags
PCMK_trace_tags
- :ref:`text <text>`
-
- *Advanced Use Only:* Send debug and trace severity messages related to
these (comma-separated) resource IDs to the detail log.
Example: ``PCMK_trace_tags="client-ip,dbfs"``
* - .. _pcmk_blackbox:
.. index::
pair: node option; PCMK_blackbox
PCMK_blackbox
- :ref:`enumeration <enumeration>`
- no
- *Advanced Use Only:* Enable blackbox logging globally (``yes`` or ``no``)
or by subsystem. A blackbox contains a rolling buffer of all logs (of all
severities). Blackboxes are stored under |CRM_BLACKBOX_DIR| by default,
by default, and their contents can be viewed using the ``qb-blackbox(8)``
command.
The blackbox recorder can be enabled at start using this variable, or at
runtime by sending a Pacemaker subsystem daemon process a ``SIGUSR1`` or
``SIGTRAP`` signal, and disabled by sending ``SIGUSR2`` (see
``kill(1)``). The blackbox will be written after a crash, assertion
failure, or ``SIGTRAP`` signal.
See :ref:`PCMK_debug <pcmk_debug>` for allowed subsystems.
Example:
``PCMK_blackbox="pacemakerd,pacemaker-execd"``
* - .. _pcmk_trace_blackbox:
.. index::
pair: node option; PCMK_trace_blackbox
PCMK_trace_blackbox
- :ref:`enumeration <enumeration>`
-
- *Advanced Use Only:* Write a blackbox whenever the message at the
specified function and line is logged. Multiple entries may be comma-
separated.
Example: ``PCMK_trace_blackbox="remote.c:144,remote.c:149"``
* - .. _pcmk_node_start_state:
.. index::
pair: node option; PCMK_node_start_state
PCMK_node_start_state
- :ref:`enumeration <enumeration>`
- default
- By default, the local host will join the cluster in an online or standby
state when Pacemaker first starts depending on whether it was previously
put into standby mode. If this variable is set to ``standby`` or
``online``, it will force the local host to join in the specified state.
* - .. _pcmk_node_action_limit:
.. index::
pair: node option; PCMK_node_action_limit
PCMK_node_action_limit
- :ref:`nonnegative integer <nonnegative_integer>`
-
- Specify the maximum number of jobs that can be scheduled on this node. If
set, this overrides the :ref:`node-action-limit <node_action_limit>`
cluster option on this node.
* - .. _pcmk_fail_fast:
.. index::
pair: node option; PCMK_fail_fast
PCMK_fail_fast
- :ref:`boolean <boolean>`
- no
- By default, if a Pacemaker subsystem crashes, the main ``pacemakerd``
process will attempt to restart it. If this variable is set to ``yes``,
``pacemakerd`` will panic the local host instead.
* - .. _pcmk_panic_action:
.. index::
pair: node option; PCMK_panic_action
PCMK_panic_action
- :ref:`enumeration <enumeration>`
- reboot
- Pacemaker will panic the local host under certain conditions. By default,
this means rebooting the host. This variable can change that behavior: if
``crash``, trigger a kernel crash (useful if you want a kernel dump to
investigate); if ``sync-reboot`` or ``sync-crash``, synchronize
filesystems before rebooting the host or triggering a kernel crash. The
sync values are more likely to preserve log messages, but with the risk
that the host may be left active if the synchronization hangs.
* - .. _pcmk_authkey_location:
.. index::
pair: node option; PCMK_authkey_location
PCMK_authkey_location
- :ref:`text <text>`
- |PCMK_AUTHKEY_FILE|
- Use the contents of this file as the authorization key to use with
Pacemaker Remote connections. This file must be readable by Pacemaker
daemons (that is, it must allow read permissions to either the
|CRM_DAEMON_USER| user or the |CRM_DAEMON_GROUP| group), and its contents
must be identical on all nodes.
* - .. _pcmk_remote_address:
.. index::
pair: node option; PCMK_remote_address
PCMK_remote_address
- :ref:`text <text>`
-
- By default, if the Pacemaker Remote service is run on the local node, it
will listen for connections on all IP addresses. This may be set to one
address to listen on instead, as a resolvable hostname or as a numeric
IPv4 or IPv6 address. When resolving names or listening on all addresses,
IPv6 will be preferred if available. When listening on an IPv6 address,
IPv4 clients will be supported via IPv4-mapped IPv6 addresses.
Example: ``PCMK_remote_address="192.0.2.1"``
* - .. _pcmk_remote_port:
.. index::
pair: node option; PCMK_remote_port
PCMK_remote_port
- :ref:`port <port>`
- 3121
- Use this TCP port number for Pacemaker Remote node connections. This
value must be the same on all nodes.
* - .. _pcmk_remote_pid1:
.. index::
pair: node option; PCMK_remote_pid1
PCMK_remote_pid1
- :ref:`enumeration <enumeration>`
- default
- *Advanced Use Only:* When a bundle resource's ``run-command`` option is
left to default, Pacemaker Remote runs as PID 1 in the bundle's
containers. When it does so, it loads environment variables from the
container's |PCMK_INIT_ENV_FILE| and performs the PID 1 responsibility of
reaping dead subprocesses.
This option controls whether those actions are performed when Pacemaker
Remote is not running as PID 1. It is intended primarily for developer
testing but can be useful when ``run-command`` is set to a separate,
custom PID 1 process that launches Pacemaker Remote.
* ``full``: Pacemaker Remote loads environment variables from
|PCMK_INIT_ENV_FILE| and reaps dead subprocesses.
* ``vars``: Pacemaker Remote loads environment variables from
|PCMK_INIT_ENV_FILE| but does not reap dead subprocesses.
* ``default``: Pacemaker Remote performs neither action.
If Pacemaker Remote is running as PID 1, this option is ignored, and the
behavior is the same as for ``full``.
* - .. _pcmk_tls_priorities:
.. index::
pair: node option; PCMK_tls_priorities
PCMK_tls_priorities
- :ref:`text <text>`
- - |PCMK_GNUTLS_PRIORITIES|
+ - |PCMK__GNUTLS_PRIORITIES|
- *Advanced Use Only:* These GnuTLS cipher priorities will be used for TLS
connections (whether for Pacemaker Remote connections or remote CIB
access, when enabled). See:
https://gnutls.org/manual/html_node/Priority-Strings.html
Pacemaker will append ``":+ANON-DH"`` for remote CIB access and
``":+DHE-PSK:+PSK"`` for Pacemaker Remote connections, as they are
required for the respective functionality.
Example:
``PCMK_tls_priorities="SECURE128:+SECURE192"``
* - .. _pcmk_dh_min_bits:
.. index::
pair: node option; PCMK_dh_min_bits
PCMK_dh_min_bits
- :ref:`nonnegative integer <nonnegative_integer>`
- 0 (no minimum)
- *Advanced Use Only:* Set a lower bound on the bit length of the prime
number generated for Diffie-Hellman parameters needed by TLS connections.
The default is no minimum.
The server (Pacemaker Remote daemon, or CIB manager configured to accept
remote clients) will use this value to provide a floor for the value
recommended by the GnuTLS library. The library will only accept a limited
number of specific values, which vary by library version, so setting
these is recommended only when required for compatibility with specific
client versions.
Clients (connecting cluster nodes or remote CIB commands) will require
that the server use a prime of at least this size. This is recommended
only when the value must be lowered in order for the client's GnuTLS
library to accept a connection to an older server.
* - .. _pcmk_dh_max_bits:
.. index::
pair: node option; PCMK_dh_max_bits
PCMK_dh_max_bits
- :ref:`nonnegative integer <nonnegative_integer>`
- 0 (no maximum)
- *Advanced Use Only:* Set an upper bound on the bit length of the prime
number generated for Diffie-Hellman parameters needed by TLS connections.
The default is no maximum.
The server (Pacemaker Remote daemon, or CIB manager configured to accept
remote clients) will use this value to provide a ceiling for the value
recommended by the GnuTLS library. The library will only accept a limited
number of specific values, which vary by library version, so setting
these is recommended only when required for compatibility with specific
client versions.
Clients do not use ``PCMK_dh_max_bits``.
* - .. _pcmk_ipc_type:
.. index::
pair: node option; PCMK_ipc_type
PCMK_ipc_type
- :ref:`enumeration <enumeration>`
- shared-mem
- *Advanced Use Only:* Force use of a particular IPC method. Allowed values:
* ``shared-mem``
* ``socket``
* ``posix``
* ``sysv``
* - .. _pcmk_ipc_buffer:
.. index::
pair: node option; PCMK_ipc_buffer
PCMK_ipc_buffer
- :ref:`nonnegative integer <nonnegative_integer>`
- 131072
- *Advanced Use Only:* Specify an IPC buffer size in bytes. This can be
useful when connecting to large clusters that result in messages
exceeding the default size (which will also result in log messages
referencing this variable).
* - .. _pcmk_cluster_type:
.. index::
pair: node option; PCMK_cluster_type
PCMK_cluster_type
- :ref:`enumeration <enumeration>`
- corosync
- *Advanced Use Only:* Specify the cluster layer to be used. If unset,
Pacemaker will detect and use a supported cluster layer, if available.
Currently, ``"corosync"`` is the only supported cluster layer. If
multiple layers are supported in the future, this will allow overriding
Pacemaker's automatic detection to select a specific one.
* - .. _pcmk_schema_directory:
.. index::
pair: node option; PCMK_schema_directory
PCMK_schema_directory
- :ref:`text <text>`
- |CRM_SCHEMA_DIRECTORY|
- *Advanced Use Only:* Specify an alternate location for RNG schemas and
XSL transforms.
* - .. _pcmk_remote_schema_directory:
.. index::
pair: node option; PCMK_remote_schema_directory
PCMK_remote_schema_directory
- :ref:`text <text>`
- |PCMK__REMOTE_SCHEMA_DIR|
- *Advanced Use Only:* Specify an alternate location on Pacemaker Remote
nodes for storing newer RNG schemas and XSL transforms fetched from
the cluster.
* - .. _pcmk_valgrind_enabled:
.. index::
pair: node option; PCMK_valgrind_enabled
PCMK_valgrind_enabled
- :ref:`enumeration <enumeration>`
- no
- *Advanced Use Only:* Whether subsystem daemons should be run under
``valgrind``. Allowed values are the same as for ``PCMK_debug``.
* - .. _pcmk_callgrind_enabled:
.. index::
pair: node option; PCMK_callgrind_enabled
PCMK_callgrind_enabled
- :ref:`enumeration <enumeration>`
- no
- *Advanced Use Only:* Whether subsystem daemons should be run under
``valgrind`` with the ``callgrind`` tool enabled. Allowed values are the
same as for ``PCMK_debug``.
* - .. _sbd_sync_resource_startup:
.. index::
pair: node option; SBD_SYNC_RESOURCE_STARTUP
SBD_SYNC_RESOURCE_STARTUP
- :ref:`boolean <boolean>`
-
- If true, ``pacemakerd`` waits for a ping from ``sbd`` during startup
before starting other Pacemaker daemons, and during shutdown after
stopping other Pacemaker daemons but before exiting. Default value is set
based on the ``--with-sbd-sync-default`` configure script option.
* - .. _sbd_watchdog_timeout:
.. index::
pair: node option; SBD_WATCHDOG_TIMEOUT
SBD_WATCHDOG_TIMEOUT
- :ref:`duration <duration>`
-
- If the ``stonith-watchdog-timeout`` cluster property is set to a negative
or invalid value, use double this value as the default if positive, or
use 0 as the default otherwise. This value must be greater than the value
of ``stonith-watchdog-timeout`` if both are set.
* - .. _valgrind_opts:
.. index::
pair: node option; VALGRIND_OPTS
VALGRIND_OPTS
- :ref:`text <text>`
-
- *Advanced Use Only:* Pass these options to valgrind, when enabled (see
``valgrind(1)``). ``"--vgdb=no"`` should usually be specified because
``pacemaker-execd`` can lower privileges when executing commands, which
would otherwise leave a bunch of unremovable files in ``/tmp``.
diff --git a/doc/sphinx/conf.py.in b/doc/sphinx/conf.py.in
index a921b3aad6..fbd91125cb 100644
--- a/doc/sphinx/conf.py.in
+++ b/doc/sphinx/conf.py.in
@@ -1,331 +1,331 @@
""" Sphinx configuration for Pacemaker documentation
"""
__copyright__ = "Copyright 2020-2023 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import datetime
import os
import sys
# Variables that can be used later in this file
authors = "the Pacemaker project contributors"
year = datetime.datetime.now().year
doc_license = "Creative Commons Attribution-ShareAlike International Public License"
doc_license += " version 4.0 or later (CC-BY-SA v4.0+)"
# rST markup to insert at beginning of every document; mainly used for
#
# .. |<abbr>| replace:: <Full text>
#
# where occurrences of |<abbr>| in the rST will be substituted with <Full text>
rst_prolog="""
.. |CFS_DISTRO| replace:: AlmaLinux
.. |CFS_DISTRO_VER| replace:: 9
.. |CRM_BLACKBOX_DIR| replace:: ``%CRM_BLACKBOX_DIR%``
.. |CRM_CONFIG_DIR| replace:: ``%CRM_CONFIG_DIR%``
.. |CRM_DAEMON_GROUP| replace:: ``%CRM_DAEMON_GROUP%``
.. |CRM_DAEMON_USER| replace:: ``%CRM_DAEMON_USER%``
.. |CRM_SCHEMA_DIRECTORY| replace:: %CRM_SCHEMA_DIRECTORY%
.. |PCMK_AUTHKEY_FILE| replace:: %PACEMAKER_CONFIG_DIR%/authkey
.. |PCMK_CONFIG_FILE| replace:: ``%CONFIGDIR%/pacemaker``
-.. |PCMK_GNUTLS_PRIORITIES| replace:: %PCMK_GNUTLS_PRIORITIES%
+.. |PCMK__GNUTLS_PRIORITIES| replace:: %PCMK__GNUTLS_PRIORITIES%
.. |PCMK_INIT_ENV_FILE| replace:: ``%PACEMAKER_CONFIG_DIR%/pcmk-init.env``
.. |PCMK_LOG_FILE| replace:: %CRM_LOG_DIR%/pacemaker.log
.. |PCMK_CONTAINER_LOG_FILE| replace:: ``/var/log/pcmk-init.log``
.. |PCMK__REMOTE_SCHEMA_DIR| replace:: %PCMK__REMOTE_SCHEMA_DIR%
.. |REMOTE_DISTRO| replace:: AlmaLinux
.. |REMOTE_DISTRO_VER| replace:: 9
"""
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('%ABS_TOP_SRCDIR%/python'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.autosummary']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = '%BOOK_ID%'
copyright = "2009-%s %s. Released under the terms of the %s" % (year, authors, doc_license)
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The full version, including alpha/beta/rc tags.
release = '%VERSION%'
# The short X.Y version.
version = release.rsplit('.', 1)[0]
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'vs'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'pyramid'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
html_style = 'pacemaker.css'
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
html_title = "%BOOK_TITLE%"
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = [ '%SRC_DIR%/_static' ]
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'Pacemakerdoc'
# -- Options for LaTeX output --------------------------------------------------
latex_engine = "xelatex"
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', '%BOOK_ID%.tex', '%BOOK_TITLE%', authors, 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', '%BOOK_ID%', 'Part of the Pacemaker documentation set', [authors], 8)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', '%BOOK_ID%', '%BOOK_TITLE%', authors, '%BOOK_TITLE%',
'Pacemaker is an advanced, scalable high-availability cluster resource manager.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = '%BOOK_TITLE%'
epub_author = authors
epub_publisher = 'ClusterLabs.org'
epub_copyright = copyright
# The language of the text. It defaults to the language option
# or en if the language is not set.
#epub_language = ''
# The scheme of the identifier. Typical schemes are ISBN or URL.
epub_scheme = 'URL'
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
epub_identifier = 'https://www.clusterlabs.org/pacemaker/doc/2.1/%BOOK_ID%/epub/%BOOK_ID%.epub'
# A unique identification for the text.
epub_uid = 'ClusterLabs.org-Pacemaker-%BOOK_ID%'
# A tuple containing the cover image and cover page html template filenames.
#epub_cover = ()
# HTML files that should be inserted before the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_pre_files = []
# HTML files that should be inserted after the pages created by sphinx.
# The format is a list of tuples containing the path and title.
#epub_post_files = []
# A list of files that should not be packed into the epub file.
epub_exclude_files = [
'_static/doctools.js',
'_static/jquery.js',
'_static/searchtools.js',
'_static/underscore.js',
'_static/basic.css',
'_static/websupport.js',
'search.html',
]
# The depth of the table of contents in toc.ncx.
#epub_tocdepth = 3
# Allow duplicate toc entries.
#epub_tocdup = True
autosummary_generate = True
diff --git a/etc/sysconfig/pacemaker.in b/etc/sysconfig/pacemaker.in
index d6a38dbce1..36c1a43d8b 100644
--- a/etc/sysconfig/pacemaker.in
+++ b/etc/sysconfig/pacemaker.in
@@ -1,377 +1,377 @@
#
# Pacemaker start-up configuration
#
# This file contains environment variables that affect Pacemaker behavior.
# They are not options stored in the Cluster Information Base (CIB) because
# they may be needed before the CIB is available.
#
## Logging
# PCMK_logfacility
#
# Enable logging via the system log or journal, using the specified log
# facility. Messages sent here are of value to all Pacemaker administrators.
# This can be disabled using "none", but that is not recommended. Allowed
# values:
#
# none
# daemon
# user
# local0
# local1
# local2
# local3
# local4
# local5
# local6
# local7
#
# Default: PCMK_logfacility="daemon"
# PCMK_logpriority
#
# Unless system logging is disabled using PCMK_logfacility=none, messages of
# the specified log severity and higher will be sent to the system log. The
# default is appropriate for most installations. Allowed values:
#
# emerg
# alert
# crit
# error
# warning
# notice
# info
# debug
#
# Default: PCMK_logpriority="notice"
# PCMK_logfile
#
# Unless set to "none", more detailed log messages will be sent to the
# specified file (in addition to the system log, if enabled). These messages
# may have extended information, and will include messages of info severity.
# This log is of more use to developers and advanced system administrators, and
# when reporting problems.
#
# Default: PCMK_logfile="@CRM_LOG_DIR@/pacemaker.log"
# PCMK_logfile_mode
#
# Pacemaker will set the permissions on the detail log to this value (see
# chmod(1)).
#
# Default: PCMK_logfile_mode="0660"
# PCMK_debug (Advanced Use Only)
#
# Whether to send debug severity messages to the detail log.
# This may be set for all subsystems (yes or no) or for specific
# (comma-separated) subsystems. Allowed subsystems are:
#
# pacemakerd
# pacemaker-attrd
# pacemaker-based
# pacemaker-controld
# pacemaker-execd
# pacemaker-fenced
# pacemaker-schedulerd
#
# Default: PCMK_debug="no"
# Example: PCMK_debug="pacemakerd,pacemaker-execd"
# PCMK_stderr (Advanced Use Only)
#
# Whether to send daemon log messages to stderr. This would be useful only
# during troubleshooting, when starting Pacemaker manually on the command line.
#
# Setting this option in this file is pointless, since this file is not read
# when starting Pacemaker manually. However, it can be set directly as an
# environment variable on the command line.
#
# Default: PCMK_stderr="no"
# PCMK_trace_functions (Advanced Use Only)
#
# Send debug and trace severity messages from these (comma-separated)
# source code functions to the detail log.
#
# Default: PCMK_trace_functions=""
# Example: PCMK_trace_functions="unpack_colocation_set,pcmk__cmp_instance"
# PCMK_trace_files (Advanced Use Only)
#
# Send debug and trace severity messages from all functions in these
# (comma-separated) source file names to the detail log.
#
# Default: PCMK_trace_files=""
# Example: PCMK_trace_files="remote.c,watchdog.c"
# PCMK_trace_formats (Advanced Use Only)
#
# Send trace severity messages that are generated by these (comma-separated)
# format strings in the source code to the detail log.
#
# Default: PCMK_trace_formats=""
# Example: PCMK_trace_formats="TLS handshake failed: %s (%d)"
# PCMK_trace_tags (Advanced Use Only)
#
# Send debug and trace severity messages related to these (comma-separated)
# resource IDs to the detail log.
#
# Default: PCMK_trace_tags=""
# Example: PCMK_trace_tags="client-ip,dbfs"
# PCMK_blackbox (Advanced Use Only)
#
# Enable blackbox logging globally (yes or no) or by subsystem. A blackbox
# contains a rolling buffer of all logs (of all severities). Blackboxes are
# stored under @CRM_BLACKBOX_DIR@ by default, and their contents can
# be viewed using the qb-blackbox(8) command.
#
# The blackbox recorder can be enabled at start using this variable, or at
# runtime by sending a Pacemaker subsystem daemon process a SIGUSR1 or SIGTRAP
# signal, and disabled by sending SIGUSR2 (see kill(1)). The blackbox will be
# written after a crash, assertion failure, or SIGTRAP signal.
#
# Default: PCMK_blackbox="no"
# Example: PCMK_blackbox="pacemaker-controld,pacemaker-fenced"
# PCMK_trace_blackbox (Advanced Use Only)
#
# Write a blackbox whenever the message at the specified function and line is
# logged. Multiple entries may be comma-separated.
#
# Default: PCMK_trace_blackbox=""
# Example: PCMK_trace_blackbox="remote.c:144,remote.c:149"
## Option overrides
# PCMK_node_start_state
#
# By default, the local host will join the cluster in an online or standby
# state when Pacemaker first starts depending on whether it was previously put
# into standby mode. If this variable is set to "standby" or "online", it will
# force the local host to join in the specified state.
#
# Default: PCMK_node_start_state="default"
# PCMK_node_action_limit
#
# Specify the maximum number of jobs that can be scheduled on this node. If set,
# this overrides the node-action-limit cluster property for this node.
#
# Default: PCMK_node_action_limit=""
## Crash Handling
# PCMK_fail_fast
#
# By default, if a Pacemaker subsystem crashes, the main pacemakerd process
# will attempt to restart it. If this variable is set to "yes", pacemakerd
# will panic the local host instead.
#
# Default: PCMK_fail_fast="no"
# PCMK_panic_action
#
# Pacemaker will panic the local host under certain conditions. By default,
# this means rebooting the host. This variable can change that behavior: if
# "crash", trigger a kernel crash (useful if you want a kernel dump to
# investigate); if "sync-reboot" or "sync-crash", synchronize filesystems
# before rebooting the host or triggering a kernel crash. The sync values are
# more likely to preserve log messages, but with the risk that the host may be
# left active if the synchronization hangs.
#
# Default: PCMK_panic_action="reboot"
## Pacemaker Remote
# PCMK_authkey_location
#
# Use the contents of this file as the authorization key to use with Pacemaker
# Remote connections. This file must be readable by Pacemaker daemons (that is,
# it must allow read permissions to either the @CRM_DAEMON_USER@ user or the
# @CRM_DAEMON_GROUP@ group), and its contents must be identical on all nodes.
#
# Default: PCMK_authkey_location="@PACEMAKER_CONFIG_DIR@/authkey"
# PCMK_remote_address
#
# By default, if the Pacemaker Remote service is run on the local node, it will
# listen for connections on all IP addresses. This may be set to one address to
# listen on instead, as a resolvable hostname or as a numeric IPv4 or IPv6
# address. When resolving names or listening on all addresses, IPv6 will be
# preferred if available. When listening on an IPv6 address, IPv4 clients will
# be supported via IPv4-mapped IPv6 addresses.
#
# Default: PCMK_remote_address=""
# Example: PCMK_remote_address="192.0.2.1"
# PCMK_remote_port
#
# Use this TCP port number for Pacemaker Remote node connections. This value
# must be the same on all nodes.
#
# Default: PCMK_remote_port="3121"
# PCMK_remote_pid1 (Advanced Use Only)
#
# When a bundle resource's "run-command" option is left to default, Pacemaker
# Remote runs as PID 1 in the bundle's containers. When it does so, it loads
# environment variables from the container's
# @PACEMAKER_CONFIG_DIR@/pcmk-init.env and performs the PID 1 responsibility of
# reaping dead subprocesses.
#
# This option controls whether those actions are performed when Pacemaker
# Remote is not running as PID 1. It is intended primarily for developer testing
# but can be useful when "run-command" is set to a separate, custom PID 1
# process that launches Pacemaker Remote.
#
# * If set to "full", Pacemaker Remote loads environment variables from
# @PACEMAKER_CONFIG_DIR@/pcmk-init.env and reaps dead subprocesses.
# * If set to "vars", Pacemaker Remote loads environment variables from
# @PACEMAKER_CONFIG_DIR@/pcmk-init.env but does not reap dead subprocesses.
# * If set to "default", Pacemaker Remote performs neither action.
#
# If Pacemaker Remote is running as PID 1, this option is ignored, and the
# behavior is the same as for "full".
#
# Default: PCMK_remote_pid1="default"
# PCMK_tls_priorities (Advanced Use Only)
#
# These GnuTLS cipher priorities will be used for TLS connections (whether for
# Pacemaker Remote connections or remote CIB access, when enabled). See:
#
# https://gnutls.org/manual/html_node/Priority-Strings.html
#
# Pacemaker will append ":+ANON-DH" for remote CIB access and ":+DHE-PSK:+PSK"
# for Pacemaker Remote connections, as they are required for the respective
# functionality.
#
-# Default: PCMK_tls_priorities="@PCMK_GNUTLS_PRIORITIES@"
+# Default: PCMK_tls_priorities="@PCMK__GNUTLS_PRIORITIES@"
# Example: PCMK_tls_priorities="SECURE128:+SECURE192:-VERS-ALL:+VERS-TLS1.2"
# PCMK_dh_max_bits (Advanced Use Only)
#
# Set an upper bound on the bit length of the prime number generated for
# Diffie-Hellman parameters needed by TLS connections. The default is no
# maximum.
#
# The server (Pacemaker Remote daemon, or CIB manager configured to accept
# remote clients) will use this value to provide a ceiling for the value
# recommended by the GnuTLS library. The library will only accept a limited
# number of specific values, which vary by library version, so setting these is
# recommended only when required for compatibility with specific client
# versions.
#
# Clients do not use PCMK_dh_max_bits.
#
# Default: PCMK_dh_max_bits="0" (no maximum)
## Inter-process Communication
# PCMK_ipc_type (Advanced Use Only)
#
# Force use of a particular IPC method. Allowed values:
#
# shared-mem
# socket
# posix
# sysv
#
# Default: PCMK_ipc_type="shared-mem"
# PCMK_ipc_buffer (Advanced Use Only)
#
# Specify an IPC buffer size in bytes. This can be useful when connecting to
# large clusters that result in messages exceeding the default size (which will
# also result in log messages referencing this variable).
#
# Default: PCMK_ipc_buffer="131072"
## Cluster type
# PCMK_cluster_type (Advanced Use Only)
#
# Specify the cluster layer to be used. If unset, Pacemaker will detect and use
# a supported cluster layer, if available. Currently, "corosync" is the only
# supported cluster layer. If multiple layers are supported in the future, this
# will allow overriding Pacemaker's automatic detection to select a specific
# one.
#
# Default: PCMK_cluster_type=""
## Developer Options
# PCMK_schema_directory (Advanced Use Only)
#
# Specify an alternate location for RNG schemas and XSL transforms.
#
# Default: PCMK_schema_directory="@CRM_SCHEMA_DIRECTORY@"
# PCMK_remote_schema_directory (Advanced Use Only)
#
# Specify an alternate location on Pacemaker Remote nodes for storing newer
# RNG schemas and XSL transforms fetched from the cluster.
#
# Default: PCMK_remote_schema_directory="@PCMK__REMOTE_SCHEMA_DIR@"
# G_SLICE (Advanced Use Only)
#
# Affect the behavior of glib's memory allocator. Setting to "always-malloc"
# when running under valgrind will help valgrind track malloc/free better;
# setting to "debug-blocks" when not running under valgrind will perform
# (somewhat expensive) memory checks.
#
# Default: G_SLICE=""
# Example: G_SLICE="always-malloc"
# MALLOC_PERTURB_ (Advanced Use Only)
#
# Setting this to a decimal byte value will make malloc() initialize newly
# allocated memory and free() wipe it, to help catch uninitialized-memory and
# use-after-free bugs.
#
# Default: MALLOC_PERTURB_=""
# Example: MALLOC_PERTURB_="221"
# MALLOC_CHECK_ (Advanced Use Only)
#
# Setting this to 3 will make malloc() and friends print to stderr and abort
# for some (inexpensive) memory checks.
#
# Default: MALLOC_CHECK_=""
# Example: MALLOC_CHECK_="3"
# PCMK_valgrind_enabled (Advanced Use Only)
#
# Whether subsystem daemons should be run under valgrind. Allowed values are
# the same as for PCMK_debug.
#
# Default: PCMK_valgrind_enabled="no"
# PCMK_callgrind_enabled
#
# Whether subsystem daemons should be run under valgrind with the callgrind
# tool enabled. Allowed values are the same as for PCMK_debug.
#
# Default: PCMK_callgrind_enabled="no"
# VALGRIND_OPTS
#
# Pass these options to valgrind, when enabled (see valgrind(1)). "--vgdb=no"
# is specified because pacemaker-execd can lower privileges when executing
# commands, which would otherwise leave a bunch of unremovable files in /tmp.
#
# Default: VALGRIND_OPTS=""
VALGRIND_OPTS="--leak-check=full --trace-children=no --vgdb=no --num-callers=25 --log-file=@CRM_PACEMAKER_DIR@/valgrind-%p --suppressions=@datadir@/pacemaker/tests/valgrind-pcmk.suppressions --gen-suppressions=all"
diff --git a/lib/common/remote.c b/lib/common/remote.c
index 54023d7754..95962c96db 100644
--- a/lib/common/remote.c
+++ b/lib/common/remote.c
@@ -1,1235 +1,1235 @@
/*
* Copyright 2008-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU Lesser General Public License
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <crm/crm.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <stdlib.h>
#include <errno.h>
#include <inttypes.h> // PRIx32
#include <glib.h>
#include <bzlib.h>
#include <crm/common/ipc_internal.h>
#include <crm/common/xml.h>
#include <crm/common/mainloop.h>
#include <crm/common/remote_internal.h>
#include <gnutls/gnutls.h>
/* Swab macros from linux/swab.h */
#ifdef HAVE_LINUX_SWAB_H
# include <linux/swab.h>
#else
/*
* casts are necessary for constants, because we never know how for sure
* how U/UL/ULL map to __u16, __u32, __u64. At least not in a portable way.
*/
#define __swab16(x) ((uint16_t)( \
(((uint16_t)(x) & (uint16_t)0x00ffU) << 8) | \
(((uint16_t)(x) & (uint16_t)0xff00U) >> 8)))
#define __swab32(x) ((uint32_t)( \
(((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \
(((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \
(((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \
(((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24)))
#define __swab64(x) ((uint64_t)( \
(((uint64_t)(x) & (uint64_t)0x00000000000000ffULL) << 56) | \
(((uint64_t)(x) & (uint64_t)0x000000000000ff00ULL) << 40) | \
(((uint64_t)(x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \
(((uint64_t)(x) & (uint64_t)0x00000000ff000000ULL) << 8) | \
(((uint64_t)(x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \
(((uint64_t)(x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \
(((uint64_t)(x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \
(((uint64_t)(x) & (uint64_t)0xff00000000000000ULL) >> 56)))
#endif
#define REMOTE_MSG_VERSION 1
#define ENDIAN_LOCAL 0xBADADBBD
struct remote_header_v0 {
uint32_t endian; /* Detect messages from hosts with different endian-ness */
uint32_t version;
uint64_t id;
uint64_t flags;
uint32_t size_total;
uint32_t payload_offset;
uint32_t payload_compressed;
uint32_t payload_uncompressed;
/* New fields get added here */
} __attribute__ ((packed));
/*!
* \internal
* \brief Retrieve remote message header, in local endianness
*
* Return a pointer to the header portion of a remote connection's message
* buffer, converting the header to local endianness if needed.
*
* \param[in,out] remote Remote connection with new message
*
* \return Pointer to message header, localized if necessary
*/
static struct remote_header_v0 *
localized_remote_header(pcmk__remote_t *remote)
{
struct remote_header_v0 *header = (struct remote_header_v0 *)remote->buffer;
if(remote->buffer_offset < sizeof(struct remote_header_v0)) {
return NULL;
} else if(header->endian != ENDIAN_LOCAL) {
uint32_t endian = __swab32(header->endian);
CRM_LOG_ASSERT(endian == ENDIAN_LOCAL);
if(endian != ENDIAN_LOCAL) {
crm_err("Invalid message detected, endian mismatch: %" PRIx32
" is neither %" PRIx32 " nor the swab'd %" PRIx32,
ENDIAN_LOCAL, header->endian, endian);
return NULL;
}
header->id = __swab64(header->id);
header->flags = __swab64(header->flags);
header->endian = __swab32(header->endian);
header->version = __swab32(header->version);
header->size_total = __swab32(header->size_total);
header->payload_offset = __swab32(header->payload_offset);
header->payload_compressed = __swab32(header->payload_compressed);
header->payload_uncompressed = __swab32(header->payload_uncompressed);
}
return header;
}
int
pcmk__tls_client_handshake(pcmk__remote_t *remote, int timeout_sec,
int *gnutls_rc)
{
const time_t time_limit = time(NULL) + timeout_sec;
if (gnutls_rc != NULL) {
*gnutls_rc = GNUTLS_E_SUCCESS;
}
do {
int rc = gnutls_handshake(*remote->tls_session);
switch (rc) {
case GNUTLS_E_SUCCESS:
return pcmk_rc_ok;
case GNUTLS_E_INTERRUPTED:
case GNUTLS_E_AGAIN:
rc = pcmk__remote_ready(remote, 1000);
if ((rc != pcmk_rc_ok) && (rc != ETIME)) { // Fatal error
return rc;
}
break;
default:
if (gnutls_rc != NULL) {
*gnutls_rc = rc;
}
return EPROTO;
}
} while (time(NULL) < time_limit);
return ETIME;
}
/*!
* \internal
* \brief Initialize a new TLS session
*
* \param[in] csock Connected socket for TLS session
* \param[in] conn_type GNUTLS_SERVER or GNUTLS_CLIENT
* \param[in] cred_type GNUTLS_CRD_ANON or GNUTLS_CRD_PSK
* \param[in] credentials TLS session credentials
*
* \return Pointer to newly created session object, or NULL on error
*/
gnutls_session_t *
pcmk__new_tls_session(int csock, unsigned int conn_type,
gnutls_credentials_type_t cred_type, void *credentials)
{
int rc = GNUTLS_E_SUCCESS;
const char *prio_base = NULL;
char *prio = NULL;
gnutls_session_t *session = NULL;
/* Determine list of acceptable ciphers, etc. Pacemaker always adds the
* values required for its functionality.
*
* For an example of anonymous authentication, see:
* http://www.manpagez.com/info/gnutls/gnutls-2.10.4/gnutls_81.php#Echo-Server-with-anonymous-authentication
*/
prio_base = pcmk__env_option(PCMK__ENV_TLS_PRIORITIES);
if (prio_base == NULL) {
- prio_base = PCMK_GNUTLS_PRIORITIES;
+ prio_base = PCMK__GNUTLS_PRIORITIES;
}
prio = crm_strdup_printf("%s:%s", prio_base,
(cred_type == GNUTLS_CRD_ANON)? "+ANON-DH" : "+DHE-PSK:+PSK");
session = gnutls_malloc(sizeof(gnutls_session_t));
if (session == NULL) {
rc = GNUTLS_E_MEMORY_ERROR;
goto error;
}
rc = gnutls_init(session, conn_type);
if (rc != GNUTLS_E_SUCCESS) {
goto error;
}
/* @TODO On the server side, it would be more efficient to cache the
* priority with gnutls_priority_init2() and set it with
* gnutls_priority_set() for all sessions.
*/
rc = gnutls_priority_set_direct(*session, prio, NULL);
if (rc != GNUTLS_E_SUCCESS) {
goto error;
}
gnutls_transport_set_ptr(*session,
(gnutls_transport_ptr_t) GINT_TO_POINTER(csock));
rc = gnutls_credentials_set(*session, cred_type, credentials);
if (rc != GNUTLS_E_SUCCESS) {
goto error;
}
free(prio);
return session;
error:
crm_err("Could not initialize %s TLS %s session: %s "
QB_XS " rc=%d priority='%s'",
(cred_type == GNUTLS_CRD_ANON)? "anonymous" : "PSK",
(conn_type == GNUTLS_SERVER)? "server" : "client",
gnutls_strerror(rc), rc, prio);
free(prio);
if (session != NULL) {
gnutls_free(session);
}
return NULL;
}
/*!
* \internal
* \brief Initialize Diffie-Hellman parameters for a TLS server
*
* \param[out] dh_params Parameter object to initialize
*
* \return Standard Pacemaker return code
* \todo The current best practice is to allow the client and server to
* negotiate the Diffie-Hellman parameters via a TLS extension (RFC 7919).
* However, we have to support both older versions of GnuTLS (<3.6) that
* don't support the extension on our side, and older Pacemaker versions
* that don't support the extension on the other side. The next best
* practice would be to use a known good prime (see RFC 5114 section 2.2),
* possibly stored in a file distributed with Pacemaker.
*/
int
pcmk__init_tls_dh(gnutls_dh_params_t *dh_params)
{
int rc = GNUTLS_E_SUCCESS;
unsigned int dh_bits = 0;
int dh_max_bits = 0;
rc = gnutls_dh_params_init(dh_params);
if (rc != GNUTLS_E_SUCCESS) {
goto error;
}
dh_bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH,
GNUTLS_SEC_PARAM_NORMAL);
if (dh_bits == 0) {
rc = GNUTLS_E_DH_PRIME_UNACCEPTABLE;
goto error;
}
pcmk__scan_min_int(pcmk__env_option(PCMK__ENV_DH_MAX_BITS), &dh_max_bits,
0);
if ((dh_max_bits > 0) && (dh_bits > dh_max_bits)) {
dh_bits = dh_max_bits;
}
crm_info("Generating Diffie-Hellman parameters with %u-bit prime for TLS",
dh_bits);
rc = gnutls_dh_params_generate2(*dh_params, dh_bits);
if (rc != GNUTLS_E_SUCCESS) {
goto error;
}
return pcmk_rc_ok;
error:
crm_err("Could not initialize Diffie-Hellman parameters for TLS: %s "
QB_XS " rc=%d", gnutls_strerror(rc), rc);
return EPROTO;
}
/*!
* \internal
* \brief Process handshake data from TLS client
*
* Read as much TLS handshake data as is available.
*
* \param[in] client Client connection
*
* \return Standard Pacemaker return code (of particular interest, EAGAIN
* if some data was successfully read but more data is needed)
*/
int
pcmk__read_handshake_data(const pcmk__client_t *client)
{
int rc = 0;
CRM_ASSERT(client && client->remote && client->remote->tls_session);
do {
rc = gnutls_handshake(*client->remote->tls_session);
} while (rc == GNUTLS_E_INTERRUPTED);
if (rc == GNUTLS_E_AGAIN) {
/* No more data is available at the moment. This function should be
* invoked again once the client sends more.
*/
return EAGAIN;
} else if (rc != GNUTLS_E_SUCCESS) {
crm_err("TLS handshake with remote client failed: %s "
QB_XS " rc=%d", gnutls_strerror(rc), rc);
return EPROTO;
}
return pcmk_rc_ok;
}
// \return Standard Pacemaker return code
static int
send_tls(gnutls_session_t *session, struct iovec *iov)
{
const char *unsent = iov->iov_base;
size_t unsent_len = iov->iov_len;
ssize_t gnutls_rc;
if (unsent == NULL) {
return EINVAL;
}
crm_trace("Sending TLS message of %llu bytes",
(unsigned long long) unsent_len);
while (true) {
gnutls_rc = gnutls_record_send(*session, unsent, unsent_len);
if (gnutls_rc == GNUTLS_E_INTERRUPTED || gnutls_rc == GNUTLS_E_AGAIN) {
crm_trace("Retrying to send %llu bytes remaining",
(unsigned long long) unsent_len);
} else if (gnutls_rc < 0) {
// Caller can log as error if necessary
crm_info("TLS connection terminated: %s " QB_XS " rc=%lld",
gnutls_strerror((int) gnutls_rc),
(long long) gnutls_rc);
return ECONNABORTED;
} else if (gnutls_rc < unsent_len) {
crm_trace("Sent %lld of %llu bytes remaining",
(long long) gnutls_rc, (unsigned long long) unsent_len);
unsent_len -= gnutls_rc;
unsent += gnutls_rc;
} else {
crm_trace("Sent all %lld bytes remaining", (long long) gnutls_rc);
break;
}
}
return pcmk_rc_ok;
}
// \return Standard Pacemaker return code
static int
send_plaintext(int sock, struct iovec *iov)
{
const char *unsent = iov->iov_base;
size_t unsent_len = iov->iov_len;
ssize_t write_rc;
if (unsent == NULL) {
return EINVAL;
}
crm_debug("Sending plaintext message of %llu bytes to socket %d",
(unsigned long long) unsent_len, sock);
while (true) {
write_rc = write(sock, unsent, unsent_len);
if (write_rc < 0) {
int rc = errno;
if ((errno == EINTR) || (errno == EAGAIN)) {
crm_trace("Retrying to send %llu bytes remaining to socket %d",
(unsigned long long) unsent_len, sock);
continue;
}
// Caller can log as error if necessary
crm_info("Could not send message: %s " QB_XS " rc=%d socket=%d",
pcmk_rc_str(rc), rc, sock);
return rc;
} else if (write_rc < unsent_len) {
crm_trace("Sent %lld of %llu bytes remaining",
(long long) write_rc, (unsigned long long) unsent_len);
unsent += write_rc;
unsent_len -= write_rc;
continue;
} else {
crm_trace("Sent all %lld bytes remaining: %.100s",
(long long) write_rc, (char *) (iov->iov_base));
break;
}
}
return pcmk_rc_ok;
}
// \return Standard Pacemaker return code
static int
remote_send_iovs(pcmk__remote_t *remote, struct iovec *iov, int iovs)
{
int rc = pcmk_rc_ok;
for (int lpc = 0; (lpc < iovs) && (rc == pcmk_rc_ok); lpc++) {
if (remote->tls_session) {
rc = send_tls(remote->tls_session, &(iov[lpc]));
continue;
}
if (remote->tcp_socket) {
rc = send_plaintext(remote->tcp_socket, &(iov[lpc]));
} else {
rc = ESOCKTNOSUPPORT;
}
}
return rc;
}
/*!
* \internal
* \brief Send an XML message over a Pacemaker Remote connection
*
* \param[in,out] remote Pacemaker Remote connection to use
* \param[in] msg XML to send
*
* \return Standard Pacemaker return code
*/
int
pcmk__remote_send_xml(pcmk__remote_t *remote, const xmlNode *msg)
{
int rc = pcmk_rc_ok;
static uint64_t id = 0;
GString *xml_text = NULL;
struct iovec iov[2];
struct remote_header_v0 *header;
CRM_CHECK((remote != NULL) && (msg != NULL), return EINVAL);
xml_text = g_string_sized_new(1024);
pcmk__xml_string(msg, 0, xml_text, 0);
CRM_CHECK(xml_text->len > 0,
g_string_free(xml_text, TRUE); return EINVAL);
header = pcmk__assert_alloc(1, sizeof(struct remote_header_v0));
iov[0].iov_base = header;
iov[0].iov_len = sizeof(struct remote_header_v0);
iov[1].iov_len = 1 + xml_text->len;
iov[1].iov_base = g_string_free(xml_text, FALSE);
id++;
header->id = id;
header->endian = ENDIAN_LOCAL;
header->version = REMOTE_MSG_VERSION;
header->payload_offset = iov[0].iov_len;
header->payload_uncompressed = iov[1].iov_len;
header->size_total = iov[0].iov_len + iov[1].iov_len;
rc = remote_send_iovs(remote, iov, 2);
if (rc != pcmk_rc_ok) {
crm_err("Could not send remote message: %s " QB_XS " rc=%d",
pcmk_rc_str(rc), rc);
}
free(iov[0].iov_base);
g_free((gchar *) iov[1].iov_base);
return rc;
}
/*!
* \internal
* \brief Obtain the XML from the currently buffered remote connection message
*
* \param[in,out] remote Remote connection possibly with message available
*
* \return Newly allocated XML object corresponding to message data, or NULL
* \note This effectively removes the message from the connection buffer.
*/
xmlNode *
pcmk__remote_message_xml(pcmk__remote_t *remote)
{
xmlNode *xml = NULL;
struct remote_header_v0 *header = localized_remote_header(remote);
if (header == NULL) {
return NULL;
}
/* Support compression on the receiving end now, in case we ever want to add it later */
if (header->payload_compressed) {
int rc = 0;
unsigned int size_u = 1 + header->payload_uncompressed;
char *uncompressed =
pcmk__assert_alloc(1, header->payload_offset + size_u);
crm_trace("Decompressing message data %d bytes into %d bytes",
header->payload_compressed, size_u);
rc = BZ2_bzBuffToBuffDecompress(uncompressed + header->payload_offset, &size_u,
remote->buffer + header->payload_offset,
header->payload_compressed, 1, 0);
rc = pcmk__bzlib2rc(rc);
if (rc != pcmk_rc_ok && header->version > REMOTE_MSG_VERSION) {
crm_warn("Couldn't decompress v%d message, we only understand v%d",
header->version, REMOTE_MSG_VERSION);
free(uncompressed);
return NULL;
} else if (rc != pcmk_rc_ok) {
crm_err("Decompression failed: %s " QB_XS " rc=%d",
pcmk_rc_str(rc), rc);
free(uncompressed);
return NULL;
}
CRM_ASSERT(size_u == header->payload_uncompressed);
memcpy(uncompressed, remote->buffer, header->payload_offset); /* Preserve the header */
remote->buffer_size = header->payload_offset + size_u;
free(remote->buffer);
remote->buffer = uncompressed;
header = localized_remote_header(remote);
}
/* take ownership of the buffer */
remote->buffer_offset = 0;
CRM_LOG_ASSERT(remote->buffer[sizeof(struct remote_header_v0) + header->payload_uncompressed - 1] == 0);
xml = pcmk__xml_parse(remote->buffer + header->payload_offset);
if (xml == NULL && header->version > REMOTE_MSG_VERSION) {
crm_warn("Couldn't parse v%d message, we only understand v%d",
header->version, REMOTE_MSG_VERSION);
} else if (xml == NULL) {
crm_err("Couldn't parse: '%.120s'", remote->buffer + header->payload_offset);
}
return xml;
}
static int
get_remote_socket(const pcmk__remote_t *remote)
{
if (remote->tls_session) {
void *sock_ptr = gnutls_transport_get_ptr(*remote->tls_session);
return GPOINTER_TO_INT(sock_ptr);
}
if (remote->tcp_socket) {
return remote->tcp_socket;
}
crm_err("Remote connection type undetermined (bug?)");
return -1;
}
/*!
* \internal
* \brief Wait for a remote session to have data to read
*
* \param[in] remote Connection to check
* \param[in] timeout_ms Maximum time (in ms) to wait
*
* \return Standard Pacemaker return code (of particular interest, pcmk_rc_ok if
* there is data ready to be read, and ETIME if there is no data within
* the specified timeout)
*/
int
pcmk__remote_ready(const pcmk__remote_t *remote, int timeout_ms)
{
struct pollfd fds = { 0, };
int sock = 0;
int rc = 0;
time_t start;
int timeout = timeout_ms;
sock = get_remote_socket(remote);
if (sock <= 0) {
crm_trace("No longer connected");
return ENOTCONN;
}
start = time(NULL);
errno = 0;
do {
fds.fd = sock;
fds.events = POLLIN;
/* If we got an EINTR while polling, and we have a
* specific timeout we are trying to honor, attempt
* to adjust the timeout to the closest second. */
if (errno == EINTR && (timeout > 0)) {
timeout = timeout_ms - ((time(NULL) - start) * 1000);
if (timeout < 1000) {
timeout = 1000;
}
}
rc = poll(&fds, 1, timeout);
} while (rc < 0 && errno == EINTR);
if (rc < 0) {
return errno;
}
return (rc == 0)? ETIME : pcmk_rc_ok;
}
/*!
* \internal
* \brief Read bytes from non-blocking remote connection
*
* \param[in,out] remote Remote connection to read
*
* \return Standard Pacemaker return code (of particular interest, pcmk_rc_ok if
* a full message has been received, or EAGAIN for a partial message)
* \note Use only with non-blocking sockets after polling the socket.
* \note This function will return when the socket read buffer is empty or an
* error is encountered.
*/
static int
read_available_remote_data(pcmk__remote_t *remote)
{
int rc = pcmk_rc_ok;
size_t read_len = sizeof(struct remote_header_v0);
struct remote_header_v0 *header = localized_remote_header(remote);
bool received = false;
ssize_t read_rc;
if(header) {
/* Stop at the end of the current message */
read_len = header->size_total;
}
/* automatically grow the buffer when needed */
if(remote->buffer_size < read_len) {
remote->buffer_size = 2 * read_len;
crm_trace("Expanding buffer to %llu bytes",
(unsigned long long) remote->buffer_size);
remote->buffer = pcmk__realloc(remote->buffer, remote->buffer_size + 1);
}
if (!received && remote->tls_session) {
read_rc = gnutls_record_recv(*(remote->tls_session),
remote->buffer + remote->buffer_offset,
remote->buffer_size - remote->buffer_offset);
if (read_rc == GNUTLS_E_INTERRUPTED) {
rc = EINTR;
} else if (read_rc == GNUTLS_E_AGAIN) {
rc = EAGAIN;
} else if (read_rc < 0) {
crm_debug("TLS receive failed: %s (%lld)",
gnutls_strerror(read_rc), (long long) read_rc);
rc = EIO;
}
received = true;
}
if (!received && remote->tcp_socket) {
read_rc = read(remote->tcp_socket,
remote->buffer + remote->buffer_offset,
remote->buffer_size - remote->buffer_offset);
if (read_rc < 0) {
rc = errno;
}
received = true;
}
if (!received) {
crm_err("Remote connection type undetermined (bug?)");
return ESOCKTNOSUPPORT;
}
/* process any errors. */
if (read_rc > 0) {
remote->buffer_offset += read_rc;
/* always null terminate buffer, the +1 to alloc always allows for this. */
remote->buffer[remote->buffer_offset] = '\0';
crm_trace("Received %lld more bytes (%llu total)",
(long long) read_rc,
(unsigned long long) remote->buffer_offset);
} else if ((rc == EINTR) || (rc == EAGAIN)) {
crm_trace("No data available for non-blocking remote read: %s (%d)",
pcmk_rc_str(rc), rc);
} else if (read_rc == 0) {
crm_debug("End of remote data encountered after %llu bytes",
(unsigned long long) remote->buffer_offset);
return ENOTCONN;
} else {
crm_debug("Error receiving remote data after %llu bytes: %s (%d)",
(unsigned long long) remote->buffer_offset,
pcmk_rc_str(rc), rc);
return ENOTCONN;
}
header = localized_remote_header(remote);
if(header) {
if(remote->buffer_offset < header->size_total) {
crm_trace("Read partial remote message (%llu of %u bytes)",
(unsigned long long) remote->buffer_offset,
header->size_total);
} else {
crm_trace("Read full remote message of %llu bytes",
(unsigned long long) remote->buffer_offset);
return pcmk_rc_ok;
}
}
return EAGAIN;
}
/*!
* \internal
* \brief Read one message from a remote connection
*
* \param[in,out] remote Remote connection to read
* \param[in] timeout_ms Fail if message not read in this many milliseconds
* (10s will be used if 0, and 60s if negative)
*
* \return Standard Pacemaker return code
*/
int
pcmk__read_remote_message(pcmk__remote_t *remote, int timeout_ms)
{
int rc = pcmk_rc_ok;
time_t start = time(NULL);
int remaining_timeout = 0;
if (timeout_ms == 0) {
timeout_ms = 10000;
} else if (timeout_ms < 0) {
timeout_ms = 60000;
}
remaining_timeout = timeout_ms;
while (remaining_timeout > 0) {
crm_trace("Waiting for remote data (%d ms of %d ms timeout remaining)",
remaining_timeout, timeout_ms);
rc = pcmk__remote_ready(remote, remaining_timeout);
if (rc == ETIME) {
crm_err("Timed out (%d ms) while waiting for remote data",
remaining_timeout);
return rc;
} else if (rc != pcmk_rc_ok) {
crm_debug("Wait for remote data aborted (will retry): %s "
QB_XS " rc=%d", pcmk_rc_str(rc), rc);
} else {
rc = read_available_remote_data(remote);
if (rc == pcmk_rc_ok) {
return rc;
} else if (rc == EAGAIN) {
crm_trace("Waiting for more remote data");
} else {
crm_debug("Could not receive remote data: %s " QB_XS " rc=%d",
pcmk_rc_str(rc), rc);
}
}
// Don't waste time retrying after fatal errors
if ((rc == ENOTCONN) || (rc == ESOCKTNOSUPPORT)) {
return rc;
}
remaining_timeout = timeout_ms - ((time(NULL) - start) * 1000);
}
return ETIME;
}
struct tcp_async_cb_data {
int sock;
int timeout_ms;
time_t start;
void *userdata;
void (*callback) (void *userdata, int rc, int sock);
};
// \return TRUE if timer should be rescheduled, FALSE otherwise
static gboolean
check_connect_finished(gpointer userdata)
{
struct tcp_async_cb_data *cb_data = userdata;
int rc;
fd_set rset, wset;
struct timeval ts = { 0, };
if (cb_data->start == 0) {
// Last connect() returned success immediately
rc = pcmk_rc_ok;
goto dispatch_done;
}
// If the socket is ready for reading or writing, the connect succeeded
FD_ZERO(&rset);
FD_SET(cb_data->sock, &rset);
wset = rset;
rc = select(cb_data->sock + 1, &rset, &wset, NULL, &ts);
if (rc < 0) { // select() error
rc = errno;
if ((rc == EINPROGRESS) || (rc == EAGAIN)) {
if ((time(NULL) - cb_data->start) < (cb_data->timeout_ms / 1000)) {
return TRUE; // There is time left, so reschedule timer
} else {
rc = ETIMEDOUT;
}
}
crm_trace("Could not check socket %d for connection success: %s (%d)",
cb_data->sock, pcmk_rc_str(rc), rc);
} else if (rc == 0) { // select() timeout
if ((time(NULL) - cb_data->start) < (cb_data->timeout_ms / 1000)) {
return TRUE; // There is time left, so reschedule timer
}
crm_debug("Timed out while waiting for socket %d connection success",
cb_data->sock);
rc = ETIMEDOUT;
// select() returned number of file descriptors that are ready
} else if (FD_ISSET(cb_data->sock, &rset)
|| FD_ISSET(cb_data->sock, &wset)) {
// The socket is ready; check it for connection errors
int error = 0;
socklen_t len = sizeof(error);
if (getsockopt(cb_data->sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
rc = errno;
crm_trace("Couldn't check socket %d for connection errors: %s (%d)",
cb_data->sock, pcmk_rc_str(rc), rc);
} else if (error != 0) {
rc = error;
crm_trace("Socket %d connected with error: %s (%d)",
cb_data->sock, pcmk_rc_str(rc), rc);
} else {
rc = pcmk_rc_ok;
}
} else { // Should not be possible
crm_trace("select() succeeded, but socket %d not in resulting "
"read/write sets", cb_data->sock);
rc = EAGAIN;
}
dispatch_done:
if (rc == pcmk_rc_ok) {
crm_trace("Socket %d is connected", cb_data->sock);
} else {
close(cb_data->sock);
cb_data->sock = -1;
}
if (cb_data->callback) {
cb_data->callback(cb_data->userdata, rc, cb_data->sock);
}
free(cb_data);
return FALSE; // Do not reschedule timer
}
/*!
* \internal
* \brief Attempt to connect socket, calling callback when done
*
* Set a given socket non-blocking, then attempt to connect to it,
* retrying periodically until success or a timeout is reached.
* Call a caller-supplied callback function when completed.
*
* \param[in] sock Newly created socket
* \param[in] addr Socket address information for connect
* \param[in] addrlen Size of socket address information in bytes
* \param[in] timeout_ms Fail if not connected within this much time
* \param[out] timer_id If not NULL, store retry timer ID here
* \param[in] userdata User data to pass to callback
* \param[in] callback Function to call when connection attempt completes
*
* \return Standard Pacemaker return code
*/
static int
connect_socket_retry(int sock, const struct sockaddr *addr, socklen_t addrlen,
int timeout_ms, int *timer_id, void *userdata,
void (*callback) (void *userdata, int rc, int sock))
{
int rc = 0;
int interval = 500;
int timer;
struct tcp_async_cb_data *cb_data = NULL;
rc = pcmk__set_nonblocking(sock);
if (rc != pcmk_rc_ok) {
crm_warn("Could not set socket non-blocking: %s " QB_XS " rc=%d",
pcmk_rc_str(rc), rc);
return rc;
}
rc = connect(sock, addr, addrlen);
if (rc < 0 && (errno != EINPROGRESS) && (errno != EAGAIN)) {
rc = errno;
crm_warn("Could not connect socket: %s " QB_XS " rc=%d",
pcmk_rc_str(rc), rc);
return rc;
}
cb_data = pcmk__assert_alloc(1, sizeof(struct tcp_async_cb_data));
cb_data->userdata = userdata;
cb_data->callback = callback;
cb_data->sock = sock;
cb_data->timeout_ms = timeout_ms;
if (rc == 0) {
/* The connect was successful immediately, we still return to mainloop
* and let this callback get called later. This avoids the user of this api
* to have to account for the fact the callback could be invoked within this
* function before returning. */
cb_data->start = 0;
interval = 1;
} else {
cb_data->start = time(NULL);
}
/* This timer function does a non-blocking poll on the socket to see if we
* can use it. Once we can, the connect has completed. This method allows us
* to connect without blocking the mainloop.
*
* @TODO Use a mainloop fd callback for this instead of polling. Something
* about the way mainloop is currently polling prevents this from
* working at the moment though. (See connect(2) regarding EINPROGRESS
* for possible new handling needed.)
*/
crm_trace("Scheduling check in %dms for whether connect to fd %d finished",
interval, sock);
timer = g_timeout_add(interval, check_connect_finished, cb_data);
if (timer_id) {
*timer_id = timer;
}
// timer callback should be taking care of cb_data
// cppcheck-suppress memleak
return pcmk_rc_ok;
}
/*!
* \internal
* \brief Attempt once to connect socket and set it non-blocking
*
* \param[in] sock Newly created socket
* \param[in] addr Socket address information for connect
* \param[in] addrlen Size of socket address information in bytes
*
* \return Standard Pacemaker return code
*/
static int
connect_socket_once(int sock, const struct sockaddr *addr, socklen_t addrlen)
{
int rc = connect(sock, addr, addrlen);
if (rc < 0) {
rc = errno;
crm_warn("Could not connect socket: %s " QB_XS " rc=%d",
pcmk_rc_str(rc), rc);
return rc;
}
rc = pcmk__set_nonblocking(sock);
if (rc != pcmk_rc_ok) {
crm_warn("Could not set socket non-blocking: %s " QB_XS " rc=%d",
pcmk_rc_str(rc), rc);
return rc;
}
return pcmk_ok;
}
/*!
* \internal
* \brief Connect to server at specified TCP port
*
* \param[in] host Name of server to connect to
* \param[in] port Server port to connect to
* \param[in] timeout_ms If asynchronous, fail if not connected in this time
* \param[out] timer_id If asynchronous and this is non-NULL, retry timer ID
* will be put here (for ease of cancelling by caller)
* \param[out] sock_fd Where to store socket file descriptor
* \param[in] userdata If asynchronous, data to pass to callback
* \param[in] callback If NULL, attempt a single synchronous connection,
* otherwise retry asynchronously then call this
*
* \return Standard Pacemaker return code
*/
int
pcmk__connect_remote(const char *host, int port, int timeout, int *timer_id,
int *sock_fd, void *userdata,
void (*callback) (void *userdata, int rc, int sock))
{
char buffer[INET6_ADDRSTRLEN];
struct addrinfo *res = NULL;
struct addrinfo *rp = NULL;
struct addrinfo hints;
const char *server = host;
int rc;
int sock = -1;
CRM_CHECK((host != NULL) && (sock_fd != NULL), return EINVAL);
// Get host's IP address(es)
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
rc = getaddrinfo(server, NULL, &hints, &res);
rc = pcmk__gaierror2rc(rc);
if (rc != pcmk_rc_ok) {
crm_err("Unable to get IP address info for %s: %s",
server, pcmk_rc_str(rc));
goto async_cleanup;
}
if (!res || !res->ai_addr) {
crm_err("Unable to get IP address info for %s: no result", server);
rc = ENOTCONN;
goto async_cleanup;
}
// getaddrinfo() returns a list of host's addresses, try them in order
for (rp = res; rp != NULL; rp = rp->ai_next) {
struct sockaddr *addr = rp->ai_addr;
if (!addr) {
continue;
}
if (rp->ai_canonname) {
server = res->ai_canonname;
}
crm_debug("Got canonical name %s for %s", server, host);
sock = socket(rp->ai_family, SOCK_STREAM, IPPROTO_TCP);
if (sock == -1) {
rc = errno;
crm_warn("Could not create socket for remote connection to %s:%d: "
"%s " QB_XS " rc=%d", server, port, pcmk_rc_str(rc), rc);
continue;
}
/* Set port appropriately for address family */
/* (void*) casts avoid false-positive compiler alignment warnings */
if (addr->sa_family == AF_INET6) {
((struct sockaddr_in6 *)(void*)addr)->sin6_port = htons(port);
} else {
((struct sockaddr_in *)(void*)addr)->sin_port = htons(port);
}
memset(buffer, 0, PCMK__NELEM(buffer));
pcmk__sockaddr2str(addr, buffer);
crm_info("Attempting remote connection to %s:%d", buffer, port);
if (callback) {
if (connect_socket_retry(sock, rp->ai_addr, rp->ai_addrlen, timeout,
timer_id, userdata, callback) == pcmk_rc_ok) {
goto async_cleanup; /* Success for now, we'll hear back later in the callback */
}
} else if (connect_socket_once(sock, rp->ai_addr,
rp->ai_addrlen) == pcmk_rc_ok) {
break; /* Success */
}
// Connect failed
close(sock);
sock = -1;
rc = ENOTCONN;
}
async_cleanup:
if (res) {
freeaddrinfo(res);
}
*sock_fd = sock;
return rc;
}
/*!
* \internal
* \brief Convert an IP address (IPv4 or IPv6) to a string for logging
*
* \param[in] sa Socket address for IP
* \param[out] s Storage for at least INET6_ADDRSTRLEN bytes
*
* \note sa The socket address can be a pointer to struct sockaddr_in (IPv4),
* struct sockaddr_in6 (IPv6) or struct sockaddr_storage (either),
* as long as its sa_family member is set correctly.
*/
void
pcmk__sockaddr2str(const void *sa, char *s)
{
switch (((const struct sockaddr *) sa)->sa_family) {
case AF_INET:
inet_ntop(AF_INET, &(((const struct sockaddr_in *) sa)->sin_addr),
s, INET6_ADDRSTRLEN);
break;
case AF_INET6:
inet_ntop(AF_INET6,
&(((const struct sockaddr_in6 *) sa)->sin6_addr),
s, INET6_ADDRSTRLEN);
break;
default:
strcpy(s, "<invalid>");
}
}
/*!
* \internal
* \brief Accept a client connection on a remote server socket
*
* \param[in] ssock Server socket file descriptor being listened on
* \param[out] csock Where to put new client socket's file descriptor
*
* \return Standard Pacemaker return code
*/
int
pcmk__accept_remote_connection(int ssock, int *csock)
{
int rc;
struct sockaddr_storage addr;
socklen_t laddr = sizeof(addr);
char addr_str[INET6_ADDRSTRLEN];
#ifdef TCP_USER_TIMEOUT
long sbd_timeout = 0;
#endif
/* accept the connection */
memset(&addr, 0, sizeof(addr));
*csock = accept(ssock, (struct sockaddr *)&addr, &laddr);
if (*csock == -1) {
rc = errno;
crm_err("Could not accept remote client connection: %s "
QB_XS " rc=%d", pcmk_rc_str(rc), rc);
return rc;
}
pcmk__sockaddr2str(&addr, addr_str);
crm_info("Accepted new remote client connection from %s", addr_str);
rc = pcmk__set_nonblocking(*csock);
if (rc != pcmk_rc_ok) {
crm_err("Could not set socket non-blocking: %s " QB_XS " rc=%d",
pcmk_rc_str(rc), rc);
close(*csock);
*csock = -1;
return rc;
}
#ifdef TCP_USER_TIMEOUT
sbd_timeout = pcmk__get_sbd_watchdog_timeout();
if (sbd_timeout > 0) {
// Time to fail and retry before watchdog
long half = sbd_timeout / 2;
unsigned int optval = (half <= UINT_MAX)? half : UINT_MAX;
rc = setsockopt(*csock, SOL_TCP, TCP_USER_TIMEOUT,
&optval, sizeof(optval));
if (rc < 0) {
rc = errno;
crm_err("Could not set TCP timeout to %d ms on remote connection: "
"%s " QB_XS " rc=%d", optval, pcmk_rc_str(rc), rc);
close(*csock);
*csock = -1;
return rc;
}
}
#endif
return rc;
}
/*!
* \brief Get the default remote connection TCP port on this host
*
* \return Remote connection TCP port number
*/
int
crm_default_remote_port(void)
{
static int port = 0;
if (port == 0) {
const char *env = pcmk__env_option(PCMK__ENV_REMOTE_PORT);
if (env) {
errno = 0;
port = strtol(env, NULL, 10);
if (errno || (port < 1) || (port > 65535)) {
crm_warn("Environment variable PCMK_" PCMK__ENV_REMOTE_PORT
" has invalid value '%s', using %d instead",
env, DEFAULT_REMOTE_PORT);
port = DEFAULT_REMOTE_PORT;
}
} else {
port = DEFAULT_REMOTE_PORT;
}
}
return port;
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Apr 21, 8:32 PM (4 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1653700
Default Alt Text
(183 KB)

Event Timeline