diff --git a/Makefile.am b/Makefile.am index fdeebb53..da0b4490 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,215 +1,215 @@ # Copyright (c) 2009 Red Hat, Inc. # # Authors: Andrew Beekhof # Steven Dake (sdake@redhat.com) # # This software licensed under BSD license, the text of which follows: # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # - Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # - Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # - Neither the name of the MontaVista Software, Inc. nor the names of its # contributors may be used to endorse or promote products derived from this # software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. SPEC = $(PACKAGE_NAME).spec TARFILE = $(PACKAGE_NAME)-$(VERSION).tar.gz EXTRA_DIST = autogen.sh $(SPEC).in \ build-aux/git-version-gen \ build-aux/gitlog-to-changelog \ build-aux/release.mk \ .version ACLOCAL_AMFLAGS = -I m4 MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure depcomp \ config.guess config.sub missing install-sh \ autoheader automake autoconf test_lense.sh \ autoscan.log configure.scan ltmain.sh test-driver dist_doc_DATA = LICENSE INSTALL README.recovery SECURITY AUTHORS SUBDIRS = include common_lib lib exec tools test cts pkgconfig \ man init conf qdevices coverity: rm -rf cov make clean cov-build --dir=cov make cov-analyze --dir cov \ --concurrency \ -co BAD_FREE:allow_first_field:true \ --security \ --wait-for-license cov-format-errors --dir cov coverity-aggressive: rm -rf cov make clean cov-build --dir=cov make cov-analyze --dir cov \ --concurrency \ --all \ --aggressiveness-level high \ --security \ --wait-for-license cov-format-errors --dir cov install-exec-local: $(INSTALL) -d $(DESTDIR)/${COROSYSCONFDIR}/service.d $(INSTALL) -d $(DESTDIR)/${COROSYSCONFDIR}/uidgid.d $(INSTALL) -d $(DESTDIR)/${localstatedir}/lib/corosync $(INSTALL) -d $(DESTDIR)/${localstatedir}/log/cluster if BUILD_QNETD - $(INSTALL) -m 750 -d $(DESTDIR)/${localstatedir}/run/corosync-qnetd - $(INSTALL) -m 750 -d $(DESTDIR)/${COROSYSCONFDIR}/qnetd + $(INSTALL) -m 770 -d $(DESTDIR)/${localstatedir}/run/corosync-qnetd + $(INSTALL) -m 770 -d $(DESTDIR)/${COROSYSCONFDIR}/qnetd endif if BUILD_QDEVICES - $(INSTALL) -m 750 -d $(DESTDIR)/${localstatedir}/run/corosync-qdevice + $(INSTALL) -m 770 -d $(DESTDIR)/${localstatedir}/run/corosync-qdevice $(INSTALL) -d $(DESTDIR)/${COROSYSCONFDIR}/qdevice/ - $(INSTALL) -m 750 -d $(DESTDIR)/${COROSYSCONFDIR}/qdevice/net + $(INSTALL) -m 770 -d $(DESTDIR)/${COROSYSCONFDIR}/qdevice/net endif uninstall-local: rmdir $(DESTDIR)/${COROSYSCONFDIR}/service.d || :; rmdir $(DESTDIR)/${COROSYSCONFDIR}/uidgid.d || :; rmdir $(DESTDIR)/${localstatedir}/lib/corosync || :; rmdir $(DESTDIR)/${localstatedir}/log/cluster || :; if BUILD_QNETD rmdir $(DESTDIR)/${localstatedir}/run/corosync-qnetd || :; rmdir $(DESTDIR)/${COROSYSCONFDIR}/qnetd || :; endif if BUILD_QDEVICES rmdir $(DESTDIR)/${localstatedir}/run/corosync-qdevice || :; rmdir $(DESTDIR)/${COROSYSCONFDIR}/qdevice/net || :; rmdir $(DESTDIR)/${COROSYSCONFDIR}/qdevice/ || :; endif if AUGTOOL check_SCRIPTS = test_lense.sh TESTS = $(check_SCRIPTS) test_lense.sh: echo "augparse -I $(srcdir)/conf/lenses/ $(srcdir)/conf/lenses/tests/test_corosync.aug" > $@ chmod +x $@ endif lint: for dir in lib exec tools test; do make -C $$dir lint; done .PHONY: doxygen doxygen: @if [ "$(DOXYGEN)" = "" ] || [ "$(DOT)" = "" ] ; then \ echo "*********************************************" ; \ echo "*** ***" ; \ echo "*** You must install doxygen and graphviz ***" ; \ echo "*** to generate the API documentation. ***" ; \ echo "*** ***" ; \ echo "*********************************************" ; \ exit 1 ; \ else \ mkdir -p doc/api && $(DOXYGEN) ; \ fi dist-clean-local: rm -f autoconf automake autoheader test_lense.sh clean-generic: rm -rf doc/api $(SPEC) $(TARFILE) test_lense.sh ## make rpm/srpm section. $(SPEC): $(SPEC).in rm -f $@-t $@ date="$(shell LC_ALL=C date "+%a %b %d %Y")" && \ if [ -f .tarball-version ]; then \ gitver="$(shell cat .tarball-version)" && \ rpmver=$$gitver && \ alphatag="" && \ dirty="" && \ numcomm=""; \ else \ gitver="$(shell git describe --abbrev=4 --match='v*' HEAD 2>/dev/null)" && \ rpmver=`echo $$gitver | sed -e "s/^v//" -e "s/-.*//g"` && \ alphatag=`echo $$gitver | sed -e "s/.*-//" -e "s/^g//"` && \ vtag=`echo $$gitver | sed -e "s/-.*//g"` && \ numcomm=`git rev-list $$vtag..HEAD | wc -l` && \ git update-index --refresh > /dev/null 2>&1 || true && \ dirty=`git diff-index --name-only HEAD 2>/dev/null`; \ fi && \ if [ "$$numcomm" = "0" ]; then numcomm=""; fi && \ if [ -n "$$numcomm" ]; then numcomm="%global numcomm $$numcomm"; fi && \ if [ "$$alphatag" = "$$gitver" ]; then alphatag=""; fi && \ if [ -n "$$alphatag" ]; then alphatag="%global alphatag $$alphatag"; fi && \ if [ -n "$$dirty" ]; then dirty="%global dirty dirty"; fi && \ sed \ -e "s#@version@#$$rpmver#g" \ -e "s#@ALPHATAG@#$$alphatag#g" \ -e "s#@NUMCOMM@#$$numcomm#g" \ -e "s#@DIRTY@#$$dirty#g" \ -e "s#@date@#$$date#g" \ $< > $@-t; \ chmod a-w $@-t mv $@-t $@ $(TARFILE): $(MAKE) dist RPMBUILDOPTS = --define "_sourcedir $(abs_builddir)" \ --define "_specdir $(abs_builddir)" \ --define "_builddir $(abs_builddir)" \ --define "_srcrpmdir $(abs_builddir)" \ --define "_rpmdir $(abs_builddir)" srpm: clean $(MAKE) $(SPEC) $(TARFILE) rpmbuild $(WITH_LIST) $(RPMBUILDOPTS) --nodeps -bs $(SPEC) rpm: clean _version $(MAKE) $(SPEC) $(TARFILE) rpmbuild $(WITH_LIST) $(RPMBUILDOPTS) -ba $(SPEC) # release/versioning BUILT_SOURCES = .version .version: echo $(VERSION) > $@-t && mv $@-t $@ dist-hook: gen-ChangeLog echo $(VERSION) > $(distdir)/.tarball-version gen_start_date = 2000-01-01 .PHONY: gen-ChangeLog _version gen-ChangeLog: if test -d .git; then \ LC_ALL=C $(top_srcdir)/build-aux/gitlog-to-changelog \ --since=$(gen_start_date) > $(distdir)/cl-t; \ rm -f $(distdir)/ChangeLog; \ mv $(distdir)/cl-t $(distdir)/ChangeLog; \ fi _version: cd $(srcdir) && rm -rf autom4te.cache .version && autoreconf -i $(MAKE) $(AM_MAKEFLAGS) Makefile maintainer-clean-local: rm -rf m4 diff --git a/corosync.spec.in b/corosync.spec.in index 6436315c..447fccd0 100644 --- a/corosync.spec.in +++ b/corosync.spec.in @@ -1,454 +1,493 @@ @ALPHATAG@ @NUMCOMM@ @DIRTY@ # Conditionals # Invoke "rpmbuild --without " or "rpmbuild --with " # to disable or enable specific features %bcond_with testagents %bcond_with watchdog %bcond_with monitoring %bcond_with snmp %bcond_with dbus %bcond_with rdma %bcond_with systemd %bcond_with upstart %bcond_with xmlconf %bcond_with runautogen %bcond_with qdevices +%bcond_with qnetd %global gitver %{?numcomm:.%{numcomm}}%{?alphatag:.%{alphatag}}%{?dirty:.%{dirty}} %global gittarver %{?numcomm:.%{numcomm}}%{?alphatag:-%{alphatag}}%{?dirty:-%{dirty}} Name: corosync Summary: The Corosync Cluster Engine and Application Programming Interfaces Version: @version@ Release: 1%{?gitver}%{?dist} License: BSD Group: System Environment/Base URL: http://corosync.github.io/corosync/ Source0: http://build.clusterlabs.org/corosync/releases/%{name}-%{version}%{?gittarver}.tar.gz # Runtime bits Requires: corosynclib = %{version}-%{release} Requires(pre): /usr/sbin/useradd Requires(post): /sbin/chkconfig Requires(preun): /sbin/chkconfig Conflicts: openais <= 0.89, openais-devel <= 0.89 # Build bits BuildRequires: groff BuildRequires: libqb-devel BuildRequires: nss-devel BuildRequires: zlib-devel %if %{with runautogen} BuildRequires: autoconf automake libtool %endif %if %{with monitoring} BuildRequires: libstatgrab-devel %endif %if %{with rdma} BuildRequires: libibverbs-devel librdmacm-devel %endif %if %{with snmp} BuildRequires: net-snmp-devel %endif %if %{with dbus} BuildRequires: dbus-devel %endif %if %{with systemd} BuildRequires: systemd-units Requires(post): systemd Requires(preun): systemd Requires(postun): systemd %endif %if %{with xmlconf} Requires: libxslt %endif -%if %{with qdevices} +%if %{with qdevices} || %{with qnetd} Requires: nss-tools %endif +%if %{with qnetd} +BuildRequires: sed +%endif BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) %prep %setup -q -n %{name}-%{version}%{?gittarver} %build %if %{with runautogen} ./autogen.sh %endif %if %{with rdma} export ibverbs_CFLAGS=-I/usr/include/infiniband \ export ibverbs_LIBS=-libverbs \ export rdmacm_CFLAGS=-I/usr/include/rdma \ export rdmacm_LIBS=-lrdmacm \ %endif %{configure} \ %if %{with testagents} --enable-testagents \ %endif %if %{with watchdog} --enable-watchdog \ %endif %if %{with monitoring} --enable-monitoring \ %endif %if %{with snmp} --enable-snmp \ %endif %if %{with dbus} --enable-dbus \ %endif %if %{with rdma} --enable-rdma \ %endif %if %{with systemd} --enable-systemd \ %endif %if %{with upstart} --enable-upstart \ %endif %if %{with xmlconf} --enable-xmlconf \ %endif %if %{with qdevices} --enable-qdevices \ +%endif +%if %{with qnetd} + --enable-qnetd \ %endif --with-initddir=%{_initrddir} \ --with-systemddir=%{_unitdir} \ --with-upstartdir=%{_sysconfdir}/init make %{_smp_mflags} %install rm -rf %{buildroot} make install DESTDIR=%{buildroot} %if %{with dbus} mkdir -p -m 0700 %{buildroot}/%{_sysconfdir}/dbus-1/system.d install -m 644 %{_builddir}/%{name}-%{version}%{?gittarver}/conf/corosync-signals.conf %{buildroot}/%{_sysconfdir}/dbus-1/system.d/corosync-signals.conf %endif ## tree fixup # drop static libs rm -f %{buildroot}%{_libdir}/*.a rm -f %{buildroot}%{_libdir}/*.la # drop docs and html docs for now rm -rf %{buildroot}%{_docdir}/* # /etc/sysconfig/corosync-notifyd mkdir -p %{buildroot}%{_sysconfdir}/sysconfig install -m 644 tools/corosync-notifyd.sysconfig.example \ %{buildroot}%{_sysconfdir}/sysconfig/corosync-notifyd # /etc/sysconfig/corosync install -m 644 init/corosync.sysconfig.example \ %{buildroot}%{_sysconfdir}/sysconfig/corosync +%if %{with qnetd} +# /etc/sysconfig/corosync-qnetd +install -m 644 init/corosync-qnetd.sysconfig.example \ + %{buildroot}%{_sysconfdir}/sysconfig/corosync-qnetd +%if %{with systemd} +sed -i -e 's/^#User=/User=/' \ + %{buildroot}%{_unitdir}/corosync-qnetd.service +%else +sed -i -e 's/^COROSYNC_QNETD_RUNAS=""$/COROSYNC_QNETD_RUNAS="coroqnetd"/' \ + %{buildroot}%{_sysconfdir}/sysconfig/corosync-qnetd +%endif +%endif + %clean rm -rf %{buildroot} %description This package contains the Corosync Cluster Engine Executive, several default APIs and libraries, default configuration files, and an init script. %post %if %{with systemd} && 0%{?systemd_post:1} %systemd_post corosync.service %else if [ $1 -eq 1 ]; then /sbin/chkconfig --add corosync || : fi %endif %preun %if %{with systemd} && 0%{?systemd_preun:1} %systemd_preun corosync.service %else if [ $1 -eq 0 ]; then /sbin/service corosync stop &>/dev/null || : /sbin/chkconfig --del corosync || : fi %endif %postun %if %{with systemd} && 0%{?systemd_postun:1} %systemd_postun %endif %files %defattr(-,root,root,-) %doc LICENSE SECURITY %{_sbindir}/corosync %{_sbindir}/corosync-keygen %{_sbindir}/corosync-cmapctl %{_sbindir}/corosync-cfgtool %{_sbindir}/corosync-cpgtool %{_sbindir}/corosync-quorumtool %{_sbindir}/corosync-notifyd %{_bindir}/corosync-blackbox %if %{with xmlconf} %{_bindir}/corosync-xmlproc %config(noreplace) %{_sysconfdir}/corosync/corosync.xml.example %dir %{_datadir}/corosync %{_datadir}/corosync/xml2conf.xsl %{_mandir}/man8/corosync-xmlproc.8* %{_mandir}/man5/corosync.xml.5* %endif %dir %{_sysconfdir}/corosync %dir %{_sysconfdir}/corosync/uidgid.d %config(noreplace) %{_sysconfdir}/corosync/corosync.conf.example %config(noreplace) %{_sysconfdir}/corosync/corosync.conf.example.udpu %config(noreplace) %{_sysconfdir}/sysconfig/corosync-notifyd %config(noreplace) %{_sysconfdir}/sysconfig/corosync %config(noreplace) %{_sysconfdir}/logrotate.d/corosync %if %{with dbus} %{_sysconfdir}/dbus-1/system.d/corosync-signals.conf %endif %if %{with snmp} %{_datadir}/snmp/mibs/COROSYNC-MIB.txt %endif %if %{with systemd} %{_unitdir}/corosync.service %{_unitdir}/corosync-notifyd.service %dir %{_datadir}/corosync %{_datadir}/corosync/corosync %{_datadir}/corosync/corosync-notifyd %else %{_initrddir}/corosync %{_initrddir}/corosync-notifyd %endif %if %{with upstart} %{_sysconfdir}/init/corosync.conf %{_sysconfdir}/init/corosync-notifyd.conf %endif %dir %{_localstatedir}/lib/corosync %dir %{_localstatedir}/log/cluster %{_mandir}/man8/corosync_overview.8* %{_mandir}/man8/corosync.8* %{_mandir}/man8/corosync-blackbox.8* %{_mandir}/man8/corosync-cmapctl.8* %{_mandir}/man8/corosync-keygen.8* %{_mandir}/man8/corosync-cfgtool.8* %{_mandir}/man8/corosync-cpgtool.8* %{_mandir}/man8/corosync-notifyd.8* %{_mandir}/man8/corosync-quorumtool.8* %{_mandir}/man5/corosync.conf.5* %{_mandir}/man5/votequorum.5* %{_mandir}/man8/cmap_keys.8* # optional testagent rpm # %if %{with testagents} %package -n corosync-testagents Summary: The Corosync Cluster Engine Test Agents Group: Development/Libraries Requires: %{name} = %{version}-%{release} %description -n corosync-testagents This package contains corosync test agents. %files -n corosync-testagents %defattr(755,root,root,-) %{_datadir}/corosync/tests/mem_leak_test.sh %{_datadir}/corosync/tests/net_breaker.sh %{_datadir}/corosync/tests/cmap-dispatch-deadlock.sh %{_datadir}/corosync/tests/shm_leak_audit.sh %{_bindir}/cpg_test_agent %{_bindir}/sam_test_agent %{_bindir}/votequorum_test_agent %endif # library # %package -n corosynclib Summary: The Corosync Cluster Engine Libraries Group: System Environment/Libraries Requires: %{name} = %{version}-%{release} %description -n corosynclib This package contains corosync libraries. %files -n corosynclib %defattr(-,root,root,-) %doc LICENSE %{_libdir}/libcfg.so.* %{_libdir}/libcpg.so.* %{_libdir}/libcmap.so.* %{_libdir}/libtotem_pg.so.* %{_libdir}/libquorum.so.* %{_libdir}/libvotequorum.so.* %{_libdir}/libsam.so.* %{_libdir}/libcorosync_common.so.* %post -n corosynclib -p /sbin/ldconfig %postun -n corosynclib -p /sbin/ldconfig %package -n corosynclib-devel Summary: The Corosync Cluster Engine Development Kit Group: Development/Libraries Requires: corosynclib = %{version}-%{release} Requires: pkgconfig Provides: corosync-devel = %{version} Obsoletes: corosync-devel < 0.92-7 %description -n corosynclib-devel This package contains include files and man pages used to develop using The Corosync Cluster Engine APIs. %files -n corosynclib-devel %defattr(-,root,root,-) %doc LICENSE %dir %{_includedir}/corosync/ %{_includedir}/corosync/corodefs.h %{_includedir}/corosync/cfg.h %{_includedir}/corosync/cmap.h %{_includedir}/corosync/corotypes.h %{_includedir}/corosync/cpg.h %{_includedir}/corosync/hdb.h %{_includedir}/corosync/sam.h %{_includedir}/corosync/quorum.h %{_includedir}/corosync/votequorum.h %dir %{_includedir}/corosync/totem/ %{_includedir}/corosync/totem/totem.h %{_includedir}/corosync/totem/totemip.h %{_includedir}/corosync/totem/totempg.h %{_libdir}/libcfg.so %{_libdir}/libcpg.so %{_libdir}/libcmap.so %{_libdir}/libtotem_pg.so %{_libdir}/libquorum.so %{_libdir}/libvotequorum.so %{_libdir}/libsam.so %{_libdir}/libcorosync_common.so %{_libdir}/pkgconfig/*.pc %{_mandir}/man3/cpg_*3* %{_mandir}/man3/quorum_*3* %{_mandir}/man3/votequorum_*3* %{_mandir}/man3/sam_*3* %{_mandir}/man8/cpg_overview.8* %{_mandir}/man8/votequorum_overview.8* %{_mandir}/man8/sam_overview.8* %{_mandir}/man3/cmap_*3* %{_mandir}/man8/cmap_overview.8* %{_mandir}/man8/quorum_overview.8* # optional qdevices # %if %{with qdevices} %package -n corosync-qdevice Summary: The Corosync Cluster Engine Qdevice Group: System Environment/Base Requires: corosync Requires: nss-tools %if %{with systemd} Requires(post): systemd Requires(preun): systemd Requires(postun): systemd %endif %description -n corosync-qdevice This package contains the Corosync Cluster Engine Qdevice, script for creating NSS certificates and an init script. %post -n corosync-qdevice %if %{with systemd} && 0%{?systemd_post:1} %systemd_post corosync-qdevice.service %else if [ $1 -eq 1 ]; then /sbin/chkconfig --add corosync-qdevice || : fi %endif %preun -n corosync-qdevice %if %{with systemd} && 0%{?systemd_preun:1} %systemd_preun corosync-qdevice.service %else if [ $1 -eq 0 ]; then /sbin/service corosync-qdevice stop &>/dev/null || : /sbin/chkconfig --del corosync-qdevice || : fi %endif %postun -n corosync-qdevice %if %{with systemd} && 0%{?systemd_postun:1} %systemd_postun %endif %files -n corosync-qdevice %defattr(-,root,root,-) +%dir %{_sysconfdir}/corosync/qdevice +%dir %config(noreplace) %{_sysconfdir}/corosync/qdevice/net +%dir %{_localstatedir}/run/corosync-qdevice %{_sbindir}/corosync-qdevice %{_sbindir}/corosync-qdevice-net-certutil %{_sbindir}/corosync-qdevice-tool %if %{with systemd} %{_unitdir}/corosync-qdevice.service %dir %{_datadir}/corosync %{_datadir}/corosync/corosync-qdevice %else %{_initrddir}/corosync-qdevice %endif +%endif + +# optional qnetd +# +%if %{with qnetd} + %package -n corosync-qnetd Summary: The Corosync Cluster Engine Qdevice Network Daemon Group: System Environment/Base Requires: nss-tools +Requires(pre): shadow-utils %if %{with systemd} Requires(post): systemd Requires(preun): systemd Requires(postun): systemd %endif %description -n corosync-qnetd This package contains the Corosync Cluster Engine Qdevice Network Daemon, script for creating NSS certificates and an init script. +%pre -n corosync-qnetd +getent group coroqnetd >/dev/null || groupadd -r coroqnetd +getent passwd coroqnetd >/dev/null || \ + useradd -r -g coroqnetd -d / -s /sbin/nologin -c "User for corosync-qnetd" coroqnetd +exit 0 + %post -n corosync-qnetd %if %{with systemd} && 0%{?systemd_post:1} %systemd_post corosync-qnetd.service %else if [ $1 -eq 1 ]; then /sbin/chkconfig --add corosync-qnetd || : fi %endif %preun -n corosync-qnetd %if %{with systemd} && 0%{?systemd_preun:1} %systemd_preun corosync-qnetd.service %else if [ $1 -eq 0 ]; then /sbin/service corosync-qnetd stop &>/dev/null || : /sbin/chkconfig --del corosync-qnetd || : fi %endif %postun -n corosync-qnetd %if %{with systemd} && 0%{?systemd_postun:1} %systemd_postun %endif %files -n corosync-qnetd %defattr(-,root,root,-) -%{_sbindir}/corosync-qnetd -%{_sbindir}/corosync-qnetd-certutil -%{_sbindir}/corosync-qnetd-tool +%dir %config(noreplace) %attr(770, coroqnetd, coroqnetd) %{_sysconfdir}/corosync/qnetd +%dir %attr(770, coroqnetd, coroqnetd) %{_localstatedir}/run/corosync-qnetd +%{_bindir}/corosync-qnetd +%{_bindir}/corosync-qnetd-certutil +%{_bindir}/corosync-qnetd-tool +%config(noreplace) %{_sysconfdir}/sysconfig/corosync-qnetd %if %{with systemd} %{_unitdir}/corosync-qnetd.service %dir %{_datadir}/corosync %{_datadir}/corosync/corosync-qnetd %else %{_initrddir}/corosync-qnetd %endif %endif %changelog * @date@ Autotools generated version - @version@-1-@numcomm@.@alphatag@.@dirty@ - Autotools generated version diff --git a/init/Makefile.am b/init/Makefile.am index 4c45ed80..7c1a556d 100644 --- a/init/Makefile.am +++ b/init/Makefile.am @@ -1,79 +1,88 @@ # Copyright (c) 2004 MontaVista Software, Inc. # Copyright (c) 2009 Red Hat, Inc. # # Authors: Steven Dake (sdake@redhat.com) # Fabio M. Di Nitto (fdinitto@redhat.com) # # All rights reserved. # # This software licensed under BSD license, the text of which follows: # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # - Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # - Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # - Neither the name of the MontaVista Software, Inc. nor the names of its # contributors may be used to endorse or promote products derived from this # software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. MAINTAINERCLEANFILES = Makefile.in EXTRA_DIST = corosync.in corosync-notifyd.in corosync.service.in \ corosync-notifyd.service.in corosync.conf.in corosync-notifyd.conf.in \ corosync.sysconfig.example if INSTALL_SYSTEMD systemdconfdir = $(SYSTEMDDIR) systemdconf_DATA = corosync.service corosync-notifyd.service initscriptdir = $(INITWRAPPERSDIR) else initscriptdir = $(INITDDIR) endif initscript_SCRIPTS = corosync corosync-notifyd if BUILD_QDEVICES -initscript_SCRIPTS += corosync-qnetd corosync-qdevice -EXTRA_DIST += corosync-qnetd.sysconfig.example corosync-qnetd.in corosync-qdevice.in \ - corosync-qnetd.service.in corosync-qdevice.service.in +initscript_SCRIPTS += corosync-qdevice +EXTRA_DIST += corosync-qdevice.in corosync-qdevice.service.in if INSTALL_SYSTEMD -systemdconf_DATA += corosync-qnetd.service corosync-qdevice.service +systemdconf_DATA += corosync-qdevice.service +endif +endif + +if BUILD_QNETD +initscript_SCRIPTS += corosync-qnetd +EXTRA_DIST += corosync-qnetd.sysconfig.example corosync-qnetd.in \ + corosync-qnetd.service.in +if INSTALL_SYSTEMD +systemdconf_DATA += corosync-qnetd.service endif endif if INSTALL_UPSTART upstartconfdir = $(UPSTARTDIR) upstartconf_DATA = corosync.conf corosync-notifyd.conf endif %: %.in Makefile rm -f $@-t $@ cat $< | sed \ -e 's#@''SBINDIR@#$(sbindir)#g' \ + -e 's#@''BINDIR@#$(bindir)#g' \ -e 's#@''SYSCONFDIR@#$(sysconfdir)#g' \ -e 's#@''INITDDIR@#$(INITDDIR)#g' \ -e 's#@''INITWRAPPERSDIR@#$(INITWRAPPERSDIR)#g' \ -e 's#@''LOCALSTATEDIR@#$(localstatedir)#g' \ -e 's#@''BASHPATH@#${BASHPATH}#g' \ > $@-t mv $@-t $@ all-local: $(initscript_SCRIPTS) $(systemdconf_DATA) $(upstartconf_DATA) clean-local: rm -rf $(initscript_SCRIPTS) $(systemdconf_DATA) $(upstartconf_DATA) diff --git a/init/corosync-qnetd.in b/init/corosync-qnetd.in index 41087d85..ac97d1f4 100755 --- a/init/corosync-qnetd.in +++ b/init/corosync-qnetd.in @@ -1,164 +1,168 @@ #!@BASHPATH@ # Authors: # Jan Friesse # # License: Revised BSD # chkconfig: - 20 80 # description: Corosync Qdevice Network daemon # processname: corosync-qnetd # ### BEGIN INIT INFO # Provides: corosync-qnetd # Required-Start: $network $syslog # Required-Stop: $network $syslog # Default-Start: # Default-Stop: # Short-Description: Starts and stops Corosync Qdevice Network daemon. # Description: Starts and stops Corosync Qdevice Network daemon. ### END INIT INFO desc="Corosync Qdevice Network daemon" prog="corosync-qnetd" # set secure PATH PATH="/sbin:/bin:/usr/sbin:/usr/bin:@SBINDIR@" success() { echo -ne "[ OK ]\r" } failure() { echo -ne "[FAILED]\r" } status() { pid=$(pidof $1 2>/dev/null) res=$? if [ $res -ne 0 ]; then echo "$1 is stopped" else echo "$1 (pid $pid) is running..." fi return $res } # rpm based distros if [ -d @SYSCONFDIR@/sysconfig ]; then [ -f @INITDDIR@/functions ] && . @INITDDIR@/functions [ -f @SYSCONFDIR@/sysconfig/$prog ] && . @SYSCONFDIR@/sysconfig/$prog [ -z "$LOCK_FILE" ] && LOCK_FILE="@LOCALSTATEDIR@/lock/subsys/$prog" fi # deb based distros if [ -d @SYSCONFDIR@/default ]; then [ -f @SYSCONFDIR@/default/$prog ] && . @SYSCONFDIR@/default/$prog [ -z "$LOCK_FILE" ] && LOCK_FILE="@LOCALSTATEDIR@/lock/$prog" fi # The version of __pids_pidof in /etc/init.d/functions calls pidof with -x # This means it matches scripts, including this one. # Redefine it here so that status (from the same file) works. # Otherwise simultaneous calls to stop() will loop forever __pids_pidof() { pidof -c -o $$ -o $PPID -o %PPID "$1" || \ pidof -c -o $$ -o $PPID -o %PPID "${1##*/}" } cluster_disabled_at_boot() { if grep -q nocluster /proc/cmdline && \ [ "$(tty)" = "/dev/console" ]; then echo -e "not configured to run at boot" failure return 1 fi return 0 } start() { echo -n "Starting $desc ($prog): " ! cluster_disabled_at_boot && return # most recent distributions use tmpfs for @LOCALSTATEDIR@/run # to avoid to clean it up on every boot. # they also assume that init scripts will create # required subdirectories for proper operations mkdir -p @LOCALSTATEDIR@/run if status $prog > /dev/null 2>&1; then success else - $prog $COROSYNC_QNETD_OPTIONS > /dev/null 2>&1 + if [ -z "$COROSYNC_QNETD_RUNAS" ];then + $prog $COROSYNC_QNETD_OPTIONS > /dev/null 2>&1 + else + runuser -s @BASHPATH@ $COROSYNC_QNETD_RUNAS -c "$prog $COROSYNC_QNETD_OPTIONS > /dev/null 2>&1" + fi if [ "$?" != 0 ]; then failure rtrn=1 else touch $LOCK_FILE success fi fi echo } stop() { ! status $prog > /dev/null 2>&1 && return echo -n "Signaling $desc ($prog) to terminate: " kill -TERM $(pidof $prog) > /dev/null 2>&1 success echo echo -n "Waiting for $prog services to unload:" while status $prog > /dev/null 2>&1; do sleep 1 echo -n "." done rm -f $LOCK_FILE success echo } restart() { stop start } rtrn=0 case "$1" in start) start ;; restart|reload|force-reload) restart ;; condrestart|try-restart) if status $prog > /dev/null 2>&1; then restart fi ;; status) status $prog rtrn=$? ;; stop) stop ;; *) echo "usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}" rtrn=2 ;; esac exit $rtrn diff --git a/init/corosync-qnetd.service.in b/init/corosync-qnetd.service.in index 0831f15b..83749108 100644 --- a/init/corosync-qnetd.service.in +++ b/init/corosync-qnetd.service.in @@ -1,13 +1,16 @@ [Unit] Description=Corosync Qdevice Network daemon ConditionKernelCommandLine=!nocluster Requires=network-online.target After=network-online.target [Service] -ExecStart=@INITWRAPPERSDIR@/corosync-qnetd start -ExecStop=@INITWRAPPERSDIR@/corosync-qnetd stop -Type=forking +EnvironmentFile=@SYSCONFDIR@/sysconfig/corosync-qnetd +ExecStart=@BINDIR@/corosync-qnetd -f $COROSYNC_QNETD_OPTIONS +Type=simple +Restart=on-abnormal +# Uncomment and set user who should be used for executing qnetd +#User=coroqnetd [Install] WantedBy=multi-user.target diff --git a/init/corosync-qnetd.sysconfig.example b/init/corosync-qnetd.sysconfig.example index 528b44f2..8d375359 100644 --- a/init/corosync-qnetd.sysconfig.example +++ b/init/corosync-qnetd.sysconfig.example @@ -1,6 +1,13 @@ # Corosync Qdevice Network daemon init script configuration file # COROSYNC_QNETD_OPTIONS specifies options passed to corosync-qnetd command # (default is no options). # See "man corosync-qnetd" for detailed descriptions of the options. COROSYNC_QNETD_OPTIONS="" + +# COROSYNC_QNETD_RUNAS specifies user under which qnetd daemon should be running +# (not set or empty is default and means "user who executes init script") +# Make sure to set correct owner of directories /etc/corosync/qnetd and +# /var/run/corosync-qnetd +# This has no effect if systemd unit is used (you have to change unit file) +COROSYNC_QNETD_RUNAS="" diff --git a/qdevices/Makefile.am b/qdevices/Makefile.am index 01128ef4..e67abbed 100644 --- a/qdevices/Makefile.am +++ b/qdevices/Makefile.am @@ -1,161 +1,162 @@ # Copyright (c) 2012-2016 Red Hat, Inc. # # Authors: Fabio M. Di Nitto (fdinitto@redhat.com) # Jan Friesse (jfriesse@redhat.com) # # This software licensed under BSD license, the text of which follows: # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # - Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # - Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # - Neither the name of the MontaVista Software, Inc. nor the names of its # contributors may be used to endorse or promote products derived from this # software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. MAINTAINERCLEANFILES = Makefile.in SUBDIRS = bin_PROGRAMS = sbin_PROGRAMS = +bin_SCRIPTS = sbin_SCRIPTS = EXTRA_DIST = if BUILD_QNETD bin_PROGRAMS += corosync-qnetd corosync-qnetd-tool -sbin_SCRIPTS += corosync-qnetd-certutil +bin_SCRIPTS += corosync-qnetd-certutil EXTRA_DIST += corosync-qnetd-certutil.sh corosync_qnetd_SOURCES = corosync-qnetd.c \ dynar.c dynar.h msg.c msg.h msgio.c msgio.h \ nss-sock.c nss-sock.h qnetd-client.c qnetd-client.h \ qnetd-client-list.c qnetd-client-list.h qnetd-log.c qnetd-log.h \ pr-poll-array.c pr-poll-array.h timer-list.c timer-list.h tlv.c tlv.h \ send-buffer-list.c send-buffer-list.h node-list.c node-list.h \ qnetd-algo-test.c qnetd-algo-test.h qnetd-algorithm.c qnetd-algorithm.h \ qnetd-algo-utils.c qnetd-algo-utils.h \ qnetd-algo-ffsplit.c qnetd-algo-ffsplit.h \ qnetd-cluster.c qnetd-cluster.h \ qnetd-cluster-list.c qnetd-cluster-list.h \ qnetd-client-send.c qnetd-client-send.h \ qnetd-algo-2nodelms.c qnetd-algo-2nodelms.h qnetd-algo-lms.c qnetd-algo-lms.h \ utils.c utils.h qnetd-instance.c qnetd-instance.h \ qnetd-client-net.c qnetd-client-net.h \ qnetd-client-msg-received.c qnetd-client-msg-received.h \ qnetd-log-debug.c qnetd-log-debug.h \ qnetd-client-algo-timer.c qnetd-client-algo-timer.h \ qnetd-dpd-timer.c qnetd-dpd-timer.h \ qnetd-ipc.c qnetd-ipc.h unix-socket-ipc.c unix-socket-ipc.h \ dynar-simple-lex.c dynar-simple-lex.h dynar-str.c dynar-str.h \ unix-socket-client.c unix-socket-client.h \ unix-socket-client-list.c unix-socket-client-list.h \ unix-socket.c unix-socket.h qnetd-ipc-cmd.c qnetd-ipc-cmd.h \ qnetd-poll-array-user-data.h qnet-config.h dynar-getopt-lex.c \ dynar-getopt-lex.h qnetd-advanced-settings.c qnetd-advanced-settings.h corosync_qnetd_tool_SOURCES = corosync-qnetd-tool.c unix-socket.c unix-socket.h dynar.c dynar.h \ dynar-str.c dynar-str.h corosync_qnetd_CFLAGS = $(nss_CFLAGS) corosync_qnetd_LDADD = $(nss_LIBS) corosync-qnetd-certutil: corosync-qnetd-certutil.sh sed -e 's#@''DATADIR@#${datadir}#g' \ -e 's#@''BASHPATH@#${BASHPATH}#g' \ -e 's#@''COROSYSCONFDIR@#${COROSYSCONFDIR}#g' \ $< > $@ endif if BUILD_QDEVICES sbin_PROGRAMS += corosync-qdevice corosync-qdevice-tool sbin_SCRIPTS += corosync-qdevice-net-certutil EXTRA_DIST += corosync-qdevice-net-certutil.sh corosync_qdevice_SOURCES = corosync-qdevice.c \ qdevice-cmap.c qdevice-cmap.h \ qdevice-instance.c qdevice-instance.h node-list.c node-list.h \ utils.c utils.h qdevice-log.c qdevice-log.h \ qdevice-log-debug.c qdevice-log-debug.h \ qdevice-votequorum.c qdevice-votequorum.h \ qdevice-model.c qdevice-model.h qdevice-model-net.c qdevice-model-net.h \ qdevice-net-instance.c qdevice-net-instance.h dynar.c dynar.h \ send-buffer-list.c send-buffer-list.h timer-list.c timer-list.h \ msg.c msg.h msgio.c msgio.h nss-sock.c nss-sock.h tlv.c tlv.h \ unix-socket.c unix-socket.h unix-socket-client.c unix-socket-client.h \ unix-socket-client-list.c unix-socket-client-list.h \ unix-socket-ipc.c unix-socket-ipc.h qdevice-ipc.c qdevice-ipc.h \ pr-poll-array.c pr-poll-array.h dynar-simple-lex.c dynar-simple-lex.h \ dynar-str.c dynar-str.h qdevice-ipc-cmd.c qdevice-ipc-cmd.h \ qdevice-net-ipc-cmd.c qdevice-net-ipc-cmd.h \ qdevice-net-poll.c qdevice-net-poll.h \ qdevice-net-send.c qdevice-net-send.h \ qdevice-net-votequorum.c qdevice-net-votequorum.h \ qdevice-net-socket.c qdevice-net-socket.h \ qdevice-net-nss.c qdevice-net-nss.h \ qdevice-net-msg-received.c qdevice-net-msg-received.h \ qdevice-net-cast-vote-timer.c qdevice-net-cast-vote-timer.h \ qdevice-net-echo-request-timer.c qdevice-net-echo-request-timer.h \ qdevice-net-algorithm.c qdevice-net-algorithm.h \ qdevice-net-algo-test.c qdevice-net-algo-test.h \ qdevice-net-algo-ffsplit.c qdevice-net-algo-ffsplit.h \ qdevice-net-algo-2nodelms.c qdevice-net-algo-2nodelms.h \ qdevice-net-algo-lms.c qdevice-net-algo-lms.h \ qdevice-net-poll-array-user-data.h \ qdevice-config.h qnet-config.h qdevice-net-disconnect-reason.h \ qdevice-model-type.h qdevice-advanced-settings.c \ qdevice-advanced-settings.h dynar-getopt-lex.c dynar-getopt-lex.h corosync_qdevice_tool_SOURCES = corosync-qdevice-tool.c unix-socket.c unix-socket.h dynar.c dynar.h \ dynar-str.c dynar-str.h corosync_qdevice_CFLAGS = $(nss_CFLAGS) corosync_qdevice_LDADD = $(nss_LIBS) $(LIBQB_LIBS) $(top_builddir)/lib/libcmap.la \ $(top_builddir)/lib/libvotequorum.la corosync-qdevice-net-certutil: corosync-qdevice-net-certutil.sh sed -e 's#@''DATADIR@#${datadir}#g' \ -e 's#@''BASHPATH@#${BASHPATH}#g' \ -e 's#@''COROSYSCONFDIR@#${COROSYSCONFDIR}#g' \ $< > $@ TESTS = qnetd-cluster-list.test dynar.test dynar-simple-lex.test \ dynar-getopt-lex.test check_PROGRAMS = qnetd-cluster-list.test dynar.test dynar-simple-lex.test \ dynar-getopt-lex.test qnetd_cluster_list_test_SOURCES = qnetd-cluster-list.c test-qnetd-cluster-list.c \ qnetd-cluster.c qnetd-cluster.h \ qnetd-client-list.c qnetd-client.c dynar.c node-list.c \ send-buffer-list.c qnetd_cluster_list_test_CFLAGS = $(nss_CFLAGS) qnetd_cluster_list_test_LDADD = $(nss_LIBS) dynar_test_SOURCES = test-dynar.c dynar.c dynar-str.c dynar_simple_lex_test_SOURCES = test-dynar-simple-lex.c dynar.c dynar-str.c dynar-simple-lex.c dynar_getopt_lex_test_SOURCES = test-dynar-getopt-lex.c dynar.c dynar-str.c dynar-getopt-lex.c endif diff --git a/qdevices/corosync-qdevice-net-certutil.sh b/qdevices/corosync-qdevice-net-certutil.sh index 341e61c3..ba0d5a69 100644 --- a/qdevices/corosync-qdevice-net-certutil.sh +++ b/qdevices/corosync-qdevice-net-certutil.sh @@ -1,400 +1,404 @@ #!@BASHPATH@ # # Copyright (c) 2015-2016 Red Hat, Inc. # # All rights reserved. # # Author: Jan Friesse (jfriesse@redhat.com) # # This software licensed under BSD license, the text of which follows: # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # - Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # - Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # - Neither the name of the Red Hat, Inc. nor the names of its # contributors may be used to endorse or promote products derived from this # software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. # BASE_DIR="@COROSYSCONFDIR@/qdevice/net" DB_DIR_QNETD="@COROSYSCONFDIR@/qnetd/nssdb" DB_DIR_NODE="$BASE_DIR/nssdb" # Validity of certificate (months) CRT_VALIDITY=1200 CA_NICKNAME="QNet CA" SERVER_NICKNAME="QNetd Cert" CLUSTER_NICKNAME="Cluster Cert" CA_SUBJECT="CN=QNet CA" SERVER_SUBJECT="CN=Qnetd Server" PWD_FILE_BASE="pwdfile.txt" NOISE_FILE_BASE="noise.txt" SERIAL_NO_FILE_BASE="serial.txt" CA_EXPORT_FILE="$DB_DIR_QNETD/qnetd-cacert.crt" CRQ_FILE_BASE="qdevice-net-node.crq" CRT_FILE_BASE="" # Generated from cluster name P12_FILE_BASE="qdevice-net-node.p12" QNETD_CERTUTIL_CMD="corosync-qnetd-certutil" usage() { echo "$0: [-i|-m|-M|-r|-s|-Q] [-c certificate] [-n cluster_name]" echo echo " -i Initialize node CA. Needs CA certificate from server" echo " -m Import cluster certificate on node (needs pk12 certificate)" echo " -r Generate cluster certificate request" echo " -M Import signed cluster certificate and export certificate with key to pk12 file" echo " -Q Quick start. Uses ssh/scp to initialze both qnetd and nodes." echo "" echo " -c certificate Ether CA, CRQ, CRT or pk12 certificate (operation dependant)" echo " -n cluster_name Name of cluster (for -r and -s operations)" echo "" echo "Typical usage:" echo "- Initialize database on QNetd server by running $QNETD_CERTUTIL_CMD -i" echo "- Copy exported QNetd CA certificate ($CA_EXPORT_FILE) to every node" echo "- On one of cluster node initialize database by running $0 -i -c `basename $CA_EXPORT_FILE`" echo "- Generate certificate request: $0 -r -n Cluster (Cluster name must match cluster_name key in the corosync.conf)" echo "- Copy exported CRQ to QNetd server" echo "- On QNetd server sign and export cluster certificate by running $QNETD_CERTUTIL_CMD -s -c `basename $CRQ_FILE_BASE` -n Cluster" echo "- Copy exported CRT to node where certificate request was created" echo "- Import certificate on node where certificate request was created by running $0 -M -c cluster-Cluster.crt" echo "- Copy output $P12_FILE_BASE to all other cluster nodes" echo "- On all other nodes in cluster:" echo " - Init database by running $0 -i -c `basename $CA_EXPORT_FILE`" echo " - Import cluster certificate and key: $0 -m -c `basename $P12_FILE_BASE`" echo "" echo "It is also possible to use Quick start (-Q). This needs properly configured ssh." echo " $0 -Q -n Cluster qnetd_server node1 node2 ... nodeN" exit 0 } create_new_noise_file() { local noise_file="$1" if [ ! -e "$noise_file" ];then echo "Creating new noise file $noise_file" (ps -elf; date; w) | sha1sum | (read sha_sum rest; echo $sha_sum) > "$noise_file" chown root:root "$noise_file" - chmod 600 "$noise_file" + chmod 660 "$noise_file" else echo "Using existing noise file $noise_file" fi } get_serial_no() { local serial_no if ! [ -f "$SERIAL_NO_FILE" ];then echo "100" > $SERIAL_NO_FILE + chown root:root "$DB_DIR" + chmod 660 "$SERIAL_NO_FILE" fi serial_no=`cat $SERIAL_NO_FILE` serial_no=$((serial_no+1)) echo "$serial_no" > $SERIAL_NO_FILE echo "$serial_no" } init_node_ca() { if [ -f "$DB_DIR/cert8.db" ];then echo "Certificate database already exists. Delete it to continue" >&2 exit 1 fi if ! [ -d "$DB_DIR" ];then echo "Creating $DB_DIR" mkdir -p "$DB_DIR" chown root:root "$DB_DIR" - chmod 700 "$DB_DIR" + chmod 770 "$DB_DIR" fi echo "Creating new key and cert db" echo -n "" > "$PWD_FILE" + chown root:root "$PWD_FILE" + chmod 660 "$PWD_FILE" certutil -N -d "$DB_DIR" -f "$PWD_FILE" chown root:root "$DB_DIR/key3.db" "$DB_DIR/cert8.db" "$DB_DIR/secmod.db" - chmod 600 "$DB_DIR/key3.db" "$DB_DIR/cert8.db" "$DB_DIR/secmod.db" + chmod 660 "$DB_DIR/key3.db" "$DB_DIR/cert8.db" "$DB_DIR/secmod.db" create_new_noise_file "$NOISE_FILE" echo "Importing CA" certutil -d "$DB_DIR" -A -t "CT,c,c" -n "$CA_NICKNAME" -f "$PWD_FILE" \ -i "$CERTIFICATE_FILE" } gen_cluster_cert_req() { if ! [ -f "$DB_DIR/cert8.db" ];then echo "Certificate database doesn't exists. Use $0 -i to create it" >&2 exit 1 fi echo "Creating new certificate request" certutil -R -s "CN=$CLUSTER_NAME" -o "$CRQ_FILE" -d "$DB_DIR" -f "$PWD_FILE" -z "$NOISE_FILE" echo "Certificate request stored in $CRQ_FILE" } import_signed_cert() { if ! [ -f "$DB_DIR/cert8.db" ];then echo "Certificate database doesn't exists. Use $0 -i to create it" >&2 exit 1 fi echo "Importing signed cluster certificate" certutil -d "$DB_DIR" -A -t "u,u,u" -n "$CLUSTER_NICKNAME" -i "$CERTIFICATE_FILE" pk12util -d "$DB_DIR" -o "$P12_FILE" -W "" -n "$CLUSTER_NICKNAME" echo "Certificate stored in $P12_FILE" } import_pk12() { if ! [ -f "$DB_DIR/cert8.db" ];then echo "Certificate database doesn't exists. Use $0 -i to create it" >&2 exit 1 fi echo "Importing cluster certificate and key" pk12util -i "$CERTIFICATE_FILE" -d "$DB_DIR" -W "" } quick_start() { qnetd_addr="$1" master_node="$2" other_nodes="$3" # Sanity check for i in "$master_node" $other_nodes;do - if ssh root@$i "[ -d \"$DB_DIR_QNETD\" ] || [ -d \"$DB_DIR_NODE\" ]";then - echo "Node $i seems to be already initialized. Please delete $DB_DIR_QNETD and $DB_DIR_NODE" >&2 + if ssh root@$i "[ -d \"$DB_DIR_NODE\" ]";then + echo "Node $i seems to be already initialized. Please delete $DB_DIR_NODE" >&2 exit 1 fi if ! ssh "root@$i" "$0" > /dev/null;then echo "Node $i doesn't have $0 installed" >&2 exit 1 fi done # Initialize qnetd server (it's no problem if server is already initialized) ssh "root@$qnetd_addr" "$QNETD_CERTUTIL_CMD -i" # Copy CA cert to all nodes and initialize them for node in "$master_node" $other_nodes;do scp "root@$qnetd_addr:$CA_EXPORT_FILE" "$node:/tmp" ssh "root@$node" "$0 -i -c \"/tmp/`basename $CA_EXPORT_FILE`\" && rm /tmp/`basename $CA_EXPORT_FILE`" done # Generate cert request ssh "root@$master_node" "$0 -r -n \"$CLUSTER_NAME\"" # Copy exported cert request to qnetd server scp "root@$master_node:$DB_DIR_NODE/$CRQ_FILE_BASE" "root@$qnetd_addr:/tmp" # Sign and export cluster certificate ssh "root@$qnetd_addr" "$QNETD_CERTUTIL_CMD -s -c \"/tmp/$CRQ_FILE_BASE\" -n \"$CLUSTER_NAME\"" # Copy exported CRT to master node scp "root@$qnetd_addr:$DB_DIR_QNETD/cluster-$CLUSTER_NAME.crt" "root@$master_node:$DB_DIR_NODE" # Import certificate ssh "root@$master_node" "$0 -M -c \"$DB_DIR_NODE/cluster-$CLUSTER_NAME.crt\"" # Copy pk12 cert to all nodes and import it for node in $other_nodes;do scp "root@$master_node:$DB_DIR_NODE/$P12_FILE" "$node:$DB_DIR_NODE/$P12_FILE" ssh "root@$node" "$0 -m -c \"$DB_DIR_NODE/$P12_FILE\"" done } OPERATION="" CERTIFICATE_FILE="" CLUSTER_NAME="" while getopts ":hiMmQrc:n:" opt; do case $opt in r) OPERATION=gen_cluster_cert_req ;; i) OPERATION=init_node_ca ;; m) OPERATION=import_pk12 ;; M) OPERATION=import_signed_cert ;; Q) OPERATION=quick_start ;; n) CLUSTER_NAME="$OPTARG" ;; h) usage ;; c) CERTIFICATE_FILE="$OPTARG" ;; \?) echo "Invalid option: -$OPTARG" >&2 exit 1 ;; :) echo "Option -$OPTARG requires an argument." >&2 exit 1 ;; esac done case "$OPERATION" in "init_qnetd_ca") DB_DIR="$DB_DIR_QNETD" ;; "init_node_ca") DB_DIR="$DB_DIR_NODE" ;; "gen_cluster_cert_req") DB_DIR="$DB_DIR_NODE" ;; "sign_cluster_cert") DB_DIR="$DB_DIR_QNETD" ;; "import_signed_cert") DB_DIR="$DB_DIR_NODE" ;; "import_pk12") DB_DIR="$DB_DIR_NODE" ;; "quick_start") DB_DIR="" ;; *) usage ;; esac PWD_FILE="$DB_DIR/$PWD_FILE_BASE" NOISE_FILE="$DB_DIR/$NOISE_FILE_BASE" SERIAL_NO_FILE="$DB_DIR/$SERIAL_NO_FILE_BASE" CRQ_FILE="$DB_DIR/$CRQ_FILE_BASE" CRT_FILE="$DB_DIR/cluster-$CLUSTER_NAME.crt" P12_FILE="$DB_DIR/$P12_FILE_BASE" case "$OPERATION" in "init_qnetd_ca") init_qnetd_ca ;; "init_node_ca") if ! [ -e "$CERTIFICATE_FILE" ];then echo "Can't open certificate file $CERTIFICATE_FILE" >&2 exit 2 fi init_node_ca ;; "gen_cluster_cert_req") if [ "$CLUSTER_NAME" == "" ];then echo "You have to specify cluster name" >&2 exit 2 fi gen_cluster_cert_req ;; "sign_cluster_cert") if ! [ -e "$CERTIFICATE_FILE" ];then echo "Can't open certificate file $CERTIFICATE_FILE" >&2 exit 2 fi if [ "$CLUSTER_NAME" == "" ];then echo "You have to specify cluster name" >&2 exit 2 fi sign_cluster_cert ;; "import_signed_cert") if ! [ -e "$CERTIFICATE_FILE" ];then echo "Can't open certificate file $CERTIFICATE_FILE" >&2 exit 2 fi import_signed_cert ;; "import_pk12") if ! [ -e "$CERTIFICATE_FILE" ];then echo "Can't open certificate file $CERTIFICATE_FILE" >&2 exit 2 fi import_pk12 ;; "quick_start") shift $((OPTIND-1)) qnetd_addr="$1" shift 1 master_node="$1" shift 1 other_nodes="$@" if [ "$CLUSTER_NAME" == "" ];then echo "You have to specify cluster name" >&2 exit 2 fi if [ "$qnetd_addr" == "" ];then echo "No QNetd server address provided." >&2 exit 2 fi if [ "$master_node" == "" ];then echo "No nodes provided." >&2 exit 2 fi quick_start "$qnetd_addr" "$master_node" "$other_nodes" ;; *) usage ;; esac diff --git a/qdevices/corosync-qnetd-certutil.sh b/qdevices/corosync-qnetd-certutil.sh index 60040220..7b2ed2da 100644 --- a/qdevices/corosync-qnetd-certutil.sh +++ b/qdevices/corosync-qnetd-certutil.sh @@ -1,215 +1,215 @@ #!@BASHPATH@ # # Copyright (c) 2015-2016 Red Hat, Inc. # # All rights reserved. # # Author: Jan Friesse (jfriesse@redhat.com) # # This software licensed under BSD license, the text of which follows: # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # - Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # - Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # - Neither the name of the Red Hat, Inc. nor the names of its # contributors may be used to endorse or promote products derived from this # software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. # CONFIG_DIR="@COROSYSCONFDIR@/qnetd" DB_DIR="$CONFIG_DIR/nssdb" # Validity of certificate (months) CRT_VALIDITY=1200 CA_NICKNAME="QNet CA" SERVER_NICKNAME="QNetd Cert" CLUSTER_NICKNAME="Cluster Cert" CA_SUBJECT="CN=QNet CA" SERVER_SUBJECT="CN=Qnetd Server" PWD_FILE="$DB_DIR/pwdfile.txt" NOISE_FILE="$DB_DIR/noise.txt" SERIAL_NO_FILE="$DB_DIR/serial.txt" CA_EXPORT_FILE="$DB_DIR/qnetd-cacert.crt" CRT_FILE_BASE="" # Generated from cluster name usage() { echo "$0: [-i|-s] [-c certificate]" echo echo " -i Initialize QNetd CA and generate server certificate" echo " -s Sign cluster certificate (needs cluster certificate)" echo " -c certificate CRQ certificate file name" echo " -n cluster_name Name of cluster (for -s operation)" exit 0 } chown_ref_cfgdir() { if [ "$UID" == "0" ];then chown --reference="$CONFIG_DIR" "$@" 2>/dev/null || chown `stat -f "%u:%g" "$CONFIG_DIR"` "$@" 2>/dev/null || return $? fi } create_new_noise_file() { local noise_file="$1" if [ ! -e "$noise_file" ];then echo "Creating new noise file $noise_file" (ps -elf; date; w) | sha1sum | (read sha_sum rest; echo $sha_sum) > "$noise_file" chown_ref_cfgdir "$noise_file" - chmod 600 "$noise_file" + chmod 660 "$noise_file" else echo "Using existing noise file $noise_file" fi } get_serial_no() { local serial_no if ! [ -f "$SERIAL_NO_FILE" ];then echo "100" > $SERIAL_NO_FILE chown_ref_cfgdir "$SERIAL_NO_FILE" - chmod 600 "$SERIAL_NO_FILE" + chmod 660 "$SERIAL_NO_FILE" fi serial_no=`cat $SERIAL_NO_FILE` serial_no=$((serial_no+1)) echo "$serial_no" > $SERIAL_NO_FILE echo "$serial_no" } init_qnetd_ca() { if [ -f "$DB_DIR/cert8.db" ];then echo "Certificate database ($DB_DIR) already exists. Delete it to initialize new db" >&2 exit 1 fi if ! [ -d "$DB_DIR" ];then echo "Creating $DB_DIR" mkdir -p "$DB_DIR" chown_ref_cfgdir "$DB_DIR" - chmod 700 "$DB_DIR" + chmod 770 "$DB_DIR" fi echo "Creating new key and cert db" echo -n "" > "$PWD_FILE" chown_ref_cfgdir "$PWD_FILE" - chmod 600 "$PWD_FILE" + chmod 660 "$PWD_FILE" certutil -N -d "$DB_DIR" -f "$PWD_FILE" chown_ref_cfgdir "$DB_DIR/key3.db" "$DB_DIR/cert8.db" "$DB_DIR/secmod.db" - chmod 600 "$DB_DIR/key3.db" "$DB_DIR/cert8.db" "$DB_DIR/secmod.db" + chmod 660 "$DB_DIR/key3.db" "$DB_DIR/cert8.db" "$DB_DIR/secmod.db" create_new_noise_file "$NOISE_FILE" echo "Creating new CA" # Create self-signed certificate (CA). Asks 3 questions (is this CA, lifetime and critical extension echo -e "y\n0\ny\n" | certutil -S -n "$CA_NICKNAME" -s "$CA_SUBJECT" -x \ -t "CT,," -m `get_serial_no` -v $CRT_VALIDITY -d "$DB_DIR" \ -z "$NOISE_FILE" -f "$PWD_FILE" -2 # Export CA certificate in ascii certutil -L -d "$DB_DIR" -n "$CA_NICKNAME" > "$CA_EXPORT_FILE" certutil -L -d "$DB_DIR" -n "$CA_NICKNAME" -a >> "$CA_EXPORT_FILE" chown_ref_cfgdir "$CA_EXPORT_FILE" certutil -S -n "$SERVER_NICKNAME" -s "$SERVER_SUBJECT" -c "$CA_NICKNAME" -t "u,u,u" -m `get_serial_no` \ -v $CRT_VALIDITY -d "$DB_DIR" -z "$NOISE_FILE" -f "$PWD_FILE" echo "QNetd CA certificate is exported as $CA_EXPORT_FILE" } sign_cluster_cert() { if ! [ -f "$DB_DIR/cert8.db" ];then echo "Certificate database doesn't exists. Use $0 -I to create it" >&2 exit 1 fi echo "Signing cluster certificate" certutil -C -v "$CRT_VALIDITY" -m `get_serial_no` -i "$CERTIFICATE_FILE" -o "$CRT_FILE" -c "$CA_NICKNAME" -d "$DB_DIR" chown_ref_cfgdir "$CRT_FILE" echo "Certificate stored in $CRT_FILE" } OPERATION="" CERTIFICATE_FILE="" CLUSTER_NAME="" while getopts ":hisc:n:" opt; do case $opt in i) OPERATION=init_qnetd_ca ;; s) OPERATION=sign_cluster_cert ;; h) usage ;; c) CERTIFICATE_FILE="$OPTARG" ;; n) CLUSTER_NAME="$OPTARG" ;; \?) echo "Invalid option: -$OPTARG" >&2 exit 1 ;; :) echo "Option -$OPTARG requires an argument." >&2 exit 1 ;; esac done [ "$OPERATION" == "" ] && usage CRT_FILE="$DB_DIR/cluster-$CLUSTER_NAME.crt" case "$OPERATION" in "init_qnetd_ca") init_qnetd_ca ;; "sign_cluster_cert") if ! [ -e "$CERTIFICATE_FILE" ];then echo "Can't open certificate file $CERTIFICATE_FILE" >&2 exit 2 fi if [ "$CLUSTER_NAME" == "" ];then echo "You have to specify cluster name" >&2 exit 2 fi sign_cluster_cert ;; *) usage ;; esac diff --git a/qdevices/qnetd-algo-ffsplit.c b/qdevices/qnetd-algo-ffsplit.c index c734a7f1..8ae73d61 100644 --- a/qdevices/qnetd-algo-ffsplit.c +++ b/qdevices/qnetd-algo-ffsplit.c @@ -1,792 +1,805 @@ /* * Copyright (c) 2015-2016 Red Hat, Inc. * * All rights reserved. * * Author: Jan Friesse (jfriesse@redhat.com) * * This software licensed under BSD license, the text of which follows: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the Red Hat, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "qnetd-algo-ffsplit.h" #include "qnetd-log.h" #include "qnetd-log-debug.h" #include "qnetd-cluster-list.h" #include "qnetd-cluster.h" #include "qnetd-client-send.h" enum qnetd_algo_ffsplit_cluster_state { QNETD_ALGO_FFSPLIT_CLUSTER_STATE_WAITING_FOR_CHANGE, QNETD_ALGO_FFSPLIT_CLUSTER_STATE_WAITING_FOR_STABLE_MEMBERSHIP, QNETD_ALGO_FFSPLIT_CLUSTER_STATE_SENDING_NACKS, QNETD_ALGO_FFSPLIT_CLUSTER_STATE_SENDING_ACKS, }; struct qnetd_algo_ffsplit_cluster_data { enum qnetd_algo_ffsplit_cluster_state cluster_state; const struct node_list *quorate_partition_node_list; }; enum qnetd_algo_ffsplit_client_state { QNETD_ALGO_FFSPLIT_CLIENT_STATE_WAITING_FOR_CHANGE, QNETD_ALGO_FFSPLIT_CLIENT_STATE_SENDING_NACK, QNETD_ALGO_FFSPLIT_CLIENT_STATE_SENDING_ACK, }; struct qnetd_algo_ffsplit_client_data { enum qnetd_algo_ffsplit_client_state client_state; uint32_t vote_info_expected_seq_num; }; enum tlv_reply_error_code qnetd_algo_ffsplit_client_init(struct qnetd_client *client) { struct qnetd_algo_ffsplit_cluster_data *cluster_data; struct qnetd_algo_ffsplit_client_data *client_data; if (qnetd_cluster_size(client->cluster) == 1) { cluster_data = malloc(sizeof(*cluster_data)); if (cluster_data == NULL) { qnetd_log(LOG_ERR, "ffsplit: Can't initialize cluster data for client %s", client->addr_str); return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } memset(cluster_data, 0, sizeof(*cluster_data)); cluster_data->cluster_state = QNETD_ALGO_FFSPLIT_CLUSTER_STATE_WAITING_FOR_CHANGE; cluster_data->quorate_partition_node_list = NULL; client->cluster->algorithm_data = cluster_data; } client_data = malloc(sizeof(*client_data)); if (client_data == NULL) { qnetd_log(LOG_ERR, "ffsplit: Can't initialize node data for client %s", client->addr_str); return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR); } memset(client_data, 0, sizeof(*client_data)); client_data->client_state = QNETD_ALGO_FFSPLIT_CLIENT_STATE_WAITING_FOR_CHANGE; client->algorithm_data = client_data; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } static int qnetd_algo_ffsplit_is_prefered_partition(const struct qnetd_client *client, const struct node_list *config_node_list, const struct node_list *membership_node_list) { uint32_t prefered_node_id; struct node_list_entry *node_entry; + int case_processed; + + prefered_node_id = 0; + case_processed = 0; switch (client->tie_breaker.mode) { case TLV_TIE_BREAKER_MODE_LOWEST: node_entry = TAILQ_FIRST(config_node_list); prefered_node_id = node_entry->node_id; TAILQ_FOREACH(node_entry, config_node_list, entries) { if (node_entry->node_id < prefered_node_id) { prefered_node_id = node_entry->node_id; } } + case_processed = 1; break; case TLV_TIE_BREAKER_MODE_HIGHEST: node_entry = TAILQ_FIRST(config_node_list); prefered_node_id = node_entry->node_id; TAILQ_FOREACH(node_entry, config_node_list, entries) { if (node_entry->node_id > prefered_node_id) { prefered_node_id = node_entry->node_id; } } + case_processed = 1; break; case TLV_TIE_BREAKER_MODE_NODE_ID: prefered_node_id = client->tie_breaker.node_id; + case_processed = 1; break; } + if (!case_processed) { + qnetd_log(LOG_CRIT, "qnetd_algo_ffsplit_is_prefered_partition unprocessed " + "tie_breaker.mode"); + exit(1); + } + return (node_list_find_node_id(membership_node_list, prefered_node_id) != NULL); } static int qnetd_algo_ffsplit_is_membership_stable(const struct qnetd_client *client, int client_leaving, const struct tlv_ring_id *ring_id, const struct node_list *config_node_list, const struct node_list *membership_node_list) { const struct qnetd_client *iter_client1, *iter_client2; const struct node_list *config_node_list1, *config_node_list2; const struct node_list *membership_node_list1, *membership_node_list2; const struct node_list_entry *iter_node1, *iter_node2; const struct node_list_entry *iter_node3, *iter_node4; const struct tlv_ring_id *ring_id1, *ring_id2; /* * Test if all active clients share same config list. */ TAILQ_FOREACH(iter_client1, &client->cluster->client_list, cluster_entries) { TAILQ_FOREACH(iter_client2, &client->cluster->client_list, cluster_entries) { if (iter_client1 == iter_client2) { continue; } if (iter_client1->node_id == client->node_id) { if (client_leaving) { continue; } config_node_list1 = config_node_list; } else { config_node_list1 = &iter_client1->configuration_node_list; } if (iter_client2->node_id == client->node_id) { if (client_leaving) { continue; } config_node_list2 = config_node_list; } else { config_node_list2 = &iter_client2->configuration_node_list; } /* * Walk thru all node ids in given config node list... */ TAILQ_FOREACH(iter_node1, config_node_list1, entries) { /* * ... and try to find given node id in other list */ iter_node2 = node_list_find_node_id(config_node_list2, iter_node1->node_id); if (iter_node2 == NULL) { /* * Node with iter_node1->node_id was not found in * config_node_list2 -> lists doesn't match */ return (0); } } } } /* * Test if same partitions share same ring ids and membership node list */ TAILQ_FOREACH(iter_client1, &client->cluster->client_list, cluster_entries) { if (iter_client1->node_id == client->node_id) { if (client_leaving) { continue; } membership_node_list1 = membership_node_list; ring_id1 = ring_id; } else { membership_node_list1 = &iter_client1->last_membership_node_list; ring_id1 = &iter_client1->last_ring_id; } /* * Walk thru all memberships nodes */ TAILQ_FOREACH(iter_node1, membership_node_list1, entries) { /* * try to find client with given node id */ iter_client2 = qnetd_cluster_find_client_by_node_id(client->cluster, iter_node1->node_id); if (iter_client2 == NULL) { /* * Client with given id is not connected */ continue; } if (iter_client2->node_id == client->node_id) { if (client_leaving) { continue; } membership_node_list2 = membership_node_list; ring_id2 = ring_id; } else { membership_node_list2 = &iter_client2->last_membership_node_list; ring_id2 = &iter_client2->last_ring_id; } /* * Compare ring ids */ if (!tlv_ring_id_eq(ring_id1, ring_id2)) { return (0); } /* * Now compare that membership node list equals, so walk thru all * members ... */ TAILQ_FOREACH(iter_node3, membership_node_list1, entries) { /* * ... and try to find given node id in other membership node list */ iter_node4 = node_list_find_node_id(membership_node_list2, iter_node3->node_id); if (iter_node4 == NULL) { /* * Node with iter_node3->node_id was not found in * membership_node_list2 -> lists doesn't match */ return (0); } } } } return (1); } static size_t qnetd_algo_ffsplit_no_active_clients_in_partition(const struct qnetd_client *client, const struct node_list *membership_node_list) { const struct node_list_entry *iter_node; const struct qnetd_client *iter_client; size_t res; res = 0; if (client == NULL || membership_node_list == NULL) { return (0); } TAILQ_FOREACH(iter_node, membership_node_list, entries) { iter_client = qnetd_cluster_find_client_by_node_id(client->cluster, iter_node->node_id); if (iter_client != NULL) { res++; } } return (res); } /* * Compares two partitions. Return 1 if client1, config_node_list1, membership_node_list1 is * "better" than client2, config_node_list2, membership_node_list2 */ static int qnetd_algo_ffsplit_partition_cmp(const struct qnetd_client *client1, const struct node_list *config_node_list1, const struct node_list *membership_node_list1, const struct qnetd_client *client2, const struct node_list *config_node_list2, const struct node_list *membership_node_list2) { size_t part1_active_clients, part2_active_clients; if (node_list_size(config_node_list1) % 2 != 0) { /* * Odd clusters never split into 50:50. */ if (node_list_size(membership_node_list1) > node_list_size(config_node_list1) / 2) { return (1); } else { return (0); } } else { if (node_list_size(membership_node_list1) > node_list_size(config_node_list1) / 2) { return (1); } else if (node_list_size(membership_node_list1) < node_list_size(config_node_list1) / 2) { return (0); } /* * 50:50 split */ /* * Check how many active clients are in partitions */ part1_active_clients = qnetd_algo_ffsplit_no_active_clients_in_partition( client1, membership_node_list1); part2_active_clients = qnetd_algo_ffsplit_no_active_clients_in_partition( client2, membership_node_list2); if (part1_active_clients > part2_active_clients) { return (1); } else if (part1_active_clients < part2_active_clients) { return (0); } /* * Number of active clients in both partitions equals. Use tie-breaker. */ if (qnetd_algo_ffsplit_is_prefered_partition(client1, config_node_list1, membership_node_list1)) { return (1); } else { return (0); } } qnetd_log(LOG_CRIT, "qnetd_algo_ffsplit_partition_cmp unhandled case"); exit(1); /* NOTREACHED */ } /* * Select best partition for given client->cluster. * If there is no partition which could become quorate, NULL is returned */ static const struct node_list * qnetd_algo_ffsplit_select_partition(const struct qnetd_client *client, int client_leaving, const struct node_list *config_node_list, const struct node_list *membership_node_list) { const struct qnetd_client *iter_client; const struct qnetd_client *best_client; const struct node_list *best_config_node_list, *best_membership_node_list; const struct node_list *iter_config_node_list, *iter_membership_node_list; best_client = NULL; best_config_node_list = best_membership_node_list = NULL; /* * Get highest score */ TAILQ_FOREACH(iter_client, &client->cluster->client_list, cluster_entries) { if (iter_client->node_id == client->node_id) { if (client_leaving) { continue; } iter_config_node_list = config_node_list; iter_membership_node_list = membership_node_list; } else { iter_config_node_list = &iter_client->configuration_node_list; iter_membership_node_list = &iter_client->last_membership_node_list; } if (qnetd_algo_ffsplit_partition_cmp(iter_client, iter_config_node_list, iter_membership_node_list, best_client, best_config_node_list, best_membership_node_list) > 0) { best_client = iter_client; best_config_node_list = iter_config_node_list; best_membership_node_list = iter_membership_node_list; } } return (best_membership_node_list); } /* * Update state of all nodes to match quorate_partition_node_list */ static void qnetd_algo_ffsplit_update_nodes_state(struct qnetd_client *client, int client_leaving, const struct node_list *quorate_partition_node_list) { const struct qnetd_client *iter_client; struct qnetd_algo_ffsplit_client_data *iter_client_data; TAILQ_FOREACH(iter_client, &client->cluster->client_list, cluster_entries) { iter_client_data = (struct qnetd_algo_ffsplit_client_data *)iter_client->algorithm_data; if (iter_client->node_id == client->node_id && client_leaving) { iter_client_data->client_state = QNETD_ALGO_FFSPLIT_CLIENT_STATE_WAITING_FOR_CHANGE; continue; } if (quorate_partition_node_list == NULL || node_list_find_node_id(quorate_partition_node_list, iter_client->node_id) == NULL) { iter_client_data->client_state = QNETD_ALGO_FFSPLIT_CLIENT_STATE_SENDING_NACK; } else { iter_client_data->client_state = QNETD_ALGO_FFSPLIT_CLIENT_STATE_SENDING_ACK; } } } /* * Send vote info. If client_leaving is set, client is ignored. if send_acks * is set, only ACK votes are send (nodes in QNETD_ALGO_FFSPLIT_CLIENT_STATE_SENDING_ACK state), * otherwise only NACK votes are send (nodes in QNETD_ALGO_FFSPLIT_CLIENT_STATE_SENDING_NACK state) * * Returns number of send votes */ static size_t qnetd_algo_ffsplit_send_votes(struct qnetd_client *client, int client_leaving, const struct tlv_ring_id *ring_id, int send_acks) { size_t sent_votes; struct qnetd_client *iter_client; struct qnetd_algo_ffsplit_client_data *iter_client_data; const struct tlv_ring_id *ring_id_to_send; enum tlv_vote vote_to_send; sent_votes = 0; TAILQ_FOREACH(iter_client, &client->cluster->client_list, cluster_entries) { if (iter_client->node_id == client->node_id) { if (client_leaving) { continue; } ring_id_to_send = ring_id; } else { ring_id_to_send = &iter_client->last_ring_id; } iter_client_data = (struct qnetd_algo_ffsplit_client_data *)iter_client->algorithm_data; vote_to_send = TLV_VOTE_UNDEFINED; if (send_acks) { if (iter_client_data->client_state == QNETD_ALGO_FFSPLIT_CLIENT_STATE_SENDING_ACK) { vote_to_send = TLV_VOTE_ACK; } } else { if (iter_client_data->client_state == QNETD_ALGO_FFSPLIT_CLIENT_STATE_SENDING_NACK) { vote_to_send = TLV_VOTE_NACK; } } if (vote_to_send != TLV_VOTE_UNDEFINED) { iter_client_data->vote_info_expected_seq_num++; sent_votes++; if (qnetd_client_send_vote_info(iter_client, iter_client_data->vote_info_expected_seq_num, ring_id_to_send, vote_to_send) == -1) { client->schedule_disconnect = 1; } } } return (sent_votes); } /* * Return number of clients in QNETD_ALGO_FFSPLIT_CLIENT_STATE_SENDING_ACK state if sending_acks is * set or number of nodes in QNETD_ALGO_FFSPLIT_CLIENT_STATE_SENDING_NACK state if sending_acks is * not set */ static size_t qnetd_algo_ffsplit_no_clients_in_sending_state(struct qnetd_client *client, int sending_acks) { size_t no_clients; struct qnetd_client *iter_client; struct qnetd_algo_ffsplit_client_data *iter_client_data; no_clients = 0; TAILQ_FOREACH(iter_client, &client->cluster->client_list, cluster_entries) { iter_client_data = (struct qnetd_algo_ffsplit_client_data *)iter_client->algorithm_data; if (sending_acks && iter_client_data->client_state == QNETD_ALGO_FFSPLIT_CLIENT_STATE_SENDING_ACK) { no_clients++; } if (!sending_acks && iter_client_data->client_state == QNETD_ALGO_FFSPLIT_CLIENT_STATE_SENDING_NACK) { no_clients++; } } return (no_clients); } static enum tlv_vote qnetd_algo_ffsplit_do(struct qnetd_client *client, int client_leaving, const struct tlv_ring_id *ring_id, const struct node_list *config_node_list, const struct node_list *membership_node_list) { struct qnetd_algo_ffsplit_cluster_data *cluster_data; const struct node_list *quorate_partition_node_list; cluster_data = (struct qnetd_algo_ffsplit_cluster_data *)client->cluster->algorithm_data; cluster_data->cluster_state = QNETD_ALGO_FFSPLIT_CLUSTER_STATE_WAITING_FOR_STABLE_MEMBERSHIP; if (!qnetd_algo_ffsplit_is_membership_stable(client, client_leaving, ring_id, config_node_list, membership_node_list)) { /* * Wait until membership is stable */ qnetd_log(LOG_DEBUG, "ffsplit: Membership for cluster %s is not yet stable", client->cluster_name); return (TLV_VOTE_WAIT_FOR_REPLY); } qnetd_log(LOG_DEBUG, "ffsplit: Membership for cluster %s is now stable", client->cluster_name); quorate_partition_node_list = qnetd_algo_ffsplit_select_partition(client, client_leaving, config_node_list, membership_node_list); cluster_data->quorate_partition_node_list = quorate_partition_node_list; if (quorate_partition_node_list == NULL) { qnetd_log(LOG_DEBUG, "ffsplit: No quorate partition was selected"); } else { qnetd_log(LOG_DEBUG, "ffsplit: Quorate partition selected"); qnetd_log_debug_dump_node_list(client, quorate_partition_node_list); } qnetd_algo_ffsplit_update_nodes_state(client, client_leaving, quorate_partition_node_list); cluster_data->cluster_state = QNETD_ALGO_FFSPLIT_CLUSTER_STATE_SENDING_NACKS; if (qnetd_algo_ffsplit_send_votes(client, client_leaving, ring_id, 0) == 0) { qnetd_log(LOG_DEBUG, "ffsplit: No client gets NACK"); /* * No one gets nack -> send acks */ cluster_data->cluster_state = QNETD_ALGO_FFSPLIT_CLUSTER_STATE_SENDING_ACKS; if (qnetd_algo_ffsplit_send_votes(client, client_leaving, ring_id, 1) == 0) { qnetd_log(LOG_DEBUG, "ffsplit: No client gets ACK"); /* * No one gets acks -> finished */ cluster_data->cluster_state = QNETD_ALGO_FFSPLIT_CLUSTER_STATE_WAITING_FOR_CHANGE; } } return (TLV_VOTE_NO_CHANGE); } enum tlv_reply_error_code qnetd_algo_ffsplit_config_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, int config_version_set, uint64_t config_version, const struct node_list *nodes, int initial, enum tlv_vote *result_vote) { if (node_list_size(nodes) == 0) { /* * Empty node list shouldn't happen */ qnetd_log(LOG_ERR, "ffsplit: Received empty config node list for client %s", client->addr_str); return (TLV_REPLY_ERROR_CODE_INVALID_CONFIG_NODE_LIST); } if (node_list_find_node_id(nodes, client->node_id) == NULL) { /* * Current node is not in node list */ qnetd_log(LOG_ERR, "ffsplit: Received config node list without client %s", client->addr_str); return (TLV_REPLY_ERROR_CODE_INVALID_CONFIG_NODE_LIST); } if (initial || node_list_size(&client->last_membership_node_list) == 0) { /* * Initial node list -> membership is going to be send by client */ *result_vote = TLV_VOTE_ASK_LATER; } else { *result_vote = qnetd_algo_ffsplit_do(client, 0, &client->last_ring_id, nodes, &client->last_membership_node_list); } return (TLV_REPLY_ERROR_CODE_NO_ERROR); } /* * Called after client sent membership node list. * All client fields are already set. Nodes is actual node list. * msg_seq_num is 32-bit number set by client. If client sent config file version, * config_version_set is set to 1 and config_version contains valid config file version. * ring_id and quorate are copied from client votequorum callback. * * Function has to return result_vote. This can be one of ack/nack, ask_later (client * should ask later for a vote) or wait_for_reply (client should wait for reply). * * Return TLV_REPLY_ERROR_CODE_NO_ERROR on success, different TLV_REPLY_ERROR_CODE_* * on failure (error is send back to client) */ enum tlv_reply_error_code qnetd_algo_ffsplit_membership_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, const struct tlv_ring_id *ring_id, const struct node_list *nodes, enum tlv_vote *result_vote) { if (node_list_size(nodes) == 0) { /* * Empty node list shouldn't happen */ qnetd_log(LOG_ERR, "ffsplit: Received empty membership node list for client %s", client->addr_str); return (TLV_REPLY_ERROR_CODE_INVALID_MEMBERSHIP_NODE_LIST); } if (node_list_find_node_id(nodes, client->node_id) == NULL) { /* * Current node is not in node list */ qnetd_log(LOG_ERR, "ffsplit: Received membership node list without client %s", client->addr_str); return (TLV_REPLY_ERROR_CODE_INVALID_MEMBERSHIP_NODE_LIST); } if (node_list_size(&client->configuration_node_list) == 0) { /* * Config node list not received -> it's going to be sent later */ *result_vote = TLV_VOTE_ASK_LATER; } else { *result_vote = qnetd_algo_ffsplit_do(client, 0, ring_id, &client->configuration_node_list, nodes); } return (TLV_REPLY_ERROR_CODE_NO_ERROR); } enum tlv_reply_error_code qnetd_algo_ffsplit_quorum_node_list_received(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_quorate quorate, const struct node_list *nodes, enum tlv_vote *result_vote) { /* * Quorum node list is informative -> no change */ *result_vote = TLV_VOTE_NO_CHANGE; return (TLV_REPLY_ERROR_CODE_NO_ERROR); } void qnetd_algo_ffsplit_client_disconnect(struct qnetd_client *client, int server_going_down) { (void)qnetd_algo_ffsplit_do(client, 1, &client->last_ring_id, &client->configuration_node_list, &client->last_membership_node_list); free(client->algorithm_data); if (qnetd_cluster_size(client->cluster) == 1) { /* * Last client in the cluster */ free(client->cluster->algorithm_data); } } enum tlv_reply_error_code qnetd_algo_ffsplit_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_vote *result_vote) { /* * Ask for vote is not supported in current algorithm */ return (TLV_REPLY_ERROR_CODE_UNSUPPORTED_DECISION_ALGORITHM_MESSAGE); } enum tlv_reply_error_code qnetd_algo_ffsplit_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num) { struct qnetd_algo_ffsplit_cluster_data *cluster_data; struct qnetd_algo_ffsplit_client_data *client_data; cluster_data = (struct qnetd_algo_ffsplit_cluster_data *)client->cluster->algorithm_data; client_data = (struct qnetd_algo_ffsplit_client_data *)client->algorithm_data; if (client_data->vote_info_expected_seq_num != msg_seq_num) { qnetd_log(LOG_DEBUG, "ffsplit: Received old vote info reply from client %s", client->addr_str); return (TLV_REPLY_ERROR_CODE_NO_ERROR); } client_data->client_state = QNETD_ALGO_FFSPLIT_CLIENT_STATE_WAITING_FOR_CHANGE; if (cluster_data->cluster_state != QNETD_ALGO_FFSPLIT_CLUSTER_STATE_SENDING_NACKS && cluster_data->cluster_state != QNETD_ALGO_FFSPLIT_CLUSTER_STATE_SENDING_ACKS) { return (TLV_REPLY_ERROR_CODE_NO_ERROR); } if (cluster_data->cluster_state == QNETD_ALGO_FFSPLIT_CLUSTER_STATE_SENDING_NACKS) { if (qnetd_algo_ffsplit_no_clients_in_sending_state(client, 0) == 0) { qnetd_log(LOG_DEBUG, "ffsplit: All NACK votes sent for cluster %s", client->cluster_name); cluster_data->cluster_state = QNETD_ALGO_FFSPLIT_CLUSTER_STATE_SENDING_ACKS; if (qnetd_algo_ffsplit_send_votes(client, 0, &client->last_ring_id, 1) == 0) { qnetd_log(LOG_DEBUG, "ffsplit: No client gets ACK"); /* * No one gets acks -> finished */ cluster_data->cluster_state = QNETD_ALGO_FFSPLIT_CLUSTER_STATE_WAITING_FOR_CHANGE; } } } else { if (qnetd_algo_ffsplit_no_clients_in_sending_state(client, 1) == 0) { qnetd_log(LOG_DEBUG, "ffsplit: All ACK votes sent for cluster %s", client->cluster_name); cluster_data->cluster_state = QNETD_ALGO_FFSPLIT_CLUSTER_STATE_WAITING_FOR_CHANGE; } } return (TLV_REPLY_ERROR_CODE_NO_ERROR); } enum tlv_reply_error_code qnetd_algo_ffsplit_timer_callback(struct qnetd_client *client, int *reschedule_timer, int *send_vote, enum tlv_vote *result_vote) { return (TLV_REPLY_ERROR_CODE_NO_ERROR); } static struct qnetd_algorithm qnetd_algo_ffsplit = { .init = qnetd_algo_ffsplit_client_init, .config_node_list_received = qnetd_algo_ffsplit_config_node_list_received, .membership_node_list_received = qnetd_algo_ffsplit_membership_node_list_received, .quorum_node_list_received = qnetd_algo_ffsplit_quorum_node_list_received, .client_disconnect = qnetd_algo_ffsplit_client_disconnect, .ask_for_vote_received = qnetd_algo_ffsplit_ask_for_vote_received, .vote_info_reply_received = qnetd_algo_ffsplit_vote_info_reply_received, .timer_callback = qnetd_algo_ffsplit_timer_callback, }; enum tlv_reply_error_code qnetd_algo_ffsplit_register() { return (qnetd_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_FFSPLIT, &qnetd_algo_ffsplit)); }