diff --git a/Makefile.am b/Makefile.am index b8e798a18f..dfd7cc7d79 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,71 +1,71 @@ # # Pacemaker code # # Copyright (C) 2004 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # EXTRA_DIST = autogen.sh ConfigureMe README.in libltdl.tar m4/gnulib-cache.m4 MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure DRF/config-h.in \ DRF/stamp-h.in libtool.m4 ltdl.m4 libltdl.tar -CORE = $(LIBLTDL_DIR) replace include lib mcp pengine cib crmd fencing tools xml +CORE = $(LIBLTDL_DIR) replace include lib mcp pengine cib crmd fencing lrmd tools xml SUBDIRS = $(CORE) cts extra doc doc_DATA = AUTHORS COPYING COPYING.LIB AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 core: @echo "Building only core components: $(CORE)" list='$(CORE)'; for subdir in $$list; do echo "Building in $$subdir"; make -C $$subdir all || exit 1; done core-install: @echo "Installing only core components: $(CORE)" list='$(CORE)'; for subdir in $$list; do echo "Installing in $$subdir"; make -C $$subdir install || exit 1; done install-exec-local: $(INSTALL) -d $(DESTDIR)/$(LCRSODIR) $(INSTALL) -d -m 750 $(DESTDIR)/$(CRM_CONFIG_DIR) $(INSTALL) -d -m 750 $(DESTDIR)/$(CRM_STATE_DIR) -chown $(CRM_DAEMON_USER):$(CRM_DAEMON_GROUP) $(DESTDIR)/$(CRM_CONFIG_DIR) -chown $(CRM_DAEMON_USER):$(CRM_DAEMON_GROUP) $(DESTDIR)/$(CRM_STATE_DIR) if BUILD_CS_PLUGIN rm -f $(DESTDIR)$(LCRSODIR)/pacemaker.lcrso $(DESTDIR)$(LCRSODIR)/service_crm.so cp $(DESTDIR)$(libdir)/service_crm.so $(DESTDIR)$(LCRSODIR)/pacemaker.lcrso endif if BUILD_HEARTBEAT_SUPPORT $(INSTALL) -d $(DESTDIR)/$(HB_DAEMON_DIR) ln -sf $(CRM_DAEMON_DIR)/attrd $(DESTDIR)$(HB_DAEMON_DIR)/ ln -sf $(CRM_DAEMON_DIR)/cib $(DESTDIR)$(HB_DAEMON_DIR)/ ln -sf $(CRM_DAEMON_DIR)/crmd $(DESTDIR)$(HB_DAEMON_DIR)/ ln -sf $(CRM_DAEMON_DIR)/pengine $(DESTDIR)$(HB_DAEMON_DIR)/ ln -sf $(CRM_DAEMON_DIR)/stonithd $(DESTDIR)$(HB_DAEMON_DIR)/ endif # Use chown because the user/group may not exist clean-generic: rm -f $(TARFILE) *.tar.bz2 *.sed dist-clean-local: rm -f autoconf automake autoheader maintainer-clean-local: rm -f libltdl.tar .PHONY: rpm pkg handy handy-copy diff --git a/configure.ac b/configure.ac index 3085945822..dbbda12ed0 100644 --- a/configure.ac +++ b/configure.ac @@ -1,1719 +1,1722 @@ dnl dnl autoconf for Pacemaker dnl dnl License: GNU General Public License (GPL) dnl =============================================== dnl Bootstrap dnl =============================================== AC_PREREQ(2.59) dnl Suggested structure: dnl information on the package dnl checks for programs dnl checks for libraries dnl checks for header files dnl checks for types dnl checks for structures dnl checks for compiler characteristics dnl checks for library functions dnl checks for system services AC_INIT(pacemaker, 1.1.7, pacemaker@oss.clusterlabs.org) CRM_DTD_VERSION="1.2" PCMK_FEATURES="" HB_PKG=heartbeat AC_CONFIG_AUX_DIR(.) AC_CANONICAL_HOST dnl Where #defines go (e.g. `AC_CHECK_HEADERS' below) dnl dnl Internal header: include/config.h dnl - Contains ALL defines dnl - include/config.h.in is generated automatically by autoheader dnl - NOT to be included in any header files except lha_internal.h dnl (which is also not to be included in any other header files) dnl dnl External header: include/crm_config.h dnl - Contains a subset of defines checked here dnl - Manually edit include/crm_config.h.in to have configure include dnl new defines dnl - Should not include HAVE_* defines dnl - Safe to include anywhere AM_CONFIG_HEADER(include/config.h include/crm_config.h) ALL_LINGUAS="en fr" AC_ARG_WITH(version, [ --with-version=version Override package version (if you're a packager needing to pretend) ], [ PACKAGE_VERSION="$withval" ]) AC_ARG_WITH(pkg-name, [ --with-pkg-name=name Override package name (if you're a packager needing to pretend) ], [ PACKAGE_NAME="$withval" ]) AM_INIT_AUTOMAKE($PACKAGE_NAME, $PACKAGE_VERSION) AC_DEFINE_UNQUOTED(PACEMAKER_VERSION, "$PACKAGE_VERSION", Current pacemaker version) PACKAGE_SERIES=`echo $PACKAGE_VERSION | awk -F. '{ print $1"."$2 }'` AC_SUBST(PACKAGE_SERIES) AC_SUBST(PACKAGE_VERSION) dnl automake >= 1.11 offers --enable-silent-rules for suppressing the output from dnl normal compilation. When a failure occurs, it will then display the full dnl command line dnl Wrap in m4_ifdef to avoid breaking on older platforms m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) dnl Example 2.4. Silent Custom Rule to Generate a File dnl %-bar.pc: %.pc dnl $(AM_V_GEN)$(LN_S) $(notdir $^) $@ CC_IN_CONFIGURE=yes export CC_IN_CONFIGURE LDD=ldd dnl ======================================================================== dnl Compiler characteristics dnl ======================================================================== AC_PROG_CC dnl Can force other with environment variable "CC". AM_PROG_CC_C_O AC_PROG_CC_STDC gl_EARLY gl_INIT AC_LIBTOOL_DLOPEN dnl Enable dlopen support... AC_LIBLTDL_CONVENIENCE dnl make libltdl a convenience lib AC_PROG_LIBTOOL AC_PROG_YACC AM_PROG_LEX AC_C_STRINGIZE AC_TYPE_SIZE_T AC_CHECK_SIZEOF(char) AC_CHECK_SIZEOF(short) AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) AC_CHECK_SIZEOF(long long) AC_STRUCT_TIMEZONE dnl =============================================== dnl Helpers dnl =============================================== cc_supports_flag() { local CFLAGS="$@" AC_MSG_CHECKING(whether $CC supports "$@") AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]], [[ ]])], [RC=0; AC_MSG_RESULT(yes)],[RC=1; AC_MSG_RESULT(no)]) return $RC } try_extract_header_define() { AC_MSG_CHECKING(if $2 in $1 exists. If not defaulting to $3) Cfile=$srcdir/extract_define.$2.${$} printf "#include \n" > ${Cfile}.c printf "#include <%s>\n" $1 >> ${Cfile}.c printf "int main(int argc, char **argv) {\n" >> ${Cfile}.c printf "#ifdef %s\n" $2 >> ${Cfile}.c printf "printf(\"%%s\", %s);\n" $2 >> ${Cfile}.c printf "#endif \n return 0; }\n" >> ${Cfile}.c $CC $CFLAGS ${Cfile}.c -o ${Cfile} value=`${Cfile}` if test x"${value}" == x""; then value=$3 fi AC_MSG_RESULT($value) printf $value rm -rf ${Cfile}.c ${Cfile} ${Cfile}.dSYM } extract_header_define() { AC_MSG_CHECKING(for $2 in $1) Cfile=$srcdir/extract_define.$2.${$} printf "#include \n" > ${Cfile}.c printf "#include <%s>\n" $1 >> ${Cfile}.c printf "int main(int argc, char **argv) { printf(\"%%s\", %s); return 0; }\n" $2 >> ${Cfile}.c $CC $CFLAGS ${Cfile}.c -o ${Cfile} value=`${Cfile}` AC_MSG_RESULT($value) printf $value rm -rf ${Cfile}.c ${Cfile} ${Cfile}.dSYM } dnl =============================================== dnl Configure Options dnl =============================================== dnl Some systems, like Solaris require a custom package name AC_ARG_WITH(pkgname, [ --with-pkgname=name name for pkg (typically for Solaris) ], [ PKGNAME="$withval" ], [ PKGNAME="LXHAhb" ], ) AC_SUBST(PKGNAME) AC_ARG_ENABLE([ansi], [ --enable-ansi force GCC to compile to ANSI/ANSI standard for older compilers. [default=no]]) AC_ARG_ENABLE([fatal-warnings], [ --enable-fatal-warnings very pedantic and fatal warnings for gcc [default=yes]]) AC_ARG_ENABLE([quiet], [ --enable-quiet Supress make output unless there is an error [default=no]]) AC_ARG_ENABLE([thread-safe], [ --enable-thread-safe Enable some client libraries to be thread safe. [default=no]]) AC_ARG_ENABLE([bundled-ltdl], [ --enable-bundled-ltdl Configure, build and install the standalone ltdl library bundled with ${PACKAGE} [default=no]]) LTDL_LIBS="" AC_ARG_ENABLE([no-stack], [ --enable-no-stack Only build the Policy Engine and pieces needed to support it [default=no]]) AC_ARG_WITH(ais, [ --with-ais Support the Corosync messaging and membership layer ], [ SUPPORT_CS=$withval ], [ SUPPORT_CS=try ], ) AC_ARG_WITH(corosync, [ --with-corosync Support the Corosync messaging and membership layer ], [ SUPPORT_CS=$withval ], [ SUPPORT_CS=try ], ) AC_ARG_WITH(heartbeat, [ --with-heartbeat Support the Heartbeat messaging and membership layer ], [ SUPPORT_HEARTBEAT=$withval ], [ SUPPORT_HEARTBEAT=try ], ) AC_ARG_WITH(cman, [ --with-cman Support the consumption of membership and quorum from cman ], [ SUPPORT_CMAN=$withval ], [ SUPPORT_CMAN=try ], ) AC_ARG_WITH(cpg, [ --with-cs-quorum Support the consumption of membership and quorum from corosync ], [ SUPPORT_CS_QUORUM=$withval ], [ SUPPORT_CS_QUORUM=try ], ) AC_ARG_WITH(snmp, [ --with-snmp Support the SNMP protocol ], [ SUPPORT_SNMP=$withval ], [ SUPPORT_SNMP=try ], ) AC_ARG_WITH(esmtp, [ --with-esmtp Support the sending mail notifications with the esmtp library ], [ SUPPORT_ESMTP=$withval ], [ SUPPORT_ESMTP=try ], ) AC_ARG_WITH(acl, [ --with-acl Support CIB ACL ], [ SUPPORT_ACL=$withval ], [ SUPPORT_ACL=no ], ) CSPREFIX="" AC_ARG_WITH(ais-prefix, [ --with-ais-prefix=DIR Prefix used when Corosync was installed [$prefix]], [ CSPREFIX=$withval ], [ CSPREFIX=$prefix ]) LCRSODIR="" AC_ARG_WITH(lcrso-dir, [ --with-lcrso-dir=DIR Corosync lcrso files. ], [ LCRSODIR="$withval" ]) INITDIR="" AC_ARG_WITH(initdir, [ --with-initdir=DIR directory for init (rc) scripts [${INITDIR}]], [ INITDIR="$withval" ]) SUPPORT_PROFILING=0 AC_ARG_WITH(profiling, [ --with-profiling Support gprof profiling ], [ SUPPORT_PROFILING=$withval ]) SUPPORT_GCOV=0 AC_ARG_WITH(gcov, [ --with-gcov Support gcov coverage testing ], [ SUPPORT_GCOV=$withval ]) PUBLICAN_BRAND="common" AC_ARG_WITH(brand, [ --with-brand=brand Brand to use for generated documentation [$PUBLICAN_BRAND]], [ PUBLICAN_BRAND="$withval" ]) AC_SUBST(PUBLICAN_BRAND) dnl =============================================== dnl General Processing dnl =============================================== AC_SUBST(HB_PKG) INIT_EXT="" echo Our Host OS: $host_os/$host AC_MSG_NOTICE(Sanitizing prefix: ${prefix}) case $prefix in NONE) prefix=/usr dnl Fix default variables - "prefix" variable if not specified if test "$localstatedir" = "\${prefix}/var"; then localstatedir="/var" fi if test "$sysconfdir" = "\${prefix}/etc"; then sysconfdir="/etc" fi ;; esac AC_MSG_NOTICE(Sanitizing exec_prefix: ${exec_prefix}) case $exec_prefix in dnl For consistency with Heartbeat, map NONE->$prefix NONE) exec_prefix=$prefix;; prefix) exec_prefix=$prefix;; esac AC_MSG_NOTICE(Sanitizing ais_prefix: ${CSPREFIX}) case $CSPREFIX in dnl For consistency with Heartbeat, map NONE->$prefix NONE) CSPREFIX=$prefix;; prefix) CSPREFIX=$prefix;; esac AC_MSG_NOTICE(Sanitizing INITDIR: ${INITDIR}) case $INITDIR in prefix) INITDIR=$prefix;; "") AC_MSG_CHECKING(which init (rc) directory to use) for initdir in /etc/init.d /etc/rc.d/init.d /sbin/init.d \ /usr/local/etc/rc.d /etc/rc.d do if test -d $initdir then INITDIR=$initdir break fi done AC_MSG_RESULT($INITDIR);; esac AC_SUBST(INITDIR) AC_MSG_NOTICE(Sanitizing libdir: ${libdir}) case $libdir in dnl For consistency with Heartbeat, map NONE->$prefix *prefix*|NONE) AC_MSG_CHECKING(which lib directory to use) for aDir in lib64 lib do trydir="${exec_prefix}/${aDir}" if test -d ${trydir} then libdir=${trydir} break fi done AC_MSG_RESULT($libdir); ;; esac dnl Expand autoconf variables so that we dont end up with '${prefix}' dnl in #defines and python scripts dnl NOTE: Autoconf deliberately leaves them unexpanded to allow dnl make exec_prefix=/foo install dnl No longer being able to do this seems like no great loss to me... eval prefix="`eval echo ${prefix}`" eval exec_prefix="`eval echo ${exec_prefix}`" eval bindir="`eval echo ${bindir}`" eval sbindir="`eval echo ${sbindir}`" eval libexecdir="`eval echo ${libexecdir}`" eval datadir="`eval echo ${datadir}`" eval sysconfdir="`eval echo ${sysconfdir}`" eval sharedstatedir="`eval echo ${sharedstatedir}`" eval localstatedir="`eval echo ${localstatedir}`" eval libdir="`eval echo ${libdir}`" eval includedir="`eval echo ${includedir}`" eval oldincludedir="`eval echo ${oldincludedir}`" eval infodir="`eval echo ${infodir}`" eval mandir="`eval echo ${mandir}`" dnl Home-grown variables eval INITDIR="${INITDIR}" eval docdir="`eval echo ${docdir}`" if test x"${docdir}" = x""; then docdir=${datadir}/doc/${PACKAGE}-${VERSION} #docdir=${datadir}/doc/packages/${PACKAGE} fi AC_SUBST(docdir) for j in prefix exec_prefix bindir sbindir libexecdir datadir sysconfdir \ sharedstatedir localstatedir libdir includedir oldincludedir infodir \ mandir INITDIR docdir do dirname=`eval echo '${'${j}'}'` if test ! -d "$dirname" then AC_MSG_WARN([$j directory ($dirname) does not exist!]) fi done dnl This OS-based decision-making is poor autotools practice; dnl feature-based mechanisms are strongly preferred. dnl dnl So keep this section to a bare minimum; regard as a "necessary evil". case "$host_os" in *bsd*) LIBS="-L/usr/local/lib" CPPFLAGS="$CPPFLAGS -I/usr/local/include" INIT_EXT=".sh" ;; *solaris*) ;; *linux*) AC_DEFINE_UNQUOTED(ON_LINUX, 1, Compiling for Linux platform) CFLAGS="$CFLAGS -I${prefix}/include" ;; darwin*) AC_DEFINE_UNQUOTED(ON_DARWIN, 1, Compiling for Darwin platform) LIBS="$LIBS -L${prefix}/lib" CFLAGS="$CFLAGS -I${prefix}/include" ;; esac dnl Eventually remove this CFLAGS="$CFLAGS -I${prefix}/include/heartbeat" AC_SUBST(INIT_EXT) AC_MSG_NOTICE(Host CPU: $host_cpu) case "$host_cpu" in ppc64|powerpc64) case $CFLAGS in *powerpc64*) ;; *) if test "$GCC" = yes; then CFLAGS="$CFLAGS -m64" fi ;; esac esac AC_MSG_CHECKING(which format is needed to print uint64_t) ac_save_CFLAGS=$CFLAGS CFLAGS="-Wall -Werror" AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [ #include #include #include ], [ int max = 512; uint64_t bignum = 42; char *buffer = malloc(max); const char *random = "random"; snprintf(buffer, max-1, "", bignum, random); fprintf(stderr, "Result: %s\n", buffer); ] )], [U64T="%lu"], [U64T="%llu"] ) CFLAGS=$ac_save_CFLAGS AC_MSG_RESULT($U64T) AC_DEFINE_UNQUOTED(U64T, "$U64T", Correct printf format for logging uint64_t) dnl =============================================== dnl Program Paths dnl =============================================== PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin" export PATH dnl Replacing AC_PROG_LIBTOOL with AC_CHECK_PROG because LIBTOOL dnl was NOT being expanded all the time thus causing things to fail. AC_CHECK_PROGS(LIBTOOL, glibtool libtool libtool15 libtool13) AM_PATH_PYTHON AC_CHECK_PROGS(MAKE, gmake make) AC_PATH_PROGS(HTML2TXT, lynx w3m) AC_PATH_PROGS(HELP2MAN, help2man) AC_PATH_PROGS(POD2MAN, pod2man, pod2man) AC_PATH_PROGS(ASCIIDOC, asciidoc) AC_PATH_PROGS(PUBLICAN, publican) AC_PATH_PROGS(INKSCAPE, inkscape) AC_PATH_PROGS(XSLTPROC, xsltproc) AC_PATH_PROGS(FOP, fop) AC_PATH_PROGS(SSH, ssh, /usr/bin/ssh) AC_PATH_PROGS(SCP, scp, /usr/bin/scp) AC_PATH_PROGS(TAR, tar) AC_PATH_PROGS(MD5, md5) AC_PATH_PROGS(TEST, test) AC_PATH_PROGS(PKGCONFIG, pkg-config) AC_PATH_PROGS(XML2CONFIG, xml2-config) AC_PATH_PROGS(VALGRIND_BIN, valgrind, /usr/bin/valgrind) AC_DEFINE_UNQUOTED(VALGRIND_BIN, "$VALGRIND_BIN", Valgrind command) dnl Disable these until we decide if the stonith config file should be supported dnl AC_PATH_PROGS(BISON, bison) dnl AC_PATH_PROGS(FLEX, flex) dnl AC_PATH_PROGS(HAVE_YACC, $YACC) if test x"${LIBTOOL}" = x""; then AC_MSG_ERROR(You need (g)libtool installed in order to build ${PACKAGE}) fi if test x"${MAKE}" = x""; then AC_MSG_ERROR(You need (g)make installed in order to build ${PACKAGE}) fi AM_CONDITIONAL(BUILD_HELP, test x"${HELP2MAN}" != x"") if test x"${HELP2MAN}" != x""; then PCMK_FEATURES="$PCMK_FEATURES generated-manpages" fi MANPAGE_XSLT="" if test x"${XSLTPROC}" != x""; then AC_MSG_CHECKING(docbook to manpage transform) XSLT=`find ${datadir} -name docbook.xsl` for xsl in $XSLT; do dname=`dirname $xsl` bname=`basename $dname` if test "$bname" = "manpages"; then MANPAGE_XSLT="$xsl" break fi done fi AC_MSG_RESULT($MANPAGE_XSLT) AC_SUBST(MANPAGE_XSLT) AM_CONDITIONAL(BUILD_XML_HELP, test x"${MANPAGE_XSLT}" != x"") if test x"${MANPAGE_XSLT}" != x""; then PCMK_FEATURES="$PCMK_FEATURES agent-manpages" fi AM_CONDITIONAL(BUILD_ASCIIDOC, test x"${ASCIIDOC}" != x"") if test x"${ASCIIDOC}" != x""; then PCMK_FEATURES="$PCMK_FEATURES ascii-docs" fi SUPPORT_STONITH_CONFIG=0 if test x"${HAVE_YACC}" != x"" -a x"${FLEX}" != x"" -a x"${BISON}" != x""; then SUPPORT_STONITH_CONFIG=1 PCMK_FEATURES="$PCMK_FEATURES st-conf" fi AM_CONDITIONAL(BUILD_STONITH_CONFIG, test $SUPPORT_STONITH_CONFIG = 1) AC_DEFINE_UNQUOTED(SUPPORT_STONITH_CONFIG, $SUPPORT_STONITH_CONFIG, Support a stand-alone stonith config file in addition to the CIB) AM_CONDITIONAL(BUILD_DOCBOOK, test x"${PUBLICAN}" != x"" -a x"${INKSCAPE}" != x"") if test x"${PUBLICAN}" != x"" -a x"${INKSCAPE}" != x""; then AC_MSG_NOTICE(Enabling publican) PCMK_FEATURES="$PCMK_FEATURES publican-docs" fi dnl ======================================================================== dnl checks for library functions to replace them dnl dnl NoSuchFunctionName: dnl is a dummy function which no system supplies. It is here to make dnl the system compile semi-correctly on OpenBSD which doesn't know dnl how to create an empty archive dnl dnl scandir: Only on BSD. dnl System-V systems may have it, but hidden and/or deprecated. dnl A replacement function is supplied for it. dnl dnl setenv: is some bsdish function that should also be avoided (use dnl putenv instead) dnl On the other hand, putenv doesn't provide the right API for the dnl code and has memory leaks designed in (sigh...) Fortunately this dnl A replacement function is supplied for it. dnl dnl strerror: returns a string that corresponds to an errno. dnl A replacement function is supplied for it. dnl dnl unsetenv: is some bsdish function that should also be avoided (No dnl replacement) dnl A replacement function is supplied for it. dnl dnl strnlen: is a gnu function similar to strlen, but safer. dnl We wrote a tolearably-fast replacement function for it. dnl dnl strndup: is a gnu function similar to strdup, but safer. dnl We wrote a tolearably-fast replacement function for it. dnl dnl daemon: is a GNU function. The daemon() function is for programs wishing to dnl detach themselves from the controlling terminal and run in the dnl background as system daemon dnl A replacement function is supplied for it. AC_REPLACE_FUNCS(alphasort inet_pton NoSuchFunctionName scandir setenv strerror strchrnul unsetenv strnlen strndup daemon strlcpy strlcat) 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) AC_CHECK_LIB(uuid, uuid_parse) dnl libuuid if test "x$ac_cv_lib_uuid_uuid_parse" != x""yes; then AC_MSG_ERROR(You do not have the libuuid development package installed) fi if test x"${PKGCONFIG}" = x""; then AC_MSG_ERROR(You need pkgconfig installed in order to build ${PACKAGE}) fi if test "x${enable_thread_safe}" = "xyes"; then GPKGNAME="gthread-2.0" else GPKGNAME="glib-2.0" fi if $PKGCONFIG --exists $GPKGNAME then GLIBCONFIG="$PKGCONFIG $GPKGNAME" else set -x echo PKG_CONFIG_PATH=$PKG_CONFIG_PATH $PKGCONFIG --exists $GPKGNAME; echo $? $PKGCONFIG --cflags $GPKGNAME; echo $? $PKGCONFIG $GPKGNAME; echo $? set +x AC_MSG_ERROR(You need glib2-devel installed in order to build ${PACKAGE}) fi AC_MSG_RESULT(using $GLIBCONFIG) USE_GHASH_COMPAT=0 AC_CHECK_LIB(glib-2.0, g_hash_table_get_values) if test "x$ac_cv_lib_glib_2_0_g_hash_table_get_values" != x""yes; then AC_MSG_WARN(Your version of Glib is too old, you should have at least 2.14) USE_GHASH_COMPAT=1 fi AC_DEFINE_UNQUOTED(USE_GHASH_COMPAT, $USE_GHASH_COMPAT, Use g_hash_table compatibility functions) AC_SUBST(USE_GHASH_COMPAT) if $PKGCONFIG --exists systemd then systemdunitdir=`$PKGCONFIG --variable=systemdsystemunitdir systemd` AC_SUBST(systemdunitdir) fi AM_CONDITIONAL(HAVE_SYSTEMD, test -n "$systemdunitdir" -a "x$systemdunitdir" != xno) # # Where is dlopen? # if test "$ac_cv_lib_c_dlopen" = yes; then LIBADD_DL="" elif test "$ac_cv_lib_dl_dlopen" = yes; then LIBADD_DL=-ldl else LIBADD_DL=${lt_cv_dlopen_libs} fi dnl dnl Check for location of gettext dnl dnl On at least Solaris 2.x, where it is in libc, specifying lintl causes dnl grief. Ensure minimal result, not the sum of all possibilities. dnl And do libc first. dnl Known examples: dnl c: Linux, Solaris 2.6+ dnl intl: BSD, AIX AC_CHECK_LIB(c, gettext) if test x$ac_cv_lib_c_gettext != xyes; then AC_CHECK_LIB(intl, gettext) fi if test x$ac_cv_lib_c_gettext != xyes -a x$ac_cv_lib_intl_gettext != xyes; then AC_MSG_ERROR(You need gettext installed in order to build ${PACKAGE}) fi if test "X$GLIBCONFIG" != X; then AC_MSG_CHECKING(for special glib includes: ) GLIBHEAD=`$GLIBCONFIG --cflags` AC_MSG_RESULT($GLIBHEAD) CPPFLAGS="$CPPFLAGS $GLIBHEAD" AC_MSG_CHECKING(for glib library flags) GLIBLIB=`$GLIBCONFIG --libs` AC_MSG_RESULT($GLIBLIB) LIBS="$LIBS $GLIBLIB" fi dnl ======================================================================== dnl Headers dnl ======================================================================== AC_HEADER_STDC AC_CHECK_HEADERS(arpa/inet.h) AC_CHECK_HEADERS(asm/types.h) AC_CHECK_HEADERS(assert.h) AC_CHECK_HEADERS(auth-client.h) AC_CHECK_HEADERS(ctype.h) AC_CHECK_HEADERS(dirent.h) AC_CHECK_HEADERS(errno.h) AC_CHECK_HEADERS(fcntl.h) AC_CHECK_HEADERS(getopt.h) AC_CHECK_HEADERS(glib.h) AC_CHECK_HEADERS(grp.h) AC_CHECK_HEADERS(limits.h) AC_CHECK_HEADERS(linux/errqueue.h) AC_CHECK_HEADERS(malloc.h) AC_CHECK_HEADERS(netdb.h) AC_CHECK_HEADERS(netinet/in.h) AC_CHECK_HEADERS(netinet/ip.h) AC_CHECK_HEADERS(pam/pam_appl.h) AC_CHECK_HEADERS(pthread.h) AC_CHECK_HEADERS(pwd.h) AC_CHECK_HEADERS(security/pam_appl.h) AC_CHECK_HEADERS(sgtty.h) AC_CHECK_HEADERS(signal.h) AC_CHECK_HEADERS(stdarg.h) AC_CHECK_HEADERS(stddef.h) AC_CHECK_HEADERS(stdio.h) AC_CHECK_HEADERS(stdlib.h) AC_CHECK_HEADERS(string.h) AC_CHECK_HEADERS(strings.h) AC_CHECK_HEADERS(sys/dir.h) AC_CHECK_HEADERS(sys/ioctl.h) AC_CHECK_HEADERS(sys/param.h) AC_CHECK_HEADERS(sys/poll.h) AC_CHECK_HEADERS(sys/resource.h) AC_CHECK_HEADERS(sys/select.h) AC_CHECK_HEADERS(sys/socket.h) AC_CHECK_HEADERS(sys/sockio.h) AC_CHECK_HEADERS(sys/stat.h) AC_CHECK_HEADERS(sys/time.h) AC_CHECK_HEADERS(sys/timeb.h) AC_CHECK_HEADERS(sys/types.h) AC_CHECK_HEADERS(sys/uio.h) AC_CHECK_HEADERS(sys/un.h) AC_CHECK_HEADERS(sys/utsname.h) AC_CHECK_HEADERS(sys/wait.h) AC_CHECK_HEADERS(time.h) AC_CHECK_HEADERS(unistd.h) AC_CHECK_HEADERS(winsock.h) dnl These headers need prerequisits before the tests will pass dnl AC_CHECK_HEADERS(net/if.h) dnl AC_CHECK_HEADERS(netinet/icmp6.h) dnl AC_CHECK_HEADERS(netinet/ip6.h) dnl AC_CHECK_HEADERS(netinet/ip_icmp.h) AC_MSG_CHECKING(for special libxml2 includes) if test "x$XML2CONFIG" = "x"; then AC_MSG_ERROR(libxml2 config not found) else XML2HEAD="`$XML2CONFIG --cflags`" AC_MSG_RESULT($XML2HEAD) AC_CHECK_LIB(xml2, xmlReadMemory) AC_CHECK_LIB(xslt, xsltApplyStylesheet) fi CPPFLAGS="$CPPFLAGS $XML2HEAD" AC_CHECK_HEADERS(libxml/xpath.h) AC_CHECK_HEADERS(libxslt/xslt.h) if test "$ac_cv_header_libxml_xpath_h" != "yes"; then AC_MSG_ERROR(The libxml developement headers were not found) fi if test "$ac_cv_header_libxslt_xslt_h" != "yes"; then AC_MSG_ERROR(The libxslt developement headers were not found) fi dnl ======================================================================== dnl Structures dnl ======================================================================== AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[[#include ]]) AC_CHECK_MEMBERS([lrm_op_t.rsc_deleted],,,[[#include ]]) dnl ======================================================================== dnl Functions dnl ======================================================================== AC_CHECK_FUNCS(g_log_set_default_handler) AC_CHECK_FUNCS(getopt, AC_DEFINE(HAVE_DECL_GETOPT, 1, [Have getopt function])) AC_CHECK_FUNCS(nanosleep, AC_DEFINE(HAVE_DECL_NANOSLEEP, 1, [Have nanosleep function])) dnl ======================================================================== dnl ltdl dnl ======================================================================== AC_CHECK_LIB(ltdl, lt_dlopen, [LTDL_foo=1]) if test "x${enable_bundled_ltdl}" = "xyes"; then if test $ac_cv_lib_ltdl_lt_dlopen = yes; then AC_MSG_NOTICE([Disabling usage of installed ltdl]) fi ac_cv_lib_ltdl_lt_dlopen=no fi LIBLTDL_DIR="" if test $ac_cv_lib_ltdl_lt_dlopen != yes ; then AC_MSG_NOTICE([Installing local ltdl]) LIBLTDL_DIR=libltdl ( cd $srcdir ; $TAR -xvf libltdl.tar ) if test "$?" -ne 0; then AC_MSG_ERROR([$TAR of libltdl.tar in $srcdir failed]) fi AC_CONFIG_SUBDIRS(libltdl) else LIBS="$LIBS -lltdl" AC_MSG_NOTICE([Using installed ltdl]) INCLTDL="" LIBLTDL="" fi AC_SUBST(INCLTDL) AC_SUBST(LIBLTDL) AC_SUBST(LIBLTDL_DIR) dnl ======================================================================== dnl bzip2 dnl ======================================================================== AC_CHECK_HEADERS(bzlib.h) AC_CHECK_LIB(bz2, BZ2_bzBuffToBuffCompress) if test x$ac_cv_lib_bz2_BZ2_bzBuffToBuffCompress != xyes ; then AC_MSG_ERROR(BZ2 libraries not found) fi if test x$ac_cv_header_bzlib_h != xyes; then AC_MSG_ERROR(BZ2 Development headers not found) fi 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 ncurse takes precedence. dnl AC_CHECK_HEADERS(curses.h) AC_CHECK_HEADERS(curses/curses.h) AC_CHECK_HEADERS(ncurses.h) AC_CHECK_HEADERS(ncurses/ncurses.h) dnl Although n-library is preferred, only look for it if the n-header was found. CURSESLIBS='' if test "$ac_cv_header_ncurses_h" = "yes"; then AC_CHECK_LIB(ncurses, printw, [CURSESLIBS='-lncurses'; AC_DEFINE(HAVE_LIBNCURSES,1, have ncurses library)] ) fi if test "$ac_cv_header_ncurses_ncurses_h" = "yes"; then AC_CHECK_LIB(ncurses, printw, [CURSESLIBS='-lncurses'; AC_DEFINE(HAVE_LIBNCURSES,1, have ncurses library)] ) fi dnl Only look for non-n-library if there was no n-library. if test X"$CURSESLIBS" = X"" -a "$ac_cv_header_curses_h" = "yes"; then AC_CHECK_LIB(curses, printw, [CURSESLIBS='-lcurses'; AC_DEFINE(HAVE_LIBCURSES,1, have curses library)] ) fi dnl Only look for non-n-library if there was no n-library. if test X"$CURSESLIBS" = X"" -a "$ac_cv_header_curses_curses_h" = "yes"; then AC_CHECK_LIB(curses, printw, [CURSESLIBS='-lcurses'; AC_DEFINE(HAVE_LIBCURSES,1, have curses library)] ) fi if test "x$CURSESLIBS" != "x"; then PCMK_FEATURES="$PCMK_FEATURES ncurses" fi dnl Check for printw() prototype compatibility if test X"$CURSESLIBS" != X"" && cc_supports_flag -Wcast-qual && cc_supports_flag -Werror; then AC_MSG_CHECKING(whether printw() requires argument of "const char *") ac_save_LIBS=$LIBS LIBS="$CURSESLIBS $LIBS" ac_save_CFLAGS=$CFLAGS CFLAGS="-Wcast-qual -Werror" AC_LINK_IFELSE( [AC_LANG_PROGRAM( [ #if defined(HAVE_CURSES_H) # include #elif defined(HAVE_NCURSES_H) # include #endif ], [printw((const char *)"Test");] )], [ac_cv_compatible_printw=yes], [ac_cv_compatible_printw=no] ) LIBS=$ac_save_LIBS CFLAGS=$ac_save_CFLAGS AC_MSG_RESULT([$ac_cv_compatible_printw]) if test "$ac_cv_compatible_printw" = no; then AC_MSG_WARN([The printw() function of your ncurses or curses library is old, we will disable usage of the library. If you want to use this library anyway, please update to newer version of the library, ncurses 5.4 or later is recommended. You can get the library from http://www.gnu.org/software/ncurses/.]) AC_MSG_NOTICE([Disabling curses]) AC_DEFINE(HAVE_INCOMPATIBLE_PRINTW, 1, [Do we have incompatible printw() in curses library?]) fi fi AC_SUBST(CURSESLIBS) dnl ======================================================================== dnl Profiling and GProf dnl ======================================================================== case $SUPPORT_PROFILING in 1|yes|true) SUPPORT_PROFILING=1 dnl Enable gprof #LIBS="$LIBS -pg" #CFLAGS="$CFLAGS -pg" dnl Disable various compiler optimizations CFLAGS="$CFLAGS -fno-omit-frame-pointer" #CFLAGS="$CFLAGS -fno-inline-functions -fno-inline-functions-called-once -fno-optimize-sibling-calls" dnl CFLAGS="$CFLAGS -fno-default-inline -fno-inline" dnl Update features PCMK_FEATURES="$PCMK_FEATURES gprof" ;; *) SUPPORT_PROFILING=0;; esac AC_DEFINE_UNQUOTED(SUPPORT_PROFILING, $SUPPORT_PROFILING, Support for gprof profiling) case $SUPPORT_GCOV in 1|yes|true) SUPPORT_GCOV=1 dnl Enable gprof #LIBS="$LIBS -pg" #CFLAGS="$CFLAGS -pg" dnl Disable various compiler optimizations CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage -fno-inline" dnl Turn off optimization so code coverage tool dnl can get accurate line numbers CFLAGS=`echo "$CFLAGS" | sed -e 's/-O[0-9]*//g'` CFLAGS="$CFLAGS -O0" dnl Update features PCMK_FEATURES="$PCMK_FEATURES gcov" ;; *) SUPPORT_PROFILING=0;; esac AC_DEFINE_UNQUOTED(SUPPORT_GCOV, $SUPPORT_GCOV, Support for gcov coverage testing) dnl ======================================================================== dnl Cluster infrastructure - Heartbeat / LibQB dnl ======================================================================== dnl Compatability checks AC_CHECK_MEMBERS([struct lrm_ops.fail_rsc],,,[[#include ]]) if test x${enable_no_stack} = xyes; then SUPPORT_HEARTBEAT=no SUPPORT_CS=no fi PKG_CHECK_MODULES(libqb, libqb, HAVE_libqb=1, HAVE_libqb=0) AC_CHECK_HEADERS(qb/qbipc_common.h) AC_CHECK_LIB(qb, qb_ipcc_is_connected) AC_CHECK_FUNCS(qb_ipcc_is_connected) LIBQB_LOG=1 PCMK_FEATURES="$PCMK_FEATURES libqb-logging" if test $ac_cv_lib_qb_qb_ipcc_is_connected != yes; then AC_MSG_FAILURE(Version of IPC in libqb is not new enough) fi AC_DEFINE_UNQUOTED(LIBQB_LOGGING, $LIBQB_LOG, Use libqb for logging) AC_DEFINE_UNQUOTED(LIBQB_IPC, 0, Use libqb for IPC) LIBS="$LIBS $libqb_LIBS" AC_CHECK_HEADERS(hb_config.h) AC_CHECK_HEADERS(glue_config.h) AC_CHECK_HEADERS(agent_config.h) GLUE_HEADER=none if test "$ac_cv_header_glue_config_h" = "yes"; then GLUE_HEADER=glue_config.h elif test "$ac_cv_header_hb_config_h" = "yes"; then GLUE_HEADER=hb_config.h else AC_MSG_WARN(cluster-glue development headers were not found) fi dnl =============================================== dnl Variables needed for substitution dnl =============================================== CRM_DTD_DIRECTORY="${datadir}/pacemaker" AC_DEFINE_UNQUOTED(CRM_DTD_DIRECTORY,"$CRM_DTD_DIRECTORY", Location for the Pacemaker Relax-NG Schema) AC_SUBST(CRM_DTD_DIRECTORY) AC_DEFINE_UNQUOTED(CRM_DTD_VERSION,"$CRM_DTD_VERSION", Current version of the Pacemaker Relax-NG Schema) AC_SUBST(CRM_DTD_VERSION) CRM_DAEMON_USER=`try_extract_header_define $GLUE_HEADER HA_CCMUSER hacluster` AC_DEFINE_UNQUOTED(CRM_DAEMON_USER,"$CRM_DAEMON_USER", User to run Pacemaker daemons as) AC_SUBST(CRM_DAEMON_USER) CRM_DAEMON_GROUP=`try_extract_header_define $GLUE_HEADER HA_APIGROUP haclient` AC_DEFINE_UNQUOTED(CRM_DAEMON_GROUP,"$CRM_DAEMON_GROUP", Group to run Pacemaker daemons as) AC_SUBST(CRM_DAEMON_GROUP) CRM_STATE_DIR=${localstatedir}/run/crm AC_DEFINE_UNQUOTED(CRM_STATE_DIR,"$CRM_STATE_DIR", Where to keep state files and sockets) AC_SUBST(CRM_STATE_DIR) PE_STATE_DIR="${localstatedir}/lib/pengine" AC_DEFINE_UNQUOTED(PE_STATE_DIR,"$PE_STATE_DIR", Where to keep PEngine outputs) AC_SUBST(PE_STATE_DIR) dnl Eventually move out of the heartbeat dir tree and create compatability code CRM_CONFIG_DIR="${localstatedir}/lib/heartbeat/crm" AC_DEFINE_UNQUOTED(CRM_CONFIG_DIR,"$CRM_CONFIG_DIR", Where to keep CIB 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) HB_DAEMON_DIR=`try_extract_header_define $GLUE_HEADER HA_LIBHBDIR $libdir/heartbeat` AC_DEFINE_UNQUOTED(HB_DAEMON_DIR,"$HB_DAEMON_DIR", Location for Heartbeat expects Pacemaker daemons to be in) AC_SUBST(HB_DAEMON_DIR) dnl Needed so that the Corosync plugin can clear out the directory as Heartbeat does HA_STATE_DIR=`try_extract_header_define $GLUE_HEADER HA_VARRUNDIR ${localstatedir}/run` AC_DEFINE_UNQUOTED(HA_STATE_DIR,"$HA_STATE_DIR", Where Heartbeat keeps state files and sockets) AC_SUBST(HA_STATE_DIR) CRM_RSCTMP_DIR=`try_extract_header_define agent_config.h HA_RSCTMPDIR $HA_STATE_DIR/heartbeat/rsctmp` AC_MSG_CHECKING(Scratch dir for resource agents) AC_MSG_RESULT($CRM_RSCTMP_DIR) AC_DEFINE_UNQUOTED(CRM_RSCTMP_DIR,"$CRM_RSCTMP_DIR", Where resource agents should keep state files) AC_SUBST(CRM_RSCTMP_DIR) dnl Needed for the location of hostcache in CTS.py HA_VARLIBHBDIR=`try_extract_header_define $GLUE_HEADER HA_VARLIBHBDIR ${localstatedir}/lib/heartbeat` AC_SUBST(HA_VARLIBHBDIR) AC_DEFINE_UNQUOTED(UUID_FILE,"$localstatedir/lib/heartbeat/hb_uuid", Location of Heartbeat's UUID file) OCF_ROOT_DIR=`try_extract_header_define $GLUE_HEADER OCF_ROOT_DIR /usr/lib/ocf` if test "X$OCF_ROOT_DIR" = X; then AC_MSG_ERROR(Could not locate OCF directory) fi AC_SUBST(OCF_ROOT_DIR) OCF_RA_DIR=`try_extract_header_define $GLUE_HEADER OCF_RA_DIR $OCF_ROOT_DIR/resource.d` AC_DEFINE_UNQUOTED(OCF_RA_DIR,"$OCF_RA_DIR", Location for OCF RAs) AC_SUBST(OCF_RA_DIR) dnl Extract this value from glue_config.h once we no longer support anything else STONITH_PLUGIN_DIR="$libdir/stonith/plugins/stonith/" AC_DEFINE_UNQUOTED(STONITH_PLUGIN_DIR,"$STONITH_PLUGIN_DIR", Location for Stonith plugins) AC_SUBST(STONITH_PLUGIN_DIR) RH_STONITH_DIR="$sbindir" AC_DEFINE_UNQUOTED(RH_STONITH_DIR,"$RH_STONITH_DIR", Location for Red Hat Stonith agents) RH_STONITH_PREFIX="fence_" AC_DEFINE_UNQUOTED(RH_STONITH_PREFIX,"$RH_STONITH_PREFIX", Prefix for Red Hat Stonith agents) AC_PATH_PROGS(GIT, git false) AC_MSG_CHECKING(build version) BUILD_VERSION=$Format:%H$ if test $BUILD_VERSION != ":%H$"; then AC_MSG_RESULT(archive hash: $BUILD_VERSION) elif test -x $GIT -a -d .git; then BUILD_VERSION=`$GIT log --pretty="format:%h" -n 1` AC_MSG_RESULT(git hash: $BUILD_VERSION) else # 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(directory based hash: $BUILD_VERSION) fi AC_DEFINE_UNQUOTED(BUILD_VERSION, "$BUILD_VERSION", Build version) AC_SUBST(BUILD_VERSION) STACKS="" CLUSTERLIBS="" dnl ======================================================================== dnl Cluster stack - Heartbeat dnl ======================================================================== case $SUPPORT_HEARTBEAT in 1|yes|true) AC_CHECK_LIB(hbclient, ll_cluster_new, [SUPPORT_HEARTBEAT=1], [AC_MSG_FAILURE(Unable to support Heartbeat: client libraries not found)]);; try) AC_CHECK_LIB(hbclient, ll_cluster_new, [SUPPORT_HEARTBEAT=1], [SUPPORT_HEARTBEAT=0]);; *) SUPPORT_HEARTBEAT=0;; esac if test $SUPPORT_HEARTBEAT = 1; then STACKS="$STACKS heartbeat" dnl On Debian, AC_CHECK_LIBS fail if a library has any unresolved symbols dnl So check for all the depenancies (so they're added to LIBS) before checking for -lplumb AC_CHECK_LIB(pils, PILLoadPlugin) AC_CHECK_LIB(plumb, G_main_add_IPC_Channel) if test x"$ac_cv_lib_plumb_G_main_add_IPC_Channel" != x"yes"; then AC_MSG_FAILURE(Core Heartbeat utility libraries not found: $ac_cv_lib_plumb_G_main_add_IPC_Channel) fi AC_DEFINE_UNQUOTED(CCM_LIBRARY, "libccmclient.so.1", Library to load for ccm support) AC_DEFINE_UNQUOTED(HEARTBEAT_LIBRARY, "libhbclient.so.1", Library to load for heartbeat support) fi AM_CONDITIONAL(BUILD_HEARTBEAT_SUPPORT, test $SUPPORT_HEARTBEAT = 1) AC_DEFINE_UNQUOTED(SUPPORT_HEARTBEAT, $SUPPORT_HEARTBEAT, Support the Heartbeat messaging and membership layer) AC_SUBST(SUPPORT_HEARTBEAT) dnl ======================================================================== dnl Cluster stack - Corosync dnl ======================================================================== dnl Normalize the values case $SUPPORT_CS in 1|yes|true) missingisfatal=1;; try) missingisfatal=0;; *) SUPPORT_CS=no;; esac AC_MSG_CHECKING(for native corosync) COROSYNC_LIBS="" CS_USES_LIBQB=0 PCMK_SERVICE_ID=9 LCRSODIR="$libdir" if test $SUPPORT_CS = no; then AC_MSG_RESULT(no (disabled)) else AC_MSG_RESULT($SUPPORT_CS, with '$CSPREFIX') PKG_CHECK_MODULES(cpg, libcpg) dnl Fatal PKG_CHECK_MODULES(cfg, libcfg) dnl Fatal PKG_CHECK_MODULES(cmap, libcmap, HAVE_cmap=1, HAVE_cmap=0) PKG_CHECK_MODULES(cman, libcman, HAVE_cman=1, HAVE_cman=0) PKG_CHECK_MODULES(confdb, libconfdb, HAVE_confdb=1, HAVE_confdb=0) PKG_CHECK_MODULES(fenced, libfenced, HAVE_fenced=1, HAVE_fenced=0) PKG_CHECK_MODULES(quorum, libquorum, HAVE_quorum=1, HAVE_quorum=0) PKG_CHECK_MODULES(oldipc, libcoroipcc, HAVE_oldipc=1, HAVE_oldipc=0) if test $HAVE_oldipc = 1; then SUPPORT_CS=1 CFLAGS="$CFLAGS $oldipc_FLAGS $cpg_FLAGS $cfg_FLAGS" COROSYNC_LIBS="$COROSYNC_LIBS $oldipc_LIBS $cpg_LIBS $cfg_LIBS" elif test $HAVE_libqb = 1; then SUPPORT_CS=1 CS_USES_LIBQB=1 CFLAGS="$CFLAGS $libqb_FLAGS $cpg_FLAGS $cfg_FLAGS" COROSYNC_LIBS="$COROSYNC_LIBS $libqb_LIBS $cpg_LIBS $cfg_LIBS" AC_CHECK_LIB(corosync_common, cs_strerror) else aisreason="corosync/libqb IPC libraries not found by pkg_config" fi AC_DEFINE_UNQUOTED(HAVE_CONFDB, $HAVE_confdb, Have the old herarchial Corosync config API) AC_DEFINE_UNQUOTED(HAVE_CMAP, $HAVE_cmap, Have the new non-herarchial Corosync config API) fi if test $SUPPORT_CS = 1 -a x$HAVE_oldipc = x0 ; then dnl Support for plugins was removed about the time the IPC was dnl moved to libqb. dnl The only option now is the built-in quorum API CFLAGS="$CFLAGS $cmap_CFLAGS $quorum_CFLAGS" COROSYNC_LIBS="$COROSYNC_LIBS $cmap_LIBS $quorum_LIBS" STACKS="$STACKS corosync-native" AC_DEFINE_UNQUOTED(SUPPORT_CS_QUORUM, 1, Support the consumption of membership and quorum from corosync) fi if test $SUPPORT_CS = 1 -a x$HAVE_confdb = x1; then dnl Need confdb to support cman and the plugins LCRSODIR=`$PKGCONFIG corosync --variable=lcrsodir` STACKS="$STACKS corosync-plugin" COROSYNC_LIBS="$COROSYNC_LIBS $confdb_LIBS" if test $SUPPORT_CMAN != no; then if test $HAVE_cman = 1 -a $HAVE_fenced = 1; then SUPPORT_CMAN=1 STACKS="$STACKS cman" CFLAGS="$CFLAGS $cman_FLAGS $fenced_FLAGS" COROSYNC_LIBS="$COROSYNC_LIBS $cman_LIBS $fenced_LIBS" fi fi fi dnl Normalize SUPPORT_CS and SUPPORT_CMAN for use with #if directives if test $SUPPORT_CMAN != 1; then SUPPORT_CMAN=0 fi if test $SUPPORT_CS = 1; then CLUSTERLIBS="$CLUSTERLIBS $COROSYNC_LIBS" elif test $SUPPORT_CS != no; then SUPPORT_CS=0 if test $missingisfatal = 0; then AC_MSG_WARN(Unable to support Corosync: $aisreason) else AC_MSG_FAILURE(Unable to support Corosync: $aisreason) fi fi AC_DEFINE_UNQUOTED(SUPPORT_COROSYNC, $SUPPORT_CS, Support the Corosync messaging and membership layer) AC_DEFINE_UNQUOTED(SUPPORT_CMAN, $SUPPORT_CMAN, Support the consumption of membership and quorum from cman) AC_DEFINE_UNQUOTED(CS_USES_LIBQB, $CS_USES_LIBQB, Does corosync use libqb for its ipc) AC_DEFINE_UNQUOTED(PCMK_SERVICE_ID, $PCMK_SERVICE_ID, Corosync service number) AM_CONDITIONAL(BUILD_CS_SUPPORT, test $SUPPORT_CS = 1) AM_CONDITIONAL(BUILD_CS_PLUGIN, test $HAVE_confdb = 1) dnl confdb went away at about the same time as plugins AC_SUBST(SUPPORT_CMAN) AC_SUBST(SUPPORT_CS) dnl dnl Cluster stack - Sanity dnl if test x${enable_no_stack} = xyes; then AC_MSG_NOTICE(No cluster stack supported. Just building the Policy Engine) PCMK_FEATURES="$PCMK_FEATURES no-cluster-stack" else AC_MSG_CHECKING(for supported stacks) if test x"$STACKS" = x; then AC_MSG_FAILURE(You must support at least one cluster stack (heartbeat or corosync) ) fi AC_MSG_RESULT($STACKS) PCMK_FEATURES="$PCMK_FEATURES $STACKS" fi AC_SUBST(CLUSTERLIBS) AC_SUBST(LCRSODIR) dnl ======================================================================== dnl SNMP dnl ======================================================================== case $SUPPORT_SNMP in 1|yes|true) missingisfatal=1;; try) missingisfatal=0;; *) SUPPORT_SNMP=no;; esac SNMPLIBS="" AC_MSG_CHECKING(for snmp support) if test $SUPPORT_SNMP = no; then AC_MSG_RESULT(no (disabled)) SUPPORT_SNMP=0 else SNMPCONFIG="" AC_MSG_RESULT($SUPPORT_SNMP) AC_CHECK_HEADERS(net-snmp/net-snmp-config.h) if test "x${ac_cv_header_net_snmp_net_snmp_config_h}" != "xyes"; then SUPPORT_SNMP="no" fi if test $SUPPORT_SNMP != no; then AC_PATH_PROGS(SNMPCONFIG, net-snmp-config) if test "X${SNMPCONFIG}" = "X"; then AC_MSG_RESULT(You need the net_snmp development package to continue.) SUPPORT_SNMP=no fi fi if test $SUPPORT_SNMP != no; then AC_MSG_CHECKING(for special snmp libraries) SNMPLIBS=`$SNMPCONFIG --agent-libs` AC_MSG_RESULT($SNMPLIBS) fi if test $SUPPORT_SNMP != no; then savedLibs=$LIBS LIBS="$LIBS $SNMPLIBS" dnl On many systems libcrypto is needed when linking against libsnmp. dnl Check to see if it exists, and if so use it. dnl AC_CHECK_LIB(crypto, CRYPTO_free, CRYPTOLIB="-lcrypto",) dnl AC_SUBST(CRYPTOLIB) AC_CHECK_FUNCS(netsnmp_transport_open_client) if test $ac_cv_func_netsnmp_transport_open_client != yes; then AC_CHECK_FUNCS(netsnmp_tdomain_transport) if test $ac_cv_func_netsnmp_tdomain_transport != yes; then SUPPORT_SNMP=no else AC_DEFINE_UNQUOTED(NETSNMPV53, 1, [Use the older 5.3 version of the net-snmp API]) fi fi LIBS=$savedLibs fi if test $SUPPORT_SNMP = no; then SNMPLIBS="" SUPPORT_SNMP=0 if test $missingisfatal = 0; then AC_MSG_WARN(Unable to support SNMP) else AC_MSG_FAILURE(Unable to support SNMP) fi else SUPPORT_SNMP=1 fi fi if test $SUPPORT_SNMP = 1; then PCMK_FEATURES="$PCMK_FEATURES snmp" fi AC_SUBST(SNMPLIBS) AM_CONDITIONAL(ENABLE_SNMP, test "$SUPPORT_SNMP" = "1") AC_DEFINE_UNQUOTED(ENABLE_SNMP, $SUPPORT_SNMP, Build in support for sending SNMP traps) dnl ======================================================================== dnl ESMTP dnl ======================================================================== case $SUPPORT_ESMTP in 1|yes|true) missingisfatal=1;; try) missingisfatal=0;; *) SUPPORT_ESMTP=no;; esac ESMTPLIB="" AC_MSG_CHECKING(for esmtp support) if test $SUPPORT_ESMTP = no; then AC_MSG_RESULT(no (disabled)) SUPPORT_ESMTP=0 else ESMTPCONFIG="" AC_MSG_RESULT($SUPPORT_ESMTP) AC_CHECK_HEADERS(libesmtp.h) if test "x${ac_cv_header_libesmtp_h}" != "xyes"; then ENABLE_ESMTP="no" fi if test $SUPPORT_ESMTP != no; then AC_PATH_PROGS(ESMTPCONFIG, libesmtp-config) if test "X${ESMTPCONFIG}" = "X"; then AC_MSG_RESULT(You need the libesmtp development package to continue.) SUPPORT_ESMTP=no fi fi if test $SUPPORT_ESMTP != no; then AC_MSG_CHECKING(for special esmtp libraries) ESMTPLIBS=`$ESMTPCONFIG --libs | tr '\n' ' '` AC_MSG_RESULT($ESMTPLIBS) fi if test $SUPPORT_ESMTP = no; then SUPPORT_ESMTP=0 if test $missingisfatal = 0; then AC_MSG_WARN(Unable to support ESMTP) else AC_MSG_FAILURE(Unable to support ESMTP) fi else SUPPORT_ESMTP=1 PCMK_FEATURES="$PCMK_FEATURES libesmtp" fi fi AC_SUBST(ESMTPLIBS) AM_CONDITIONAL(ENABLE_ESMTP, test "$SUPPORT_ESMTP" = "1") AC_DEFINE_UNQUOTED(ENABLE_ESMTP, $SUPPORT_ESMTP, Build in support for sending mail notifications with ESMTP) dnl ======================================================================== dnl ACL dnl ======================================================================== case $SUPPORT_ACL in 1|yes|true) missingisfatal=1;; try) missingisfatal=0;; *) SUPPORT_ACL=no;; esac AC_MSG_CHECKING(for acl support) if test $SUPPORT_ACL = no; then AC_MSG_RESULT(no (disabled)) SUPPORT_ACL=0 else AC_MSG_RESULT($SUPPORT_ACL) AC_CHECK_MEMBERS([struct IPC_CHANNEL.farside_uid], [SUPPORT_ACL=1], [SUPPORT_ACL=0], [[#include ]]) if test $SUPPORT_ACL = 0; then if test $missingisfatal = 0; then AC_MSG_WARN(Unable to support ACL. You need to use cluster-glue >= 1.0.6) else AC_MSG_FAILURE(Unable to support ACL. You need to use cluster-glue >= 1.0.6) fi fi fi if test $SUPPORT_ACL = 1; then PCMK_FEATURES="$PCMK_FEATURES acls" fi AM_CONDITIONAL(ENABLE_ACL, test "$SUPPORT_ACL" = "1") AC_DEFINE_UNQUOTED(ENABLE_ACL, $SUPPORT_ACL, Build in support for CIB ACL) dnl ======================================================================== dnl GnuTLS dnl ======================================================================== AC_CHECK_HEADERS(gnutls/gnutls.h) AC_CHECK_HEADERS(security/pam_appl.h pam/pam_appl.h) dnl GNUTLS library: Attempt to determine by 'libgnutls-config' program. dnl If no 'libgnutls-config', try traditional autoconf means. AC_PATH_PROGS(LIBGNUTLS_CONFIG, libgnutls-config) if test -n "$LIBGNUTLS_CONFIG"; then AC_MSG_CHECKING(for gnutls header flags) GNUTLSHEAD="`$LIBGNUTLS_CONFIG --cflags`"; AC_MSG_RESULT($GNUTLSHEAD) AC_MSG_CHECKING(for gnutls library flags) GNUTLSLIBS="`$LIBGNUTLS_CONFIG --libs`"; AC_MSG_RESULT($GNUTLSLIBS) fi AC_CHECK_LIB(gnutls, gnutls_init) AC_CHECK_FUNCS(gnutls_priority_set_direct) AC_SUBST(GNUTLSHEAD) AC_SUBST(GNUTLSLIBS) dnl ======================================================================== dnl System Health dnl ======================================================================== dnl Check if servicelog development package is installed SERVICELOG=servicelog-1 SERVICELOG_EXISTS="no" AC_MSG_CHECKING(for $SERVICELOG packages) if $PKGCONFIG --exists $SERVICELOG then PKG_CHECK_MODULES([SERVICELOG], [servicelog-1]) SERVICELOG_EXISTS="yes" fi AC_MSG_RESULT($SERVICELOG_EXISTS) AM_CONDITIONAL(BUILD_SERVICELOG, test "$SERVICELOG_EXISTS" = "yes") dnl Check if OpenIMPI packages and servicelog are installed OPENIPMI="OpenIPMI OpenIPMIposix" OPENIPMI_SERVICELOG_EXISTS="no" AC_MSG_CHECKING(for $SERVICELOG $OPENIPMI packages) if $PKGCONFIG --exists $OPENIPMI $SERVICELOG then PKG_CHECK_MODULES([OPENIPMI_SERVICELOG],[OpenIPMI OpenIPMIposix]) OPENIPMI_SERVICELOG_EXISTS="yes" fi AC_MSG_RESULT($OPENIPMI_SERVICELOG_EXISTS) AM_CONDITIONAL(BUILD_OPENIPMI_SERVICELOG, test "$OPENIPMI_SERVICELOG_EXISTS" = "yes") dnl ======================================================================== dnl Compiler flags dnl ======================================================================== dnl Make sure that CFLAGS is not exported. If the user did dnl not have CFLAGS in their environment then this should have dnl no effect. However if CFLAGS was exported from the user's dnl environment, then the new CFLAGS will also be exported dnl to sub processes. CC_ERRORS="" CC_EXTRAS="" if export | fgrep " CFLAGS=" > /dev/null; then SAVED_CFLAGS="$CFLAGS" unset CFLAGS CFLAGS="$SAVED_CFLAGS" unset SAVED_CFLAGS fi if test "$GCC" != yes; then CFLAGS="$CFLAGS -g" enable_fatal_warnings=no else CFLAGS="$CFLAGS -ggdb" # We had to eliminate -Wnested-externs because of libtool changes EXTRA_FLAGS="-fgnu89-inline -fstack-protector-all -Wall -Waggregate-return -Wbad-function-cast -Wcast-align -Wdeclaration-after-statement -Wendif-labels -Wfloat-equal -Wformat=2 -Wformat-security -Wformat-nonliteral -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -Wno-long-long -Wno-strict-aliasing -Wno-unused-but-set-variable -Wpointer-arith -Wstrict-prototypes -Wunsigned-char -Wwrite-strings" # Additional warnings it might be nice to enable one day # -Wshadow # -Wunreachable-code for j in $EXTRA_FLAGS do if cc_supports_flag $j then CC_EXTRAS="$CC_EXTRAS $j" fi done dnl In lib/ais/Makefile.am there's a gcc option available as of v4.x GCC_MAJOR=`gcc -v 2>&1 | awk 'END{print $3}' | sed 's/[.].*//'` AM_CONDITIONAL(GCC_4, test "${GCC_MAJOR}" = 4) dnl System specific options case "$host_os" in *linux*|*bsd*) if test "${enable_fatal_warnings}" = "unknown"; then enable_fatal_warnings=yes fi ;; esac if test "x${enable_fatal_warnings}" != xno && cc_supports_flag -Werror ; then enable_fatal_warnings=yes else enable_fatal_warnings=no fi if test "x${enable_ansi}" = xyes && cc_supports_flag -std=iso9899:199409 ; then AC_MSG_NOTICE(Enabling ANSI Compatibility) CC_EXTRAS="$CC_EXTRAS -ansi -D_GNU_SOURCE -DANSI_ONLY" fi AC_MSG_NOTICE(Activated additional gcc flags: ${CC_EXTRAS}) fi CFLAGS="$CFLAGS $CC_EXTRAS" NON_FATAL_CFLAGS="$CFLAGS" AC_SUBST(NON_FATAL_CFLAGS) dnl dnl We reset CFLAGS to include our warnings *after* all function dnl checking goes on, so that our warning flags don't keep the dnl AC_*FUNCS() calls above from working. In particular, -Werror will dnl *always* cause us troubles if we set it before here. dnl dnl if test "x${enable_fatal_warnings}" = xyes ; then AC_MSG_NOTICE(Enabling Fatal Warnings) CFLAGS="$CFLAGS -Werror" fi AC_SUBST(CFLAGS) dnl This is useful for use in Makefiles that need to remove one specific flag CFLAGS_COPY="$CFLAGS" AC_SUBST(CFLAGS_COPY) AC_SUBST(LIBADD_DL) dnl extra flags for dynamic linking libraries AC_SUBST(LIBADD_INTL) dnl extra flags for GNU gettext stuff... AC_SUBST(LOCALE) dnl Options for cleaning up the compiler output QUIET_LIBTOOL_OPTS="" QUIET_MAKE_OPTS="" if test "x${enable_quiet}" = "xyes"; then QUIET_LIBTOOL_OPTS="--quiet" QUIET_MAKE_OPTS="--quiet" fi AC_MSG_RESULT(Supress make details: ${enable_quiet}) dnl Put the above variables to use LIBTOOL="${LIBTOOL} --tag=CC \$(QUIET_LIBTOOL_OPTS)" MAKE="${MAKE} \$(QUIET_MAKE_OPTS)" AC_SUBST(CC) AC_SUBST(MAKE) AC_SUBST(LIBTOOL) AC_SUBST(QUIET_MAKE_OPTS) AC_SUBST(QUIET_LIBTOOL_OPTS) AC_DEFINE_UNQUOTED(CRM_FEATURES, "$PCMK_FEATURES", Set of enabled features) AC_SUBST(PCMK_FEATURES) dnl The Makefiles and shell scripts we output AC_CONFIG_FILES(Makefile \ cts/Makefile \ cts/CTSvars.py \ cts/LSBDummy \ cts/benchmark/Makefile \ cts/benchmark/clubench \ cib/Makefile \ crmd/Makefile \ pengine/Makefile \ pengine/regression.core.sh \ doc/Makefile \ doc/Pacemaker_Explained/publican.cfg \ doc/Clusters_from_Scratch/publican.cfg \ include/Makefile \ include/crm/Makefile \ include/crm/common/Makefile \ include/crm/pengine/Makefile \ replace/Makefile \ lib/Makefile \ lib/pcmk.pc \ lib/pcmk-pe.pc \ lib/pcmk-cib.pc \ lib/ais/Makefile \ lib/common/Makefile \ lib/cluster/Makefile \ lib/cib/Makefile \ lib/pengine/Makefile \ lib/transition/Makefile \ lib/fencing/Makefile \ + lib/lrmd/Makefile \ + lib/services/Makefile \ lib/plugins/Makefile \ lib/plugins/lrm/Makefile \ mcp/Makefile \ mcp/pacemaker \ mcp/pacemaker.service \ fencing/Makefile \ +lrmd/Makefile \ extra/Makefile \ extra/resources/Makefile \ extra/rgmanager/Makefile \ tools/Makefile \ tools/crm_report \ tools/coverage.sh \ tools/hb2openais.sh \ tools/crm_primitive.py \ xml/Makefile \ lib/gnu/Makefile \ ) dnl Now process the entire list of files added by previous dnl calls to AC_CONFIG_FILES() AC_OUTPUT() dnl ***************** dnl Configure summary dnl ***************** AC_MSG_RESULT([]) AC_MSG_RESULT([$PACKAGE configuration:]) AC_MSG_RESULT([ Version = ${VERSION} (Build: $BUILD_VERSION)]) AC_MSG_RESULT([ Features =${PCMK_FEATURES}]) AC_MSG_RESULT([]) AC_MSG_RESULT([ Prefix = ${prefix}]) AC_MSG_RESULT([ Executables = ${sbindir}]) AC_MSG_RESULT([ Man pages = ${mandir}]) AC_MSG_RESULT([ Libraries = ${libdir}]) AC_MSG_RESULT([ Header files = ${includedir}]) AC_MSG_RESULT([ Arch-independent files = ${datadir}]) AC_MSG_RESULT([ State information = ${localstatedir}]) AC_MSG_RESULT([ System configuration = ${sysconfdir}]) AC_MSG_RESULT([ Corosync Plugins = ${LCRSODIR}]) AC_MSG_RESULT([]) AC_MSG_RESULT([ Use system LTDL = ${ac_cv_lib_ltdl_lt_dlopen}]) AC_MSG_RESULT([]) AC_MSG_RESULT([ HA group name = ${CRM_DAEMON_GROUP}]) AC_MSG_RESULT([ HA user name = ${CRM_DAEMON_USER}]) AC_MSG_RESULT([]) AC_MSG_RESULT([ CFLAGS = ${CFLAGS}]) AC_MSG_RESULT([ Libraries = ${LIBS}]) AC_MSG_RESULT([ Stack Libraries = ${CLUSTERLIBS}]) diff --git a/include/crm/lrmd.h b/include/crm/lrmd.h new file mode 100644 index 0000000000..1b6b7005e1 --- /dev/null +++ b/include/crm/lrmd.h @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2012 David Vossel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef LRMD__H +#define LRMD__H + +typedef struct lrmd_s lrmd_t; +typedef struct lrmd_key_value_s lrmd_key_value_t; +struct lrmd_key_value_t; + +/* *INDENT-OFF* */ +#define F_LRMD_OPERATION "lrmd_op" +#define F_LRMD_CLIENTNAME "lrmd_clientname" +#define F_LRMD_CLIENTID "lrmd_clientid" +#define F_LRMD_CALLBACK_TOKEN "lrmd_async_id" +#define F_LRMD_CALLID "lrmd_callid" +#define F_LRMD_CANCEL_CALLID "lrmd_cancel_callid" +#define F_LRMD_CALLOPTS "lrmd_callopt" +#define F_LRMD_CALLDATA "lrmd_calldata" +#define F_LRMD_RC "lrmd_rc" +#define F_LRMD_EXEC_RC "lrmd_exec_rc" +#define F_LRMD_OP_STATUS "lrmd_exec_op_status" +#define F_LRMD_TIMEOUT "lrmd_timeout" +#define F_LRMD_CLASS "lrmd_class" +#define F_LRMD_PROVIDER "lrmd_provider" +#define F_LRMD_TYPE "lrmd_type" +#define F_LRMD_ORIGIN "lrmd_origin" + +#define F_LRMD_RSC_RUN_TIME "lrmd_run_time" +#define F_LRMD_RSC_RCCHANGE_TIME "lrmd_rcchange_time" +#define F_LRMD_RSC_EXEC_TIME "lrmd_exec_time" +#define F_LRMD_RSC_QUEUE_TIME "lrmd_queue_time" + +#define F_LRMD_RSC_ID "lrmd_rsc_id" +#define F_LRMD_RSC_ACTION "lrmd_rsc_action" +#define F_LRMD_RSC_USERDATA_STR "lrmd_rsc_userdata_str" +#define F_LRMD_RSC_OUTPUT "lrmd_rsc_output" +#define F_LRMD_RSC_START_DELAY "lrmd_rsc_start_delay" +#define F_LRMD_RSC_INTERVAL "lrmd_rsc_interval" +#define F_LRMD_RSC_METADATA "lrmd_rsc_metadata_res" +#define F_LRMD_RSC_DELETED "lrmd_rsc_deleted" +#define F_LRMD_RSC "lrmd_rsc" + +#define LRMD_OP_RSC_CHK_REG "lrmd_rsc_check_register" +#define LRMD_OP_RSC_REG "lrmd_rsc_register" +#define LRMD_OP_RSC_EXEC "lrmd_rsc_exec" +#define LRMD_OP_RSC_CANCEL "lrmd_rsc_cancel" +#define LRMD_OP_RSC_UNREG "lrmd_rsc_unregister" +#define LRMD_OP_RSC_INFO "lrmd_rsc_info" +#define LRMD_OP_RSC_METADATA "lrmd_rsc_metadata" + +#define T_LRMD "lrmd" +#define T_LRMD_REPLY "lrmd_reply" +#define T_LRMD_NOTIFY "lrmd_notify" +/* *INDENT-ON* */ + +lrmd_t *lrmd_api_new(void); +bool lrmd_dispatch(lrmd_t *lrmd); +void lrmd_api_delete(lrmd_t * lrmd); +lrmd_key_value_t *lrmd_key_value_add(lrmd_key_value_t *kvp, + const char *key, + const char *value); + +/* *INDENT-OFF* */ +/* Reserved for future use */ +enum lrmd_call_options { + lrmd_opt_none = 0x00000000, + /* lrmd_opt_sync_call = 0x00000001, //Not implemented, patches welcome. */ +}; + +enum lrmd_errors { + lrmd_ok = 0, + lrmd_pending = -1, + lrmd_err_generic = -2, + lrmd_err_internal = -3, + lrmd_err_not_supported = -4, + lrmd_err_connection = -5, + lrmd_err_missing = -6, + lrmd_err_exists = -7, + lrmd_err_timeout = -8, + lrmd_err_ipc = -9, + lrmd_err_peer = -10, + lrmd_err_unknown_operation = -11, + lrmd_err_unknown_rsc = -12, + lrmd_err_none_available = -13, + lrmd_err_authentication = -14, + lrmd_err_signal = -15, + lrmd_err_exec_failed = -16, + lrmd_err_no_metadata = -17, + lrmd_err_stonith_connection = -18, + lrmd_err_provider_required = -19, +}; + +enum lrmd_callback_event { + lrmd_event_register, + lrmd_event_unregister, + lrmd_event_exec_complete, + lrmd_event_disconnect, +}; + +enum lrmd_exec_rc { + PCMK_EXECRA_OK = 0, + PCMK_EXECRA_UNKNOWN_ERROR = 1, + PCMK_EXECRA_INVALID_PARAM = 2, + PCMK_EXECRA_UNIMPLEMENT_FEATURE = 3, + PCMK_EXECRA_INSUFFICIENT_PRIV = 4, + PCMK_EXECRA_NOT_INSTALLED = 5, + PCMK_EXECRA_NOT_CONFIGURED = 6, + PCMK_EXECRA_NOT_RUNNING = 7, + PCMK_EXECRA_RUNNING_MASTER = 8, + PCMK_EXECRA_FAILED_MASTER = 9, + + /* For status command only */ + PCMK_EXECRA_STATUS_UNKNOWN = 14, +}; +/* *INDENT-ON* */ + +typedef struct lrmd_event_data_s { + /*! Type of event, register, unregister, call_completed... */ + enum lrmd_callback_event type; + + /*! The resource this event occurred on. */ + const char *rsc_id; + /*! The action performed, start, stop, monitor... */ + const char *op_type; + /*! The userdata string given do exec() api function */ + const char *user_data; + + /*! The client api call id associated with this event */ + int call_id; + /*! The operation's timeout period in ms. */ + int timeout; + /*! The operation's recurring interval in ms. */ + int interval; + /*! The operation's start delay value in ms. */ + int start_delay; + /*! This operation that just completed is on a deleted rsc. */ + int rsc_deleted; + + /*! The executed ra return code */ + enum lrmd_exec_rc rc; + /*! The lrmd status returned for exec_complete events */ + int op_status; + /*! stdout from resource agent operation */ + const char *output; + /*! Timestamp of when op ran */ + unsigned int t_run; + /*! Timestamp of last rc change */ + unsigned int t_rcchange; + /*! Time in length op took to execute */ + unsigned int exec_time; + /*! Time in length spent in queue */ + unsigned int queue_time; + + /* This is a GHashTable containing the + * parameters given to the operation */ + void *params; +} lrmd_event_data_t; + +lrmd_event_data_t *lrmd_copy_event(lrmd_event_data_t *event); +void lrmd_free_event(lrmd_event_data_t *event); + +typedef struct lrmd_rsc_info_s { + char *id; + char *type; + char *class; + char *provider; +} lrmd_rsc_info_t; + +lrmd_rsc_info_t *lrmd_copy_rsc_info(lrmd_rsc_info_t *rsc_info); +void lrmd_free_rsc_info(lrmd_rsc_info_t *rsc_info); + +typedef void (*lrmd_event_callback)(lrmd_event_data_t *event); + +typedef struct lrmd_list_s { + const char *val; + struct lrmd_list_s *next; +} lrmd_list_t; + +void lrmd_list_freeall(lrmd_list_t *head); + +typedef struct lrmd_api_operations_s +{ + /*! + * \brief Connect from the lrmd. + * + * \retval 0, success + * \retval negative error code on failure + */ + int (*connect) (lrmd_t *lrmd, const char *client_name, int *fd); + + /*! + * \brief Disconnect from the lrmd. + * + * \retval 0, success + * \retval negative error code on failure + */ + int (*disconnect)(lrmd_t *lrmd); + + /*! + * \brief Register a resource with the lrmd. + * + * \note Synchronous, guaranteed to occur in daemon before function returns. + * + * \retval 0, success + * \retval negative error code on failure + */ + int (*register_rsc) (lrmd_t *lrmd, + const char *rsc_id, + const char *class, + const char *provider, + const char *agent, + enum lrmd_call_options options); + + /*! + * \brief Retrieve registration info for a rsc + * + * \retval info on success + * \retval NULL on failure + */ + lrmd_rsc_info_t *(*get_rsc_info) (lrmd_t *lrmd, + const char *rsc_id, + enum lrmd_call_options options); + + /*! + * \brief Unregister a resource from the lrmd. + * + * \note All pending and recurring operations will be cancelled + * automatically. + * + * \note Synchronous, guaranteed to occur in daemon before function returns. + * + * \retval 0, success + * \retval -1, success, but operations are currently executing on the rsc which will + * return once they are completed. + * \retval negative error code on failure + * + */ + int (*unregister_rsc) (lrmd_t *lrmd, + const char *rsc_id, + enum lrmd_call_options options); + + /*! + * \brief Sets the callback to receive lrmd events on. + */ + void (*set_callback) (lrmd_t *lrmd, + lrmd_event_callback callback); + + /*! + * \brief Issue a command on a resource + * + * \note Asynchronous, command is queued in daemon on function return, but + * execution of command is not synced. + * + * \note Operations on individual resources are guaranteed to occur + * in the order the client api calls them in. + * + * \note Operations between different resources are not guaranteed + * to occur in any specific order in relation to one another + * regardless of what order the client api is called in. + * \retval call_id to track async event result on success + * \retval negative error code on failure + */ + int (*exec)(lrmd_t *lrmd, + const char *rsc_id, + const char *action, + const char *userdata, /* userdata string given back in event notification */ + int interval, /* ms */ + int timeout, /* ms */ + int start_delay, /* ms */ + enum lrmd_call_options options, + lrmd_key_value_t *params); /* ownership of params is given up to api here */ + + /*! + * \brief Cancel a recurring command. + * + * \note Synchronous, guaranteed to occur in daemon before function returns. + * + * \note The cancel is completed async from this call. + * We can be guaranteed the cancel has completed once + * the callback receives an exec_complete event with + * the lrmd_op_status signifying that the operation is + * cancelled. + * \note For each resource, cancel operations and exec operations + * are processed in the order they are received. + * It is safe to assume that for a single resource, a cancel + * will occur in the lrmd before an exec if the client's cancel + * api call occurs before the exec api call. + * + * It is not however safe to assume any operation on one resource will + * occur before an operation on another resource regardless of + * the order the client api is called in. + * + * \retval 0, cancel command sent. + * \retval negative error code on failure + */ + int (*cancel)(lrmd_t *lrmd, + const char *rsc_id, + const char *action, + int interval); + + /*! + * \brief Get the metadata documentation for a resource. + * + * \note Value is returned in output. Output must be freed when set + * + * \retval lrmd_ok success + * \retval negative error code on failure + */ + int (*get_metadata) (lrmd_t *lrmd, + const char *class, + const char *provider, + const char *agent, + char **output, + enum lrmd_call_options options); + + /*! + * \brief Retrieve a list of installed resource agents. + * + * \note if class is not provided, all known agents will be returned + * \note list must be freed using lrmd_list_freeall() + * + * \retval num items in list on success + * \retval negative error code on failure + */ + int (*list_agents)(lrmd_t *lrmd, lrmd_list_t **agents, const char *class, const char *provider); + + /*! + * \brief Retrieve a list of resource agent providers + * + * \note When the agent is provided, only the agent's provider will be returned + * \note When no agent is supplied, all providers will be returned. + * \note List must be freed using lrmd_list_freeall() + * + * \retval num items in list on success + * \retval negative error code on failure + */ + int (*list_ocf_providers)(lrmd_t *lrmd, + const char *agent, + lrmd_list_t **providers); + +} lrmd_api_operations_t; + +struct lrmd_s { + lrmd_api_operations_t *cmds; + void *private; +}; + +static inline const char * +lrmd_event_rc2str(enum lrmd_exec_rc rc) +{ + switch(rc) { + case PCMK_EXECRA_OK: + return "ok"; + case PCMK_EXECRA_UNKNOWN_ERROR: + return "unknown error"; + case PCMK_EXECRA_INVALID_PARAM: + return "invalid parameter"; + case PCMK_EXECRA_UNIMPLEMENT_FEATURE: + return "unimplemented feature"; + case PCMK_EXECRA_INSUFFICIENT_PRIV: + return "insufficient privileges"; + case PCMK_EXECRA_NOT_INSTALLED: + return "not installed"; + case PCMK_EXECRA_NOT_CONFIGURED: + return "not configured"; + case PCMK_EXECRA_NOT_RUNNING: + return "not running"; + case PCMK_EXECRA_RUNNING_MASTER: + return "master"; + case PCMK_EXECRA_FAILED_MASTER: + return "master (failed)"; + case PCMK_EXECRA_STATUS_UNKNOWN: + return "status: unknown"; + default: + break; + } + return ""; +} + +static inline const char * +lrmd_event_type2str(enum lrmd_callback_event type) +{ + switch (type) { + case lrmd_event_register: + return "register"; + case lrmd_event_unregister: + return "unregister"; + case lrmd_event_exec_complete: + return "exec_complete"; + case lrmd_event_disconnect: + return "disconnect"; + } + return "unknown"; +} + +#endif diff --git a/lib/Makefile.am b/lib/Makefile.am index 19ebf7cda8..388e21686e 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,49 +1,49 @@ # # Copyright (C) 2004-2009 Andrew Beekhof # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = pcmk.pc.in pcmk-cib.pc.in pcmk-pe.pc.in LIBS = cib pe target_LIBS = $(LIBS:%=pcmk-%.pc) target_PACKAGE = pcmk.pc all-local: $(target_LIBS) $(target_PACKAGE) install-exec-local: $(target_LIBS) $(target_PACKAGE) $(INSTALL) -d $(DESTDIR)/$(libdir)/pkgconfig $(INSTALL) -m 644 $(target_LIBS) $(target_PACKAGE) $(DESTDIR)/$(libdir)/pkgconfig uninstall-local: cd $(DESTDIR)/$(libdir)/pkgconfig && rm -f $(target_LIBS) $(target_PACKAGE) rmdir $(DESTDIR)/$(libdir)/pkgconfig 2> /dev/null || : clean-local: rm -f *.pc ## Subdirectories... -SUBDIRS = gnu common pengine transition cib fencing cluster plugins +SUBDIRS = gnu common pengine transition cib fencing services lrmd cluster plugins DIST_SUBDIRS = $(SUBDIRS) ais if BUILD_CS_PLUGIN SUBDIRS += ais endif diff --git a/lib/lrmd/Makefile.am b/lib/lrmd/Makefile.am new file mode 100644 index 0000000000..d727f80ceb --- /dev/null +++ b/lib/lrmd/Makefile.am @@ -0,0 +1,34 @@ +# Copyright (c) 2012 David Vossel +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# + +MAINTAINERCLEANFILES = Makefile.in + +INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ + -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \ + -I$(top_builddir) -I$(top_srcdir) + +lib_LTLIBRARIES = liblrmd.la + +liblrmd_la_SOURCES = lrmd_client.c +liblrmd_la_LDFLAGS = -version-info 1:0:0 +liblrmd_la_LIBADD = $(top_builddir)/lib/common/libcrmcommon.la \ + $(top_builddir)/lib/services/libcrmservice.la \ + $(top_builddir)/lib/fencing/libstonithd.la + + +AM_CFLAGS = $(INCLUDES) diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c new file mode 100644 index 0000000000..ff5e37ae62 --- /dev/null +++ b/lib/lrmd/lrmd_client.c @@ -0,0 +1,1086 @@ +/* + * Copyright (c) 2012 David Vossel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +CRM_TRACE_INIT_DATA(lrmd); + +static stonith_t *stonith_api = NULL; +typedef struct lrmd_key_value_s { + char *key; + char *value; + struct lrmd_key_value_s *next; +} lrmd_key_value_t; + +typedef struct lrmd_private_s { + int call_id; + + char *token; + crm_ipc_t *ipc; + mainloop_io_t *source; + + lrmd_event_callback callback; + +} lrmd_private_t; + +static lrmd_list_t * +lrmd_list_add(lrmd_list_t *head, const char *value) +{ + lrmd_list_t *p, *end; + + crm_malloc0(p, sizeof(lrmd_list_t)); + p->val = crm_strdup(value); + + end = head; + while (end && end->next) { + end = end->next; + } + + if (end) { + end->next = p; + } else { + head = p; + } + + return head; +} + +void +lrmd_list_freeall(lrmd_list_t *head) +{ + lrmd_list_t *p; + while (head) { + char *val = (char *) head->val; + p = head->next; + crm_free(val); + crm_free(head); + head = p; + } +} + +lrmd_key_value_t * +lrmd_key_value_add(lrmd_key_value_t *head, const char *key, const char *value) +{ + lrmd_key_value_t *p, *end; + + crm_malloc0(p, sizeof(lrmd_key_value_t)); + p->key = crm_strdup(key); + p->value = crm_strdup(value); + + end = head; + while (end && end->next) { + end = end->next; + } + + if (end) { + end->next = p; + } else { + head = p; + } + + return head; +} + +static void +lrmd_key_value_freeall(lrmd_key_value_t * head) +{ + lrmd_key_value_t *p; + while (head) { + p = head->next; + crm_free(head->key); + crm_free(head->value); + crm_free(head); + head = p; + } +} + +static void +dup_attr(gpointer key, gpointer value, gpointer user_data) +{ + g_hash_table_replace(user_data, crm_strdup(key), crm_strdup(value)); +} + +lrmd_event_data_t *lrmd_copy_event(lrmd_event_data_t *event) +{ + lrmd_event_data_t *copy = NULL; + + crm_malloc0(copy, sizeof(lrmd_event_data_t)); + + /* This will get all the int values. + * we just have to be careful not to leave any + * dangling pointers to strings. */ + memcpy(copy, event, sizeof(lrmd_event_data_t)); + + copy->rsc_id = event->rsc_id ? crm_strdup(event->rsc_id) : NULL; + copy->op_type = event->op_type ? crm_strdup(event->op_type) : NULL; + copy->user_data = event->user_data ? crm_strdup(event->user_data) : NULL; + copy->output = event->output ? crm_strdup(event->output) : NULL; + + if (event->params) { + copy->params = g_hash_table_new_full(crm_str_hash, + g_str_equal, + g_hash_destroy_str, + g_hash_destroy_str); + + if (copy->params != NULL) { + g_hash_table_foreach(event->params, dup_attr, copy->params); + } + } + + return copy; +} + +void lrmd_free_event(lrmd_event_data_t *event) +{ + if (!event) { + return; + } + + /* crm_free gives me grief if i try to cast */ + free((char *) event->rsc_id); + free((char *) event->op_type); + free((char *) event->user_data); + free((char *) event->output); + if (event->params) { + g_hash_table_destroy(event->params); + } + crm_free(event); +} + +static int +lrmd_dispatch_internal(const char *buffer, ssize_t length, gpointer userdata) +{ + const char *type; + lrmd_t *lrmd = userdata; + lrmd_private_t *native = lrmd->private; + lrmd_event_data_t event = { 0, }; + xmlNode *msg; + + if (!native->callback) { + /* no callback set */ + return 1; + } + + msg = string2xml(buffer); + type = crm_element_value(msg, F_LRMD_OPERATION); + crm_element_value_int(msg, F_LRMD_CALLID, &event.call_id); + event.rsc_id = crm_element_value(msg, F_LRMD_RSC_ID); + + if (crm_str_eq(type, LRMD_OP_RSC_REG, TRUE)) { + event.type = lrmd_event_register; + } else if (crm_str_eq(type, LRMD_OP_RSC_UNREG, TRUE)) { + event.type = lrmd_event_unregister; + } else if (crm_str_eq(type, LRMD_OP_RSC_EXEC, TRUE)) { + crm_element_value_int(msg, F_LRMD_TIMEOUT, &event.timeout); + crm_element_value_int(msg, F_LRMD_RSC_INTERVAL, &event.interval); + crm_element_value_int(msg, F_LRMD_RSC_START_DELAY, &event.start_delay); + crm_element_value_int(msg, F_LRMD_EXEC_RC, (int *) &event.rc); + crm_element_value_int(msg, F_LRMD_OP_STATUS, &event.op_status); + crm_element_value_int(msg, F_LRMD_RSC_DELETED, &event.rsc_deleted); + + crm_element_value_int(msg, F_LRMD_RSC_RUN_TIME, (int *) &event.t_run); + crm_element_value_int(msg, F_LRMD_RSC_RCCHANGE_TIME, (int *) &event.t_rcchange); + crm_element_value_int(msg, F_LRMD_RSC_EXEC_TIME, (int *) &event.exec_time); + crm_element_value_int(msg, F_LRMD_RSC_QUEUE_TIME, (int *) &event.queue_time); + + event.op_type = crm_element_value(msg, F_LRMD_RSC_ACTION); + event.user_data = crm_element_value(msg, F_LRMD_RSC_USERDATA_STR); + event.output = crm_element_value(msg, F_LRMD_RSC_OUTPUT); + event.type = lrmd_event_exec_complete; + + event.params = xml2list(msg); + } + + native->callback(&event); + + if (event.params) { + g_hash_table_destroy(event.params); + } + free_xml(msg); + return 1; +} + +/* Not used with mainloop */ +bool +lrmd_dispatch(lrmd_t *lrmd) +{ + gboolean stay_connected = TRUE; + lrmd_private_t *private = NULL; + + CRM_ASSERT(lrmd != NULL); + private = lrmd->private; + + while(crm_ipc_ready(private->ipc)) { + if (crm_ipc_read(private->ipc) > 0) { + const char *msg = crm_ipc_buffer(private->ipc); + lrmd_dispatch_internal(msg, strlen(msg), lrmd); + } + if (crm_ipc_connected(private->ipc) == FALSE) { + crm_err("Connection closed"); + stay_connected = FALSE; + } + } + + return stay_connected; +} + +static xmlNode * +lrmd_create_op(int call_id, + const char *token, + const char *op, xmlNode *data, + enum lrmd_call_options options) +{ + xmlNode *op_msg = create_xml_node(NULL, "lrmd_command"); + + CRM_CHECK(op_msg != NULL, return NULL); + CRM_CHECK(token != NULL, return NULL); + + crm_xml_add(op_msg, F_XML_TAGNAME, "lrmd_command"); + + crm_xml_add(op_msg, F_TYPE, T_LRMD); + crm_xml_add(op_msg, F_LRMD_CALLBACK_TOKEN, token); + crm_xml_add(op_msg, F_LRMD_OPERATION, op); + crm_xml_add_int(op_msg, F_LRMD_CALLID, call_id); + crm_trace("Sending call options: %.8lx, %d", (long)options, options); + crm_xml_add_int(op_msg, F_LRMD_CALLOPTS, options); + + if (data != NULL) { + add_message_xml(op_msg, F_LRMD_CALLDATA, data); + } + + return op_msg; +} + +static void +lrmd_connection_destroy(gpointer userdata) +{ + lrmd_t *lrmd = userdata; + lrmd_private_t *native = lrmd->private; + crm_info("connection destroyed"); + if (native->callback) { + lrmd_event_data_t event = { 0, }; + event.type = lrmd_event_disconnect; + native->callback(&event); + } +} + +static int +lrmd_send_command(lrmd_t * lrmd, + const char *op, + xmlNode *data, + xmlNode **output_data, + int timeout, /* ms. defaults to 1000 if set to 0 */ + enum lrmd_call_options options) +{ + int rc = lrmd_ok; + int reply_id = -1; + lrmd_private_t *native = lrmd->private; + xmlNode *op_msg = NULL; + xmlNode *op_reply = NULL; + + if (!native->ipc) { + return lrmd_err_connection; + } + + if (op == NULL) { + crm_err("No operation specified"); + return lrmd_err_missing; + } + + native->call_id++; + if (native->call_id < 1) { + native->call_id = 1; + } + + CRM_CHECK(native->token != NULL,;); + + op_msg = lrmd_create_op(native->call_id, + native->token, + op, + data, + options); + + if (op_msg == NULL) { + return lrmd_err_missing; + } + + crm_xml_add_int(op_msg, F_LRMD_TIMEOUT, timeout); + + rc = crm_ipc_send(native->ipc, op_msg, &op_reply, timeout); + free_xml(op_msg); + + if (rc < 0) { + crm_perror(LOG_ERR, "Couldn't perform %s operation (timeout=%d): %d", op, timeout, rc); + rc = lrmd_err_ipc; + goto done; + } + + rc = lrmd_ok; + crm_element_value_int(op_reply, F_LRMD_CALLID, &reply_id); + if (reply_id == native->call_id) { + crm_trace("reply received"); + if (crm_element_value_int(op_reply, F_LRMD_RC, &rc) != 0) { + rc = lrmd_err_peer; + goto done; + } + + if (output_data) { + *output_data = op_reply; + op_reply = NULL; /* Prevent subsequent free */ + } + + } else if (reply_id <= 0) { + crm_err("Recieved bad reply: No id set"); + crm_log_xml_err(op_reply, "Bad reply"); + rc = lrmd_err_peer; + } else { + crm_err("Recieved bad reply: %d (wanted %d)", reply_id, native->call_id); + crm_log_xml_err(op_reply, "Old reply"); + rc = lrmd_err_peer; + } + + crm_log_xml_trace(op_reply, "Reply"); + +done: + if (crm_ipc_connected(native->ipc) == FALSE) { + crm_err("LRMD disconnected"); + } + + free_xml(op_reply); + return rc; +} + +static int +lrmd_api_connect(lrmd_t * lrmd, const char *name, int *fd) +{ + int rc = lrmd_ok; + lrmd_private_t *native = lrmd->private; + static struct ipc_client_callbacks lrmd_callbacks = { + .dispatch = lrmd_dispatch_internal, + .destroy = lrmd_connection_destroy + }; + + crm_info("Connecting to lrmd"); + + if (fd) { + /* No mainloop */ + native->ipc = crm_ipc_new("lrmd", 0); + if (native->ipc && crm_ipc_connect(native->ipc)) { + *fd = crm_ipc_get_fd(native->ipc); + } else if (native->ipc) { + rc = lrmd_err_connection; + } + } else { + native->source = mainloop_add_ipc_client("lrmd", 0, lrmd, &lrmd_callbacks); + native->ipc = mainloop_get_ipc_client(native->source); + } + + if (native->ipc == NULL) { + crm_debug("Could not connect to the LRMD API"); + rc = lrmd_err_connection; + } + + if (!rc) { + xmlNode *reply = NULL; + xmlNode *hello = create_xml_node(NULL, "lrmd_command"); + + crm_xml_add(hello, F_TYPE, T_LRMD); + crm_xml_add(hello, F_LRMD_OPERATION, CRM_OP_REGISTER); + crm_xml_add(hello, F_LRMD_CLIENTNAME, name); + + rc = crm_ipc_send(native->ipc, hello, &reply, -1); + + if (rc < 0) { + crm_perror(LOG_DEBUG, "Couldn't complete registration with the lrmd API: %d", rc); + rc = lrmd_err_ipc; + } else if(reply == NULL) { + crm_err("Did not receive registration reply"); + rc = lrmd_err_internal; + } else { + const char *msg_type = crm_element_value(reply, F_LRMD_OPERATION); + const char *tmp_ticket = crm_element_value(reply, F_LRMD_CLIENTID); + + if (safe_str_neq(msg_type, CRM_OP_REGISTER)) { + crm_err("Invalid registration message: %s", msg_type); + crm_log_xml_err(reply, "Bad reply"); + rc = lrmd_err_internal; + } else if (tmp_ticket == NULL) { + crm_err("No registration token provided"); + crm_log_xml_err(reply, "Bad reply"); + rc = lrmd_err_internal; + } else { + crm_trace("Obtained registration token: %s", tmp_ticket); + native->token = crm_strdup(tmp_ticket); + rc = lrmd_ok; + } + } + + free_xml(reply); + free_xml(hello); + } + + return rc; +} + +static int +lrmd_api_disconnect(lrmd_t * lrmd) +{ + lrmd_private_t *native = lrmd->private; + + crm_info("Disconnecting from lrmd service"); + + if (native->source) { + mainloop_del_ipc_client(native->source); + native->source = NULL; + native->ipc = NULL; + } else if (native->ipc) { + crm_ipc_close(native->ipc); + crm_ipc_destroy(native->ipc); + native->source = NULL; + native->ipc = NULL; + } + + crm_free(native->token); + return 0; +} + +static int +lrmd_api_register_rsc(lrmd_t *lrmd, + const char *rsc_id, + const char *class, + const char *provider, + const char *type, + enum lrmd_call_options options) +{ + int rc = lrmd_ok; + xmlNode *data = NULL; + + if (!class || !type || !rsc_id) { + return lrmd_err_missing; + } + if (safe_str_eq(class, "ocf") && !provider) { + return lrmd_err_provider_required; + } + + data = create_xml_node(NULL, F_LRMD_RSC); + + crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__); + crm_xml_add(data, F_LRMD_RSC_ID, rsc_id); + crm_xml_add(data, F_LRMD_CLASS, class); + crm_xml_add(data, F_LRMD_PROVIDER, provider); + crm_xml_add(data, F_LRMD_TYPE, type); + rc = lrmd_send_command(lrmd, LRMD_OP_RSC_REG, data, NULL, 0, options); + free_xml(data); + + return rc; +} + +static int +lrmd_api_unregister_rsc(lrmd_t *lrmd, + const char *rsc_id, + enum lrmd_call_options options) +{ + int rc = lrmd_ok; + xmlNode *data = create_xml_node(NULL, F_LRMD_RSC); + + crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__); + crm_xml_add(data, F_LRMD_RSC_ID, rsc_id); + rc = lrmd_send_command(lrmd, LRMD_OP_RSC_UNREG, data, NULL, 0, options); + free_xml(data); + + return rc; +} + +lrmd_rsc_info_t *lrmd_copy_rsc_info(lrmd_rsc_info_t *rsc_info) +{ + lrmd_rsc_info_t *copy = NULL; + + crm_malloc0(copy, sizeof(lrmd_rsc_info_t)); + + copy->id = crm_strdup(rsc_info->id); + copy->type = crm_strdup(rsc_info->type); + copy->class = crm_strdup(rsc_info->class); + if (rsc_info->provider) { + copy->provider = crm_strdup(rsc_info->provider); + } + + return copy; +} + +void lrmd_free_rsc_info(lrmd_rsc_info_t *rsc_info) +{ + if (!rsc_info) { + return; + } + crm_free(rsc_info->id); + crm_free(rsc_info->type); + crm_free(rsc_info->class); + crm_free(rsc_info->provider); + crm_free(rsc_info); +} + +static lrmd_rsc_info_t * +lrmd_api_get_rsc_info(lrmd_t *lrmd, + const char *rsc_id, + enum lrmd_call_options options) +{ + int rc = lrmd_ok; + lrmd_rsc_info_t *rsc_info = NULL; + xmlNode *data = create_xml_node(NULL, F_LRMD_RSC); + xmlNode *output = NULL; + const char *class = NULL; + const char *provider = NULL; + const char *type = NULL; + + crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__); + crm_xml_add(data, F_LRMD_RSC_ID, rsc_id); + rc = lrmd_send_command(lrmd, LRMD_OP_RSC_INFO, data, &output, 0, options); + free_xml(data); + + class = crm_element_value(output, F_LRMD_CLASS); + provider = crm_element_value(output, F_LRMD_PROVIDER); + type = crm_element_value(output, F_LRMD_TYPE); + + if (!output) { + return NULL; + } else if (!class || !type) { + free_xml(output); + return NULL; + } else if (safe_str_eq(class, "ocf") && !provider) { + free_xml(output); + return NULL; + } + + crm_malloc0(rsc_info, sizeof(lrmd_rsc_info_t)); + rsc_info->id = crm_strdup(rsc_id); + rsc_info->class = crm_strdup(class); + if (provider) { + rsc_info->provider = crm_strdup(provider); + } + rsc_info->type = crm_strdup(type); + + free_xml(output); + return rsc_info; +} + +static void +lrmd_api_set_callback(lrmd_t *lrmd, lrmd_event_callback callback) +{ + lrmd_private_t *native = lrmd->private; + + native->callback = callback; +} + +static int +stonith_get_metadata(const char *provider, const char *type, char **output) +{ + int rc = lrmd_ok; + stonith_api->cmds->metadata(stonith_api, st_opt_sync_call, type, provider, output, 0); + if (*output == NULL) { + rc = lrmd_err_no_metadata; + } + return rc; +} + +static int +lsb_get_metadata(const char *type, char **output) +{ + +#define lsb_metadata_template \ +"\n"\ +"\n"\ +"\n"\ +" 1.0\n"\ +" \n"\ +" %s"\ +" \n"\ +" %s\n"\ +" \n"\ +" \n"\ +" \n"\ +" \n"\ +" \n"\ +" \n"\ +" \n"\ +" \n"\ +" \n"\ +" \n"\ +" \n"\ +" \n"\ +" %s\n"\ +" %s\n"\ +" %s\n"\ +" %s\n"\ +" %s\n"\ +" %s\n"\ +" %s\n"\ +" \n"\ +"\n" + +#define LSB_INITSCRIPT_INFOBEGIN_TAG "### BEGIN INIT INFO" +#define LSB_INITSCRIPT_INFOEND_TAG "### END INIT INFO" +#define PROVIDES "# Provides:" +#define REQ_START "# Required-Start:" +#define REQ_STOP "# Required-Stop:" +#define SHLD_START "# Should-Start:" +#define SHLD_STOP "# Should-Stop:" +#define DFLT_START "# Default-Start:" +#define DFLT_STOP "# Default-Stop:" +#define SHORT_DSCR "# Short-Description:" +#define DESCRIPTION "# Description:" + +#define lsb_meta_helper_free_value(m) \ + if ((m) != NULL) { \ + xmlFree(m); \ + (m) = NULL; \ + } + +#define lsb_meta_helper_get_value(buffer, ptr, keyword) \ + if (!ptr && !strncasecmp(buffer, keyword, strlen(keyword))) { \ + (ptr) = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST buffer+strlen(keyword)); \ + continue; \ + } + + char ra_pathname[PATH_MAX] = { 0, }; + FILE *fp; + GString *meta_data = NULL; + char buffer[1024]; + char *provides = NULL; + char *req_start = NULL; + char *req_stop = NULL; + char *shld_start = NULL; + char *shld_stop = NULL; + char *dflt_start = NULL; + char *dflt_stop = NULL; + char *s_dscrpt = NULL; + char *xml_l_dscrpt = NULL; + GString *l_dscrpt = NULL; + + snprintf(ra_pathname, sizeof(ra_pathname), "%s%s%s", + type[0] == '/' ? "" : LSB_ROOT_DIR, + type[0] == '/' ? "" : "/", + type); + + if (!(fp = fopen(ra_pathname, "r"))) { + return lrmd_err_no_metadata; + } + + /* Enter into the lsb-compliant comment block */ + while (fgets(buffer, sizeof(buffer), fp)) { + /* Now suppose each of the following eight arguments contain only one line */ + lsb_meta_helper_get_value(buffer, provides, PROVIDES) + lsb_meta_helper_get_value(buffer, req_start, REQ_START) + lsb_meta_helper_get_value(buffer, req_stop, REQ_STOP) + lsb_meta_helper_get_value(buffer, shld_start, SHLD_START) + lsb_meta_helper_get_value(buffer, shld_stop, SHLD_STOP) + lsb_meta_helper_get_value(buffer, dflt_start, DFLT_START) + lsb_meta_helper_get_value(buffer, dflt_stop, DFLT_STOP) + lsb_meta_helper_get_value(buffer, s_dscrpt, SHORT_DSCR) + + /* Long description may cross multiple lines */ + if ((l_dscrpt == NULL) && (0 == strncasecmp(buffer, DESCRIPTION, strlen(DESCRIPTION))) ) { + l_dscrpt = g_string_new(buffer+strlen(DESCRIPTION)); + /* Between # and keyword, more than one space, or a tab character, + * indicates the continuation line. Extracted from LSB init script standard */ + while (fgets(buffer, sizeof(buffer), fp)) { + if (!strncmp(buffer, "# ", 3) || !strncmp(buffer, "#\t", 2)) { + buffer[0] = ' '; + l_dscrpt = g_string_append(l_dscrpt, buffer); + } else { + fputs(buffer, fp); + break; /* Long description ends */ + } + } + continue; + } + if (l_dscrpt) { + xml_l_dscrpt = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST (l_dscrpt->str)); + } + if (!strncasecmp(buffer, LSB_INITSCRIPT_INFOEND_TAG, strlen(LSB_INITSCRIPT_INFOEND_TAG))) { + /* Get to the out border of LSB comment block */ + break; + } + if (buffer[0] != '#') { + break; /* Out of comment block in the beginning */ + } + } + fclose(fp); + + meta_data = g_string_new(""); + g_string_sprintf(meta_data, lsb_metadata_template, type + , (xml_l_dscrpt == NULL)? type : xml_l_dscrpt + , (s_dscrpt == NULL)? type : s_dscrpt + , (provides == NULL)? "" : provides + , (req_start == NULL)? "" : req_start + , (req_stop == NULL)? "" : req_stop + , (shld_start == NULL)? "" : shld_start + , (shld_stop == NULL)? "" : shld_stop + , (dflt_start == NULL)? "" : dflt_start + , (dflt_stop == NULL)? "" : dflt_stop); + + lsb_meta_helper_free_value(xml_l_dscrpt); + lsb_meta_helper_free_value(s_dscrpt); + lsb_meta_helper_free_value(provides); + lsb_meta_helper_free_value(req_start); + lsb_meta_helper_free_value(req_stop); + lsb_meta_helper_free_value(shld_start); + lsb_meta_helper_free_value(shld_stop); + lsb_meta_helper_free_value(dflt_start); + lsb_meta_helper_free_value(dflt_stop); + + if (l_dscrpt) { + g_string_free(l_dscrpt, TRUE); + } + + *output = crm_strdup(meta_data->str); + g_string_free(meta_data, TRUE); + + return lrmd_ok; +} + +static int +ocf_get_metadata(const char *provider, const char *type, char **output) +{ + svc_action_t *action = action = resources_action_create("get_meta", + "ocf", + provider, + type, + "meta-data", + 0, + 5000, + NULL); + + if (!(services_action_sync(action))) { + crm_err("Failed to retrieve meta-data for ocf:%s:%s", provider, type); + services_action_free(action); + return lrmd_err_no_metadata; + } + + if (!action->stdout_data) { + crm_err("Failed to retrieve meta-data for ocf:%s:%s", provider, type); + services_action_free(action); + return lrmd_err_no_metadata; + } + + *output = crm_strdup(action->stdout_data); + services_action_free(action); + + return lrmd_ok; +} + +static int +lrmd_api_get_metadata(lrmd_t *lrmd, + const char *class, + const char *provider, + const char *type, + char **output, + enum lrmd_call_options options) +{ + + if (!class || !type) { + return lrmd_err_missing; + } + + if (safe_str_eq(class, "stonith")) { + return stonith_get_metadata(provider, type, output); + } else if (safe_str_eq(class, "ocf")) { + return ocf_get_metadata(provider, type, output); + } else if (safe_str_eq(class, "lsb")) { + return lsb_get_metadata(type, output); + } + + return lrmd_err_no_metadata; +} + +static int +lrmd_api_exec(lrmd_t *lrmd, + const char *rsc_id, + const char *action, + const char *userdata, + int interval, /* ms */ + int timeout, /* ms */ + int start_delay, /* ms */ + enum lrmd_call_options options, + lrmd_key_value_t *params) +{ + int rc = lrmd_ok; + xmlNode *data = create_xml_node(NULL, F_LRMD_RSC); + xmlNode *args = create_xml_node(data, XML_TAG_ATTRS); + + crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__); + crm_xml_add(data, F_LRMD_RSC_ID, rsc_id); + crm_xml_add(data, F_LRMD_RSC_ACTION, action); + crm_xml_add(data, F_LRMD_RSC_USERDATA_STR, userdata); + crm_xml_add_int(data, F_LRMD_RSC_INTERVAL, interval); + crm_xml_add_int(data, F_LRMD_TIMEOUT, timeout); + crm_xml_add_int(data, F_LRMD_RSC_START_DELAY, start_delay); + + for (; params; params = params->next) { + hash2field((gpointer) params->key, (gpointer) params->value, args); + } + + rc = lrmd_send_command(lrmd, LRMD_OP_RSC_EXEC, data, NULL, timeout, options); + free_xml(data); + + lrmd_key_value_freeall(params); + return rc; +} + +static int +lrmd_api_cancel(lrmd_t *lrmd, + const char *rsc_id, + const char *action, + int interval) +{ + int rc = lrmd_ok; + xmlNode *data = create_xml_node(NULL, F_LRMD_RSC); + + crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__); + crm_xml_add(data, F_LRMD_RSC_ACTION, action); + crm_xml_add(data, F_LRMD_RSC_ID, rsc_id); + crm_xml_add_int(data, F_LRMD_RSC_INTERVAL, interval); + rc = lrmd_send_command(lrmd, LRMD_OP_RSC_CANCEL, data, NULL, 0, 0); + free_xml(data); + return rc; +} + +static int +list_stonith_agents(lrmd_list_t **resources) +{ + int rc = 0; + stonith_key_value_t *stonith_resources = NULL; + stonith_key_value_t *dIter = NULL; + + stonith_api->cmds->list(stonith_api, st_opt_sync_call, NULL, &stonith_resources, 0); + + for (dIter = stonith_resources; dIter; dIter = dIter->next) { + rc++; + *resources = lrmd_list_add(*resources, dIter->value); + } + + stonith_key_value_freeall(stonith_resources, 1, 0); + return rc; +} + +static int +list_lsb_agents(lrmd_list_t **resources) +{ + int rc = 0; + GListPtr gIter = NULL; + GList *agents = NULL; + + agents = resources_list_agents("lsb", NULL); + for (gIter = agents; gIter != NULL; gIter = gIter->next) { + *resources = lrmd_list_add(*resources, (const char *) gIter->data); + rc++; + } + g_list_free_full(agents, free); + return rc; +} + +static int +list_service_agents(lrmd_list_t **resources) +{ + int rc = 0; + GListPtr gIter = NULL; + GList *agents = NULL; + + agents = resources_list_agents("service", NULL); + for (gIter = agents; gIter != NULL; gIter = gIter->next) { + *resources = lrmd_list_add(*resources, (const char *) gIter->data); + rc++; + } + g_list_free_full(agents, free); + return rc; +} + +static int +list_systemd_agents(lrmd_list_t **resources) +{ + int rc = 0; + GListPtr gIter = NULL; + GList *agents = NULL; + + agents = resources_list_agents("systemd", NULL); + for (gIter = agents; gIter != NULL; gIter = gIter->next) { + *resources = lrmd_list_add(*resources, (const char *) gIter->data); + rc++; + } + g_list_free_full(agents, free); + return rc; +} + +static int +list_ocf_agents(lrmd_list_t **resources, const char *list_provider) +{ + int rc = 0; + char *provider = NULL; + GList *ocf_providers = NULL; + GList *agents = NULL; + GListPtr gIter = NULL; + GListPtr gIter2 = NULL; + + ocf_providers = resources_list_providers("ocf"); + + for (gIter = ocf_providers; gIter != NULL; gIter = gIter->next) { + provider = gIter->data; + + if (list_provider && !safe_str_eq(list_provider, provider)) { + continue; + } + agents = resources_list_agents("ocf", provider); + for (gIter2 = agents; gIter2 != NULL; gIter2 = gIter2->next) { + *resources = lrmd_list_add(*resources, (const char *) gIter2->data); + rc++; + } + g_list_free_full(agents, free); + } + + g_list_free_full(ocf_providers, free); + return rc; +} + +static int +lrmd_api_list_agents(lrmd_t *lrmd, lrmd_list_t **resources, const char *class, const char *provider) +{ + int rc = lrmd_ok; + + if (safe_str_eq(class, "ocf")) { + rc += list_ocf_agents(resources, provider); + } else if (safe_str_eq(class, "lsb")) { + rc += list_lsb_agents(resources); + } else if (safe_str_eq(class, "systemd")) { + rc += list_systemd_agents(resources); + } else if (safe_str_eq(class, "service")) { + rc += list_service_agents(resources); + } else if (safe_str_eq(class, "stonith")) { + rc += list_stonith_agents(resources); + } else if (!class) { + rc += list_ocf_agents(resources, provider); + rc += list_systemd_agents(resources); + rc += list_lsb_agents(resources); + rc += list_stonith_agents(resources); + } else { + crm_err("Unknown class %s", class); + rc = lrmd_err_generic; + } + + return rc; +} + +static int +does_provider_have_agent(const char *agent, const char *provider, const char *class) +{ + int found = 0; + GList *agents = NULL; + GListPtr gIter2 = NULL; + agents = resources_list_agents(class, provider); + for (gIter2 = agents; gIter2 != NULL; gIter2 = gIter2->next) { + if (safe_str_eq(agent, gIter2->data)) { + found = 1; + } + } + g_list_free_full(agents, free); + + return found; +} + +static int +lrmd_api_list_ocf_providers(lrmd_t *lrmd, const char *agent, lrmd_list_t **providers) +{ + int rc = lrmd_ok; + char *provider = NULL; + GList *ocf_providers = NULL; + GListPtr gIter = NULL; + + ocf_providers = resources_list_providers("ocf"); + + for (gIter = ocf_providers; gIter != NULL; gIter = gIter->next) { + provider = gIter->data; + if (!agent || does_provider_have_agent(agent, provider, "ocf")) { + *providers = lrmd_list_add(*providers, (const char *) gIter->data); + rc++; + } + } + + g_list_free_full(ocf_providers, free); + + return rc; +} + +lrmd_t * +lrmd_api_new(void) +{ + lrmd_t *new_lrmd = NULL; + lrmd_private_t *pvt = NULL; + + crm_malloc0(new_lrmd, sizeof(lrmd_t)); + crm_malloc0(pvt, sizeof(lrmd_private_t)); + crm_malloc0(new_lrmd->cmds, sizeof(lrmd_api_operations_t)); + + new_lrmd->private = pvt; + + new_lrmd->cmds->connect = lrmd_api_connect; + new_lrmd->cmds->disconnect = lrmd_api_disconnect; + new_lrmd->cmds->register_rsc = lrmd_api_register_rsc; + new_lrmd->cmds->unregister_rsc = lrmd_api_unregister_rsc; + new_lrmd->cmds->get_rsc_info = lrmd_api_get_rsc_info; + new_lrmd->cmds->set_callback = lrmd_api_set_callback; + new_lrmd->cmds->get_metadata = lrmd_api_get_metadata; + new_lrmd->cmds->exec = lrmd_api_exec; + new_lrmd->cmds->cancel = lrmd_api_cancel; + new_lrmd->cmds->list_agents = lrmd_api_list_agents; + new_lrmd->cmds->list_ocf_providers = lrmd_api_list_ocf_providers; + + if (!stonith_api) { + stonith_api = stonith_api_new(); + } + + return new_lrmd; +} + +void +lrmd_api_delete(lrmd_t * lrmd) +{ + lrmd->cmds->disconnect(lrmd); /* no-op if already disconnected */ + crm_free(lrmd->cmds); + crm_free(lrmd->private); + crm_free(lrmd); +} diff --git a/lrmd/Makefile.am b/lrmd/Makefile.am new file mode 100644 index 0000000000..0be956e459 --- /dev/null +++ b/lrmd/Makefile.am @@ -0,0 +1,35 @@ +# Copyright (c) 2012 David Vossel +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +MAINTAINERCLEANFILES = Makefile.in + +lrmdlibdir = $(CRM_DAEMON_DIR) + +## binary progs +lrmdlib_PROGRAMS = lrmd +noinst_PROGRAMS = lrmd_test + +lrmd_SOURCES = main.c lrmd.c +lrmd_LDADD = $(top_builddir)/lib/common/libcrmcommon.la \ + $(top_builddir)/lib/services/libcrmservice.la \ + $(top_builddir)/lib/lrmd/liblrmd.la \ + $(top_builddir)/lib/fencing/libstonithd.la + +lrmd_test_SOURCES = test.c +lrmd_test_LDADD = $(top_builddir)/lib/common/libcrmcommon.la \ + $(top_builddir)/lib/lrmd/liblrmd.la \ + $(top_builddir)/lib/services/libcrmservice.la diff --git a/lrmd/lrmd.c b/lrmd/lrmd.c new file mode 100644 index 0000000000..2934c2e427 --- /dev/null +++ b/lrmd/lrmd.c @@ -0,0 +1,973 @@ +/* + * Copyright (c) 2012 David Vossel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_SYS_TIMEB_H +#include +#endif + +GHashTable *rsc_list = NULL; +GHashTable *client_list = NULL; + +typedef struct lrmd_cmd_s { + int timeout; + int interval; + int start_delay; + + int call_id; + int exec_rc; + int lrmd_op_status; + + /* Timer ids, must be removed on cmd destruction. */ + int delay_id; + int stonith_recurring_id; + + int rsc_deleted; + + char *origin; + char *rsc_id; + char *action; + char *output; + char *userdata_str; + +#ifdef HAVE_SYS_TIMEB_H + /* Timestamp of when op ran */ + struct timeb t_run; + /* Timestamp of when op was queued */ + struct timeb t_queue; + /* Timestamp of last rc change */ + struct timeb t_rcchange; +#endif + + GHashTable *params; +} lrmd_cmd_t; + +static void cmd_finalize(lrmd_cmd_t *cmd, lrmd_rsc_t *rsc); +static gboolean lrmd_rsc_dispatch(gpointer user_data); + +static lrmd_rsc_t * +build_rsc_from_xml(xmlNode *msg) +{ + xmlNode *rsc_xml = get_xpath_object("//"F_LRMD_RSC, msg, LOG_ERR); + lrmd_rsc_t *rsc = NULL; + + crm_malloc0(rsc, sizeof(lrmd_rsc_t)); + rsc->rsc_id = crm_element_value_copy(rsc_xml, F_LRMD_RSC_ID); + rsc->class = crm_element_value_copy(rsc_xml, F_LRMD_CLASS); + rsc->provider = crm_element_value_copy(rsc_xml, F_LRMD_PROVIDER); + rsc->type = crm_element_value_copy(rsc_xml, F_LRMD_TYPE); + rsc->work = mainloop_add_trigger(G_PRIORITY_HIGH, lrmd_rsc_dispatch, rsc); + return rsc; +} + +static lrmd_cmd_t * +create_lrmd_cmd(xmlNode *msg) +{ + xmlNode *rsc_xml = get_xpath_object("//"F_LRMD_RSC, msg, LOG_ERR); + lrmd_cmd_t *cmd = NULL; + + crm_malloc0(cmd, sizeof(lrmd_cmd_t)); + + crm_element_value_int(msg, F_LRMD_CALLID, &cmd->call_id); + crm_element_value_int(rsc_xml, F_LRMD_RSC_INTERVAL, &cmd->interval); + crm_element_value_int(rsc_xml, F_LRMD_TIMEOUT, &cmd->timeout); + crm_element_value_int(rsc_xml, F_LRMD_RSC_START_DELAY, &cmd->start_delay); + + cmd->origin = crm_element_value_copy(rsc_xml, F_LRMD_ORIGIN); + cmd->action = crm_element_value_copy(rsc_xml, F_LRMD_RSC_ACTION); + cmd->userdata_str = crm_element_value_copy(rsc_xml, F_LRMD_RSC_USERDATA_STR); + cmd->rsc_id = crm_element_value_copy(rsc_xml, F_LRMD_RSC_ID); + + cmd->params = xml2list(rsc_xml); + + return cmd; +} + +static void +free_lrmd_cmd(lrmd_cmd_t *cmd) +{ + if (cmd->stonith_recurring_id) { + g_source_remove(cmd->stonith_recurring_id); + } + if (cmd->delay_id) { + g_source_remove(cmd->delay_id); + } + if (cmd->params) { + g_hash_table_destroy(cmd->params); + } + crm_free(cmd->origin); + crm_free(cmd->action); + crm_free(cmd->userdata_str); + crm_free(cmd->rsc_id); + crm_free(cmd->output); + crm_free(cmd); +} + +static gboolean +stonith_recurring_op_helper(gpointer data) +{ + lrmd_cmd_t *cmd = data; + lrmd_rsc_t *rsc = NULL; + + cmd->stonith_recurring_id = 0; + + rsc = cmd->rsc_id ? g_hash_table_lookup(rsc_list, cmd->rsc_id) : NULL; + + if (!rsc) { + /* This will never happen, but for the sake of completion + * this is what should happen if it did. */ + cmd->lrmd_op_status = PCMK_LRM_OP_CANCELLED; + cmd_finalize(cmd, NULL); + } else { + /* take it out of recurring_ops list, and put it in the pending ops + * to be executed */ + rsc->recurring_ops = g_list_remove(rsc->recurring_ops, cmd); + rsc->pending_ops = g_list_append(rsc->pending_ops, cmd); +#ifdef HAVE_SYS_TIMEB_H + ftime(&cmd->t_queue); +#endif + mainloop_set_trigger(rsc->work); + } + + return FALSE; +} + +static gboolean +start_delay_helper(gpointer data) +{ + lrmd_cmd_t *cmd = data; + lrmd_rsc_t *rsc = NULL; + + cmd->delay_id = 0; + rsc = cmd->rsc_id ? g_hash_table_lookup(rsc_list, cmd->rsc_id) : NULL; + + if (rsc) { + mainloop_set_trigger(rsc->work); + } + + return FALSE; +} + +static void +schedule_lrmd_cmd(lrmd_rsc_t *rsc, lrmd_cmd_t *cmd) +{ + CRM_CHECK(cmd != NULL, return); + CRM_CHECK(rsc != NULL, return); + + crm_trace("Scheduling %s on %s", cmd->action, rsc->rsc_id); + rsc->pending_ops = g_list_append(rsc->pending_ops, cmd); +#ifdef HAVE_SYS_TIMEB_H + ftime(&cmd->t_queue); +#endif + mainloop_set_trigger(rsc->work); + + if (cmd->start_delay) { + cmd->delay_id = g_timeout_add(cmd->start_delay, start_delay_helper, cmd); + } + +} + +static void +send_reply(lrmd_client_t *client, int rc, int call_id) +{ + int send_rc = 0; + xmlNode *reply = NULL; + + reply = create_xml_node(NULL, T_LRMD_REPLY); + crm_xml_add(reply, F_LRMD_ORIGIN, __FUNCTION__); + crm_xml_add_int(reply, F_LRMD_RC, rc); + crm_xml_add_int(reply, F_LRMD_CALLID, call_id); + + send_rc = crm_ipcs_send(client->channel, reply, FALSE); + + free_xml(reply); + if (send_rc < 0) { + crm_warn("LRMD reply to %s failed: %d", client->name, send_rc); + } +} + +static void +send_client_notify(gpointer key, gpointer value, gpointer user_data) +{ + xmlNode *update_msg = user_data; + lrmd_client_t *client = value; + + if (client == NULL) { + crm_err("Asked to send event to NULL client"); + return; + } else if (client->channel == NULL) { + crm_trace("Asked to send event to disconnected client"); + return; + } else if (client->name == NULL) { + crm_trace("Asked to send event to client with no name"); + return; + } + + if (crm_ipcs_send(client->channel, update_msg, TRUE) <= 0) { + crm_warn("Notification of client %s/%s failed", + client->name, client->id); + } +} + +#ifdef HAVE_SYS_TIMEB_H +static int +time_diff_ms(struct timeb *now, struct timeb *old) +{ + int sec = difftime(now->time, old->time); + int ms = now->millitm - old->millitm; + + if (old->time == 0) { + return 0; + } + + return (sec * 1000) + ms; +} +#endif + +static void +send_cmd_complete_notify(lrmd_cmd_t *cmd) +{ +#ifdef HAVE_SYS_TIMEB_H + struct timeb now = { 0, }; +#endif + xmlNode *notify = NULL; + notify = create_xml_node(NULL, T_LRMD_NOTIFY); + + crm_xml_add(notify, F_LRMD_ORIGIN, __FUNCTION__); + crm_xml_add_int(notify, F_LRMD_TIMEOUT, cmd->timeout); + crm_xml_add_int(notify, F_LRMD_RSC_INTERVAL, cmd->interval); + crm_xml_add_int(notify, F_LRMD_RSC_START_DELAY, cmd->start_delay); + crm_xml_add_int(notify, F_LRMD_EXEC_RC, cmd->exec_rc); + crm_xml_add_int(notify, F_LRMD_OP_STATUS, cmd->lrmd_op_status); + crm_xml_add_int(notify, F_LRMD_CALLID, cmd->call_id); + crm_xml_add_int(notify, F_LRMD_RSC_DELETED, cmd->rsc_deleted); + +#ifdef HAVE_SYS_TIMEB_H + ftime(&now); + crm_xml_add_int(notify, F_LRMD_RSC_RUN_TIME, cmd->t_run.time); + crm_xml_add_int(notify, F_LRMD_RSC_RCCHANGE_TIME, cmd->t_rcchange.time); + crm_xml_add_int(notify, F_LRMD_RSC_EXEC_TIME, time_diff_ms(&now, &cmd->t_run)); + crm_xml_add_int(notify, F_LRMD_RSC_QUEUE_TIME, time_diff_ms(&cmd->t_run, &cmd->t_queue)); +#endif + + crm_xml_add(notify, F_LRMD_OPERATION, LRMD_OP_RSC_EXEC); + crm_xml_add(notify, F_LRMD_RSC_ID, cmd->rsc_id); + crm_xml_add(notify, F_LRMD_RSC_ACTION, cmd->action); + crm_xml_add(notify, F_LRMD_RSC_USERDATA_STR, cmd->userdata_str); + crm_xml_add(notify, F_LRMD_RSC_OUTPUT, cmd->output); + + if (cmd->params) { + char *key = NULL; + char *value = NULL; + GHashTableIter iter; + + xmlNode *args = create_xml_node(notify, XML_TAG_ATTRS); + g_hash_table_iter_init(&iter, cmd->params); + while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) { + hash2field((gpointer) key, (gpointer) value, args); + } + } + + g_hash_table_foreach(client_list, send_client_notify, notify); + + free_xml(notify); +} + +static void +send_generic_notify(int rc, xmlNode *request) +{ + int call_id = 0; + xmlNode *notify = NULL; + xmlNode *rsc_xml = get_xpath_object("//"F_LRMD_RSC, request, LOG_ERR); + const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID); + const char *op = crm_element_value(request, F_LRMD_OPERATION); + + crm_element_value_int(request, F_LRMD_CALLID, &call_id); + + notify = create_xml_node(NULL, T_LRMD_NOTIFY); + crm_xml_add(notify, F_LRMD_ORIGIN, __FUNCTION__); + crm_xml_add_int(notify, F_LRMD_RC, rc); + crm_xml_add_int(notify, F_LRMD_CALLID, call_id); + crm_xml_add(notify, F_LRMD_OPERATION, op); + crm_xml_add(notify, F_LRMD_RSC_ID, rsc_id); + + g_hash_table_foreach(client_list, send_client_notify, notify); + + free_xml(notify); +} + +static void +cmd_finalize(lrmd_cmd_t *cmd, lrmd_rsc_t *rsc) +{ + crm_trace("Resource operation rsc:%s action:%s completed", cmd->rsc_id, cmd->action); + + if (rsc && (rsc->active == cmd)) { + rsc->active = NULL; + mainloop_set_trigger(rsc->work); + } + + if (!rsc) { + cmd->rsc_deleted = 1; + } + + send_cmd_complete_notify(cmd); + + + if (cmd->interval && (cmd->lrmd_op_status == PCMK_LRM_OP_CANCELLED)) { + if (rsc) { + rsc->recurring_ops = g_list_remove(rsc->recurring_ops, cmd); + rsc->pending_ops = g_list_remove(rsc->pending_ops, cmd); + } + free_lrmd_cmd(cmd); + } else if (cmd->interval == 0) { + if (rsc) { + rsc->pending_ops = g_list_remove(rsc->pending_ops, cmd); + } + free_lrmd_cmd(cmd); + } else { + /* Clear all the values pertaining just to the last iteration of a recurring op. */ + cmd->lrmd_op_status = 0; + memset(&cmd->t_run, 0, sizeof(cmd->t_run)); + memset(&cmd->t_queue, 0, sizeof(cmd->t_queue)); + crm_free(cmd->output); + } +} + +static int +lsb2uniform_rc(const char *action, int rc) +{ + if (rc < 0) { + return PCMK_EXECRA_UNKNOWN_ERROR; + } + + /* status has different return codes that everything else. */ + if (!safe_str_eq(action, "status") && !safe_str_eq(action, "monitor")) { + if (rc > PCMK_LSB_NOT_RUNNING) { + return PCMK_EXECRA_UNKNOWN_ERROR; + } + return rc; + } + + switch (rc) { + case PCMK_LSB_STATUS_OK: + return PCMK_EXECRA_OK; + case PCMK_LSB_STATUS_NOT_INSTALLED: + return PCMK_EXECRA_NOT_INSTALLED; + case PCMK_LSB_STATUS_VAR_PID: + case PCMK_LSB_STATUS_VAR_LOCK: + case PCMK_LSB_STATUS_NOT_RUNNING: + return PCMK_EXECRA_NOT_RUNNING; + default: + return PCMK_EXECRA_UNKNOWN_ERROR; + } + + return PCMK_EXECRA_UNKNOWN_ERROR; +} +static int +ocf2uniform_rc(int rc) +{ + if (rc < 0 || rc > PCMK_OCF_FAILED_MASTER) { + return PCMK_EXECRA_UNKNOWN_ERROR; + } + + return rc; +} + +static int +stonith2uniform_rc(const char *action, int rc) +{ + if (rc == st_err_unknown_device) { + if (safe_str_eq(action, "stop")) { + rc = PCMK_EXECRA_OK; + } else if (safe_str_eq(action, "start")) { + rc = PCMK_EXECRA_NOT_INSTALLED; + } else { + rc = PCMK_EXECRA_NOT_RUNNING; + } + } else if (rc != 0) { + rc = PCMK_EXECRA_UNKNOWN_ERROR; + } + return rc; +} + +static int +get_uniform_rc(const char *standard, const char *action, int rc) +{ + if (safe_str_eq(standard, "ocf")) { + return ocf2uniform_rc(rc); + } else if (safe_str_eq(standard, "stonith")) { + return stonith2uniform_rc(action, rc); + } else { + return lsb2uniform_rc(action, rc); + } +} + +static void +action_complete(svc_action_t *action) +{ + lrmd_rsc_t *rsc; + lrmd_cmd_t *cmd = action->cb_data; + + if (!cmd) { + crm_err("LRMD action (%s) completed does not match any known operations.", action->id); + return; + } + +#ifdef HAVE_SYS_TIMEB_H + if (cmd->exec_rc != action->rc) { + ftime(&cmd->t_rcchange); + } +#endif + + cmd->exec_rc = get_uniform_rc(action->standard, cmd->action, action->rc); + cmd->lrmd_op_status = action->status; + rsc = cmd->rsc_id ? g_hash_table_lookup(rsc_list, cmd->rsc_id) : NULL; + + if (action->stdout_data) { + cmd->output = crm_strdup(action->stdout_data); + } + + cmd_finalize(cmd, rsc); +} + +static int +lrmd_rsc_execute_stonith(lrmd_rsc_t *rsc, lrmd_cmd_t *cmd) +{ + int rc = 0; + stonith_t *stonith_api = get_stonith_connection(); + + if (!stonith_api) { + cmd->exec_rc = get_uniform_rc("stonith", cmd->action, st_err_connection); + cmd->lrmd_op_status = PCMK_LRM_OP_ERROR; + cmd_finalize(cmd, rsc); + return lrmd_err_stonith_connection; + } + + if (safe_str_eq(cmd->action, "start")) { + char *key = NULL; + char *value = NULL; + stonith_key_value_t *device_params = NULL; + + if (cmd->params) { + GHashTableIter iter; + g_hash_table_iter_init(&iter, cmd->params); + while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) { + device_params = stonith_key_value_add(device_params, key, value); + } + } + + rc = stonith_api->cmds->register_device(stonith_api, + st_opt_sync_call, + cmd->rsc_id, + rsc->provider, + rsc->type, + device_params); + + stonith_key_value_freeall(device_params, 1, 1); + if (rc == 0) { + rc = stonith_api->cmds->call(stonith_api, + st_opt_sync_call, + cmd->rsc_id, + "monitor", + NULL, + cmd->timeout); + } + } else if (safe_str_eq(cmd->action, "stop")) { + rc = stonith_api->cmds->remove_device(stonith_api, st_opt_sync_call, cmd->rsc_id); + } else if (safe_str_eq(cmd->action, "monitor")) { + rc = stonith_api->cmds->call(stonith_api, + st_opt_sync_call, + cmd->rsc_id, + cmd->action, + NULL, + cmd->timeout); + } + + cmd->exec_rc = get_uniform_rc("stonith", cmd->action, rc); + + /* Attempt to map return codes to op status if possible */ + if (rc) { + switch (rc) { + case st_err_not_supported: + cmd->lrmd_op_status = PCMK_LRM_OP_NOTSUPPORTED; + break; + case st_err_timeout: + cmd->lrmd_op_status = PCMK_LRM_OP_TIMEOUT; + break; + default: + cmd->lrmd_op_status = PCMK_LRM_OP_ERROR; + } + } else { + cmd->lrmd_op_status = PCMK_LRM_OP_DONE; + } + + if (cmd->interval > 0) { + rsc->recurring_ops = g_list_append(rsc->recurring_ops, cmd); + cmd->stonith_recurring_id = g_timeout_add(cmd->interval, stonith_recurring_op_helper, cmd); + } + cmd_finalize(cmd, rsc); + + return rc; +} + +static const char * +normalize_action_name(lrmd_rsc_t *rsc, const char *action) +{ + if (safe_str_eq(action, "monitor") && + (safe_str_eq(rsc->class, "lsb") || + safe_str_eq(rsc->class, "service") || + safe_str_eq(rsc->class, "systemd"))) { + return "status"; + } + return action; +} + +static void +dup_attr(gpointer key, gpointer value, gpointer user_data) +{ + g_hash_table_replace(user_data, crm_strdup(key), crm_strdup(value)); +} + +static int +lrmd_rsc_execute_service_lib(lrmd_rsc_t *rsc, lrmd_cmd_t *cmd) +{ + svc_action_t *action = NULL; + GHashTable *params_copy = NULL; + + crm_trace("Creating action, resource:%s action:%s class:%s provider:%s agent:%s", + rsc->rsc_id, + cmd->action, + rsc->class, + rsc->provider, + rsc->type); + + if (cmd->params) { + params_copy = g_hash_table_new_full(crm_str_hash, + g_str_equal, + g_hash_destroy_str, + g_hash_destroy_str); + + if (params_copy != NULL) { + g_hash_table_foreach(cmd->params, dup_attr, params_copy); + } + } + + action = resources_action_create(rsc->rsc_id, + rsc->class, + rsc->provider, + rsc->type, + normalize_action_name(rsc, cmd->action), + cmd->interval, + cmd->timeout, + params_copy); + + if (!action) { + crm_err("Failed to create action, action:%s on resource %s", cmd->action, rsc->rsc_id); + cmd->lrmd_op_status = PCMK_LRM_OP_ERROR; + goto exec_done; + } + + action->cb_data = cmd; + if (!services_action_async(action, action_complete)) { + services_action_free(action); + action = NULL; + cmd->lrmd_op_status = PCMK_LRM_OP_ERROR; + goto exec_done; + } + + if (cmd->interval) { + rsc->recurring_ops = g_list_append(rsc->recurring_ops, cmd); + } + + /* The cmd will be finalized by the action_complete callback after + * the service library is done with it */ + rsc->active = cmd; /* only one op at a time for a rsc */ + cmd = NULL; + +exec_done: + if (cmd) { + cmd_finalize(cmd, rsc); + } + return TRUE; +} + +static gboolean +lrmd_rsc_execute(lrmd_rsc_t *rsc) +{ + lrmd_cmd_t *cmd = NULL; + CRM_CHECK(rsc != NULL, return FALSE); + + if (rsc->active) { + crm_trace("%s is still active", rsc->rsc_id); + return TRUE; + } + + if (rsc->pending_ops) { + GList *first = rsc->pending_ops; + cmd = first->data; + if (cmd->delay_id) { + crm_trace("Command %s %s was asked to run too early, waiting for start_delay timeout of %dms", + cmd->rsc_id, cmd->action, cmd->start_delay); + return TRUE; + } + rsc->pending_ops = g_list_remove_link(rsc->pending_ops, first); + g_list_free_1(first); + +#ifdef HAVE_SYS_TIMEB_H + ftime(&cmd->t_run); + } +#endif + + if (!cmd) { + crm_trace("Nothing further to do for %s", rsc->rsc_id); + return TRUE; + } + + if (safe_str_eq(rsc->class, "stonith")) { + lrmd_rsc_execute_stonith(rsc, cmd); + } else { + lrmd_rsc_execute_service_lib(rsc, cmd); + } + + return TRUE; +} + +static gboolean +lrmd_rsc_dispatch(gpointer user_data) +{ + return lrmd_rsc_execute(user_data); +} + +void +free_rsc(gpointer data) +{ + GListPtr gIter = NULL; + lrmd_rsc_t *rsc = data; + int is_stonith = safe_str_eq(rsc->class, "stonith"); + + for (gIter = rsc->pending_ops; gIter != NULL; gIter = gIter->next) { + lrmd_cmd_t *cmd = gIter->data; + /* command was never executed */ + cmd->lrmd_op_status = PCMK_LRM_OP_CANCELLED; + cmd_finalize(cmd, NULL); + } + /* frees list, but not list elements. */ + g_list_free(rsc->pending_ops); + + for (gIter = rsc->recurring_ops; gIter != NULL; gIter = gIter->next) { + lrmd_cmd_t *cmd = gIter->data; + if (is_stonith) { + cmd->lrmd_op_status = PCMK_LRM_OP_CANCELLED; + cmd_finalize(cmd, NULL); + } else { + /* This command is already handed off to service library, + * let service library cancel it and tell us via the callback + * when it is cancelled. The rsc can be safely destroyed + * even if we are waiting for the cancel result */ + services_action_cancel(rsc->rsc_id, cmd->action, cmd->interval); + } + } + /* frees list, but not list elements. */ + g_list_free(rsc->recurring_ops); + + crm_free(rsc->rsc_id); + crm_free(rsc->class); + crm_free(rsc->provider); + crm_free(rsc->type); + mainloop_destroy_trigger(rsc->work); + + crm_free(rsc); +} + +static int +process_lrmd_signon(lrmd_client_t *client, xmlNode *request) +{ + xmlNode *reply = create_xml_node(NULL, "reply"); + crm_xml_add(reply, F_LRMD_OPERATION, CRM_OP_REGISTER); + crm_xml_add(reply, F_LRMD_CLIENTID, client->id); + crm_ipcs_send(client->channel, reply, FALSE); + + free_xml(reply); + return lrmd_ok; +} + +static int +process_lrmd_rsc_register(lrmd_client_t *client, xmlNode *request) +{ + int rc = lrmd_ok; + lrmd_rsc_t *rsc = build_rsc_from_xml(request); + lrmd_rsc_t *dup = g_hash_table_lookup(rsc_list, rsc->rsc_id); + + if (dup && + safe_str_eq(rsc->class, dup->class) && + safe_str_eq(rsc->provider, dup->provider) && + safe_str_eq(rsc->type, dup->type)) { + + crm_warn("Can't add, RSC '%s' already present in the rsc list (%d active resources)", + rsc->rsc_id, g_hash_table_size(rsc_list)); + + free_rsc(rsc); + return rc; + } + + g_hash_table_replace(rsc_list, rsc->rsc_id, rsc); + crm_info("Added '%s' to the rsc list (%d active resources)", + rsc->rsc_id, g_hash_table_size(rsc_list)); + + return rc; +} + +static void +process_lrmd_get_rsc_info(lrmd_client_t *client, xmlNode *request) +{ + int rc = lrmd_ok; + int send_rc = 0; + int call_id = 0; + xmlNode *rsc_xml = get_xpath_object("//"F_LRMD_RSC, request, LOG_ERR); + const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID); + xmlNode *reply = NULL; + lrmd_rsc_t *rsc = NULL; + + crm_element_value_int(request, F_LRMD_CALLID, &call_id); + + if (!rsc_id) { + rc = lrmd_err_unknown_rsc; + goto get_rsc_done; + } + + if (!(rsc = g_hash_table_lookup(rsc_list, rsc_id))) { + crm_info("Resource '%s' not found (%d active resources)", + rsc_id, g_hash_table_size(rsc_list)); + rc = lrmd_err_unknown_rsc; + goto get_rsc_done; + } + +get_rsc_done: + + reply = create_xml_node(NULL, T_LRMD_REPLY); + crm_xml_add(reply, F_LRMD_ORIGIN, __FUNCTION__); + crm_xml_add_int(reply, F_LRMD_RC, rc); + crm_xml_add_int(reply, F_LRMD_CALLID, call_id); + + if (rsc) { + crm_xml_add(reply, F_LRMD_RSC_ID, rsc->rsc_id); + crm_xml_add(reply, F_LRMD_CLASS, rsc->class); + crm_xml_add(reply, F_LRMD_PROVIDER, rsc->provider); + crm_xml_add(reply, F_LRMD_TYPE, rsc->type); + } + + send_rc = crm_ipcs_send(client->channel, reply, FALSE); + + if (send_rc < 0) { + crm_warn("LRMD reply to %s failed: %d", client->name, send_rc); + } + + free_xml(reply); +} + +static int +process_lrmd_rsc_unregister(lrmd_client_t *client, xmlNode *request) +{ + int rc = lrmd_ok; + lrmd_rsc_t *rsc = NULL; + xmlNode *rsc_xml = get_xpath_object("//"F_LRMD_RSC, request, LOG_ERR); + const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID); + + if (!rsc_id) { + return lrmd_err_unknown_rsc; + } + + if (!(rsc = g_hash_table_lookup(rsc_list, rsc_id))) { + crm_info("Resource '%s' not found (%d active resources)", + rsc_id, g_hash_table_size(rsc_list)); + return lrmd_err_unknown_rsc; + } + + if (rsc->active) { + /* let the caller know there are still active ops on this rsc to watch for */ + rc = lrmd_pending; + } + + g_hash_table_remove(rsc_list, rsc_id); + + return rc; +} + +static int +process_lrmd_rsc_exec(lrmd_client_t *client, xmlNode *request) +{ + lrmd_rsc_t *rsc = NULL; + lrmd_cmd_t *cmd = NULL; + xmlNode *rsc_xml = get_xpath_object("//"F_LRMD_RSC, request, LOG_ERR); + const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID); + + if (!rsc_id) { + return lrmd_err_missing; + } + if (!(rsc = g_hash_table_lookup(rsc_list, rsc_id))) { + return lrmd_err_unknown_rsc; + } + + cmd = create_lrmd_cmd(request); + schedule_lrmd_cmd(rsc, cmd); + + return cmd->call_id; +} + +static int +cancel_op(const char *rsc_id, const char *action, int interval) +{ + GListPtr gIter = NULL; + lrmd_rsc_t *rsc = g_hash_table_lookup(rsc_list, rsc_id); + + /* How to cancel an action. + * 1. Check pending ops list, if it hasn't been handed off + * to the service library or stonith recurring list remove + * it there and that will stop it. + * 2. If it isn't in the pending ops list, then its either a + * recurring op in the stonith recurring list, or the service + * library's recurring list. Stop it there + * 3. If not found in any lists, then this operation has either + * been executed already and is not a recurring operation, or + * never existed. + */ + if (!rsc) { + return lrmd_err_unknown_rsc; + } + + for (gIter = rsc->pending_ops; gIter != NULL; gIter = gIter->next) { + lrmd_cmd_t *cmd = gIter->data; + + if (safe_str_eq(cmd->action, action) && cmd->interval == interval) { + cmd->lrmd_op_status = PCMK_LRM_OP_CANCELLED; + cmd_finalize(cmd, rsc); + return lrmd_ok; + } + } + + if (safe_str_eq(rsc->class, "stonith")) { + /* The service library does not handle stonith operations. + * We have to handle recurring stonith opereations ourselves. */ + for (gIter = rsc->recurring_ops; gIter != NULL; gIter = gIter->next) { + lrmd_cmd_t *cmd = gIter->data; + + if (safe_str_eq(cmd->action, action) && cmd->interval == interval) { + cmd->lrmd_op_status = PCMK_LRM_OP_CANCELLED; + cmd_finalize(cmd, rsc); + return lrmd_ok; + } + } + } else if (services_action_cancel(rsc_id, normalize_action_name(rsc, action), interval) == TRUE) { + /* The service library will tell the action_complete callback function + * this action was cancelled, which will destroy the cmd and remove + * it from the recurring_op list. Do not do that in this function + * if the service library says it cancelled it. */ + return lrmd_ok; + } + + return lrmd_err_unknown_operation; +} + +static int +process_lrmd_rsc_cancel(lrmd_client_t *client, xmlNode *request) +{ + xmlNode *rsc_xml = get_xpath_object("//"F_LRMD_RSC, request, LOG_ERR); + const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID); + const char *action = crm_element_value(rsc_xml, F_LRMD_RSC_ACTION); + int interval = 0; + + crm_element_value_int(rsc_xml, F_LRMD_RSC_INTERVAL, &interval); + + if (!rsc_id || !action) { + return lrmd_err_missing; + } + + return cancel_op(rsc_id, action, interval); +} + +void +process_lrmd_message(lrmd_client_t *client, xmlNode *request) +{ + int rc = lrmd_ok; + int call_options = 0; + int call_id = 0; + const char *op = crm_element_value(request, F_LRMD_OPERATION); + int do_reply = 0; + int do_notify = 0; + int exit = 0; + + crm_element_value_int(request, F_LRMD_CALLOPTS, &call_options); + crm_element_value_int(request, F_LRMD_CALLID, &call_id); + + if (crm_str_eq(op, CRM_OP_REGISTER, TRUE)) { + rc = process_lrmd_signon(client, request); + } else if (crm_str_eq(op, LRMD_OP_RSC_REG, TRUE)) { + rc = process_lrmd_rsc_register(client, request); + do_notify = 1; + do_reply = 1; + } else if (crm_str_eq(op, LRMD_OP_RSC_INFO, TRUE)) { + process_lrmd_get_rsc_info(client, request); + } else if (crm_str_eq(op, LRMD_OP_RSC_UNREG, TRUE)) { + rc = process_lrmd_rsc_unregister(client, request); + /* don't notify anyone about failed un-registers */ + if (rc == lrmd_ok || rc == lrmd_pending) { + do_notify = 1; + } + do_reply = 1; + } else if (crm_str_eq(op, LRMD_OP_RSC_EXEC, TRUE)) { + rc = process_lrmd_rsc_exec(client, request); + do_reply = 1; + } else if (crm_str_eq(op, LRMD_OP_RSC_CANCEL, TRUE)) { + rc = process_lrmd_rsc_cancel(client, request); + do_reply = 1; + } else if (crm_str_eq(op, CRM_OP_QUIT, TRUE)) { + do_reply = 1; + exit = 1; + } else { + rc = lrmd_err_unknown_operation; + do_reply = 1; + crm_err("Unknown %s from %s", op, client->name); + crm_log_xml_warn(request, "UnknownOp"); + } + + if (do_reply) { + send_reply(client, rc, call_id); + } + + if (do_notify) { + send_generic_notify(rc, request); + } + + if (exit) { + lrmd_shutdown(0); + } +} + diff --git a/lrmd/lrmd_private.h b/lrmd/lrmd_private.h new file mode 100644 index 0000000000..2e0dbf27e8 --- /dev/null +++ b/lrmd/lrmd_private.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2012 David Vossel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef LRMD_PVT__H +#define LRMD_PVT__H + +#include +#include +#include +#include + +GHashTable *rsc_list; +GHashTable *client_list; + +typedef struct lrmd_rsc_s { + char *rsc_id; + char *class; + char *provider; + char *type; + + /* NEVER dereference this pointer, + * It simply exists as a switch to let us know + * when the currently active operation has completed */ + void *active; + + /* Operations in this list + * have not been executed yet. */ + GList *pending_ops; + /* Operations in this list are recurring operations + * that have been handed off from the pending ops list. */ + GList *recurring_ops; + + crm_trigger_t *work; +} lrmd_rsc_t; + +typedef struct lrmd_client_s { + char *id; + char *name; + + qb_ipcs_connection_t *channel; + + long long flags; + +} lrmd_client_t; + +void process_lrmd_message(lrmd_client_t *client, xmlNode *request); + +void free_rsc(gpointer data); + +void lrmd_shutdown(int nsig); + +/* + * \brief Don't worry about freeing this connection. It is + * taken care of after mainloop exits by the main() function. + */ +stonith_t *get_stonith_connection(void); + +#endif diff --git a/lrmd/main.c b/lrmd/main.c new file mode 100644 index 0000000000..4e02db9e4d --- /dev/null +++ b/lrmd/main.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2012 David Vossel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +GMainLoop *mainloop = NULL; +qb_ipcs_service_t *ipcs = NULL; +stonith_t *stonith_api = NULL; + + +stonith_t *get_stonith_connection(void) +{ + if (stonith_api && stonith_api->state == stonith_disconnected) { + stonith_api_delete(stonith_api); + stonith_api = NULL; + } + + if (!stonith_api) { + int rc = 0; + int tries = 10; + stonith_api = stonith_api_new(); + do { + rc = stonith_api->cmds->connect(stonith_api, "lrmd", NULL); + if (rc == stonith_ok) { + break; + } + sleep(1); + tries--; + } while (tries); + + if (rc) { + crm_err("Unable to connect to stonith daemon to execute command. error: %s", stonith_error2string(rc)); + stonith_api_delete(stonith_api); + stonith_api = NULL; + } + } + return stonith_api; +} + +static int32_t +lrmd_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid) +{ + struct qb_ipcs_connection_stats stats = { 0, }; + + qb_ipcs_connection_stats_get(c, &stats, 1); + crm_info("Accepting client connection: %p pid=%d for uid=%d gid=%d", + c, stats.client_pid, uid, gid); + return 0; +} + +static void +lrmd_ipc_created(qb_ipcs_connection_t *c) +{ + cl_uuid_t client_id; + lrmd_client_t *new_client = NULL; + char uuid_str[UU_UNPARSE_SIZEOF]; + + crm_malloc0(new_client, sizeof(lrmd_client_t)); + new_client->channel = c; + + cl_uuid_generate(&client_id); + cl_uuid_unparse(&client_id, uuid_str); + + new_client->id = crm_strdup(uuid_str); + crm_trace("LRMD client connection established. %p id: %s", c, new_client->id); + + g_hash_table_insert(client_list, new_client->id, new_client); + qb_ipcs_context_set(c, new_client); +} + +static int32_t +lrmd_ipc_dispatch(qb_ipcs_connection_t *c, void *data, size_t size) +{ + xmlNode *request = crm_ipcs_recv(c, data, size); + lrmd_client_t *client = (lrmd_client_t *) qb_ipcs_context_get(c); + + CRM_CHECK(client != NULL, crm_err("Invalid client"); return FALSE); + CRM_CHECK(client->id != NULL, crm_err("Invalid client: %p", client); return FALSE); + + if (!request) { + return 0; + } + + if (!client->name) { + const char *value = crm_element_value(request, F_LRMD_CLIENTNAME); + + if (value == NULL) { + client->name = crm_itoa(crm_ipcs_client_pid(c)); + } else { + client->name = crm_strdup(value); + } + } + + crm_xml_add(request, F_LRMD_CLIENTID, client->id); + crm_xml_add(request, F_LRMD_CLIENTNAME, client->name); + + process_lrmd_message(client, request); + + free_xml(request); + return 0; +} + +static int32_t +lrmd_ipc_closed(qb_ipcs_connection_t *c) +{ + lrmd_client_t *client = (lrmd_client_t *) qb_ipcs_context_get(c); + int found = 0; + + if (!client) { + crm_err("No client for ipc"); + return 0; + } + + if (client->id) { + found = g_hash_table_remove(client_list, client->id); + } + + if (!found) { + crm_err("Asked to remove unknown client with id %d", client->id); + } + + return 0; +} + +static void +lrmd_ipc_destroy(qb_ipcs_connection_t *c) +{ + lrmd_client_t *client = (lrmd_client_t *) qb_ipcs_context_get(c); + + if (!client) { + crm_err("No client for ipc"); + return; + } + + crm_info("LRMD client disconnecting %p - name: %s id: %s", c, client->name, client->id); + + qb_ipcs_context_set(c, NULL); + crm_free(client->name); + crm_free(client->id); + crm_free(client); +} + +static struct qb_ipcs_service_handlers lrmd_ipc_callbacks = +{ + .connection_accept = lrmd_ipc_accept, + .connection_created = lrmd_ipc_created, + .msg_process = lrmd_ipc_dispatch, + .connection_closed = lrmd_ipc_closed, + .connection_destroyed = lrmd_ipc_destroy +}; + +void +lrmd_shutdown(int nsig) +{ + crm_info("Terminating with %d clients", g_hash_table_size(client_list)); + if (ipcs) { + mainloop_del_ipc_server(ipcs); + } + exit(0); +} + +static int +try_server_create(void) +{ + int tries = 10; + + /* + * This should complete on the first iteration. The only + * known reason for why this would fail is if another lrmd process + * already exists on the system. To avoid this situation + * we attempt to connect to the old lrmd process and shut it down + * using the client library. + */ + do { + ipcs = mainloop_add_ipc_server(CRM_SYSTEM_LRMD, QB_IPC_SHM, &lrmd_ipc_callbacks); + + if (ipcs == NULL) { + xmlNode *reply = NULL; + xmlNode *hello = create_xml_node(NULL, "lrmd_command"); + crm_ipc_t *ipc = crm_ipc_new("lrmd", 0); + + if (ipc && crm_ipc_connect(ipc)) { + crm_xml_add(hello, F_TYPE, T_LRMD); + crm_xml_add(hello, F_LRMD_OPERATION, CRM_OP_QUIT); + crm_xml_add(hello, F_LRMD_CLIENTNAME, "new_lrmd"); + + crm_ipc_send(ipc, hello, &reply, -1); + + crm_ipc_close(ipc); + crm_ipc_destroy(ipc); + } + free_xml(reply); + free_xml(hello); + + crm_err("New IPC server could not be created because another lrmd process exists, sending shutdown command to old lrmd process."); + } + + tries --; + } while (!ipcs && tries); + + return ipcs ? 0 : -1; +} + +int +main(int argc, char ** argv) +{ + int rc = 0; + + crm_log_init("lrmd", LOG_INFO, TRUE, FALSE, argc, argv, FALSE); + + rsc_list = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, free_rsc); + client_list = g_hash_table_new(crm_str_hash, g_str_equal); + if (try_server_create()) { + crm_err("Failed to allocate lrmd server. shutting down"); + exit(-1); + } + + mainloop_add_signal(SIGTERM, lrmd_shutdown); + mainloop = g_main_new(FALSE); + crm_info("Starting"); + g_main_run(mainloop); + + mainloop_del_ipc_server(ipcs); + g_hash_table_destroy(client_list); + g_hash_table_destroy(rsc_list); + + if (stonith_api) { + stonith_api->cmds->disconnect(stonith_api); + stonith_api_delete(stonith_api); + } + + return rc; +} diff --git a/lrmd/test.c b/lrmd/test.c new file mode 100644 index 0000000000..60279bd2d0 --- /dev/null +++ b/lrmd/test.c @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2012 David Vossel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +#include +#include + +#include +#include +#include + +#include + +/* *INDENT-OFF* */ +static struct crm_option long_options[] = { + {"help", 0, 0, '?'}, + {"verbose", 0, 0, 'V', "\t\tPrint out logs and events to screen"}, + {"quiet", 0, 0, 'Q', "\t\tSuppress all output to screen"}, + /* just incase we have to add data to events, + * we don't want break a billion regression tests. Instead + * we'll create different versions */ + {"listen", 1, 0, 'l', "\tListen for a specific event string"}, + {"event-ver", 1, 0, 'e', "\tVersion of event to listen to"}, + {"api-call", 1, 0, 'c', "\tDirectly relates to lrmd api functions"}, + {"no-wait", 0, 0, 'w', "\tMake api call and do not wait for result."}, + {"-spacer-", 1, 0, '-', "\nParameters for api-call option"}, + {"action", 1, 0, 'a'}, + {"rsc-id", 1, 0, 'r'}, + {"cancel-call-id", 1, 0, 'x'}, + {"provider", 1, 0, 'P'}, + {"class", 1, 0, 'C'}, + {"type", 1, 0, 'T'}, + {"interval", 1, 0, 'i'}, + {"timeout", 1, 0, 't'}, + {"start-delay", 1, 0, 's'}, + {"param-key", 1, 0, 'k'}, + {"param-val", 1, 0, 'v'}, + + {"-spacer-", 1, 0, '-'}, + {0, 0, 0, 0} +}; +/* *INDENT-ON* */ + +static int exec_call_id = 0; + +static struct { + int verbose; + int quiet; + int print; + int interval; + int timeout; + int start_delay; + int cancel_call_id; + int event_version; + int no_wait; + int no_connect; + const char *api_call; + const char *rsc_id; + const char *provider; + const char *class; + const char *type; + const char *action; + const char *listen; + lrmd_key_value_t *params; +} options; + +GMainLoop *mainloop = NULL; +lrmd_t *lrmd_conn = NULL; + +static char event_buf_v0[1024]; + +#define print_result(result) \ + if (!options.quiet) { \ + result; \ + } \ + +#define report_event(event) \ + snprintf(event_buf_v0, sizeof(event_buf_v0), "NEW_EVENT event_type:%s rsc_id:%s action:%s rc:%s op_status:%s", \ + lrmd_event_type2str(event->type), \ + event->rsc_id, \ + event->op_type ? event->op_type : "none", \ + lrmd_event_rc2str(event->rc), \ + services_lrm_status_str(event->op_status)); \ + crm_info("%s", event_buf_v0);; + +static void +test_shutdown(int nsig) +{ + lrmd_api_delete(lrmd_conn); +} + +static void +read_events(lrmd_event_data_t *event) +{ + report_event(event); + if (options.listen) { + if (safe_str_eq(options.listen, event_buf_v0)) { + print_result(printf("LISTEN EVENT SUCCESSFUL\n")); + exit(0); + } + } + + if (exec_call_id && (event->call_id == exec_call_id)) { + if (event->op_status == 0) { + print_result(printf("API-CALL SUCCESSFUL for 'exec'\n")); + } else { + print_result(printf("API-CALL FAILURE for 'exec', rc:%d lrmd_op_status:%s\n", + event->rc, + services_lrm_status_str(event->op_status))); + exit(-1); + } + + if (!options.listen) { + exit(0); + } + } +} + +static gboolean +timeout_err(gpointer data) +{ + print_result(printf("LISTEN EVENT FAILURE - timeout occurred, never found.\n")); + exit(-1); + + return FALSE; +} + +static void +try_connect(void) +{ + int tries = 10; + int i = 0; + int rc = 0; + + + for (i = 0; i < tries; i++) { + rc = lrmd_conn->cmds->connect(lrmd_conn, "lrmd", NULL); + + if (!rc) { + crm_info("lrmd client connection established"); + return; + } else { + crm_info("lrmd client connection failed"); + } + sleep(1); + } + + print_result(printf("API CONNECTION FAILURE\n")); + exit(-1); +} + +static gboolean +start_test(gpointer user_data) +{ + int rc = 0; + + if (!options.no_connect) { + try_connect(); + } + lrmd_conn->cmds->set_callback(lrmd_conn, read_events); + + if (options.timeout) { + g_timeout_add(options.timeout, timeout_err, NULL); + } + + if (!options.api_call) { + return 0; + } + + if (safe_str_eq(options.api_call, "exec")) { + rc = lrmd_conn->cmds->exec(lrmd_conn, + options.rsc_id, + options.action, + NULL, + options.interval, + options.timeout, + options.start_delay, + 0, + options.params); + + if (rc > 0) { + exec_call_id = rc; + print_result(printf("API-CALL 'exec' action pending, waiting on response\n")); + } + + } else if (safe_str_eq(options.api_call, "register_rsc")) { + rc = lrmd_conn->cmds->register_rsc(lrmd_conn, + options.rsc_id, + options.class, + options.provider, + options.type, + 0); + } else if (safe_str_eq(options.api_call, "get_rsc_info")) { + lrmd_rsc_info_t *rsc_info; + + rsc_info = lrmd_conn->cmds->get_rsc_info(lrmd_conn, options.rsc_id, 0); + + if (rsc_info) { + print_result(printf("RSC_INFO: id:%s class:%s provider:%s type:%s\n", + rsc_info->id, rsc_info->class, rsc_info->provider ? rsc_info->provider : "", rsc_info->type)); + lrmd_free_rsc_info(rsc_info); + rc = lrmd_ok; + } else { + rc = -1; + } + } else if (safe_str_eq(options.api_call, "unregister_rsc")) { + rc = lrmd_conn->cmds->unregister_rsc(lrmd_conn, + options.rsc_id, + 0); + } else if (safe_str_eq(options.api_call, "cancel")) { + rc = lrmd_conn->cmds->cancel(lrmd_conn, + options.rsc_id, + options.action, + options.interval); + } else if (safe_str_eq(options.api_call, "metadata")) { + char *output = NULL; + rc = lrmd_conn->cmds->get_metadata(lrmd_conn, + options.class, + options.provider, + options.type, &output, 0); + if (rc == lrmd_ok) { + print_result(printf("%s", output)); + crm_free(output); + } + } else if (safe_str_eq(options.api_call, "list_agents")) { + lrmd_list_t *list = NULL; + lrmd_list_t *iter = NULL; + rc = lrmd_conn->cmds->list_agents(lrmd_conn, &list, options.class, options.provider); + + if (rc > 0) { + print_result(printf("%d agents found\n", rc)); + for (iter = list; iter != NULL; iter = iter->next) { + print_result(printf("%s\n", iter->val)); + } + lrmd_list_freeall(list); + rc = 0; + } else { + print_result(printf("API_CALL FAILURE - no agents found\n")); + rc = -1; + } + } else if (safe_str_eq(options.api_call, "list_ocf_providers")) { + lrmd_list_t *list = NULL; + lrmd_list_t *iter = NULL; + rc = lrmd_conn->cmds->list_ocf_providers(lrmd_conn, options.type, &list); + + if (rc > 0) { + print_result(printf("%d providers found\n", rc)); + for (iter = list; iter != NULL; iter = iter->next) { + print_result(printf("%s\n", iter->val)); + } + lrmd_list_freeall(list); + rc = 0; + } else { + print_result(printf("API_CALL FAILURE - no providers found\n")); + rc = -1; + } + } else if (options.api_call) { + print_result(printf("API-CALL FAILURE unknown action '%s'\n", options.action)); + exit(-1); + } + + if (rc < 0) { + print_result(printf("API-CALL FAILURE for '%s' api_rc:%d\n", options.api_call, rc)); + exit(-1); + } + + if (options.api_call && rc == lrmd_ok) { + print_result(printf("API-CALL SUCCESSFUL for '%s'\n", options.api_call)); + if (!options.listen) { + exit(0); + } + } + + if (options.no_wait) { + /* just make the call and exit regardless of anything else. */ + exit(0); + } + + return 0; +} + +int main(int argc, char ** argv) +{ + int option_index = 0; + int argerr = 0; + int flag; + char *key = NULL; + char *val = NULL; + crm_trigger_t *trig; + + crm_set_options(NULL, "mode [options]", long_options, + "Inject commands into the lrmd and watch for events\n"); + + while (1) { + flag = crm_get_option(argc, argv, &option_index); + if (flag == -1) + break; + + switch(flag) { + case '?': + crm_help(flag, LSB_EXIT_OK); + break; + case 'V': + options.verbose = 1; + break; + case 'Q': + options.quiet = 1; + options.verbose = 0; + break; + case 'e': + options.event_version = atoi(optarg); + break; + case 'l': + options.listen = optarg; + break; + case 'w': + options.no_wait = 1; + break; + case 'c': + options.api_call = optarg; + break; + case 'a': + options.action = optarg; + break; + case 'r': + options.rsc_id = optarg; + break; + case 'x': + options.cancel_call_id = atoi(optarg); + break; + case 'P': + options.provider = optarg; + break; + case 'C': + options.class = optarg; + break; + case 'T': + options.type = optarg; + break; + case 'i': + options.interval = atoi(optarg); + break; + case 't': + options.timeout = atoi(optarg); + break; + case 's': + options.start_delay = atoi(optarg); + break; + case 'k': + key = optarg; + if (key && val) { + options.params = lrmd_key_value_add(options.params, key, val); + key = val = NULL; + } + break; + case 'v': + val = optarg; + if (key && val) { + options.params = lrmd_key_value_add(options.params, key, val); + key = val = NULL; + } + break; + default: + ++argerr; + break; + } + } + + if (argerr) { + crm_help('?', LSB_EXIT_GENERIC); + } + if (optind > argc) { + ++argerr; + } + + + if (!options.listen && + (safe_str_eq(options.api_call, "metadata") || + safe_str_eq(options.api_call, "list_agents") || + safe_str_eq(options.api_call, "list_ocf_providers"))) { + options.no_connect = 1; + } + + crm_log_init("lrmd_ctest", LOG_INFO, TRUE, options.verbose ? TRUE : FALSE, argc, argv, FALSE); + + /* if we can't perform an api_call or listen for events, + * there is nothing to do */ + if (!options.api_call && !options.listen) { + crm_err("Nothing to be done. Please specify 'api-call' and/or 'listen'"); + return 0; + } + + lrmd_conn = lrmd_api_new(); + trig = mainloop_add_trigger(G_PRIORITY_HIGH, start_test, NULL); + mainloop_set_trigger(trig); + mainloop_add_signal(SIGTERM, test_shutdown); + + crm_info("Starting"); + mainloop = g_main_new(FALSE); + g_main_run(mainloop); + lrmd_api_delete(lrmd_conn); + return 0; +}