--- /dev/null
+*.o
+*.a
+*.so*
+*.lo
+*.la
+.libs
+.deps
+.version
+doc
+Makefile
+Makefile.in
+corosync-qdevice.spec
+aclocal.m4
+autom4te.cache/
+compile
+config.guess
+config.log
+config.status
+config.sub
+configure
+corosync-*.tar*
+depcomp
+install-sh
+libtool
+ltmain.sh
+m4
+missing
+tags
+ID
+Doxyfile
+config.h*
+stamp-*
+test-driver
--- /dev/null
+-----------------------------------------------------------------------------
+The following license applies to every file in this source distribution except
+for the files git-version-gen, and gitlog-to-changelog.
+
+The git* files, which are available under GPLv3 or later, are only used by
+our release process to generate text file content and are not part of any
+generated binary.
+-----------------------------------------------------------------------------
+
+Copyright (c) 2015-2018 Red Hat, Inc.
+
+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 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.
+
+-----------------------------------------------------------------------------
+The corosync-qdevice project uses software for release processing which generates
+changelogs and version information for the software. These programs are not
+used by the generated binaries or libraries These files are git-version-gen
+and gitlog-to-changelog.
+-----------------------------------------------------------------------------
+The license for these files is as follows:
+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 3 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, see <http://www.gnu.org/licenses/>.
--- /dev/null
+# 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 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.
+
+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 \
+ autoscan.log configure.scan ltmain.sh test-driver
+
+dist_doc_DATA = LICENSE
+
+SUBDIRS = qdevices man init
+
+install-exec-local:
+if BUILD_QNETD
+ $(INSTALL) -m 770 -d $(DESTDIR)/${localstatedir}/run/corosync-qnetd
+ $(INSTALL) -m 770 -d $(DESTDIR)/${COROSYSCONFDIR}/qnetd
+endif
+if BUILD_QDEVICES
+ $(INSTALL) -m 770 -d $(DESTDIR)/${localstatedir}/run/corosync-qdevice
+ $(INSTALL) -d $(DESTDIR)/${COROSYSCONFDIR}/qdevice/
+ $(INSTALL) -m 770 -d $(DESTDIR)/${COROSYSCONFDIR}/qdevice/net
+endif
+
+uninstall-local:
+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
+
+dist-clean-local:
+ rm -f autoconf automake autoheader
+
+clean-generic:
+ rm -rf doc/api $(SPEC) $(TARFILE)
+
+## 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
--- /dev/null
+Corosync-qdevice
+----------------
+corosync-qdevice is a daemon running on each node of a cluster. It provides
+a configured number of votes to the quorum subsystem based on a third-party
+arbitrator's decision. Its primary use is to allow a cluster to sustain more
+node failures than standard quorum rules allow. It is recommended for clusters
+with an even number of nodes and highly recommended for 2 node clusters.
+
+corosync-qnetd is a daemon running outside of the cluster with the purpose
+of providing a vote to the corosync-qdevice model net. It's designed to
+support multiple clusters and be almost configuration and state free.
+New clusters are handled dynamically and no configuration file exists.
+It's also able to run as non-root user - which is recommended.
+Connection between the corosync-qdevice model net client can be optionally
+configured with TLS client certificate checking. The communication protocol
+between server and client is designed to be very simple and allow
+backwards compatibility.
+
+Originally both qdevice and qnetd were part of the Corosync codebase
+(https://github.com/corosync/corosync) but because it's got quite big we
+decided to split it into it's own sub project.
+
+Dependencies
+------------
+* Corosync >= 2.0
+* NSS
+
+Installation
+------------
+$ ./autogen.sh
+$ ./configure
+$ make
+$ sudo make install
--- /dev/null
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+mkdir -p m4
+echo Building configuration system...
+autoreconf -i && echo Now run ./configure and make
--- /dev/null
+#!/bin/sh
+# Print a version string.
+scriptversion=2010-10-13.20; # UTC
+
+# Copyright (C) 2007-2010 Free Software Foundation, Inc.
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
+# It may be run two ways:
+# - from a git repository in which the "git describe" command below
+# produces useful output (thus requiring at least one signed tag)
+# - from a non-git-repo directory containing a .tarball-version file, which
+# presumes this script is invoked like "./git-version-gen .tarball-version".
+
+# In order to use intra-version strings in your project, you will need two
+# separate generated version string files:
+#
+# .tarball-version - present only in a distribution tarball, and not in
+# a checked-out repository. Created with contents that were learned at
+# the last time autoconf was run, and used by git-version-gen. Must not
+# be present in either $(srcdir) or $(builddir) for git-version-gen to
+# give accurate answers during normal development with a checked out tree,
+# but must be present in a tarball when there is no version control system.
+# Therefore, it cannot be used in any dependencies. GNUmakefile has
+# hooks to force a reconfigure at distribution time to get the value
+# correct, without penalizing normal development with extra reconfigures.
+#
+# .version - present in a checked-out repository and in a distribution
+# tarball. Usable in dependencies, particularly for files that don't
+# want to depend on config.h but do want to track version changes.
+# Delete this file prior to any autoconf run where you want to rebuild
+# files to pick up a version string change; and leave it stale to
+# minimize rebuild time after unrelated changes to configure sources.
+#
+# It is probably wise to add these two files to .gitignore, so that you
+# don't accidentally commit either generated file.
+#
+# Use the following line in your configure.ac, so that $(VERSION) will
+# automatically be up-to-date each time configure is run (and note that
+# since configure.ac no longer includes a version string, Makefile rules
+# should not depend on configure.ac for version updates).
+#
+# AC_INIT([GNU project],
+# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+# [bug-project@example])
+#
+# Then use the following lines in your Makefile.am, so that .version
+# will be present for dependencies, and so that .tarball-version will
+# exist in distribution tarballs.
+#
+# BUILT_SOURCES = $(top_srcdir)/.version
+# $(top_srcdir)/.version:
+# echo $(VERSION) > $@-t && mv $@-t $@
+# dist-hook:
+# echo $(VERSION) > $(distdir)/.tarball-version
+
+case $# in
+ 1|2) ;;
+ *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version" \
+ '[TAG-NORMALIZATION-SED-SCRIPT]'
+ exit 1;;
+esac
+
+tarball_version_file=$1
+tag_sed_script="${2:-s/x/x/}"
+nl='
+'
+
+# Avoid meddling by environment variable of the same name.
+v=
+
+# First see if there is a tarball-only version file.
+# then try "git describe", then default.
+if test -f $tarball_version_file
+then
+ v=`cat $tarball_version_file` || exit 1
+ case $v in
+ *$nl*) v= ;; # reject multi-line output
+ [0-9]*) ;;
+ *) v= ;;
+ esac
+ test -z "$v" \
+ && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
+fi
+
+if test -n "$v"
+then
+ : # use $v
+# Otherwise, if there is at least one git commit involving the working
+# directory, and "git describe" output looks sensible, use that to
+# derive a version string.
+elif test "`git log -1 --pretty=format:x . 2>&1`" = x \
+ && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
+ || git describe --abbrev=4 HEAD 2>/dev/null` \
+ && v=`printf '%s\n' "$v" | sed "$tag_sed_script"` \
+ && case $v in
+ v[0-9]*) ;;
+ *) (exit 1) ;;
+ esac
+then
+ # Is this a new git that lists number of commits since the last
+ # tag or the previous older version that did not?
+ # Newer: v6.10-77-g0f8faeb
+ # Older: v6.10-g0f8faeb
+ case $v in
+ *-*-*) : git describe is okay three part flavor ;;
+ *-*)
+ : git describe is older two part flavor
+ # Recreate the number of commits and rewrite such that the
+ # result is the same as if we were using the newer version
+ # of git describe.
+ vtag=`echo "$v" | sed 's/-.*//'`
+ numcommits=`git rev-list "$vtag"..HEAD | wc -l`
+ v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
+ ;;
+ esac
+
+ # Change the first '-' to a '.', so version-comparing tools work properly.
+ # Remove the "g" in git describe's output string, to save a byte.
+ v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
+else
+ v=UNKNOWN
+fi
+
+v=`echo "$v" |sed 's/^v//'`
+
+# Don't declare a version "dirty" merely because a time stamp has changed.
+git update-index --refresh > /dev/null 2>&1
+
+dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
+case "$dirty" in
+ '') ;;
+ *) # Append the suffix only if there isn't one already.
+ case $v in
+ *-dirty) ;;
+ *) v="$v-dirty" ;;
+ esac ;;
+esac
+
+# Omit the trailing newline, so that m4_esyscmd can use the result directly.
+echo "$v" | tr -d "$nl"
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
--- /dev/null
+eval '(exit $?0)' && eval 'exec perl -wS "$0" ${1+"$@"}'
+ & eval 'exec perl -wS "$0" $argv:q'
+ if 0;
+# Convert git log output to ChangeLog format.
+
+my $VERSION = '2009-10-30 13:46'; # UTC
+# The definition above must lie within the first 8 lines in order
+# for the Emacs time-stamp write hook (at end) to update it.
+# If you change this file with Emacs, please let the write hook
+# do its job. Otherwise, update this string manually.
+
+# Copyright (C) 2008-2010 Free Software Foundation, Inc.
+
+# 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 3 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, see <http://www.gnu.org/licenses/>.
+
+# Written by Jim Meyering
+
+use strict;
+use warnings;
+use Getopt::Long;
+use POSIX qw(strftime);
+
+(my $ME = $0) =~ s|.*/||;
+
+# use File::Coda; # http://meyering.net/code/Coda/
+END {
+ defined fileno STDOUT or return;
+ close STDOUT and return;
+ warn "$ME: failed to close standard output: $!\n";
+ $? ||= 1;
+}
+
+sub usage ($)
+{
+ my ($exit_code) = @_;
+ my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
+ if ($exit_code != 0)
+ {
+ print $STREAM "Try `$ME --help' for more information.\n";
+ }
+ else
+ {
+ print $STREAM <<EOF;
+Usage: $ME [OPTIONS] [ARGS]
+
+Convert git log output to ChangeLog format. If present, any ARGS
+are passed to "git log". To avoid ARGS being parsed as options to
+$ME, they may be preceded by '--'.
+
+OPTIONS:
+
+ --since=DATE convert only the logs since DATE;
+ the default is to convert all log entries.
+ --format=FMT set format string for commit subject and body;
+ see 'man git-log' for the list of format metacharacters;
+ the default is '%s%n%b%n'
+
+ --help display this help and exit
+ --version output version information and exit
+
+EXAMPLE:
+
+ $ME --since=2008-01-01 > ChangeLog
+ $ME -- -n 5 foo > last-5-commits-to-branch-foo
+
+EOF
+ }
+ exit $exit_code;
+}
+
+# If the string $S is a well-behaved file name, simply return it.
+# If it contains white space, quotes, etc., quote it, and return the new string.
+sub shell_quote($)
+{
+ my ($s) = @_;
+ if ($s =~ m![^\w+/.,-]!)
+ {
+ # Convert each single quote to '\''
+ $s =~ s/\'/\'\\\'\'/g;
+ # Then single quote the string.
+ $s = "'$s'";
+ }
+ return $s;
+}
+
+sub quoted_cmd(@)
+{
+ return join (' ', map {shell_quote $_} @_);
+}
+
+{
+ my $since_date = '1970-01-01 UTC';
+ my $format_string = '%s%n%b%n';
+ GetOptions
+ (
+ help => sub { usage 0 },
+ version => sub { print "$ME version $VERSION\n"; exit },
+ 'since=s' => \$since_date,
+ 'format=s' => \$format_string,
+ ) or usage 1;
+
+ my @cmd = (qw (git log --log-size), "--since=$since_date",
+ '--pretty=format:%ct %an <%ae>%n%n'.$format_string, @ARGV);
+ open PIPE, '-|', @cmd
+ or die ("$ME: failed to run `". quoted_cmd (@cmd) ."': $!\n"
+ . "(Is your Git too old? Version 1.5.1 or later is required.)\n");
+
+ my $prev_date_line = '';
+ while (1)
+ {
+ defined (my $in = <PIPE>)
+ or last;
+ $in =~ /^log size (\d+)$/
+ or die "$ME:$.: Invalid line (expected log size):\n$in";
+ my $log_nbytes = $1;
+
+ my $log;
+ my $n_read = read PIPE, $log, $log_nbytes;
+ $n_read == $log_nbytes
+ or die "$ME:$.: unexpected EOF\n";
+
+ my @line = split "\n", $log;
+ my $author_line = shift @line;
+ defined $author_line
+ or die "$ME:$.: unexpected EOF\n";
+ $author_line =~ /^(\d+) (.*>)$/
+ or die "$ME:$.: Invalid line "
+ . "(expected date/author/email):\n$author_line\n";
+
+ my $date_line = sprintf "%s $2\n", strftime ("%F", localtime ($1));
+ # If this line would be the same as the previous date/name/email
+ # line, then arrange not to print it.
+ if ($date_line ne $prev_date_line)
+ {
+ $prev_date_line eq ''
+ or print "\n";
+ print $date_line;
+ }
+ $prev_date_line = $date_line;
+
+ # Omit "Signed-off-by..." lines.
+ @line = grep !/^Signed-off-by: .*>$/, @line;
+
+ # If there were any lines
+ if (@line == 0)
+ {
+ warn "$ME: warning: empty commit message:\n $date_line\n";
+ }
+ else
+ {
+ # Remove leading and trailing blank lines.
+ while ($line[0] =~ /^\s*$/) { shift @line; }
+ while ($line[$#line] =~ /^\s*$/) { pop @line; }
+
+ # Prefix each non-empty line with a TAB.
+ @line = map { length $_ ? "\t$_" : '' } @line;
+
+ print "\n", join ("\n", @line), "\n";
+ }
+
+ defined ($in = <PIPE>)
+ or last;
+ $in ne "\n"
+ and die "$ME:$.: unexpected line:\n$in";
+ }
+
+ close PIPE
+ or die "$ME: error closing pipe from " . quoted_cmd (@cmd) . "\n";
+ # FIXME-someday: include $PROCESS_STATUS in the diagnostic
+}
+
+# Local Variables:
+# mode: perl
+# indent-tabs-mode: nil
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "my $VERSION = '"
+# time-stamp-format: "%:y-%02m-%02d %02H:%02M"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "'; # UTC"
+# End:
--- /dev/null
+# to build official release tarballs, handle tagging and publish.
+
+# signing key
+gpgsignkey=
+
+project=corosync-qdevice
+
+all: checks setup tag tarballs sha256 sign
+
+checks:
+ifeq (,$(version))
+ @echo ERROR: need to define version=
+ @exit 1
+endif
+ @if [ ! -d .git ]; then \
+ echo This script needs to be executed from top level cluster git tree; \
+ exit 1; \
+ fi
+
+setup: checks
+ ./autogen.sh
+ ./configure
+ make maintainer-clean
+
+tag: setup ./tag-$(version)
+
+tag-$(version):
+ifeq (,$(release))
+ @echo Building test release $(version), no tagging
+else
+ git tag -a -m "v$(version) release" v$(version) HEAD
+ @touch $@
+endif
+
+tarballs: tag
+ ./autogen.sh
+ ./configure
+ make distcheck
+
+sha256: tarballs $(project)-$(version).sha256
+
+$(project)-$(version).sha256:
+ifeq (,$(release))
+ @echo Building test release $(version), no sha256
+else
+ sha256sum $(project)-$(version)*tar* | sort -k2 > $@
+endif
+
+sign: sha256 $(project)-$(version).sha256.asc
+
+$(project)-$(version).sha256.asc: $(project)-$(version).sha256
+ifeq (,$(gpgsignkey))
+ @echo No GPG signing key defined
+else
+ifeq (,$(release))
+ @echo Building test release $(version), no sign
+else
+ gpg --default-key $(gpgsignkey) \
+ --detach-sign \
+ --armor \
+ $<
+endif
+endif
+
+publish:
+ifeq (,$(release))
+ @echo Building test release $(version), no publishing!
+else
+ @echo CHANGEME git push --tags origin
+ @echo CHANGEME scp $(project)-$(version).* \
+ fedorahosted.org:$(project)
+endif
+
+clean:
+ rm -rf $(project)-* tag-*
--- /dev/null
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+# bootstrap / init
+AC_PREREQ([2.61])
+
+AC_INIT([corosync-qdevice],
+ m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+ [users@clusterlabs.org])
+
+AC_USE_SYSTEM_EXTENSIONS
+
+AM_INIT_AUTOMAKE([foreign 1.11])
+
+LT_PREREQ([2.2.6])
+LT_INIT
+
+AM_SILENT_RULES([yes])
+
+AC_CONFIG_SRCDIR([qdevices/corosync-qdevice.c])
+AC_CONFIG_HEADER([config.h])
+AC_CONFIG_MACRO_DIR([m4])
+
+AC_CANONICAL_HOST
+
+AC_LANG([C])
+
+AC_SUBST(WITH_LIST, [""])
+
+dnl Fix default variables - "prefix" variable if not specified
+if test "$prefix" = "NONE"; then
+ prefix="/usr"
+
+ dnl Fix "localstatedir" variable if not specified
+ if test "$localstatedir" = "\${prefix}/var"; then
+ localstatedir="/var"
+ fi
+ dnl Fix "sysconfdir" variable if not specified
+ if test "$sysconfdir" = "\${prefix}/etc"; then
+ sysconfdir="/etc"
+ fi
+ dnl Fix "libdir" variable if not specified
+ if test "$libdir" = "\${exec_prefix}/lib"; then
+ if test -e /usr/lib64; then
+ libdir="/usr/lib64"
+ else
+ libdir="/usr/lib"
+ fi
+ fi
+fi
+
+if test "$srcdir" = "."; then
+ AC_MSG_NOTICE([building in place srcdir:$srcdir])
+ AC_DEFINE([BUILDING_IN_PLACE], 1, [building in place])
+else
+ AC_MSG_NOTICE([building out of tree srcdir:$srcdir])
+fi
+
+# Checks for programs.
+
+# check stolen from gnulib/m4/gnu-make.m4
+if ! ${MAKE-make} --version /cannot/make/this >/dev/null 2>&1; then
+ AC_MSG_ERROR([you don't seem to have GNU make; it is required])
+fi
+
+AC_PROG_AWK
+AC_PROG_GREP
+AC_PROG_SED
+AC_PROG_CPP
+AC_PROG_CC
+AC_PROG_CC_C99
+if test "x$ac_cv_prog_cc_c99" = "xno"; then
+ AC_MSG_ERROR(["C99 support is required"])
+fi
+AC_PROG_LN_S
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+PKG_PROG_PKG_CONFIG
+AC_PATH_PROG([BASHPATH], [bash])
+AC_CHECK_PROGS([GROFF], [groff])
+
+# Checks for typedefs.
+AC_TYPE_UID_T
+AC_TYPE_INT16_T
+AC_TYPE_INT32_T
+AC_TYPE_INT64_T
+AC_TYPE_INT8_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_TYPE_UINT8_T
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+
+# Checks for libraries.
+PKG_CHECK_MODULES([nss],[nss])
+PKG_CHECK_MODULES([qb], [libqb])
+PKG_CHECK_MODULES([corosync_common], [libcorosync_common])
+PKG_CHECK_MODULES([cmap], [libcmap])
+PKG_CHECK_MODULES([votequorum], [libvotequorum])
+
+AC_CONFIG_FILES([Makefile
+ qdevices/Makefile
+ man/Makefile
+ init/Makefile
+ ])
+
+# ===============================================
+# Helpers
+# ===============================================
+
+## helper for CC stuff
+cc_supports_flag() {
+ BACKUP="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $@ $unknown_warnings_as_errors"
+ AC_MSG_CHECKING([whether $CC supports "$@"])
+ AC_PREPROC_IFELSE([AC_LANG_PROGRAM([])],
+ [RC=0; AC_MSG_RESULT([yes])],
+ [RC=1; AC_MSG_RESULT([no])])
+ CPPFLAGS="$BACKUP"
+ return $RC
+}
+
+## local defines
+PACKAGE_FEATURES=""
+
+# local options
+AC_ARG_ENABLE([fatal-warnings],
+ [ --enable-fatal-warnings : enable fatal warnings. ],
+ [ default="no" ])
+
+AC_ARG_ENABLE([debug],
+ [ --enable-debug : enable debug build. ],
+ [ default="no" ])
+
+AC_ARG_ENABLE([secure-build],
+ [ --enable-secure-build : enable PIE/RELRO build. ],
+ [],
+ [enable_secure_build="yes"])
+
+AC_ARG_ENABLE([systemd],
+ [ --enable-systemd : Install systemd service files],,
+ [ enable_systemd="no" ])
+AM_CONDITIONAL(INSTALL_SYSTEMD, test x$enable_systemd = xyes)
+
+AC_ARG_WITH([initconfigdir],
+ [AS_HELP_STRING([--with-initconfigdir=DIR],
+ [configuration directory @<:@SYSCONFDIR/sysconfig@:>@])],
+ [INITCONFIGDIR="$withval"],
+ [INITCONFIGDIR='${sysconfdir}/sysconfig'])
+AC_SUBST([INITCONFIGDIR])
+
+AC_ARG_WITH([initddir],
+ [ --with-initddir=DIR : path to init script directory. ],
+ [ INITDDIR="$withval" ],
+ [ INITDDIR="$sysconfdir/init.d" ])
+
+AC_ARG_WITH([systemddir],
+ [ --with-systemddir=DIR : path to systemd unit files directory. ],
+ [ SYSTEMDDIR="$withval" ],
+ [ SYSTEMDDIR="/lib/systemd/system" ])
+
+AC_ARG_ENABLE([qdevices],
+ [ --disable-qdevices : Quorum devices support ],,
+ [ enable_qdevices="yes" ])
+AM_CONDITIONAL(BUILD_QDEVICES, test x$enable_qdevices = xyes)
+AC_ARG_ENABLE([qnetd],
+ [ --disable-qnetd : Quorum Net Daemon support ],,
+ [ enable_qnetd="yes" ])
+AM_CONDITIONAL(BUILD_QNETD, test x$enable_qnetd = xyes)
+
+# *FLAGS handling goes here
+
+ENV_CFLAGS="$CFLAGS"
+ENV_CPPFLAGS="$CPPFLAGS"
+ENV_LDFLAGS="$LDFLAGS"
+
+# debug build stuff
+if test "x${enable_debug}" = xyes; then
+ AC_DEFINE_UNQUOTED([DEBUG], [1], [Compiling Debugging code])
+ OPT_CFLAGS="-O0"
+ PACKAGE_FEATURES="$PACKAGE_FEATURES debug"
+else
+ OPT_CFLAGS="-O3"
+fi
+
+# gdb flags
+if test "x${GCC}" = xyes; then
+ GDB_FLAGS="-ggdb3"
+else
+ GDB_FLAGS="-g"
+fi
+
+if test "x${enable_systemd}" = xyes; then
+ PKG_CHECK_MODULES([libsystemd], [libsystemd])
+ AC_DEFINE([HAVE_LIBSYSTEMD], [1], [have systemd interface library])
+ PACKAGE_FEATURES="$PACKAGE_FEATURES systemd"
+ WITH_LIST="$WITH_LIST --with systemd"
+fi
+if test "x${enable_qdevices}" = xyes; then
+ PACKAGE_FEATURES="$PACKAGE_FEATURES qdevices"
+fi
+if test "x${enable_qnetd}" = xyes; then
+ PACKAGE_FEATURES="$PACKAGE_FEATURES qnetd"
+fi
+
+# extra warnings
+EXTRA_WARNINGS=""
+
+WARNLIST="
+ all
+ shadow
+ missing-prototypes
+ missing-declarations
+ strict-prototypes
+ declaration-after-statement
+ pointer-arith
+ write-strings
+ cast-align
+ bad-function-cast
+ missing-format-attribute
+ format=2
+ format-security
+ format-nonliteral
+ no-long-long
+ unsigned-char
+ gnu89-inline
+ no-strict-aliasing
+ "
+
+for j in $WARNLIST; do
+ if cc_supports_flag -W$j; then
+ EXTRA_WARNINGS="$EXTRA_WARNINGS -W$j";
+ fi
+done
+
+if test "x${enable_fatal_warnings}" = xyes && \
+ cc_supports_flag -Werror ; then
+ AC_MSG_NOTICE([Enabling Fatal Warnings (-Werror)])
+ WERROR_CFLAGS="-Werror"
+ PACKAGE_FEATURES="$PACKAGE_FEATURES fatal-warnings"
+else
+ WERROR_CFLAGS=""
+fi
+
+if test "x${enable_secure_build}" = xyes; then
+ # stolen from apache configure snippet
+ AC_CACHE_CHECK([whether $CC accepts PIE flags], [ap_cv_cc_pie], [
+ save_CFLAGS=$CFLAGS
+ save_LDFLAGS=$LDFLAGS
+ CFLAGS="$CFLAGS -fPIE"
+ LDFLAGS="$LDFLAGS -pie"
+ AC_TRY_RUN([static int foo[30000]; int main () { return 0; }],
+ [ap_cv_cc_pie=yes], [ap_cv_cc_pie=no], [ap_cv_cc_pie=yes])
+ CFLAGS=$save_CFLAGS
+ LDFLAGS=$save_LDFLAGS
+ ])
+ if test "$ap_cv_cc_pie" = "yes"; then
+ SEC_FLAGS="$SEC_FLAGS -fPIE"
+ SEC_LDFLAGS="$SEC_LDFLAGS -pie"
+ PACKAGE_FEATURES="$PACKAGE_FEATURES pie"
+ fi
+
+ # similar to above
+ AC_CACHE_CHECK([whether $CC accepts RELRO flags], [ap_cv_cc_relro], [
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -Wl,-z,relro"
+ AC_TRY_RUN([static int foo[30000]; int main () { return 0; }],
+ [ap_cv_cc_relro=yes], [ap_cv_cc_relro=no], [ap_cv_cc_relro=yes])
+ LDFLAGS=$save_LDFLAGS
+ ])
+ if test "$ap_cv_cc_relro" = "yes"; then
+ SEC_LDFLAGS="$SEC_LDFLAGS -Wl,-z,relro"
+ PACKAGE_FEATURES="$PACKAGE_FEATURES relro"
+ fi
+
+ AC_CACHE_CHECK([whether $CC accepts BINDNOW flags], [ap_cv_cc_bindnow], [
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -Wl,-z,now"
+ AC_TRY_RUN([static int foo[30000]; int main () { return 0; }],
+ [ap_cv_cc_bindnow=yes], [ap_cv_cc_bindnow=no], [ap_cv_cc_bindnow=yes])
+ LDFLAGS=$save_LDFLAGS
+ ])
+ if test "$ap_cv_cc_bindnow" = "yes"; then
+ SEC_LDFLAGS="$SEC_LDFLAGS -Wl,-z,now"
+ PACKAGE_FEATURES="$PACKAGE_FEATURES bindnow"
+ fi
+fi
+
+AC_CACHE_CHECK([whether $CC accepts "--as-needed"], [ap_cv_cc_as_needed], [
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -Wl,--as-needed"
+ AC_TRY_RUN([static int foo[30000]; int main () { return 0; }],
+ [ap_cv_cc_as_needed=yes], [ap_cv_cc_as_needed=no], [ap_cv_cc_as_needed=yes])
+ LDFLAGS=$save_LDFLAGS
+])
+
+# define global include dirs
+INCLUDE_DIRS="$INCLUDE_DIRS -I\$(top_builddir)/include -I\$(top_srcdir)/include"
+
+# final build of *FLAGS
+CFLAGS="$ENV_CFLAGS $lt_prog_compiler_pic $SEC_FLAGS $OPT_CFLAGS $GDB_FLAGS \
+ $EXTRA_WARNINGS \
+ $WERROR_CFLAGS"
+CPPFLAGS="$ENV_CPPFLAGS $INCLUDE_DIRS"
+LDFLAGS="$ENV_LDFLAGS $lt_prog_compiler_pic $SEC_LDFLAGS"
+
+if test "$ap_cv_cc_as_needed" = "yes"; then
+ LDFLAGS="$LDFLAGS -Wl,--as-needed"
+fi
+
+# substitute what we need:
+AC_SUBST([BASHPATH])
+AC_SUBST([INITDDIR])
+AC_SUBST([SYSTEMDDIR])
+AC_SUBST([LOGDIR])
+AC_SUBST([LOGROTATEDIR])
+
+AC_SUBST([SOMAJOR])
+AC_SUBST([SOMINOR])
+AC_SUBST([SOMICRO])
+AC_SUBST([SONAME])
+
+AC_SUBST([NSS_LDFLAGS])
+
+AM_CONDITIONAL(BUILD_HTML_DOCS, test -n "${GROFF}")
+
+AC_DEFINE_UNQUOTED([LOCALSTATEDIR], "$(eval echo ${localstatedir})", [localstate directory])
+
+COROSYSCONFDIR=${sysconfdir}/corosync
+AC_SUBST([COROSYSCONFDIR])
+AC_DEFINE_UNQUOTED([COROSYSCONFDIR], "$(eval echo ${COROSYSCONFDIR})", [corosync-qdevice config directory])
+
+AC_DEFINE_UNQUOTED([PACKAGE_FEATURES], "${PACKAGE_FEATURES}", [corosync-qdevice built-in features])
+
+AC_OUTPUT
+
+AC_MSG_RESULT([])
+AC_MSG_RESULT([$PACKAGE configuration:])
+AC_MSG_RESULT([ Version = ${VERSION}])
+AC_MSG_RESULT([ Prefix = ${prefix}])
+AC_MSG_RESULT([ Executables = ${sbindir}])
+AC_MSG_RESULT([ Man pages = ${mandir}])
+AC_MSG_RESULT([ Doc dir = ${docdir}])
+AC_MSG_RESULT([ Libraries = ${libdir}])
+AC_MSG_RESULT([ Header files = ${includedir}])
+AC_MSG_RESULT([ Arch-independent files = ${datadir}])
+AC_MSG_RESULT([ State information = ${localstatedir}])
+AC_MSG_RESULT([ System configuration = ${sysconfdir}])
+AC_MSG_RESULT([ System init.d directory = ${INITDDIR}])
+AC_MSG_RESULT([ System systemd directory = ${SYSTEMDDIR}])
+AC_MSG_RESULT([ Log directory = ${LOGDIR}])
+AC_MSG_RESULT([ Log rotate directory = ${LOGROTATEDIR}])
+AC_MSG_RESULT([ corosync config dir = ${COROSYSCONFDIR}])
+AC_MSG_RESULT([ init config directory = ${INITCONFIGDIR}])
+AC_MSG_RESULT([ Features = ${PACKAGE_FEATURES}])
+AC_MSG_RESULT([])
+AC_MSG_RESULT([$PACKAGE build info:])
+AC_MSG_RESULT([ Default optimization = ${OPT_CFLAGS}])
+AC_MSG_RESULT([ Default debug options = ${GDB_CFLAGS}])
+AC_MSG_RESULT([ Extra compiler warnings = ${EXTRA_WARNING}])
+AC_MSG_RESULT([ Env. defined CFLAG = ${ENV_CFLAGS}])
+AC_MSG_RESULT([ Env. defined CPPFLAGS = ${ENV_CPPFLAGS}])
+AC_MSG_RESULT([ Env. defined LDFLAGS = ${ENV_LDFLAGS}])
+AC_MSG_RESULT([ Fatal War. CFLAGS = ${WERROR_CFLAGS}])
+AC_MSG_RESULT([ Final CFLAGS = ${CFLAGS}])
+AC_MSG_RESULT([ Final CPPFLAGS = ${CPPFLAGS}])
+AC_MSG_RESULT([ Final LDFLAGS = ${LDFLAGS}])
--- /dev/null
+@ALPHATAG@
+@NUMCOMM@
+@DIRTY@
+
+# Conditionals
+# Invoke "rpmbuild --without <feature>" or "rpmbuild --with <feature>"
+# to disable or enable specific features
+%bcond_with runautogen
+%bcond_with systemd
+
+%global gitver %{?numcomm:.%{numcomm}}%{?alphatag:.%{alphatag}}%{?dirty:.%{dirty}}
+%global gittarver %{?numcomm:.%{numcomm}}%{?alphatag:-%{alphatag}}%{?dirty:-%{dirty}}
+
+Name: corosync-qdevice
+Summary: The Corosync Cluster Engine Qdevice
+Version: @version@
+Release: 1%{?gitver}%{?dist}
+License: BSD
+Group: System Environment/Base
+URL: https://github.com/corosync/corosync-qdevice
+Source0: https://github.com/corosync/corosync-qdevice/releases/download/v%{version}%{?gittarver}/%{name}-%{version}%{?gittarver}.tar.gz
+
+# Runtime bits
+Requires: corosync >= 2.4.0
+Requires: corosynclib >= 2.4.0
+Requires: nss-tools
+
+%if %{with systemd}
+Requires(post): systemd
+Requires(preun): systemd
+Requires(postun): systemd
+%else
+Requires(post): /sbin/chkconfig
+Requires(preun): /sbin/chkconfig
+%endif
+
+# Build bits
+BuildRequires: corosynclib-devel
+BuildRequires: groff
+BuildRequires: libqb-devel
+BuildRequires: nss-devel
+BuildRequires: sed
+
+%if %{with runautogen}
+BuildRequires: autoconf automake libtool
+%endif
+%if %{with systemd}
+BuildRequires: systemd-units
+BuildRequires: systemd-devel
+%endif
+
+BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
+
+%prep
+%setup -q -n %{name}-%{version}%{?gittarver}
+
+%build
+%if %{with runautogen}
+./autogen.sh
+%endif
+
+%{configure} \
+%if %{with systemd}
+ --enable-systemd \
+%endif
+ --enable-qdevices \
+ --enable-qnetd \
+ --with-initddir=%{_initrddir} \
+ --with-systemddir=%{_unitdir}
+
+make %{_smp_mflags}
+
+%install
+rm -rf %{buildroot}
+
+make install DESTDIR=%{buildroot}
+
+## tree fixup
+# drop docs and html docs for now
+rm -rf %{buildroot}%{_docdir}/*
+mkdir -p %{buildroot}%{_sysconfdir}/sysconfig
+# /etc/sysconfig/corosync-qdevice
+install -m 644 init/corosync-qdevice.sysconfig.example \
+ %{buildroot}%{_sysconfdir}/sysconfig/corosync-qdevice
+# /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
+
+%clean
+rm -rf %{buildroot}
+
+%description
+This package contains the Corosync Cluster Engine Qdevice, script for creating
+NSS certificates and an init script.
+
+%post
+%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
+%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
+%if %{with systemd} && 0%{?systemd_postun:1}
+%systemd_postun
+%endif
+
+%files
+%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
+%config(noreplace) %{_sysconfdir}/sysconfig/corosync-qdevice
+%if %{with systemd}
+%{_unitdir}/corosync-qdevice.service
+%else
+%{_initrddir}/corosync-qdevice
+%endif
+%{_mandir}/man8/corosync-qdevice-tool.8*
+%{_mandir}/man8/corosync-qdevice-net-certutil.8*
+%{_mandir}/man8/corosync-qdevice.8*
+
+%package -n corosync-qnetd
+Summary: The Corosync Cluster Engine Qdevice Network Daemon
+Group: System Environment/Base
+Requires: nss-tools
+Requires(pre): shadow-utils
+Requires(pre): /usr/sbin/useradd
+
+%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,-)
+%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
+%else
+%{_initrddir}/corosync-qnetd
+%endif
+%{_mandir}/man8/corosync-qnetd-tool.8*
+%{_mandir}/man8/corosync-qnetd-certutil.8*
+%{_mandir}/man8/corosync-qnetd.8*
+
+%changelog
+* @date@ Autotools generated version <nobody@nowhere.org> - @version@-1-@numcomm@.@alphatag@.@dirty@
+- Autotools generated version
--- /dev/null
+corosync
+corosync-notifyd
+corosync.service
+corosync-notifyd.service
+corosync-qnetd
+corosync-qnetd.service
+corosync-qdevice
+corosync-qdevice.service
--- /dev/null
+# Copyright (c) 2004 MontaVista Software, Inc.
+# Copyright (c) 2009 - 2018 Red Hat, Inc.
+#
+# Authors: Jan Friesse (jfriesse@redhat.com)
+# 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 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.
+
+MAINTAINERCLEANFILES = Makefile.in
+
+EXTRA_DIST = corosync-qdevice.sysconfig.example corosync-qdevice.in \
+ corosync-qdevice.service.in \
+ corosync-qnetd.sysconfig.example corosync-qnetd.in \
+ corosync-qnetd.service.in
+
+if INSTALL_SYSTEMD
+systemdconfdir = $(SYSTEMDDIR)
+systemdconf_DATA =
+else
+initscriptdir = $(INITDDIR)
+initscript_SCRIPTS =
+endif
+
+if BUILD_QDEVICES
+if INSTALL_SYSTEMD
+systemdconf_DATA += corosync-qdevice.service
+else
+initscript_SCRIPTS += corosync-qdevice
+endif
+endif
+
+if BUILD_QNETD
+if INSTALL_SYSTEMD
+systemdconf_DATA += corosync-qnetd.service
+else
+initscript_SCRIPTS += corosync-qnetd
+endif
+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#@''INITCONFIGDIR@#$(INITCONFIGDIR)#g' \
+ -e 's#@''INITDDIR@#$(INITDDIR)#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)
--- /dev/null
+#!@BASHPATH@
+
+# Authors:
+# Jan Friesse <jfriesse@redhat.com>
+#
+# License: Revised BSD
+
+# chkconfig: - 20 80
+# description: Corosync Qdevice daemon
+# processname: corosync-qdevice
+#
+### BEGIN INIT INFO
+# Provides: corosync-qdevice
+# Required-Start: corosync
+# Required-Stop: corosync
+# Default-Start:
+# Default-Stop:
+# Short-Description: Starts and stops Corosync Qdevice daemon.
+# Description: Starts and stops Corosync Qdevice daemon.
+### END INIT INFO
+
+desc="Corosync Qdevice daemon"
+prog="corosync-qdevice"
+
+# 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
+}
+
+[ -f @INITCONFIGDIR@/$prog ] && . @INITCONFIGDIR@/$prog
+
+case '@INITCONFIGDIR@' in
+ */sysconfig) # rpm based distros
+ [ -f @INITDDIR@/functions ] && . @INITDDIR@/functions
+ [ -z "$LOCK_FILE" ] && LOCK_FILE="@LOCALSTATEDIR@/lock/subsys/$prog";;
+ */default) # deb based distros
+ [ -z "$LOCK_FILE" ] && LOCK_FILE="@LOCALSTATEDIR@/lock/$prog";;
+esac
+
+# 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
+ if [ ! -d "@LOCALSTATEDIR@/run/corosync-qdevice" ];then
+ mkdir -p "@LOCALSTATEDIR@/run/corosync-qdevice"
+ chmod 0770 "@LOCALSTATEDIR@/run/corosync-qdevice"
+ fi
+
+ if status $prog > /dev/null 2>&1; then
+ success
+ else
+ $prog $COROSYNC_QDEVICE_OPTIONS > /dev/null 2>&1
+
+ 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
--- /dev/null
+[Unit]
+Description=Corosync Qdevice daemon
+Documentation=man:corosync-qdevice
+ConditionKernelCommandLine=!nocluster
+Requires=corosync.service
+After=corosync.service
+
+[Service]
+EnvironmentFile=-@INITCONFIGDIR@/corosync-qdevice
+ExecStart=@SBINDIR@/corosync-qdevice -f $COROSYNC_QDEVICE_OPTIONS
+Type=notify
+Restart=on-abnormal
+RuntimeDirectory=corosync-qdevice
+RuntimeDirectoryMode=0770
+
+[Install]
+WantedBy=multi-user.target
--- /dev/null
+# Corosync Qdevice daemon init script configuration file
+
+# COROSYNC_QDEVICE_OPTIONS specifies options passed to corosync-qdevice command
+# (default is no options).
+# See "man corosync-qdevice" for detailed descriptions of the options.
+COROSYNC_QDEVICE_OPTIONS=""
--- /dev/null
+#!@BASHPATH@
+
+# Authors:
+# Jan Friesse <jfriesse@redhat.com>
+#
+# 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
+}
+
+[ -f @INITCONFIGDIR@/$prog ] && . @INITCONFIGDIR@/$prog
+
+case '@INITCONFIGDIR@' in
+ */sysconfig) # rpm based distros
+ [ -f @INITDDIR@/functions ] && . @INITDDIR@/functions
+ [ -z "$LOCK_FILE" ] && LOCK_FILE="@LOCALSTATEDIR@/lock/subsys/$prog";;
+ */default) # deb based distros
+ [ -z "$LOCK_FILE" ] && LOCK_FILE="@LOCALSTATEDIR@/lock/$prog";;
+esac
+
+# 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
+ if [ ! -d "@LOCALSTATEDIR@/run/corosync-qnetd" ];then
+ mkdir -p "@LOCALSTATEDIR@/run/corosync-qnetd"
+ chmod 0770 "@LOCALSTATEDIR@/run/corosync-qnetd"
+ if [ ! -z "$COROSYNC_QNETD_RUNAS" ];then
+ chown "$COROSYNC_QNETD_RUNAS:$COROSYNC_QNETD_RUNAS" "@LOCALSTATEDIR@/run/corosync-qnetd"
+ fi
+ fi
+
+ if status $prog > /dev/null 2>&1; then
+ success
+ else
+ 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
--- /dev/null
+[Unit]
+Description=Corosync Qdevice Network daemon
+Documentation=man:corosync-qnetd
+ConditionKernelCommandLine=!nocluster
+Requires=network-online.target
+After=network-online.target
+
+[Service]
+EnvironmentFile=-@INITCONFIGDIR@/corosync-qnetd
+ExecStart=@BINDIR@/corosync-qnetd -f $COROSYNC_QNETD_OPTIONS
+Type=notify
+Restart=on-abnormal
+# Uncomment and set user who should be used for executing qnetd
+#User=coroqnetd
+RuntimeDirectory=corosync-qnetd
+RuntimeDirectoryMode=0770
+
+[Install]
+WantedBy=multi-user.target
--- /dev/null
+# 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=""
--- /dev/null
+*.html
+*.3
--- /dev/null
+# Copyright (c) 2004 MontaVista Software, Inc.
+# Copyright (c) 2009 - 2018 Red Hat, Inc.
+#
+# Authors: Jan Friesse (jfriesse@redhat.com)
+# 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 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.
+
+MAINTAINERCLEANFILES = Makefile.in
+
+qnetd_man = corosync-qnetd-tool.8 \
+ corosync-qnetd-certutil.8 \
+ corosync-qnetd.8
+
+qdevices_man = corosync-qdevice-tool.8 \
+ corosync-qdevice-net-certutil.8 \
+ corosync-qdevice.8
+
+EXTRA_DIST = $(qnetd_man) \
+ $(qdevices_man)
+
+dist_man_MANS =
+
+if BUILD_QNETD
+dist_man_MANS += $(qnetd_man)
+endif
+
+if BUILD_QDEVICES
+dist_man_MANS += $(qdevices_man)
+endif
+
+HTML_DOCS = $(dist_man_MANS:%=%.html) $(man_MANS:%=%.html)
+
+# developer man page generation
+%.3: %.3.in $(autogen_common)
+ @echo Generating $@ man page && \
+ rm -f $@-t-t $@-t $@ && \
+ date="$$(LC_ALL=C date "+%F" $${SOURCE_DATE_EPOCH+-d @$$SOURCE_DATE_EPOCH})" && \
+ awk "{print}(\$$1 ~ /@COMMONIPCERRORS@/){exit 0}" ${top_srcdir}/man/$@.in > $@-t-t && \
+ cat ${top_srcdir}/man/$(autogen_common) >> $@-t-t && \
+ awk -v p=0 "(\$$1 ~ /@COMMONIPCERRORS@/){p = 1} {if(p==1)print}" ${top_srcdir}/man/$@.in >> $@-t-t && \
+ cat $@-t-t | \
+ sed -e 's#@BUILDDATE@#'$$date'#g' \
+ -e 's#@COMMONIPCERRORS@##g' \
+ > $@-t && \
+ rm -f $@-t-t && \
+ mv $@-t $@
+
+clean-local:
+ rm -rf $(HTML_DOCS) $(autogen_man)
+
+if BUILD_HTML_DOCS
+%.html: %
+ $(GROFF) -mandoc -Thtml $^ > $@
+
+install-data-local:
+ $(INSTALL) -d $(DESTDIR)/${docdir}/html
+ $(INSTALL) -m 644 $(HTML_DOCS) $(DESTDIR)/${docdir}/html/
+
+uninstall-local:
+ cd $(DESTDIR)/${docdir}/html && rm -f $(HTML_DOCS)
+ rmdir $(DESTDIR)/${docdir}/html 2> /dev/null || :
+
+all-local: $(HTML_DOCS)
+endif
--- /dev/null
+.\"/*
+.\" * Copyright (C) 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 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.
+.\" */
+.TH COROSYNC-QDEVICE-NET-CERTUTIL 8 2016-06-28
+.SH NAME
+corosync-qdevice-net-certutil - tool to generate qdevice model net TLS certificates
+.SH SYNOPSIS
+.B "corosync-qdevice-net-certutil [-i|-m|-M|-r|-s|-Q] [-c certificate] [-n cluster_name]"
+.SH DESCRIPTION
+.B corosync-qdevice-net-certutil
+is a frontend for NSS certutil used for generating client certificate for the net model of
+qdevice.
+.SH OPTIONS
+.TP
+.B -i
+Initialize the QDevice Net NSS certificate database.
+The default directory for the database is /etc/corosync/qdevice/net/. This directory
+has to be writable by the current user. It needs the QNetd CA certificate passed as the
+.B -c
+parameter. This certificate can be found on the server running QNetd in the file
+/etc/corosync/qnetd/nssdb/qnetd-cacert.crt.
+.TP
+.B -m
+Import the cluster certificate and key from a pk12 file.
+.TP
+.B -r
+Generate a certificate request. The certificate request is exported into
+/etc/corosync/qdevice/net/qdevice-net-node.crq. It is necessary to
+pass the cluster name using the
+.B -n
+parameter. The cluster name has to match the one defined in /etc/corosync/corosync.conf.
+.TP
+.B -M
+Import a signed certificate and export a certificate with private key into
+pk12 file.
+.TP
+.B -Q
+Use ssh/scp to properly set both
+.B corosync-qnetd
+and
+.B corosync-qdevice
+certificates on all nodes. It's highly recommended that you use an ssh agent,
+or ssh/scp will keep asking for a password - roughly 8 times the number of nodes.
+.TP
+.B -c
+File with certificate to load.
+.TP
+.B -n
+Name of the cluster.
+.SH SEE ALSO
+.BR corosync-qnetd (8)
+.BR corosync-qdevice (8)
+.SH AUTHOR
+Jan Friesse
+.PP
--- /dev/null
+.\"/*
+.\" * Copyright (C) 2016-2017 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 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.
+.\" */
+.TH COROSYNC-QDEVICE-TOOL 8 2017-10-17
+.SH NAME
+corosync-qdevice-tool \- corosync-qdevice control interface.
+.SH SYNOPSIS
+.B "corosync-qdevice-tool [-Hhsv] [-p qdevice_ipc_socket_path]"
+.SH DESCRIPTION
+.B corosync-qdevice-tool
+is a frontend to the internal corosync-qdevice IPC. Its main purpose is to show important
+information about the current internal state of
+.B corosync-qdevice.
+.SH OPTIONS
+.TP
+.B -H
+Properly shutdown the
+.B corosync-qdevice
+process
+.TP
+.B -h
+Display a short usage text
+.TP
+.B -s
+Display the status of the
+.B corosync-qdevice
+process. The output is described in its own section below.
+.TP
+.B -v
+Display more verbose output for the
+.B -s
+option.
+.TP
+.B -p
+Path to the
+.B corosync-qdevice
+communication socket.
+
+.SH STATUS COMMAND OUTPUT
+.nf
+Qdevice information
+-------------------
+Model: Net
+Node ID: 1
+HB interval: 10000ms
+Sync HB interval: 30000ms
+Configured node list:
+ 0 Node ID = 1
+Heuristics: Enabled
+Ring ID: 1.a00000000021b48
+Membership node list: 1
+Quorate: Yes
+Quorum node list:
+ 0 Node ID = 1, State = member
+Expected votes: 2
+Last poll call: 2016-06-24T17:05:20 (cast vote)
+
+Qdevice-net information
+----------------------
+Cluster name: Cluster
+QNetd host: localhost:5403
+Connect timeout: 8000ms
+HB interval: 8000ms
+VQ vote timer interval: 5000ms
+TLS: Supported
+Algorithm: Fifty-Fifty split
+Tie-breaker: Node with lowest node ID
+Poll timer running: Yes (cast vote)
+State: Connected
+Heuristics result: Pass (regular: Pass, membership: Fail, connect: Fail)
+TLS active: Yes (client certificate sent)
+Connected since: 2016-06-24T17:02:35
+Echo reply received: 2016-06-24T17:05:15
+.fi
+
+The output is split into a generic qdevice section and a model specific section.
+Most of the items are just taken from corosync.conf file. It's helpful to note that the
+.I Membership node list
+is the membership list of the current node and should match the quorum node list.
+.I Last poll call
+is the timestamp (in iso format) of the last call to the votequorum_qdevice_poll
+function.
+
+For model net, it's good to check the
+.I Poll timer running
+state. Internally, model net supports 3 states. Not voting (when
+.I Poll timer running
+is No, which means
+.B corosync-qdevice
+is waiting for
+.B corosync-qnetd
+to reply), voting (without cast vote, it means that the
+.B corosync-qnetd
+algorithm decides that the current node shouldn't get a vote) and voting (with cast vote).
+.SH SEE ALSO
+.BR corosync-qnetd (8)
+.BR corosync-qdevice (8)
+.SH AUTHOR
+Jan Friesse
+.PP
--- /dev/null
+.\"/*
+.\" * Copyright (C) 2016-2017 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 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.
+.\" */
+.TH COROSYNC-QDEVICE 8 2017-10-17
+.SH NAME
+corosync-qdevice \- QDevice daemon
+.SH SYNOPSIS
+.B "corosync-qdevice [-dfh] [-S option=value[,option2=value2,...]]"
+
+.SH DESCRIPTION
+.B corosync-qdevice
+is a daemon running on each node of a cluster. It provides a configured
+number of votes to the
+quorum subsystem based on a third-party arbitrator's decision. Its primary use
+is to allow a cluster to sustain more node failures than standard quorum rules allow.
+It is recommended for clusters with an even number of nodes and highly recommended
+for 2 node clusters.
+.SH OPTIONS
+.TP
+.B -d
+Forcefully turn on debug information without the need to change corosync.conf.
+.TP
+.B -f
+Do not daemonize, run in the foreground.
+.TP
+.B -h
+Show short help text
+.TP
+.B -S
+Set advanced settings described in its own section below. This option
+shouldn't be generally used because most of the options are
+not safe to change.
+.SH CONFIGURATION
+.B corosync-qdevice
+reads its configuration from corosync.conf file.
+
+The main configuration is within
+.B quorum.device
+sub-key. Each model also has its own configuration within a
+similarly named sub-key.
+.TP
+.B model
+Specifies the model to be used. This parameter is required.
+.B corosync-qdevice
+is modular and is able to support multiple different models. The model basically
+defines what type of arbitrator is used. Currently only
+.I net
+is supported.
+.TP
+.B timeout
+Specifies how often
+.B corosync-qdevice
+should call the votequorum_poll function. It is also used by the
+.I net
+model to adjust
+its hearbeat timeout. It is recommended that you don't change this value.
+Default is
+.IR 10000 .
+.TP
+.B sync_timeout
+Specifies how often
+.B corosync-qdevice
+should call the votequorum_poll function during a sync phase. It is recommended that you don't change this value.
+Default is
+.IR 30000 .
+.TP
+.B votes
+The number of votes provided to the cluster by qdevice. Default is (number_of_nodes - 1) or generally
+sum(votes_per_node) - 1.
+
+.PP
+.B quorum.device.heuristics
+subkey holds the configuration of the heuristics. Heuristics are set of commands executed locally on
+startup, cluster membership change, successful connect to
+.B corosync-qnetd
+and optionally also at regular times. Commands are executed in parallel.
+When all commands finish successfully
+(their return error code is zero) on time,
+heuristics have passed, otherwise they have failed. The heuristics result is sent to
+.B corosync-qnetd
+and there it's used in calculations to determine which partition should be quorate.
+.TP
+.B timeout
+Specifies maximum time in milliseconds how long
+.B corosync-qdevice
+waits till the heuristics commands finish. If some command doesn't finish before the timeout, it's
+killed and heuristics fail. This timeout is used for heuristics executed at regular times.
+Default value is half of the
+.BR quorum.device.timeout ", so"
+.IR 5000 .
+.TP
+.B sync_timeout
+Similar to quorum.device.heuristics.timeout but used during membership changes. Default
+value is half of the
+.BR quorum.device.sync_timeout ", so"
+.IR 15000 .
+.TP
+.B interval
+Specifies interval between two regular heuristics execution. Default value is
+3 *
+.BR quorum.device.timeout ", so"
+.IR 30000 .
+.TP
+.B mode
+Can be one of
+.IR on ", " sync " or " off
+and specifies mode of operation of heuristics. Default is
+.IR off ,
+which means heuristics are disabled. When
+.I sync
+is set, heuristics are executed only during startup, membership change and when connection
+to
+.B corosync-qnetd
+is established. When heuristics should be running also on regular basis, this option
+should be set to
+.I on
+value.
+.TP
+.B exec_NAME
+defines executables.
+.I NAME
+can be arbitrary valid cmap key name string and it has no special meaning.
+The value of this variable must contain a command to execute. The value is parsed (split)
+into arguments similarly as Bourne shell would do. Quoting is possible by
+using backslash and double quotes.
+
+.PP
+.B quorum.device.net
+subkey holds the configuration for
+.B model
+.IR net .
+.TP
+.B tls
+Can be one of
+.IR on ", " off " or " required
+and specifies if tls should be used.
+.I on
+means a connection with TLS is attempted first, but if the server doesn't advertise TLS support
+then non-TLS will be used.
+.I off
+is used then TLS is not required and it's then not even tried. This mode is the
+only one which doesn't need a properly initialized NSS database.
+.I required
+means TLS is required and if the server doesn't support TLS, qdevice will
+exit with error message. Default is
+.IR on .
+.TP
+.B host
+Specifies the IP address or host name of the qnetd server to be used. This parameter
+is required.
+.TP
+.B port
+Specifies TCP port of qnetd server. Default is
+.IR 5403 .
+.TP
+.B algorithm
+Decision algorithm. Can be one of the
+.I ffsplit
+or
+.IR lms .
+(actually there are also
+.I test
+and
+.IR 2nodelms ,
+both of which are mainly for developers and shouldn't be used for production clusters).
+For a description of what each algorithm means and how the algorithms differ see their
+individual sections.
+Default value is
+.IR ffsplit .
+.TP
+.B tie_breaker
+can be one of
+.IR lowest ", " highest
+or valid_node_id (number) values. It's used as a fallback if qdevice has to decide between two or more
+equal partitions.
+.I lowest
+means the partition with the lowest node id is chosen.
+.I highest
+means the partition with highest node id is chosen. And valid_node_id means that the partition
+containing the node with the given node id is chosen.
+Default is
+.IR lowest .
+.TP
+.B connect_timeout
+Timeout when
+.B corosync-qdevice
+is trying to connect to
+.B corosync-qnetd
+host. Default is 0.8 *
+.BR quorum.sync_timeout .
+.TP
+.B force_ip_version
+can be one of
+.I 0|4|6
+and forces the software to use the given IP version.
+.I 0
+(default value) means IPv6 is preferred and IPv4 should be used as a fallback.
+
+.PP
+Logging configuration is within the
+.B logging
+directive.
+.B corosync-qdevice
+parses and supports most of the options with exception of
+.BR to_logfile ", " logfile
+and
+.BR logfile_priority .
+The
+.B logger_subsys
+sub-directive can be also used if
+.B subsys
+is set to
+.IR QDEVICE .
+
+.PP
+For
+.B corosync-qdevice
+to work correctly, the
+.B nodelist
+directive has to be used and properly configured. Also the
+.I net
+model requires that
+.B totem.cluster_name
+option is set.
+
+.SH MODEL NET TLS CONFIGURATION
+For
+.B model
+.I net
+to work using TLS, it's necessary to create the NSS database, import Qnetd
+CA certificate, and get/distribute a valid client certificate.
+
+If pcs is used (recommended) the following steps are not needed because pcs does them automatically.
+
+.B corosync-qdevice-net-certutil
+is the tool to perform required actions semi-automatically. Please consult the help output of
+it and its man page. For a first time configuration it may make sense to start with the
+.B -Q
+option.
+
+If TLS is not required just edit corosync.conf file and set
+.B quorum.device.net.tls
+to
+.IR off .
+
+.SH MODEL NET ALGORITHMS
+Algorithms are used to change behavior of how
+.B corosync-qnetd
+provides votes to a given node/partition. Currently there are two algorithms supported.
+.TP
+.B ffsplit
+This one makes sense only for clusters with an even number of nodes. It provides exactly one
+vote to the partition with the highest number of active nodes. If there are two exactly
+similar partitions,
+it provides its vote to the partition with higher score. The score is computed
+as (number_of_connected_nodes +
+number_of_connected_nodes_with_passed_heuristics - number_of_connected_nodes_with_failed_heuristics)
+If the scores are equal, the vote is provided to partition with the most clients connected to the qnetd
+server. If this number is also equal, then the tie_breaker is used. It is able to transition
+its vote if the currently active partition becomes partitioned and a non-active partition
+still has at least 50% of the active nodes. Because of this, a vote is not provided
+if the qnetd connection is not active.
+
+To use this algorithm it's required to set the number of votes per node to 1 (default)
+and the qdevice number of votes has to be also 1. This is achieved by setting
+.B quorum.device.votes
+key in corosync.conf file to 1.
+.TP
+.B lms
+Last-man-standing. If the node is the only one left in the cluster that can see the
+qnetd server then we return a vote.
+
+If more than one node can see the qnetd server but some nodes can't
+see each other then the cluster is divided up into 'partitions' based on
+their ring_id and this algorithm returns a vote to the partition with highest
+heuristics score (computed the same way as for the
+.B ffsplit
+algorithm), or if there is more than 1 partition with equal scores,
+the largest active partition or,
+if there is more than 1 equal partition, the partition that contains the tie_breaker
+node (lowest, highest, etc). For LMS to work, the number
+of qdevice votes has to be set to default (so just delete
+.B quorum.device.votes
+key from corosync.conf).
+
+.SH ADVANCED SETTINGS
+Set by using
+.B -S
+option. The default value is shown in parentheses) Options
+beginning with
+.B net_
+prefix are specific to
+.B model
+.IR net .
+.TP
+.B lock_file
+Lock file location. (/var/run/corosync-qdevice/corosync-qdevice.pid)
+.TP
+.B local_socket_file
+Internal IPC socket file location. (/var/run/corosync-qdevice/corosync-qdevice.sock)
+.TP
+.B local_socket_backlog
+Parameter passed to listen syscall. (10)
+.TP
+.B max_cs_try_again
+How many times to retry the call to a corosync function which has returned CS_ERR_TRY_AGAIN. (10)
+.TP
+.B votequorum_device_name
+Name used for qdevice registration. (Qdevice)
+.TP
+.B ipc_max_clients
+Maximum allowed simultaneous IPC clients. (10)
+.TP
+.B ipc_max_receive_size
+Maximum size of a message received by IPC client. (4096)
+.TP
+.B ipc_max_send_size
+Maximum size of a message allowed to be sent to an IPC client. (65536)
+.TP
+.B master_wins
+Force enable/disable master wins. (default is model)
+.TP
+.B heuristics_ipc_max_send_buffers
+Maximum number of heuristics worker send buffers. (128)
+.TP
+.B heuristics_ipc_max_send_receive_size
+Maximum size of a message allowed to be send to, or received from heuristics worker. (4096)
+.TP
+.B heuristics_min_timeout
+Minimum heuristics timeout accepted by client in ms. (1000)
+.TP
+.B heuristics_max_timeout
+Maximum heuristics timeout accepted by client in ms. (120000)
+.TP
+.B heuristics_min_interval
+Minimum heuristics interval accepted by client in ms. (1000)
+.TP
+.B heuristics_max_interval
+Maximum heuristics interval accepted by client in ms. (3600000)
+.TP
+.B heuristics_max_execs
+Maximum number of exec_ commands. (32)
+.TP
+.B heuristics_use_execvp
+Use execvp instead of execv for executing commands. (off)
+.TP
+.B heuristics_max_processes
+Maximum number of processes running at one time. (160)
+.TP
+.B heuristics_kill_list_interval
+Interval between status is gathered and eventually signal is sent
+to processes which didn't finished on time in ms. (5000)
+.TP
+.B net_nss_db_dir
+NSS database directory. (/etc/corosync/qdevice/net/nssdb)
+.TP
+.B net_initial_msg_receive_size
+Initial (used during connection parameters negotiation)
+maximum size of the receive buffer for message (maximum
+allowed message size received from qnetd). (32768)
+.TP
+.B net_initial_msg_send_size
+Initial (used during connection parameter negotiation)
+maximum size of one send buffer (message) to be sent to server. (32768)
+.TP
+.B net_min_msg_send_size
+Minimum required size of one send buffer (message) to be sent to server. (32768)
+.TP
+.B net_max_msg_receive_size
+Maximum allowed size of receive buffer for a message sent by server. (16777216)
+.TP
+.B net_max_send_buffers
+Maximum number of send buffers. (10)
+.TP
+.B net_nss_qnetd_cn
+Canonical name of qnetd server certificate. (Qnetd Server)
+.TP
+.B net_nss_client_cert_nickname
+NSS nickname of qdevice client certificate. (Cluster Cert)
+.TP
+.B net_heartbeat_interval_min
+Minimum heartbeat timeout accepted by client in ms. (1000)
+.TP
+.B net_heartbeat_interval_max
+Maximum heartbeat timeout accepted by client in ms. (120000)
+.TP
+.B net_min_connect_timeout
+Minimum connection timeout accepted by client in ms. (1000)
+.TP
+.B net_max_connect_timeout
+Maximum connection timeout accepted by client in ms. (120000)
+.TP
+.B net_test_algorithm_enabled
+Enable test algorithm. (if built with --enable-debug on, otherwise off)
+
+.SH EXAMPLE
+Define qdevice with
+.I net
+model connecting to qnetd running on qnetd.example.org host, using
+.I ffsplit
+algorithm.
+Heuristics is set to
+.I sync
+mode and executes two commands.
+
+.nf
+quorum {
+ provider: corosync_votequorum
+ device {
+ votes: 1
+ model: net
+ net {
+ tls: on
+ host: qnetd.example.org
+ algorithm: ffsplit
+ }
+ heuristics {
+ mode: sync
+ exec_ping: /bin/ping -q -c 1 "www.example.org"
+ exec_test_txt_exists: /usr/bin/test -f /tmp/test.txt
+ }
+}
+.fi
+.SH SEE ALSO
+.BR corosync-qdevice-tool (8)
+.BR corosync-qdevice-net-certutil (8)
+.BR corosync-qnetd (8)
+.BR corosync.conf (5)
+.SH AUTHOR
+Jan Friesse
+.PP
--- /dev/null
+.\"/*
+.\" * Copyright (C) 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 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.
+.\" */
+.TH COROSYNC-QNETD-CERTUTIL 8 2016-06-28
+.SH NAME
+corosync-qnetd-certutil - tool to generate qnetd TLS certificates
+.SH SYNOPSIS
+.B "corosync-qnetd-certutil [-i|-s] [-c certificate] [-n cluster_name]"
+.SH DESCRIPTION
+.B corosync-qnetd-certutil
+is a frontend for the NSS certutil, it is used for generating the QNetd CA (Certificate Authority),
+server certificate and signing cluster certificate used by
+.B corosync-qdevice
+when using the model 'net'.
+.SH OPTIONS
+.TP
+.B -i
+Initialize the QNetd NSS certificate database and generate the QNetd CA and server certificates.
+The default directory for the database is /etc/corosync/qnetd. This directory must be
+writeable by the current user. The QNetd CA certificate is also exported into the file
+/etc/corosync/qnetd/nssdb/qnetd-cacert.crt.
+.TP
+.B -s
+Sign the cluster certificate. It is necessary to pass the cluster name (as
+configured in corosync.conf) and the certificate request file - see options below.
+The signed certificate will be written to the
+file /etc/corosync/qnetd/nssdb/cluster-$ClusterName.crt
+.TP
+.B -c
+Certificate request file to sign.
+.TP
+.B -n
+Name of the cluster.
+.SH NOTES
+If qnetd is executed by a non root user, /etc/corosync/qnetd and its subdirectories must be owned by (or have group access for) the given user. If
+.B corosync-qnetd-certutil
+is executed as root it tries to copy the owner and group of /etc/corosync/qnetd to all of the created files.
+.SH SEE ALSO
+.BR corosync-qnetd (8)
+.BR corosync-qdevice (8)
+.SH AUTHOR
+Jan Friesse
+.PP
--- /dev/null
+.\"/*
+.\" * Copyright (C) 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 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.
+.\" */
+.TH COROSYNC-QNETD-TOOL 8 2016-06-23
+.SH NAME
+corosync-qnetd-tool \- corosync-qnetd control interface.
+.SH SYNOPSIS
+.B "corosync-qnetd-tool [-Hhlsv] [-c cluster_name] [-p qnetd_ipc_socket_path]"
+.SH DESCRIPTION
+.B corosync-qnetd-tool
+is a frontend to the internal corosync-qnetd IPC. Its main purpose is to show important
+information about the current internal state of
+.B corosync-qnetd.
+.SH OPTIONS
+.TP
+.B -H
+Properly shutdown the
+.B corosync-qnetd
+process
+.TP
+.B -h
+Display a short usage text
+.TP
+.B -l
+List all clients connected to the
+.B corosync-qnetd
+process. The output is described in its own section below.
+.TP
+.B -s
+Display status of the
+.B corosync-qnetd
+process.
+.TP
+.B -v
+Display more verbose output for options
+.B -l
+and
+.B -s
+.TP
+.B -c
+Used only with the
+.B -l
+option. By default, information about all clients from all clusters is displayed, with
+this option it's possible to filter information from a single cluster given the
+.I cluster_name.
+.TP
+.B -p
+Path to the
+.B corosync-qnetd
+communication socket.
+
+.SH LIST COMMAND OUTPUT
+.nf
+Cluster "Cluster":
+ Algorithm: Fifty-Fifty split
+ Tie-breaker: Node with lowest node ID
+ Node ID 1:
+ Client address: ::ffff:127.0.0.1:52000
+ HB interval: 8000ms
+ Configured node list: 1, 2
+ Ring ID: 1.a00000000021b40
+ Membership node list: 1, 2
+ TLS active: Yes (client certificate verified)
+ Vote: No change (ACK)
+ ...
+.fi
+
+The output contains a list of clusters. Each cluster has the cluster common options
+.I Algorithm
+and
+.I Tie-breaker
+as configured in the corosync.conf file. Information about nodes follows.
+.I Client address
+is the IP address and port of the client.
+.I HB interval
+is the heartbeat interval between
+.B corosync-qnetd
+and
+.B corosync-qdevice
+client. This option can be configured in corosync.conf.
+.I Configured node list
+is the list of nodes configured in corosync.conf.
+.I Ring ID
+and
+.I Membership node list
+are self-explanatory.
+.I TLS active
+describes if an encrypted transport is used between server and client.
+.I Vote
+is last vote sent to
+.B corosync-qdevice
+client. The last ACK/NACK vote (if it exists) is in parentheses.
+.SH SEE ALSO
+.BR corosync-qnetd (8)
+.BR corosync-qdevice (8)
+.SH AUTHOR
+Jan Friesse
+.PP
--- /dev/null
+.\"/*
+.\" * Copyright (C) 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 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.
+.\" */
+.TH COROSYNC-QNETD 8 2016-06-29
+.SH NAME
+corosync-qnetd \- QNet daemon
+.SH SYNOPSIS
+.B "corosync-qnetd [-46dfhv] [-l listen_addr] [-p listen_port] [-s tls]
+.B [-c client_cert_required] [-m max_clients] [-S option=value[,option2=value2,...]]"
+
+.SH DESCRIPTION
+.B corosync-qnetd
+is a daemon running outside of the cluster with the purpose of providing a vote to the
+.B corosync-qdevice
+model net. It's designed to support multiple clusters and be almost configuration
+and state free. New clusters are handled dynamically and no configuration file exists.
+It's also able to run as non-root user - which is recommended. Connection between the
+.B corosync-qdevice
+model net client can be optionally configured with TLS client certificate checking.
+The communication protocol between server and client is designed to be very simple
+and allow backwards compatibility.
+.SH OPTIONS
+.TP
+.B -4
+and its counterpart
+.B -6
+are used to force IPv4 or IPv6 communication. The default is to listen on both address families.
+.TP
+.B -d
+Turn on debug logging. By default the messages sent to syslog are purely operational, this
+option sends additional debug messages. For even more detail use the
+.B -d
+parameter twice.
+.TP
+.B -f
+Do not daemonize, run in the foreground.
+.TP
+.B -h
+Show short help text
+.TP
+.B -v
+Show version and supported communication protocol messages/options.
+.TP
+.B -l
+IP address to listen on. By default the daemon listens on all addresses (wildcard).
+.TP
+.B -p
+TCP port to listen on. Default port is 5403.
+.TP
+.B -s
+Determines if TLS should be used and can be one of
+.I on/off/required
+(the default is
+.I on
+).
+.I on
+means TLS is enabled but the client is not required to start TLS,
+.I off
+means TLS is completely disabled, and
+.I required
+means TLS is required.
+.I on
+and
+.I required
+require the NSS database to be properly initialized by running the
+.B corosync-qnetd-certutil
+command.
+.TP
+.B -c
+can be set to
+.I on/off.
+This option only makes sense if TLS is enabled. When
+.B -c
+is
+.I on
+a client is required to send its client certificate (default).
+.TP
+.B -m
+Maximum simultaneous clients. The default is 0 which means no limit.
+.TP
+.B -S
+Set advanced settings described in its own section below. This option
+shouldn't be generally used because most of the options are
+not safe to change.
+.SH UNPRIVILEGED USER CONFIGURATION
+It's generally recommended to run
+.B corosync-qnetd
+as a non root user. If you get a package from a distribution its highly
+possible that the packager has done all the hard work for you. If the installation
+is performed from source code, a few steps have to be taken.
+
+First it's necessary to create an unprivileged user/group. The following commands
+can be used (executed as root):
+
+.nf
+# groupadd -r coroqnetd
+# useradd -r -g coroqnetd -d / -s /sbin/nologin -c "User for corosync-qnetd" coroqnetd
+.fi
+
+The next step is to set the correct owner and group on /etc/corosync/qnetd and /var/run/corosync-qnetd
+directories.
+
+.nf
+# chown -R coroqnetd:coroqnetd /etc/corosync/qnetd /var/run/corosync-qnetd
+.fi
+
+Some systems have the /var/run directory on a tmpfs file system which gets discarded after
+a reboot. The solution is to use an initscript which takes care of the /var/run/corosync-qnetd
+creation and sets the correct owner and permissions. For systems with systemd it's possible
+to use a tmpfile.d configuration file (installed by default if systemd is enabled during
+corosync compilation).
+
+The last step is to make sure
+.B corosync-qnetd
+is really executed as an unprivileged user. For initscript systems it's enough to set the
+line COROSYNC_QNETD_RUNAS in /etc/(sysconfig|default)/corosync-qnetd file. If the file
+is not already installed then use the one provided in the corosync source code
+(init/corosync-qnetd.sysconfig.example). For systemd, overwrite/copy the
+corosync-qnetd.service unit file and uncomment/change the "User=" directive.
+
+.SH TLS CONFIGURATION
+For TLS to work its necessary to create the NSS database. If pcs is used then the following
+steps are not needed because pcs does them automatically.
+
+.B corosync-qnetd-certutil
+is the tool to perform required actions. Just run:
+
+.nf
+# corosync-qnetd-certutil -i
+.fi
+
+If TLS is not required then simply edit /etc/(sysconfig|default)/corosync-qnetd or
+systemd unit file and add the parameter
+.B -s
+.I off
+in the proper place.
+
+.SH ADVANCED SETTINGS
+Set by the
+.B -S
+option. The default value is shown in parentheses.
+.TP
+.B listen_backlog
+Parameter passed to the listen syscall on the network socket. (10)
+.TP
+.B max_client_send_buffers
+Maximum number of send buffers for one client. (32)
+.TP
+.B max_client_send_size
+Maximum size of one send buffer (message) to be sent to a client. (32768)
+.TP
+.B max_client_receive_size
+Maximum size of the receive buffer for a client message (maximum
+allowed message size received by client). (32768)
+.TP
+.B nss_db_dir
+NSS database directory. (/etc/corosync/qnetd/nssdb)
+.TP
+.B cert_nickname
+NSS nickname of qnetd server certificate. (QNetd Cert)
+.TP
+.B heartbeat_interval_min
+Minimum heartbeat timeout accepted by server in ms. (1000)
+.TP
+.B heartbeat_interval_max
+Maximum heartbeat timeout accepted by server in ms. (120000)
+.TP
+.B dpd_enabled
+Dead peer detection enabled. (on)
+.TP
+.B dpd_interval
+How often the DPD algorithm detects dead peers in ms. (10000)
+.TP
+.B lock_file
+Lock file location. (/var/run/corosync-qnetd/corosync-qnetd.pid)
+.TP
+.B local_socket_file
+Internal IPC socket file location. (/var/run/corosync-qnetd/corosync-qnetd.sock)
+.TP
+.B local_socket_backlog
+Parameter passed to listen syscall on the local socket. (10)
+.TP
+.B ipc_max_clients
+Maximum allowed simultaneous IPC clients. (10)
+.TP
+.B ipc_max_receive_size
+Maximum size of a message received by IPC client. (4096)
+.TP
+.B ipc_max_send_size
+Maximum size of a message sent to an IPC client. (10485760)
+.SH SEE ALSO
+.BR corosync-qnetd-tool (8)
+.BR corosync-qnetd-certutil (8)
+.BR corosync-qdevice (8)
+.SH AUTHOR
+Jan Friesse
+.PP
--- /dev/null
+corosync-qdevice
+corosync-qdevice-tool
+corosync-qnetd-certutil
+corosync-qdevice-net-certutil
+corosync-qnetd
+corosync-qnetd-tool
+*.test
--- /dev/null
+# Copyright (c) 2012-2018 Red Hat, Inc.
+#
+# Authors: Jan Friesse (jfriesse@redhat.com)
+# Fabio M. Di Nitto (fdinitto@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.
+
+MAINTAINERCLEANFILES = Makefile.in
+
+SUBDIRS =
+
+bin_PROGRAMS =
+sbin_PROGRAMS =
+bin_SCRIPTS =
+sbin_SCRIPTS =
+EXTRA_DIST = corosync-qnetd-certutil.sh corosync-qdevice-net-certutil.sh
+
+if BUILD_QNETD
+
+bin_PROGRAMS += corosync-qnetd corosync-qnetd-tool
+
+bin_SCRIPTS += corosync-qnetd-certutil
+
+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 utils.c utils.h
+
+corosync_qnetd_CFLAGS = $(nss_CFLAGS) $(libsystemd_CFLAGS)
+corosync_qnetd_LDADD = $(nss_LIBS) $(libsystemd_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
+
+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 \
+ qdevice-heuristics.h qdevice-heuristics.c \
+ qdevice-heuristics-worker.h qdevice-heuristics-worker.c \
+ qdevice-heuristics-io.h qdevice-heuristics-io.c \
+ qdevice-heuristics-worker-instance.h \
+ qdevice-heuristics-worker-log.h qdevice-heuristics-worker-log.c \
+ qdevice-heuristics-log.h qdevice-heuristics-log.c \
+ qdevice-heuristics-instance.h qdevice-heuristics-instance.c \
+ qdevice-heuristics-mode.h qdevice-heuristics-mode.c \
+ qdevice-heuristics-exec-list.c qdevice-heuristics-exec-list.h \
+ qdevice-heuristics-cmd.c qdevice-heuristics-cmd.h \
+ qdevice-heuristics-worker-cmd.c qdevice-heuristics-worker-cmd.h \
+ qdevice-heuristics-cmd-str.h \
+ qdevice-heuristics-exec-result.c qdevice-heuristics-exec-result.h \
+ process-list.h process-list.c \
+ qdevice-net-heuristics.c qdevice-net-heuristics.h \
+ qdevice-heuristics-result-notifier.c qdevice-heuristics-result-notifier.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 utils.c utils.h
+
+corosync_qdevice_CFLAGS = $(nss_CFLAGS) $(libsystemd_CFLAGS) $(cmap_CFLAGS) \
+ $(qb_CFLAGS) $(votequorum_CFLAGS) $(corosync_common_CFLAGS)
+corosync_qdevice_LDADD = $(nss_LIBS) $(libsystemd_LIBS) $(cmap_LIBS) \
+ $(qb_LIBS) $(votequorum_LIBS) $(corosync_common_LIBS)
+
+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 process-list.test
+check_PROGRAMS = qnetd-cluster-list.test dynar.test dynar-simple-lex.test \
+ dynar-getopt-lex.test process-list.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
+process_list_test_SOURCES = test-process-list.c dynar.c dynar-str.c dynar-simple-lex.c \
+ process-list.c
+
+endif
+
+clean-local:
+ rm -rf $(bin_SCRIPTS) $(sbin_SCRIPTS)
--- /dev/null
+#!@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 0660 "$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 0660 "$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 0770 "$DB_DIR"
+ fi
+
+ echo "Creating new key and cert db"
+ echo -n "" > "$PWD_FILE"
+ chown root:root "$PWD_FILE"
+ chmod 0660 "$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 0660 "$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_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
--- /dev/null
+/*
+ * 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 <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qdevice-config.h"
+
+#include "dynar.h"
+#include "dynar-str.h"
+#include "utils.h"
+#include "unix-socket.h"
+
+#define IPC_READ_BUF_SIZE 512
+
+enum qdevice_tool_operation {
+ QDEVICE_TOOL_OPERATION_NONE,
+ QDEVICE_TOOL_OPERATION_SHUTDOWN,
+ QDEVICE_TOOL_OPERATION_STATUS,
+};
+
+enum qdevice_tool_exit_code {
+ QDEVICE_TOOL_EXIT_CODE_NO_ERROR = 0,
+ QDEVICE_TOOL_EXIT_CODE_USAGE = 1,
+ QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR = 2,
+ QDEVICE_TOOL_EXIT_CODE_SOCKET_CONNECT = 3,
+ QDEVICE_TOOL_EXIT_CODE_QDEVICE_RETURNED_ERROR = 4,
+};
+
+static void
+usage(void)
+{
+
+ printf("usage: %s [-Hhsv] [-p qdevice_ipc_socket_path]\n",
+ QDEVICE_TOOL_PROGRAM_NAME);
+}
+
+static void
+cli_parse(int argc, char * const argv[], enum qdevice_tool_operation *operation,
+ int *verbose, char **socket_path)
+{
+ int ch;
+
+ *operation = QDEVICE_TOOL_OPERATION_NONE;
+ *verbose = 0;
+ *socket_path = strdup(QDEVICE_DEFAULT_LOCAL_SOCKET_FILE);
+
+ if (*socket_path == NULL) {
+ errx(QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR,
+ "Can't alloc memory for socket path string");
+ }
+
+ while ((ch = getopt(argc, argv, "Hhsvp:")) != -1) {
+ switch (ch) {
+ case 'H':
+ *operation = QDEVICE_TOOL_OPERATION_SHUTDOWN;
+ break;
+ case 's':
+ *operation = QDEVICE_TOOL_OPERATION_STATUS;
+ break;
+ case 'v':
+ *verbose = 1;
+ break;
+ case 'p':
+ free(*socket_path);
+ *socket_path = strdup(optarg);
+ if (*socket_path == NULL) {
+ errx(QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR,
+ "Can't alloc memory for socket path string");
+ }
+ break;
+ case 'h':
+ case '?':
+ usage();
+ exit(QDEVICE_TOOL_EXIT_CODE_USAGE);
+ break;
+ }
+ }
+
+ if (*operation == QDEVICE_TOOL_OPERATION_NONE) {
+ usage();
+ exit(QDEVICE_TOOL_EXIT_CODE_USAGE);
+ }
+}
+
+static int
+store_command(struct dynar *str, enum qdevice_tool_operation operation, int verbose)
+{
+ const char *nline = "\n\0";
+ const int nline_len = 2;
+
+ switch (operation) {
+ case QDEVICE_TOOL_OPERATION_NONE:
+ errx(QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR, "Unhandled operation none");
+ break;
+ case QDEVICE_TOOL_OPERATION_SHUTDOWN:
+ if (dynar_str_cat(str, "shutdown ") != 0) {
+ return (-1);
+ }
+ break;
+ case QDEVICE_TOOL_OPERATION_STATUS:
+ if (dynar_str_cat(str, "status ") != 0) {
+ return (-1);
+ }
+ break;
+ }
+
+ if (verbose) {
+ if (dynar_str_cat(str, "verbose ") != 0) {
+ return (-1);
+ }
+ }
+
+ if (dynar_cat(str, nline, nline_len) != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * -1 - Internal error (can't alloc memory)
+ * 0 - No error
+ * 1 - IPC returned error
+ * 2 - Unknown status line
+ */
+static int
+read_ipc_reply(FILE *f)
+{
+ struct dynar read_str;
+ int ch;
+ int status_readed;
+ int res;
+ static const char *ok_str = "OK";
+ static const char *err_str = "Error";
+ int err_set;
+ char c;
+
+ dynar_init(&read_str, IPC_READ_BUF_SIZE);
+
+ status_readed = 0;
+ err_set = 0;
+ res = 0;
+
+ while ((ch = fgetc(f)) != EOF) {
+ if (status_readed) {
+ putc(ch, (err_set ? stderr : stdout));
+ } else {
+ if (ch == '\r') {
+ } else if (ch == '\n') {
+ status_readed = 1;
+
+ c = '\0';
+ if (dynar_cat(&read_str, &c, sizeof(c)) != 0) {
+ res = -1;
+ goto exit_destroy;
+ }
+
+ if (strcasecmp(dynar_data(&read_str), ok_str) == 0) {
+ } else if (strcasecmp(dynar_data(&read_str), err_str) == 0) {
+ err_set = 1;
+ res = 1;
+ fprintf(stderr, "Error: ");
+ } else {
+ res = 2;
+ goto exit_destroy;
+ }
+ } else {
+ c = ch;
+ if (dynar_cat(&read_str, &c, sizeof(c)) != 0) {
+ res = -1;
+ goto exit_destroy;
+ }
+ }
+ }
+ }
+
+exit_destroy:
+ dynar_destroy(&read_str);
+
+ return (res);
+}
+
+int
+main(int argc, char * const argv[])
+{
+ enum qdevice_tool_operation operation;
+ int verbose;
+ char *socket_path;
+ int sock_fd;
+ FILE *sock;
+ struct dynar send_str;
+ int res;
+ int exit_code;
+
+ exit_code = QDEVICE_TOOL_EXIT_CODE_NO_ERROR;
+
+ cli_parse(argc, argv, &operation, &verbose, &socket_path);
+
+ dynar_init(&send_str, QDEVICE_DEFAULT_IPC_MAX_RECEIVE_SIZE);
+
+ sock_fd = unix_socket_client_create(socket_path, 0);
+ if (sock_fd == -1) {
+ err(QDEVICE_TOOL_EXIT_CODE_SOCKET_CONNECT,
+ "Can't connect to QDevice socket (is QDevice running?)");
+ }
+
+ sock = fdopen(sock_fd, "w+t");
+ if (sock == NULL) {
+ err(QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR, "Can't open QDevice socket fd");
+ }
+
+ if (store_command(&send_str, operation, verbose) != 0) {
+ errx(QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR, "Can't store command");
+ }
+
+ res = fprintf(sock, "%s", dynar_data(&send_str));
+ if (res < 0 || (size_t)res != strlen(dynar_data(&send_str)) ||
+ fflush(sock) != 0) {
+ errx(QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR, "Can't send command");
+ }
+
+ res = read_ipc_reply(sock);
+ switch (res) {
+ case -1:
+ errx(QDEVICE_TOOL_EXIT_CODE_INTERNAL_ERROR, "Internal error during IPC status line read");
+ break;
+ case 0:
+ break;
+ case 1:
+ exit_code = QDEVICE_TOOL_EXIT_CODE_QDEVICE_RETURNED_ERROR;
+ break;
+ case 2:
+ errx(QDEVICE_TOOL_EXIT_CODE_SOCKET_CONNECT, "Unknown status line returned by IPC server");
+ break;
+ }
+
+ if (fclose(sock) != 0) {
+ warn("Can't close QDevice socket");
+ }
+
+ free(socket_path);
+ dynar_destroy(&send_str);
+
+ return (exit_code);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <err.h>
+#include <signal.h>
+
+#include "dynar.h"
+#include "dynar-str.h"
+#include "dynar-getopt-lex.h"
+#include "qdevice-advanced-settings.h"
+#include "qdevice-config.h"
+#include "qdevice-cmap.h"
+#include "qdevice-heuristics.h"
+#include "qdevice-ipc.h"
+#include "qdevice-log.h"
+#include "qdevice-model.h"
+#include "qdevice-votequorum.h"
+#include "utils.h"
+
+#ifdef HAVE_LIBSYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+
+struct qdevice_instance *global_instance;
+
+static void
+signal_int_handler(int sig)
+{
+ qdevice_log(LOG_DEBUG, "SIGINT received - closing local unix socket");
+ qdevice_ipc_close(global_instance);
+}
+
+static void
+signal_term_handler(int sig)
+{
+ qdevice_log(LOG_DEBUG, "SIGTERM received - closing server socket");
+ qdevice_ipc_close(global_instance);
+}
+
+static void
+signal_handlers_register(void)
+{
+ struct sigaction act;
+
+ act.sa_handler = signal_int_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+
+ sigaction(SIGINT, &act, NULL);
+
+ act.sa_handler = signal_term_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+
+ sigaction(SIGTERM, &act, NULL);
+
+ act.sa_handler = SIG_DFL;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+
+ sigaction(SIGCHLD, &act, NULL);
+
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+
+ sigaction(SIGPIPE, &act, NULL);
+}
+
+static void
+usage(void)
+{
+
+ printf("usage: %s [-dfh] [-S option=value[,option2=value2,...]]\n", QDEVICE_PROGRAM_NAME);
+}
+
+static void
+cli_parse_long_opt(struct qdevice_advanced_settings *advanced_settings, const char *long_opt)
+{
+ struct dynar_getopt_lex lex;
+ struct dynar dynar_long_opt;
+ const char *opt;
+ const char *val;
+ int res;
+
+ dynar_init(&dynar_long_opt, strlen(long_opt) + 1);
+ if (dynar_str_cpy(&dynar_long_opt, long_opt) != 0) {
+ errx(1, "Can't alloc memory for long option");
+ }
+
+ dynar_getopt_lex_init(&lex, &dynar_long_opt);
+
+ while (dynar_getopt_lex_token_next(&lex) == 0 && strcmp(dynar_data(&lex.option), "") != 0) {
+ opt = dynar_data(&lex.option);
+ val = dynar_data(&lex.value);
+
+ res = qdevice_advanced_settings_set(advanced_settings, opt, val);
+ switch (res) {
+ case -1:
+ errx(1, "Unknown option '%s'", opt);
+ break;
+ case -2:
+ errx(1, "Invalid value '%s' for option '%s'", val, opt);
+ break;
+ }
+ }
+
+ dynar_getopt_lex_destroy(&lex);
+ dynar_destroy(&dynar_long_opt);
+}
+
+static void
+cli_parse(int argc, char * const argv[], int *foreground, int *force_debug,
+ struct qdevice_advanced_settings *advanced_settings)
+{
+ int ch;
+
+ *foreground = 0;
+ *force_debug = 0;
+
+ while ((ch = getopt(argc, argv, "dfhS:")) != -1) {
+ switch (ch) {
+ case 'd':
+ *force_debug = 1;
+ break;
+ case 'f':
+ *foreground = 1;
+ break;
+ case 'S':
+ cli_parse_long_opt(advanced_settings, optarg);
+ break;
+ case 'h':
+ case '?':
+ usage();
+ exit(1);
+ break;
+ }
+ }
+}
+
+int
+main(int argc, char * const argv[])
+{
+ struct qdevice_instance instance;
+ struct qdevice_advanced_settings advanced_settings;
+ int foreground;
+ int force_debug;
+ int lock_file;
+ int another_instance_running;
+
+ if (qdevice_advanced_settings_init(&advanced_settings) != 0) {
+ errx(1, "Can't alloc memory for advanced settings");
+ }
+
+ cli_parse(argc, argv, &foreground, &force_debug, &advanced_settings);
+
+ qdevice_instance_init(&instance, &advanced_settings);
+
+ qdevice_heuristics_init(&instance.heuristics_instance, &advanced_settings);
+ instance.heuristics_instance.qdevice_instance_ptr = &instance;
+
+ qdevice_cmap_init(&instance);
+ qdevice_log_init(&instance, force_debug);
+
+ /*
+ * Daemonize
+ */
+ if (!foreground) {
+ utils_tty_detach();
+ }
+
+ if ((lock_file = utils_flock(advanced_settings.lock_file, getpid(),
+ &another_instance_running)) == -1) {
+ if (another_instance_running) {
+ qdevice_log(LOG_ERR, "Another instance is running");
+ } else {
+ qdevice_log_err(LOG_ERR, "Can't acquire lock");
+ }
+
+ exit(1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Initializing votequorum");
+ qdevice_votequorum_init(&instance);
+
+ qdevice_log(LOG_DEBUG, "Initializing local socket");
+ if (qdevice_ipc_init(&instance) != 0) {
+ return (1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Registering qdevice models");
+ qdevice_model_register_all();
+
+ qdevice_log(LOG_DEBUG, "Configuring qdevice");
+ if (qdevice_instance_configure_from_cmap(&instance) != 0) {
+ return (1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Configuring master_wins");
+ if (qdevice_votequorum_master_wins(&instance, (advanced_settings.master_wins ==
+ QDEVICE_ADVANCED_SETTINGS_MASTER_WINS_FORCE_ON ? 1 : 0)) != 0) {
+ return (1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Getting configuration node list");
+ if (qdevice_cmap_store_config_node_list(&instance) != 0) {
+ return (1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Initializing qdevice model");
+ if (qdevice_model_init(&instance) != 0) {
+ return (1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Initializing cmap tracking");
+ if (qdevice_cmap_add_track(&instance) != 0) {
+ return (1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Waiting for ring id");
+ if (qdevice_votequorum_wait_for_ring_id(&instance) != 0) {
+ return (1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Waiting for initial heuristics exec result");
+ if (qdevice_heuristics_wait_for_initial_exec_result(&instance.heuristics_instance) != 0) {
+ return (1);
+ }
+
+ global_instance = &instance;
+ signal_handlers_register();
+
+ qdevice_log(LOG_DEBUG, "Running qdevice model");
+#ifdef HAVE_LIBSYSTEMD
+ sd_notify (0, "READY=1");
+#endif
+ if (qdevice_model_run(&instance) != 0) {
+ return (1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Removing cmap tracking");
+ if (qdevice_cmap_del_track(&instance) != 0) {
+ return (1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Destroying qdevice model");
+ qdevice_model_destroy(&instance);
+
+ qdevice_log(LOG_DEBUG, "Destroying qdevice ipc");
+ qdevice_ipc_destroy(&instance);
+
+ qdevice_log(LOG_DEBUG, "Destroying votequorum and cmap");
+ qdevice_votequorum_destroy(&instance);
+ qdevice_cmap_destroy(&instance);
+
+ qdevice_log(LOG_DEBUG, "Destroying heuristics");
+ qdevice_heuristics_destroy(&instance.heuristics_instance);
+
+ qdevice_log(LOG_DEBUG, "Closing log");
+ qdevice_log_close(&instance);
+
+ qdevice_instance_destroy(&instance);
+
+ qdevice_advanced_settings_destroy(&advanced_settings);
+
+ return (0);
+}
--- /dev/null
+#!@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] [-n cluster_name]"
+ 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 0660 "$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 0660 "$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 0770 "$DB_DIR"
+ fi
+
+ echo "Creating new key and cert db"
+ echo -n "" > "$PWD_FILE"
+ chown_ref_cfgdir "$PWD_FILE"
+ chmod 0660 "$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 0660 "$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
--- /dev/null
+/*
+ * 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 <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qnet-config.h"
+
+#include "dynar.h"
+#include "dynar-str.h"
+#include "utils.h"
+#include "unix-socket.h"
+
+#define IPC_READ_BUF_SIZE 512
+
+enum qnetd_tool_operation {
+ QNETD_TOOL_OPERATION_NONE,
+ QNETD_TOOL_OPERATION_SHUTDOWN,
+ QNETD_TOOL_OPERATION_STATUS,
+ QNETD_TOOL_OPERATION_LIST,
+};
+
+enum qnetd_tool_exit_code {
+ QNETD_TOOL_EXIT_CODE_NO_ERROR = 0,
+ QNETD_TOOL_EXIT_CODE_USAGE = 1,
+ QNETD_TOOL_EXIT_CODE_INTERNAL_ERROR = 2,
+ QNETD_TOOL_EXIT_CODE_SOCKET_CONNECT = 3,
+ QNETD_TOOL_EXIT_CODE_QNETD_RETURNED_ERROR = 4,
+};
+
+static void
+usage(void)
+{
+
+ printf("usage: %s [-Hhlsv] [-c cluster_name] [-p qnetd_ipc_socket_path]\n",
+ QNETD_TOOL_PROGRAM_NAME);
+}
+
+static void
+cli_parse(int argc, char * const argv[], enum qnetd_tool_operation *operation,
+ int *verbose, char **cluster_name, char **socket_path)
+{
+ int ch;
+
+ *operation = QNETD_TOOL_OPERATION_NONE;
+ *verbose = 0;
+ *cluster_name = NULL;
+ *socket_path = strdup(QNETD_DEFAULT_LOCAL_SOCKET_FILE);
+
+ if (*socket_path == NULL) {
+ errx(QNETD_TOOL_EXIT_CODE_INTERNAL_ERROR,
+ "Can't alloc memory for socket path string");
+ }
+
+ while ((ch = getopt(argc, argv, "Hhlsvc:p:")) != -1) {
+ switch (ch) {
+ case 'H':
+ *operation = QNETD_TOOL_OPERATION_SHUTDOWN;
+ break;
+ case 'l':
+ *operation = QNETD_TOOL_OPERATION_LIST;
+ break;
+ case 's':
+ *operation = QNETD_TOOL_OPERATION_STATUS;
+ break;
+ case 'v':
+ *verbose = 1;
+ break;
+ case 'c':
+ free(*cluster_name);
+ *cluster_name = strdup(optarg);
+ if (*cluster_name == NULL) {
+ errx(QNETD_TOOL_EXIT_CODE_INTERNAL_ERROR,
+ "Can't alloc memory for cluster name string");
+ }
+ break;
+ case 'p':
+ free(*socket_path);
+ *socket_path = strdup(optarg);
+ if (*socket_path == NULL) {
+ errx(QNETD_TOOL_EXIT_CODE_INTERNAL_ERROR,
+ "Can't alloc memory for socket path string");
+ }
+ break;
+ case 'h':
+ case '?':
+ usage();
+ exit(QNETD_TOOL_EXIT_CODE_USAGE);
+ break;
+ }
+ }
+
+ if (*operation == QNETD_TOOL_OPERATION_NONE) {
+ usage();
+ exit(QNETD_TOOL_EXIT_CODE_USAGE);
+ }
+}
+
+static int
+store_command(struct dynar *str, enum qnetd_tool_operation operation, int verbose,
+ const char *cluster_name)
+{
+ const char *nline = "\n\0";
+ const int nline_len = 2;
+
+ switch (operation) {
+ case QNETD_TOOL_OPERATION_NONE:
+ errx(QNETD_TOOL_EXIT_CODE_INTERNAL_ERROR, "Unhandled operation none");
+ break;
+ case QNETD_TOOL_OPERATION_SHUTDOWN:
+ if (dynar_str_cat(str, "shutdown ") != 0) {
+ return (-1);
+ }
+ break;
+ case QNETD_TOOL_OPERATION_STATUS:
+ if (dynar_str_cat(str, "status ") != 0) {
+ return (-1);
+ }
+ break;
+ case QNETD_TOOL_OPERATION_LIST:
+ if (dynar_str_cat(str, "list ") != 0) {
+ return (-1);
+ }
+ break;
+ }
+
+ if (verbose) {
+ if (dynar_str_cat(str, "verbose ") != 0) {
+ return (-1);
+ }
+ }
+
+ if (cluster_name != NULL) {
+ if (dynar_str_cat(str, "cluster ") != 0 ||
+ dynar_str_quote_cat(str, cluster_name) != 0 ||
+ dynar_str_cat(str, " ") != 0) {
+ return (-1);
+ }
+ }
+
+ if (dynar_cat(str, nline, nline_len) != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * -1 - Internal error (can't alloc memory)
+ * 0 - No error
+ * 1 - IPC returned error
+ * 2 - Unknown status line
+ */
+static int
+read_ipc_reply(FILE *f)
+{
+ struct dynar read_str;
+ int ch;
+ int status_readed;
+ int res;
+ static const char *ok_str = "OK";
+ static const char *err_str = "Error";
+ int err_set;
+ char c;
+
+ dynar_init(&read_str, IPC_READ_BUF_SIZE);
+
+ status_readed = 0;
+ err_set = 0;
+ res = 0;
+
+ while ((ch = fgetc(f)) != EOF) {
+ if (status_readed) {
+ putc(ch, (err_set ? stderr : stdout));
+ } else {
+ if (ch == '\r') {
+ } else if (ch == '\n') {
+ status_readed = 1;
+
+ c = '\0';
+ if (dynar_cat(&read_str, &c, sizeof(c)) != 0) {
+ res = -1;
+ goto exit_destroy;
+ }
+
+ if (strcasecmp(dynar_data(&read_str), ok_str) == 0) {
+ } else if (strcasecmp(dynar_data(&read_str), err_str) == 0) {
+ err_set = 1;
+ res = 1;
+ fprintf(stderr, "Error: ");
+ } else {
+ res = 2;
+ goto exit_destroy;
+ }
+ } else {
+ c = ch;
+ if (dynar_cat(&read_str, &c, sizeof(c)) != 0) {
+ res = -1;
+ goto exit_destroy;
+ }
+ }
+ }
+ }
+
+exit_destroy:
+ dynar_destroy(&read_str);
+
+ return (res);
+}
+
+int
+main(int argc, char * const argv[])
+{
+ enum qnetd_tool_operation operation;
+ int verbose;
+ char *cluster_name;
+ char *socket_path;
+ int sock_fd;
+ FILE *sock;
+ struct dynar send_str;
+ int res;
+ int exit_code;
+
+ exit_code = QNETD_TOOL_EXIT_CODE_NO_ERROR;
+
+ cli_parse(argc, argv, &operation, &verbose, &cluster_name, &socket_path);
+
+ dynar_init(&send_str, QNETD_DEFAULT_IPC_MAX_RECEIVE_SIZE);
+
+ sock_fd = unix_socket_client_create(socket_path, 0);
+ if (sock_fd == -1) {
+ err(QNETD_TOOL_EXIT_CODE_SOCKET_CONNECT,
+ "Can't connect to qnetd socket (is QNetd running?)");
+ }
+
+ sock = fdopen(sock_fd, "w+t");
+ if (sock == NULL) {
+ err(QNETD_TOOL_EXIT_CODE_INTERNAL_ERROR, "Can't open QNetd socket fd");
+ }
+
+ if (store_command(&send_str, operation, verbose, cluster_name) != 0) {
+ errx(QNETD_TOOL_EXIT_CODE_INTERNAL_ERROR, "Can't store command");
+ }
+
+ res = fprintf(sock, "%s", dynar_data(&send_str));
+ if (res < 0 || (size_t)res != strlen(dynar_data(&send_str)) ||
+ fflush(sock) != 0) {
+ errx(QNETD_TOOL_EXIT_CODE_INTERNAL_ERROR, "Can't send command");
+ }
+
+ res = read_ipc_reply(sock);
+ switch (res) {
+ case -1:
+ errx(QNETD_TOOL_EXIT_CODE_INTERNAL_ERROR, "Internal error during IPC status line read");
+ break;
+ case 0:
+ break;
+ case 1:
+ exit_code = QNETD_TOOL_EXIT_CODE_QNETD_RETURNED_ERROR;
+ break;
+ case 2:
+ errx(QNETD_TOOL_EXIT_CODE_SOCKET_CONNECT, "Unknown status line returned by IPC server");
+ break;
+ }
+
+ if (fclose(sock) != 0) {
+ warn("Can't close QNetd socket");
+ }
+
+ free(cluster_name);
+ free(socket_path);
+ dynar_destroy(&send_str);
+
+ return (exit_code);
+}
--- /dev/null
+/*
+ * 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 <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "qnet-config.h"
+
+#include "dynar.h"
+#include "dynar-str.h"
+#include "dynar-getopt-lex.h"
+#include "nss-sock.h"
+#include "pr-poll-array.h"
+#include "qnetd-advanced-settings.h"
+#include "qnetd-algorithm.h"
+#include "qnetd-instance.h"
+#include "qnetd-ipc.h"
+#include "qnetd-log.h"
+#include "qnetd-client-net.h"
+#include "qnetd-client-msg-received.h"
+#include "qnetd-poll-array-user-data.h"
+#include "utils.h"
+#include "msg.h"
+
+#ifdef HAVE_LIBSYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+
+/*
+ * This is global variable used for comunication with main loop and signal (calls close)
+ */
+struct qnetd_instance *global_instance;
+
+enum tlv_decision_algorithm_type
+ qnetd_static_supported_decision_algorithms[QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE] = {
+ TLV_DECISION_ALGORITHM_TYPE_TEST,
+ TLV_DECISION_ALGORITHM_TYPE_FFSPLIT,
+ TLV_DECISION_ALGORITHM_TYPE_2NODELMS,
+ TLV_DECISION_ALGORITHM_TYPE_LMS,
+};
+
+static void
+qnetd_err_nss(void)
+{
+
+ qnetd_log_nss(LOG_CRIT, "NSS error");
+
+ exit(1);
+}
+
+static void
+qnetd_warn_nss(void)
+{
+
+ qnetd_log_nss(LOG_WARNING, "NSS warning");
+}
+
+static PRPollDesc *
+qnetd_pr_poll_array_create(struct qnetd_instance *instance)
+{
+ struct pr_poll_array *poll_array;
+ const struct qnetd_client_list *client_list;
+ struct qnetd_client *client;
+ PRPollDesc *poll_desc;
+ struct qnetd_poll_array_user_data *user_data;
+ const struct unix_socket_client_list *ipc_client_list;
+ struct unix_socket_client *ipc_client;
+
+ poll_array = &instance->poll_array;
+ client_list = &instance->clients;
+ ipc_client_list = &instance->local_ipc.clients;
+
+ pr_poll_array_clean(poll_array);
+
+ if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+ return (NULL);
+ }
+
+ poll_desc->fd = instance->server.socket;
+ poll_desc->in_flags = PR_POLL_READ;
+
+ user_data->type = QNETD_POLL_ARRAY_USER_DATA_TYPE_SOCKET;
+
+ if (qnetd_ipc_is_closed(instance)) {
+ qnetd_log(LOG_DEBUG, "Listening socket is closed");
+
+ return (NULL);
+ }
+
+ if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+ return (NULL);
+ }
+
+ poll_desc->fd = instance->ipc_socket_poll_fd;
+ poll_desc->in_flags = PR_POLL_READ;
+ user_data->type = QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET;
+
+ TAILQ_FOREACH(client, client_list, entries) {
+ if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+ return (NULL);
+ }
+ poll_desc->fd = client->socket;
+ poll_desc->in_flags = PR_POLL_READ;
+
+ if (!send_buffer_list_empty(&client->send_buffer_list)) {
+ poll_desc->in_flags |= PR_POLL_WRITE;
+ }
+
+ user_data->type = QNETD_POLL_ARRAY_USER_DATA_TYPE_CLIENT;
+ user_data->client = client;
+ }
+
+ TAILQ_FOREACH(ipc_client, ipc_client_list, entries) {
+ if (!ipc_client->reading_line && !ipc_client->writing_buffer) {
+ continue;
+ }
+
+ if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+ return (NULL);
+ }
+
+ poll_desc->fd = ((struct qnetd_ipc_user_data *)ipc_client->user_data)->nspr_poll_fd;
+ if (ipc_client->reading_line) {
+ poll_desc->in_flags |= PR_POLL_READ;
+ }
+
+ if (ipc_client->writing_buffer) {
+ poll_desc->in_flags |= PR_POLL_WRITE;
+ }
+
+ user_data->type = QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT;
+ user_data->ipc_client = ipc_client;
+ }
+
+ pr_poll_array_gc(poll_array);
+
+ return (poll_array->array);
+}
+
+static int
+qnetd_poll(struct qnetd_instance *instance)
+{
+ struct qnetd_client *client;
+ PRPollDesc *pfds;
+ PRInt32 poll_res;
+ ssize_t i;
+ int client_disconnect;
+ struct qnetd_poll_array_user_data *user_data;
+ struct unix_socket_client *ipc_client;
+
+ client = NULL;
+ client_disconnect = 0;
+
+ pfds = qnetd_pr_poll_array_create(instance);
+ if (pfds == NULL) {
+ return (-1);
+ }
+
+ if ((poll_res = PR_Poll(pfds, pr_poll_array_size(&instance->poll_array),
+ timer_list_time_to_expire(&instance->main_timer_list))) >= 0) {
+ timer_list_expire(&instance->main_timer_list);
+
+ /*
+ * Walk thru pfds array and process events
+ */
+ for (i = 0; i < pr_poll_array_size(&instance->poll_array); i++) {
+ user_data = pr_poll_array_get_user_data(&instance->poll_array, i);
+
+ client = NULL;
+ ipc_client = NULL;
+ client_disconnect = 0;
+
+ switch (user_data->type) {
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_SOCKET:
+ break;
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_CLIENT:
+ client = user_data->client;
+ client_disconnect = client->schedule_disconnect;
+ break;
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET:
+ break;
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT:
+ ipc_client = user_data->ipc_client;
+ client_disconnect = ipc_client->schedule_disconnect;
+ }
+
+ if (!client_disconnect && poll_res > 0 &&
+ pfds[i].out_flags & PR_POLL_READ) {
+ switch (user_data->type) {
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_SOCKET:
+ qnetd_client_net_accept(instance);
+ break;
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_CLIENT:
+ if (qnetd_client_net_read(instance, client) == -1) {
+ client_disconnect = 1;
+ }
+ break;
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET:
+ qnetd_ipc_accept(instance, &ipc_client);
+ break;
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT:
+ qnetd_ipc_io_read(instance, ipc_client);
+ break;
+ }
+ }
+
+ if (!client_disconnect && poll_res > 0 &&
+ pfds[i].out_flags & PR_POLL_WRITE) {
+ switch (user_data->type) {
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_SOCKET:
+ /*
+ * Poll write on listen socket -> fatal error
+ */
+ qnetd_log(LOG_CRIT, "POLL_WRITE on listening socket");
+
+ return (-1);
+ break;
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_CLIENT:
+ if (qnetd_client_net_write(instance, client) == -1) {
+ client_disconnect = 1;
+ }
+ break;
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET:
+ qnetd_log(LOG_CRIT, "POLL_WRITE on listening IPC socket");
+ return (-1);
+ break;
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT:
+ qnetd_ipc_io_write(instance, ipc_client);
+ break;
+ }
+ }
+
+ if (!client_disconnect && poll_res > 0 &&
+ (pfds[i].out_flags & (PR_POLL_ERR|PR_POLL_NVAL|PR_POLL_HUP|PR_POLL_EXCEPT)) &&
+ !(pfds[i].out_flags & (PR_POLL_READ|PR_POLL_WRITE))) {
+ switch (user_data->type) {
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_SOCKET:
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET:
+ if (pfds[i].out_flags != PR_POLL_NVAL) {
+ /*
+ * Poll ERR on listening socket is fatal error.
+ * POLL_NVAL is used as a signal to quit poll loop.
+ */
+ qnetd_log(LOG_CRIT, "POLL_ERR (%u) on listening "
+ "socket", pfds[i].out_flags);
+ } else {
+ qnetd_log(LOG_DEBUG, "Listening socket is closed");
+ }
+
+ return (-1);
+ break;
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_CLIENT:
+ qnetd_log(LOG_DEBUG, "POLL_ERR (%u) on client socket. "
+ "Disconnecting.", pfds[i].out_flags);
+
+ client_disconnect = 1;
+ break;
+ case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT:
+ qnetd_log(LOG_DEBUG, "POLL_ERR (%u) on ipc client socket."
+ " Disconnecting.", pfds[i].out_flags);
+
+ client_disconnect = 1;
+ break;
+ }
+ }
+
+ /*
+ * If client is scheduled for disconnect, disconnect it
+ */
+ if (user_data->type == QNETD_POLL_ARRAY_USER_DATA_TYPE_CLIENT &&
+ client_disconnect) {
+ qnetd_instance_client_disconnect(instance, client, 0);
+ } else if (user_data->type == QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT &&
+ (client_disconnect || ipc_client->schedule_disconnect)) {
+ qnetd_ipc_client_disconnect(instance, ipc_client);
+ }
+ }
+ }
+
+
+ return (0);
+}
+
+static void
+signal_int_handler(int sig)
+{
+
+ qnetd_log(LOG_DEBUG, "SIGINT received - closing server IPC socket");
+
+ qnetd_ipc_close(global_instance);
+}
+
+static void
+signal_term_handler(int sig)
+{
+
+ qnetd_log(LOG_DEBUG, "SIGTERM received - closing server IPC socket");
+
+ qnetd_ipc_close(global_instance);
+}
+
+static void
+signal_handlers_register(void)
+{
+ struct sigaction act;
+
+ act.sa_handler = signal_int_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+
+ sigaction(SIGINT, &act, NULL);
+
+ act.sa_handler = signal_term_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+
+ sigaction(SIGTERM, &act, NULL);
+}
+
+static void
+usage(void)
+{
+
+ printf("usage: %s [-46dfhv] [-l listen_addr] [-p listen_port] [-s tls]\n", QNETD_PROGRAM_NAME);
+ printf("%14s[-c client_cert_required] [-m max_clients] [-S option=value[,option2=value2,...]]\n", "");
+}
+
+static void
+display_version(void)
+{
+ enum msg_type *supported_messages;
+ size_t no_supported_messages;
+ size_t zi;
+
+ msg_get_supported_messages(&supported_messages, &no_supported_messages);
+ printf("Corosync Qdevice Network Daemon, version '%s'\n\n", VERSION);
+ printf("Supported algorithms: ");
+ for (zi = 0; zi < QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE; zi++) {
+ if (zi != 0) {
+ printf(", ");
+ }
+ printf("%s (%u)",
+ tlv_decision_algorithm_type_to_str(qnetd_static_supported_decision_algorithms[zi]),
+ qnetd_static_supported_decision_algorithms[zi]);
+ }
+ printf("\n");
+ printf("Supported message types: ");
+ for (zi = 0; zi < no_supported_messages; zi++) {
+ if (zi != 0) {
+ printf(", ");
+ }
+ printf("%s (%u)", msg_type_to_str(supported_messages[zi]), supported_messages[zi]);
+ }
+ printf("\n");
+}
+
+static void
+cli_parse_long_opt(struct qnetd_advanced_settings *advanced_settings, const char *long_opt)
+{
+ struct dynar_getopt_lex lex;
+ struct dynar dynar_long_opt;
+ const char *opt;
+ const char *val;
+ int res;
+
+ dynar_init(&dynar_long_opt, strlen(long_opt) + 1);
+ if (dynar_str_cpy(&dynar_long_opt, long_opt) != 0) {
+ errx(1, "Can't alloc memory for long option");
+ }
+
+ dynar_getopt_lex_init(&lex, &dynar_long_opt);
+
+ while (dynar_getopt_lex_token_next(&lex) == 0 && strcmp(dynar_data(&lex.option), "") != 0) {
+ opt = dynar_data(&lex.option);
+ val = dynar_data(&lex.value);
+
+ res = qnetd_advanced_settings_set(advanced_settings, opt, val);
+ switch (res) {
+ case -1:
+ errx(1, "Unknown option '%s'", opt);
+ break;
+ case -2:
+ errx(1, "Invalid value '%s' for option '%s'", val, opt);
+ break;
+ }
+ }
+
+ dynar_getopt_lex_destroy(&lex);
+ dynar_destroy(&dynar_long_opt);
+}
+
+static void
+cli_parse(int argc, char * const argv[], char **host_addr, uint16_t *host_port, int *foreground,
+ int *debug_log, int *bump_log_priority, enum tlv_tls_supported *tls_supported,
+ int *client_cert_required, size_t *max_clients, PRIntn *address_family,
+ struct qnetd_advanced_settings *advanced_settings)
+{
+ int ch;
+ char *ep;
+ long long int tmpll;
+
+ *host_addr = NULL;
+ *host_port = QNETD_DEFAULT_HOST_PORT;
+ *foreground = 0;
+ *debug_log = 0;
+ *bump_log_priority = 0;
+ *tls_supported = QNETD_DEFAULT_TLS_SUPPORTED;
+ *client_cert_required = QNETD_DEFAULT_TLS_CLIENT_CERT_REQUIRED;
+ *max_clients = QNETD_DEFAULT_MAX_CLIENTS;
+ *address_family = PR_AF_UNSPEC;
+
+ while ((ch = getopt(argc, argv, "46dfhvc:l:m:p:S:s:")) != -1) {
+ switch (ch) {
+ case '4':
+ *address_family = PR_AF_INET;
+ break;
+ case '6':
+ *address_family = PR_AF_INET6;
+ break;
+ case 'f':
+ *foreground = 1;
+ break;
+ case 'd':
+ if (*debug_log) {
+ *bump_log_priority = 1;
+ }
+ *debug_log = 1;
+ break;
+ case 'c':
+ if ((*client_cert_required = utils_parse_bool_str(optarg)) == -1) {
+ errx(1, "client_cert_required should be on/yes/1, off/no/0");
+ }
+ break;
+ case 'l':
+ free(*host_addr);
+ *host_addr = strdup(optarg);
+ if (*host_addr == NULL) {
+ errx(1, "Can't alloc memory for host addr string");
+ }
+ break;
+ case 'm':
+ errno = 0;
+
+ tmpll = strtoll(optarg, &ep, 10);
+ if (tmpll < 0 || errno != 0 || *ep != '\0') {
+ errx(1, "max clients value %s is invalid", optarg);
+ }
+ *max_clients = (size_t)tmpll;
+ break;
+ case 'p':
+ *host_port = strtol(optarg, &ep, 10);
+ if (*host_port <= 0 || *host_port > ((uint16_t)~0) || *ep != '\0') {
+ errx(1, "host port must be in range 0-65535");
+ }
+ break;
+ case 'S':
+ cli_parse_long_opt(advanced_settings, optarg);
+ break;
+ case 's':
+ if (strcasecmp(optarg, "on") == 0) {
+ *tls_supported = QNETD_DEFAULT_TLS_SUPPORTED;
+ } else if (strcasecmp(optarg, "off") == 0) {
+ *tls_supported = TLV_TLS_UNSUPPORTED;
+ } else if (strcasecmp(optarg, "req") == 0) {
+ *tls_supported = TLV_TLS_REQUIRED;
+ } else {
+ errx(1, "tls must be one of on, off, req");
+ }
+ break;
+ case 'v':
+ display_version();
+ exit(1);
+ break;
+ case 'h':
+ case '?':
+ usage();
+ exit(1);
+ break;
+ }
+ }
+}
+
+int
+main(int argc, char * const argv[])
+{
+ struct qnetd_instance instance;
+ struct qnetd_advanced_settings advanced_settings;
+ char *host_addr;
+ uint16_t host_port;
+ int foreground;
+ int debug_log;
+ int bump_log_priority;
+ enum tlv_tls_supported tls_supported;
+ int client_cert_required;
+ size_t max_clients;
+ PRIntn address_family;
+ int lock_file;
+ int another_instance_running;
+
+ if (qnetd_advanced_settings_init(&advanced_settings) != 0) {
+ errx(1, "Can't alloc memory for advanced settings");
+ }
+
+ cli_parse(argc, argv, &host_addr, &host_port, &foreground, &debug_log, &bump_log_priority,
+ &tls_supported, &client_cert_required, &max_clients, &address_family, &advanced_settings);
+
+ if (foreground) {
+ qnetd_log_init(QNETD_LOG_TARGET_STDERR);
+ } else {
+ qnetd_log_init(QNETD_LOG_TARGET_SYSLOG);
+ }
+
+ qnetd_log_set_debug(debug_log);
+ qnetd_log_set_priority_bump(bump_log_priority);
+
+ /*
+ * Daemonize
+ */
+ if (!foreground) {
+ utils_tty_detach();
+ }
+
+ if ((lock_file = utils_flock(advanced_settings.lock_file, getpid(),
+ &another_instance_running)) == -1) {
+ if (another_instance_running) {
+ qnetd_log(LOG_ERR, "Another instance is running");
+ } else {
+ qnetd_log_err(LOG_ERR, "Can't acquire lock");
+ }
+
+ exit(1);
+ }
+
+ qnetd_log(LOG_DEBUG, "Initializing nss");
+ if (nss_sock_init_nss((tls_supported != TLV_TLS_UNSUPPORTED ?
+ advanced_settings.nss_db_dir : NULL)) != 0) {
+ qnetd_err_nss();
+ }
+
+ if (SSL_ConfigServerSessionIDCache(0, 0, 0, NULL) != SECSuccess) {
+ qnetd_err_nss();
+ }
+
+ if (qnetd_instance_init(&instance, tls_supported, client_cert_required,
+ max_clients, &advanced_settings) == -1) {
+ qnetd_log(LOG_ERR, "Can't initialize qnetd");
+ exit(1);
+ }
+ instance.host_addr = host_addr;
+ instance.host_port = host_port;
+
+ if (tls_supported != TLV_TLS_UNSUPPORTED && qnetd_instance_init_certs(&instance) == -1) {
+ qnetd_err_nss();
+ }
+
+ qnetd_log(LOG_DEBUG, "Initializing local socket");
+ if (qnetd_ipc_init(&instance) != 0) {
+ return (1);
+ }
+
+ qnetd_log(LOG_DEBUG, "Creating listening socket");
+ instance.server.socket = nss_sock_create_listen_socket(instance.host_addr,
+ instance.host_port, address_family);
+ if (instance.server.socket == NULL) {
+ qnetd_err_nss();
+ }
+
+ if (nss_sock_set_non_blocking(instance.server.socket) != 0) {
+ qnetd_err_nss();
+ }
+
+ if (PR_Listen(instance.server.socket, instance.advanced_settings->listen_backlog) !=
+ PR_SUCCESS) {
+ qnetd_err_nss();
+ }
+
+ global_instance = &instance;
+ signal_handlers_register();
+
+ qnetd_log(LOG_DEBUG, "Registering algorithms");
+ if (qnetd_algorithm_register_all() != 0) {
+ exit(1);
+ }
+
+ qnetd_log(LOG_DEBUG, "QNetd ready to provide service");
+
+#ifdef HAVE_LIBSYSTEMD
+ sd_notify(0, "READY=1");
+#endif
+
+ /*
+ * MAIN LOOP
+ */
+ while (qnetd_poll(&instance) == 0) {
+ }
+
+ /*
+ * Cleanup
+ */
+ qnetd_ipc_destroy(&instance);
+
+ if (PR_Close(instance.server.socket) != PR_SUCCESS) {
+ qnetd_warn_nss();
+ }
+
+ CERT_DestroyCertificate(instance.server.cert);
+ SECKEY_DestroyPrivateKey(instance.server.private_key);
+
+ SSL_ClearSessionCache();
+
+ SSL_ShutdownServerSessionIDCache();
+
+ qnetd_instance_destroy(&instance);
+
+ qnetd_advanced_settings_destroy(&advanced_settings);
+
+ if (NSS_Shutdown() != SECSuccess) {
+ qnetd_warn_nss();
+ }
+
+ if (PR_Cleanup() != PR_SUCCESS) {
+ qnetd_warn_nss();
+ }
+
+ qnetd_log_close();
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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 <string.h>
+
+#include "dynar-getopt-lex.h"
+
+void
+dynar_getopt_lex_init(struct dynar_getopt_lex *lex, struct dynar *input)
+{
+
+ memset(lex, 0, sizeof(*lex));
+ lex->input = input;
+ dynar_init(&lex->option, dynar_max_size(input));
+ dynar_init(&lex->value, dynar_max_size(input));
+}
+
+void
+dynar_getopt_lex_destroy(struct dynar_getopt_lex *lex)
+{
+
+ dynar_destroy(&lex->option);
+ dynar_destroy(&lex->value);
+ memset(lex, 0, sizeof(*lex));
+}
+
+/*
+ * 0 - no error
+ * -1 - Can't add character
+ */
+int
+dynar_getopt_lex_token_next(struct dynar_getopt_lex *lex)
+{
+ size_t pos;
+ size_t size;
+ char *str;
+ char ch;
+ int state;
+
+ dynar_clean(&lex->option);
+ dynar_clean(&lex->value);
+
+ size = dynar_size(lex->input);
+ str = dynar_data(lex->input);
+
+ state = 1;
+ pos = lex->pos;
+
+ while (state != 0 && pos < size) {
+ ch = str[pos];
+
+ switch (state) {
+ case 1:
+ /*
+ * Read option name, wait for = or ,
+ */
+ if (ch == '=') {
+ pos++;
+ state = 2;
+ } else if (ch == ',') {
+ pos++;
+ state = 0;
+ } else {
+ pos++;
+ if (dynar_cat(&lex->option, &ch, sizeof(ch)) != 0) {
+ return (-1);
+ }
+ }
+ break;
+ case 2:
+ /*
+ * Wait for end of str or ,
+ */
+ if (ch == ',') {
+ pos++;
+ state = 0;
+ } else {
+ pos++;
+ if (dynar_cat(&lex->value, &ch, sizeof(ch)) != 0) {
+ return (-1);
+ }
+ }
+ break;
+ }
+ }
+
+ ch = '\0';
+ if (dynar_cat(&lex->option, &ch, sizeof(ch)) != 0) {
+ return (-1);
+ }
+ if (dynar_cat(&lex->value, &ch, sizeof(ch)) != 0) {
+ return (-1);
+ }
+
+ lex->pos = pos;
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _DYNAR_GETOPT_LEX_H_
+#define _DYNAR_GETOPT_LEX_H_
+
+#include "dynar.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct dynar_getopt_lex {
+ struct dynar option;
+ struct dynar value;
+ struct dynar *input;
+ size_t pos;
+};
+
+extern void dynar_getopt_lex_init(struct dynar_getopt_lex *lex, struct dynar *input);
+
+extern void dynar_getopt_lex_destroy(struct dynar_getopt_lex *lex);
+
+extern int dynar_getopt_lex_token_next(struct dynar_getopt_lex *lex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DYNAR_GETOPT_LEX_H_ */
--- /dev/null
+/*
+ * 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 <string.h>
+
+#include "dynar-simple-lex.h"
+
+/*
+ * Simple_lex is going to be used in protocol and it's not good idea to depend on locale
+ */
+static int
+dynar_simple_lex_is_space(char ch)
+{
+ return (ch == ' ' || ch == '\f' || ch == '\n' || ch == '\r' || ch == '\t' || ch == '\v');
+}
+
+void
+dynar_simple_lex_init(struct dynar_simple_lex *lex, struct dynar *input,
+ enum dynar_simple_lex_type lex_type)
+{
+
+ memset(lex, 0, sizeof(*lex));
+ lex->input = input;
+ lex->lex_type = lex_type;
+ dynar_init(&lex->token, dynar_max_size(input));
+}
+
+void
+dynar_simple_lex_destroy(struct dynar_simple_lex *lex)
+{
+
+ dynar_destroy(&lex->token);
+ memset(lex, 0, sizeof(*lex));
+}
+
+struct dynar *
+dynar_simple_lex_token_next(struct dynar_simple_lex *lex)
+{
+ size_t pos;
+ size_t size;
+ char *str;
+ char ch, ch2;
+ int add_char;
+ int state;
+
+ dynar_clean(&lex->token);
+
+ size = dynar_size(lex->input);
+ str = dynar_data(lex->input);
+
+ state = 1;
+ pos = lex->pos;
+
+ while (state != 0) {
+ if (pos < size) {
+ ch = str[pos];
+ } else {
+ ch = '\0';
+ }
+
+ add_char = 0;
+
+ switch (state) {
+ case 1:
+ /*
+ * Skip spaces. Newline is special and means end of processing
+ */
+ if (pos >= size || ch == '\n' || ch == '\r') {
+ state = 0;
+ } else if (dynar_simple_lex_is_space(ch)) {
+ pos++;
+ } else {
+ state = 2;
+ }
+ break;
+ case 2:
+ /*
+ * Read word
+ */
+ if (pos >= size) {
+ state = 0;
+ } else if ((lex->lex_type == DYNAR_SIMPLE_LEX_TYPE_BACKSLASH ||
+ lex->lex_type == DYNAR_SIMPLE_LEX_TYPE_QUOTE) && ch == '\\') {
+ pos++;
+ state = 3;
+ } else if (lex->lex_type == DYNAR_SIMPLE_LEX_TYPE_QUOTE &&
+ ch == '"') {
+ pos++;
+ state = 4;
+ } else if (dynar_simple_lex_is_space(ch)) {
+ state = 0;
+ } else {
+ pos++;
+ add_char = 1;
+ }
+ break;
+ case 3:
+ /*
+ * Process backslash
+ */
+ if (pos >= size || ch == '\n' || ch == '\r') {
+ /*
+ * End of string. Do not include backslash (it's just ignored)
+ */
+ state = 0;
+ } else {
+ add_char = 1;
+ state = 2;
+ pos++;
+ }
+ break;
+ case 4:
+ /*
+ * Quote word
+ */
+ if (pos >= size) {
+ state = 0;
+ } else if (ch == '\\') {
+ state = 5;
+ pos++;
+ } else if (ch == '"') {
+ state = 2;
+ pos++;
+ } else if (ch == '\n' || ch == '\r') {
+ state = 0;
+ } else {
+ pos++;
+ add_char = 1;
+ }
+ break;
+ case 5:
+ /*
+ * Quote word backslash
+ */
+ if (pos >= size || ch == '\n' || ch == '\r') {
+ /*
+ * End of string. Do not include backslash (it's just ignored)
+ */
+ state = 0;
+ } else if (ch == '\\' || ch == '"') {
+ add_char = 1;
+ state = 4;
+ pos++;
+ } else {
+ ch2 = '\\';
+ if (dynar_cat(&lex->token, &ch2, sizeof(ch2)) != 0) {
+ return (NULL);
+ }
+
+ add_char = 1;
+ state = 4;
+ pos++;
+ }
+ break;
+ }
+
+ if (add_char) {
+ if (dynar_cat(&lex->token, &ch, sizeof(ch)) != 0) {
+ return (NULL);
+ }
+ }
+ }
+
+ ch = '\0';
+ if (dynar_cat(&lex->token, &ch, sizeof(ch)) != 0) {
+ return (NULL);
+ }
+
+ lex->pos = pos;
+
+ return (&lex->token);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _DYNAR_SIMPLE_LEX_H_
+#define _DYNAR_SIMPLE_LEX_H_
+
+#include "dynar.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum dynar_simple_lex_type {
+ DYNAR_SIMPLE_LEX_TYPE_PLAIN,
+ DYNAR_SIMPLE_LEX_TYPE_BACKSLASH,
+ DYNAR_SIMPLE_LEX_TYPE_QUOTE,
+};
+
+struct dynar_simple_lex {
+ struct dynar token;
+ struct dynar *input;
+ enum dynar_simple_lex_type lex_type;
+ size_t pos;
+};
+
+extern void dynar_simple_lex_init(struct dynar_simple_lex *lex, struct dynar *input,
+ enum dynar_simple_lex_type lex_type);
+
+extern void dynar_simple_lex_destroy(struct dynar_simple_lex *lex);
+
+extern struct dynar *dynar_simple_lex_token_next(struct dynar_simple_lex *lex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DYNAR_SIMPLE_LEX_H_ */
--- /dev/null
+/*
+ * 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 <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "dynar-str.h"
+
+int
+dynar_str_cpy(struct dynar *dest, const char *str)
+{
+
+ if (strlen(str) > dynar_max_size(dest)) {
+ return (-1);
+ }
+
+ dynar_clean(dest);
+
+ return (dynar_str_cat(dest, str));
+}
+
+int
+dynar_str_cat(struct dynar *dest, const char *str)
+{
+
+ return (dynar_cat(dest, str, strlen(str)));
+}
+
+int
+dynar_str_prepend(struct dynar *dest, const char *str)
+{
+
+ return (dynar_prepend(dest, str, strlen(str)));
+}
+
+int
+dynar_str_vcatf(struct dynar *dest, const char *format, va_list ap)
+{
+ int to_write;
+ int written;
+ va_list ap_copy;
+ size_t allocated;
+ char buf;
+ char *p;
+
+ /*
+ * Find out how much bytes is needed
+ */
+ va_copy(ap_copy, ap);
+ to_write = vsnprintf(&buf, sizeof(buf), format, ap_copy);
+ va_end(ap_copy);
+
+ if (to_write < 0) {
+ return (-1);
+ }
+
+ if ((size_t)to_write < sizeof(buf)) {
+ /*
+ * Writing 1 byte string (snprintf writes also '\0') means string is empty
+ */
+
+ return (0);
+ }
+
+ allocated = to_write + 1;
+ if (dynar_prealloc(dest, allocated) != 0) {
+ return (-1);
+ }
+
+ p = dynar_data(dest) + dynar_size(dest);
+
+ va_copy(ap_copy, ap);
+ written = vsnprintf(p, allocated, format, ap_copy);
+ va_end(ap_copy);
+
+ if (written < 0) {
+ return (-1);
+ }
+
+ if ((size_t)written >= allocated) {
+ return (-1);
+ }
+
+ dest->size += written;
+
+ return (written);
+}
+
+int
+dynar_str_catf(struct dynar *dest, const char *format, ...)
+{
+ va_list ap;
+ int res;
+
+ va_start(ap, format);
+ res = dynar_str_vcatf(dest, format, ap);
+ va_end(ap);
+
+ return (res);
+}
+
+int
+dynar_str_quote_cat(struct dynar *dest, const char *str)
+{
+ size_t zi;
+
+ if (dynar_str_cat(dest, "\"") != 0) {
+ return (-1);
+ }
+
+ for (zi = 0; zi < strlen(str); zi++) {
+ if (str[zi] == '"' || str[zi] == '\\') {
+ if (dynar_str_cat(dest, "\\") != 0) {
+ return (-1);
+ }
+ }
+
+ if (dynar_cat(dest, &str[zi], sizeof(str[zi])) != 0) {
+ return (-1);
+ }
+ }
+
+ if (dynar_str_cat(dest, "\"") != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+dynar_str_quote_cpy(struct dynar *dest, const char *str)
+{
+
+ dynar_clean(dest);
+
+ return (dynar_str_quote_cat(dest, str));
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _DYNAR_STR_H_
+#define _DYNAR_STR_H_
+
+#include <stdarg.h>
+
+#include "dynar.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int dynar_str_cpy(struct dynar *dest, const char *str);
+
+extern int dynar_str_cat(struct dynar *dest, const char *str);
+
+extern int dynar_str_catf(struct dynar *dest, const char *format, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+
+extern int dynar_str_vcatf(struct dynar *dest, const char *format, va_list ap)
+ __attribute__((__format__(__printf__, 2, 0)));
+
+extern int dynar_str_prepend(struct dynar *dest, const char *str);
+
+extern int dynar_str_quote_cat(struct dynar *dest, const char *str);
+
+extern int dynar_str_quote_cpy(struct dynar *dest, const char *str);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DYNAR_STR_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dynar.h"
+
+void
+dynar_init(struct dynar *array, size_t maximum_size)
+{
+
+ memset(array, 0, sizeof(*array));
+ array->maximum_size = maximum_size;
+}
+
+void
+dynar_set_max_size(struct dynar *array, size_t maximum_size)
+{
+
+ array->maximum_size = maximum_size;
+}
+
+int
+dynar_set_size(struct dynar *array, size_t size)
+{
+
+ if (size > dynar_max_size(array)) {
+ dynar_set_max_size(array, size);
+ }
+
+ if (size > dynar_size(array)) {
+ if (dynar_prealloc(array, size - dynar_size(array)) == -1) {
+ return (-1);
+ }
+ }
+
+ array->size = size;
+
+ return (0);
+}
+
+void
+dynar_destroy(struct dynar *array)
+{
+
+ free(array->data);
+ dynar_init(array, array->maximum_size);
+}
+
+void
+dynar_clean(struct dynar *array)
+{
+
+ array->size = 0;
+}
+
+size_t
+dynar_size(const struct dynar *array)
+{
+
+ return (array->size);
+}
+
+size_t
+dynar_max_size(const struct dynar *array)
+{
+
+ return (array->maximum_size);
+}
+
+char *
+dynar_data(const struct dynar *array)
+{
+
+ return (array->data);
+}
+
+static int
+dynar_realloc(struct dynar *array, size_t new_array_size)
+{
+ char *new_data;
+
+ if (new_array_size > array->maximum_size) {
+ return (-1);
+ }
+
+ new_data = realloc(array->data, new_array_size);
+
+ if (new_data == NULL) {
+ return (-1);
+ }
+
+ array->allocated = new_array_size;
+ array->data = new_data;
+
+ return (0);
+}
+
+int
+dynar_prealloc(struct dynar *array, size_t size)
+{
+ size_t new_size;
+
+ if (array->size + size > array->maximum_size) {
+ return (-1);
+ }
+
+ if (array->size + size > array->allocated) {
+ new_size = (array->allocated + size) * 2;
+ if (new_size > array->maximum_size) {
+ new_size = array->maximum_size;
+ }
+
+ if (dynar_realloc(array, new_size) == -1) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+int
+dynar_cat(struct dynar *array, const void *src, size_t size)
+{
+
+ if (dynar_prealloc(array, size) != 0) {
+ return (-1);
+ }
+
+ memmove(array->data + array->size, src, size);
+ array->size += size;
+
+ return (0);
+}
+
+int
+dynar_prepend(struct dynar *array, const void *src, size_t size)
+{
+
+ if (dynar_prealloc(array, size) != 0) {
+ return (-1);
+ }
+
+ memmove(array->data + size, array->data, array->size);
+ memmove(array->data, src, size);
+ array->size += size;
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _DYNAR_H_
+#define _DYNAR_H_
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Dynamic array structure
+ */
+struct dynar {
+ char *data;
+ size_t size;
+ size_t allocated;
+ size_t maximum_size;
+};
+
+extern void dynar_init(struct dynar *array, size_t maximum_size);
+
+extern void dynar_destroy(struct dynar *array);
+
+extern void dynar_clean(struct dynar *array);
+
+extern size_t dynar_size(const struct dynar *array);
+
+extern size_t dynar_max_size(const struct dynar *array);
+
+extern void dynar_set_max_size(struct dynar *array, size_t maximum_size);
+
+extern char *dynar_data(const struct dynar *array);
+
+extern int dynar_cat(struct dynar *array, const void *src, size_t size);
+
+extern int dynar_prealloc(struct dynar *array, size_t size);
+
+extern int dynar_prepend(struct dynar *array, const void *src, size_t size);
+
+extern int dynar_set_size(struct dynar *array, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DYNAR_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <sys/types.h>
+#include <arpa/inet.h>
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "msg.h"
+
+#define MSG_TYPE_LENGTH 2
+#define MSG_LENGTH_LENGTH 4
+
+#define MSG_STATIC_SUPPORTED_MESSAGES_SIZE 18
+
+enum msg_type msg_static_supported_messages[MSG_STATIC_SUPPORTED_MESSAGES_SIZE] = {
+ MSG_TYPE_PREINIT,
+ MSG_TYPE_PREINIT_REPLY,
+ MSG_TYPE_STARTTLS,
+ MSG_TYPE_INIT,
+ MSG_TYPE_INIT_REPLY,
+ MSG_TYPE_SERVER_ERROR,
+ MSG_TYPE_SET_OPTION,
+ MSG_TYPE_SET_OPTION_REPLY,
+ MSG_TYPE_ECHO_REQUEST,
+ MSG_TYPE_ECHO_REPLY,
+ MSG_TYPE_NODE_LIST,
+ MSG_TYPE_NODE_LIST_REPLY,
+ MSG_TYPE_ASK_FOR_VOTE,
+ MSG_TYPE_ASK_FOR_VOTE_REPLY,
+ MSG_TYPE_VOTE_INFO,
+ MSG_TYPE_VOTE_INFO_REPLY,
+ MSG_TYPE_HEURISTICS_CHANGE,
+ MSG_TYPE_HEURISTICS_CHANGE_REPLY,
+};
+
+size_t
+msg_get_header_length(void)
+{
+ return (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH);
+}
+
+static void
+msg_add_type(struct dynar *msg, enum msg_type type)
+{
+ uint16_t ntype;
+
+ ntype = htons((uint16_t)type);
+ dynar_cat(msg, &ntype, sizeof(ntype));
+}
+
+enum msg_type
+msg_get_type(const struct dynar *msg)
+{
+ uint16_t ntype;
+ uint16_t type;
+
+ memcpy(&ntype, dynar_data(msg), sizeof(ntype));
+ type = ntohs(ntype);
+
+ return (type);
+}
+
+/*
+ * We don't know size of message before call of this function, so zero is
+ * added. Real value is set afterwards by msg_set_len.
+ */
+static void
+msg_add_len(struct dynar *msg)
+{
+ uint32_t len;
+
+ len = 0;
+ dynar_cat(msg, &len, sizeof(len));
+}
+
+static void
+msg_set_len(struct dynar *msg, uint32_t len)
+{
+ uint32_t nlen;
+
+ nlen = htonl(len);
+ memcpy(dynar_data(msg) + MSG_TYPE_LENGTH, &nlen, sizeof(nlen));
+}
+
+/*
+ * Used only for echo reply msg. All other messages should use msg_add_type.
+ */
+static void
+msg_set_type(struct dynar *msg, enum msg_type type)
+{
+ uint16_t ntype;
+
+ ntype = htons((uint16_t)type);
+ memcpy(dynar_data(msg), &ntype, sizeof(ntype));
+}
+
+uint32_t
+msg_get_len(const struct dynar *msg)
+{
+ uint32_t nlen;
+ uint32_t len;
+
+ memcpy(&nlen, dynar_data(msg) + MSG_TYPE_LENGTH, sizeof(nlen));
+ len = ntohl(nlen);
+
+ return (len);
+}
+
+
+size_t
+msg_create_preinit(struct dynar *msg, const char *cluster_name, int add_msg_seq_number,
+ uint32_t msg_seq_number)
+{
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_PREINIT);
+ msg_add_len(msg);
+
+ if (add_msg_seq_number) {
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ if (tlv_add_cluster_name(msg, cluster_name) == -1) {
+ goto small_buf_err;
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_preinit_reply(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number,
+ enum tlv_tls_supported tls_supported, int tls_client_cert_required)
+{
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_PREINIT_REPLY);
+ msg_add_len(msg);
+
+ if (add_msg_seq_number) {
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ if (tlv_add_tls_supported(msg, tls_supported) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_tls_client_cert_required(msg, tls_client_cert_required) == -1) {
+ goto small_buf_err;
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_starttls(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number)
+{
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_STARTTLS);
+ msg_add_len(msg);
+
+ if (add_msg_seq_number) {
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_server_error(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number,
+ enum tlv_reply_error_code reply_error_code)
+{
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_SERVER_ERROR);
+ msg_add_len(msg);
+
+ if (add_msg_seq_number) {
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ if (tlv_add_reply_error_code(msg, reply_error_code) == -1) {
+ goto small_buf_err;
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+static uint16_t *
+msg_convert_msg_type_array_to_u16_array(const enum msg_type *msg_type_array, size_t array_size)
+{
+ uint16_t *u16a;
+ size_t i;
+
+ u16a = malloc(sizeof(*u16a) * array_size);
+ if (u16a == NULL) {
+ return (NULL);
+ }
+
+ for (i = 0; i < array_size; i++) {
+ u16a[i] = (uint16_t)msg_type_array[i];
+ }
+
+ return (u16a);
+}
+
+size_t
+msg_create_init(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number,
+ enum tlv_decision_algorithm_type decision_algorithm,
+ const enum msg_type *supported_msgs, size_t no_supported_msgs,
+ const enum tlv_opt_type *supported_opts, size_t no_supported_opts, uint32_t node_id,
+ uint32_t heartbeat_interval, const struct tlv_tie_breaker *tie_breaker,
+ const struct tlv_ring_id *ring_id)
+{
+ uint16_t *u16a;
+ int res;
+
+ u16a = NULL;
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_INIT);
+ msg_add_len(msg);
+
+ if (add_msg_seq_number) {
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ if (supported_msgs != NULL && no_supported_msgs > 0) {
+ u16a = msg_convert_msg_type_array_to_u16_array(supported_msgs, no_supported_msgs);
+
+ if (u16a == NULL) {
+ goto small_buf_err;
+ }
+
+ res = tlv_add_u16_array(msg, TLV_OPT_SUPPORTED_MESSAGES, u16a, no_supported_msgs);
+
+ free(u16a);
+
+ if (res == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ if (supported_opts != NULL && no_supported_opts > 0) {
+ if (tlv_add_supported_options(msg, supported_opts, no_supported_opts) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ if (tlv_add_node_id(msg, node_id) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_decision_algorithm(msg, decision_algorithm) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_heartbeat_interval(msg, heartbeat_interval) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_tie_breaker(msg, tie_breaker) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_ring_id(msg, ring_id) == -1) {
+ goto small_buf_err;
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_init_reply(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number,
+ enum tlv_reply_error_code reply_error_code,
+ const enum msg_type *supported_msgs, size_t no_supported_msgs,
+ const enum tlv_opt_type *supported_opts, size_t no_supported_opts,
+ size_t server_maximum_request_size, size_t server_maximum_reply_size,
+ const enum tlv_decision_algorithm_type *supported_decision_algorithms,
+ size_t no_supported_decision_algorithms)
+{
+ uint16_t *u16a;
+ int res;
+
+ u16a = NULL;
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_INIT_REPLY);
+ msg_add_len(msg);
+
+ if (tlv_add_reply_error_code(msg, reply_error_code) == -1) {
+ goto small_buf_err;
+ }
+
+ if (supported_msgs != NULL && no_supported_msgs > 0) {
+ u16a = msg_convert_msg_type_array_to_u16_array(supported_msgs, no_supported_msgs);
+
+ if (u16a == NULL) {
+ goto small_buf_err;
+ }
+
+ res = tlv_add_u16_array(msg, TLV_OPT_SUPPORTED_MESSAGES, u16a, no_supported_msgs);
+
+ free(u16a);
+
+ if (res == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ if (supported_opts != NULL && no_supported_opts > 0) {
+ if (tlv_add_supported_options(msg, supported_opts, no_supported_opts) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ if (add_msg_seq_number) {
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ if (tlv_add_server_maximum_request_size(msg, server_maximum_request_size) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_server_maximum_reply_size(msg, server_maximum_reply_size) == -1) {
+ goto small_buf_err;
+ }
+
+ if (supported_decision_algorithms != NULL && no_supported_decision_algorithms > 0) {
+ if (tlv_add_supported_decision_algorithms(msg, supported_decision_algorithms,
+ no_supported_decision_algorithms) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_set_option(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number,
+ int add_heartbeat_interval, uint32_t heartbeat_interval)
+{
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_SET_OPTION);
+ msg_add_len(msg);
+
+ if (add_msg_seq_number) {
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ if (add_heartbeat_interval) {
+ if (tlv_add_heartbeat_interval(msg, heartbeat_interval) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_set_option_reply(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number,
+ uint32_t heartbeat_interval)
+{
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_SET_OPTION_REPLY);
+ msg_add_len(msg);
+
+ if (add_msg_seq_number) {
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ if (tlv_add_heartbeat_interval(msg, heartbeat_interval) == -1) {
+ goto small_buf_err;
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_echo_request(struct dynar *msg, int add_msg_seq_number, uint32_t msg_seq_number)
+{
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_ECHO_REQUEST);
+ msg_add_len(msg);
+
+ if (add_msg_seq_number) {
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_echo_reply(struct dynar *msg, const struct dynar *echo_request_msg)
+{
+
+ dynar_clean(msg);
+
+ if (dynar_cat(msg, dynar_data(echo_request_msg), dynar_size(echo_request_msg)) == -1) {
+ goto small_buf_err;
+ }
+
+ msg_set_type(msg, MSG_TYPE_ECHO_REPLY);
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_node_list(struct dynar *msg,
+ uint32_t msg_seq_number, enum tlv_node_list_type node_list_type,
+ int add_ring_id, const struct tlv_ring_id *ring_id,
+ int add_config_version, uint64_t config_version,
+ int add_quorate, enum tlv_quorate quorate,
+ int add_heuristics, enum tlv_heuristics heuristics,
+ const struct node_list *nodes)
+{
+ struct node_list_entry *node_info;
+ struct tlv_node_info tlv_ni;
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_NODE_LIST);
+ msg_add_len(msg);
+
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_node_list_type(msg, node_list_type) == -1) {
+ goto small_buf_err;
+ }
+
+ if (add_ring_id) {
+ if (tlv_add_ring_id(msg, ring_id) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ if (add_config_version) {
+ if (tlv_add_config_version(msg, config_version) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ if (add_quorate) {
+ if (tlv_add_quorate(msg, quorate) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ TAILQ_FOREACH(node_info, nodes, entries) {
+ node_list_entry_to_tlv_node_info(node_info, &tlv_ni);
+
+ if (tlv_add_node_info(msg, &tlv_ni) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ if (add_heuristics && heuristics != TLV_HEURISTICS_UNDEFINED) {
+ if (tlv_add_heuristics(msg, heuristics) == -1) {
+ goto small_buf_err;
+ }
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_node_list_reply(struct dynar *msg, uint32_t msg_seq_number,
+ enum tlv_node_list_type node_list_type, const struct tlv_ring_id *ring_id,
+ enum tlv_vote vote)
+{
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_NODE_LIST_REPLY);
+ msg_add_len(msg);
+
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_node_list_type(msg, node_list_type) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_ring_id(msg, ring_id) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_vote(msg, vote) == -1) {
+ goto small_buf_err;
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_ask_for_vote(struct dynar *msg, uint32_t msg_seq_number)
+{
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_ASK_FOR_VOTE);
+ msg_add_len(msg);
+
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_ask_for_vote_reply(struct dynar *msg, uint32_t msg_seq_number,
+ const struct tlv_ring_id *ring_id, enum tlv_vote vote)
+{
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_ASK_FOR_VOTE_REPLY);
+ msg_add_len(msg);
+
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_vote(msg, vote) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_ring_id(msg, ring_id) == -1) {
+ goto small_buf_err;
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_vote_info(struct dynar *msg, uint32_t msg_seq_number, const struct tlv_ring_id *ring_id,
+ enum tlv_vote vote)
+{
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_VOTE_INFO);
+ msg_add_len(msg);
+
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_vote(msg, vote) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_ring_id(msg, ring_id) == -1) {
+ goto small_buf_err;
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_vote_info_reply(struct dynar *msg, uint32_t msg_seq_number)
+{
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_VOTE_INFO_REPLY);
+ msg_add_len(msg);
+
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_heuristics_change(struct dynar *msg, uint32_t msg_seq_number,
+ enum tlv_heuristics heuristics)
+{
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_HEURISTICS_CHANGE);
+ msg_add_len(msg);
+
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_heuristics(msg, heuristics) == -1) {
+ goto small_buf_err;
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+size_t
+msg_create_heuristics_change_reply(struct dynar *msg, uint32_t msg_seq_number,
+ const struct tlv_ring_id *ring_id, enum tlv_heuristics heuristics, enum tlv_vote vote)
+{
+
+ dynar_clean(msg);
+
+ msg_add_type(msg, MSG_TYPE_HEURISTICS_CHANGE_REPLY);
+ msg_add_len(msg);
+
+ if (tlv_add_msg_seq_number(msg, msg_seq_number) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_vote(msg, vote) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_ring_id(msg, ring_id) == -1) {
+ goto small_buf_err;
+ }
+
+ if (tlv_add_heuristics(msg, heuristics) == -1) {
+ goto small_buf_err;
+ }
+
+ msg_set_len(msg, dynar_size(msg) - (MSG_TYPE_LENGTH + MSG_LENGTH_LENGTH));
+
+ return (dynar_size(msg));
+
+small_buf_err:
+ return (0);
+}
+
+int
+msg_is_valid_msg_type(const struct dynar *msg)
+{
+ enum msg_type type;
+ size_t i;
+
+ type = msg_get_type(msg);
+
+ for (i = 0; i < MSG_STATIC_SUPPORTED_MESSAGES_SIZE; i++) {
+ if (msg_static_supported_messages[i] == type) {
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+void
+msg_decoded_init(struct msg_decoded *decoded_msg)
+{
+
+ memset(decoded_msg, 0, sizeof(*decoded_msg));
+
+ node_list_init(&decoded_msg->nodes);
+}
+
+void
+msg_decoded_destroy(struct msg_decoded *decoded_msg)
+{
+
+ free(decoded_msg->cluster_name);
+ free(decoded_msg->supported_messages);
+ free(decoded_msg->supported_options);
+ free(decoded_msg->supported_decision_algorithms);
+ node_list_free(&decoded_msg->nodes);
+
+ msg_decoded_init(decoded_msg);
+}
+
+/*
+ * 0 - No error
+ * -1 - option with invalid length
+ * -2 - Unable to allocate memory
+ * -3 - Inconsistent msg (tlv len > msg size)
+ * -4 - invalid option content
+ */
+int
+msg_decode(const struct dynar *msg, struct msg_decoded *decoded_msg)
+{
+ struct tlv_iterator tlv_iter;
+ uint16_t *u16a;
+ uint32_t u32;
+ uint64_t u64;
+ struct tlv_ring_id ring_id;
+ struct tlv_node_info node_info;
+ struct tlv_tie_breaker tie_breaker;
+ size_t zi;
+ enum tlv_opt_type opt_type;
+ int iter_res;
+ int res;
+
+ msg_decoded_destroy(decoded_msg);
+
+ decoded_msg->type = msg_get_type(msg);
+
+ tlv_iter_init(msg, msg_get_header_length(), &tlv_iter);
+
+ while ((iter_res = tlv_iter_next(&tlv_iter)) > 0) {
+ opt_type = tlv_iter_get_type(&tlv_iter);
+
+ switch (opt_type) {
+ case TLV_OPT_MSG_SEQ_NUMBER:
+ if ((res = tlv_iter_decode_u32(&tlv_iter, &u32)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->seq_number_set = 1;
+ decoded_msg->seq_number = u32;
+ break;
+ case TLV_OPT_CLUSTER_NAME:
+ if ((res = tlv_iter_decode_str(&tlv_iter, &decoded_msg->cluster_name,
+ &decoded_msg->cluster_name_len)) != 0) {
+ return (-2);
+ }
+ break;
+ case TLV_OPT_TLS_SUPPORTED:
+ if ((res = tlv_iter_decode_tls_supported(&tlv_iter,
+ &decoded_msg->tls_supported)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->tls_supported_set = 1;
+ break;
+ case TLV_OPT_TLS_CLIENT_CERT_REQUIRED:
+ if ((res = tlv_iter_decode_client_cert_required(&tlv_iter,
+ &decoded_msg->tls_client_cert_required)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->tls_client_cert_required_set = 1;
+ break;
+ case TLV_OPT_SUPPORTED_MESSAGES:
+ free(decoded_msg->supported_messages);
+
+ if ((res = tlv_iter_decode_u16_array(&tlv_iter, &u16a,
+ &decoded_msg->no_supported_messages)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->supported_messages =
+ malloc(sizeof(enum msg_type) * decoded_msg->no_supported_messages);
+
+ if (decoded_msg->supported_messages == NULL) {
+ free(u16a);
+ return (-2);
+ }
+
+ for (zi = 0; zi < decoded_msg->no_supported_messages; zi++) {
+ decoded_msg->supported_messages[zi] = (enum msg_type)u16a[zi];
+ }
+
+ free(u16a);
+ break;
+ case TLV_OPT_SUPPORTED_OPTIONS:
+ free(decoded_msg->supported_options);
+
+ if ((res = tlv_iter_decode_supported_options(&tlv_iter,
+ &decoded_msg->supported_options,
+ &decoded_msg->no_supported_options)) != 0) {
+ return (res);
+ }
+ break;
+ case TLV_OPT_REPLY_ERROR_CODE:
+ if ((res = tlv_iter_decode_reply_error_code(&tlv_iter,
+ &decoded_msg->reply_error_code)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->reply_error_code_set = 1;
+ break;
+ case TLV_OPT_SERVER_MAXIMUM_REQUEST_SIZE:
+ if ((res = tlv_iter_decode_u32(&tlv_iter, &u32)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->server_maximum_request_size_set = 1;
+ decoded_msg->server_maximum_request_size = u32;
+ break;
+ case TLV_OPT_SERVER_MAXIMUM_REPLY_SIZE:
+ if ((res = tlv_iter_decode_u32(&tlv_iter, &u32)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->server_maximum_reply_size_set = 1;
+ decoded_msg->server_maximum_reply_size = u32;
+ break;
+ case TLV_OPT_NODE_ID:
+ if ((res = tlv_iter_decode_u32(&tlv_iter, &u32)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->node_id_set = 1;
+ decoded_msg->node_id = u32;
+ break;
+ case TLV_OPT_SUPPORTED_DECISION_ALGORITHMS:
+ free(decoded_msg->supported_decision_algorithms);
+
+ if ((res = tlv_iter_decode_supported_decision_algorithms(&tlv_iter,
+ &decoded_msg->supported_decision_algorithms,
+ &decoded_msg->no_supported_decision_algorithms)) != 0) {
+ return (res);
+ }
+ break;
+ case TLV_OPT_DECISION_ALGORITHM:
+ if ((res = tlv_iter_decode_decision_algorithm(&tlv_iter,
+ &decoded_msg->decision_algorithm)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->decision_algorithm_set = 1;
+ break;
+ case TLV_OPT_HEARTBEAT_INTERVAL:
+ if ((res = tlv_iter_decode_u32(&tlv_iter, &u32)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->heartbeat_interval_set = 1;
+ decoded_msg->heartbeat_interval = u32;
+ break;
+ case TLV_OPT_RING_ID:
+ if ((res = tlv_iter_decode_ring_id(&tlv_iter, &ring_id)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->ring_id_set = 1;
+ memcpy(&decoded_msg->ring_id, &ring_id, sizeof(ring_id));
+ break;
+ case TLV_OPT_CONFIG_VERSION:
+ if ((res = tlv_iter_decode_u64(&tlv_iter, &u64)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->config_version_set = 1;
+ decoded_msg->config_version = u64;
+ break;
+ case TLV_OPT_DATA_CENTER_ID:
+ if ((res = tlv_iter_decode_u32(&tlv_iter, &u32)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->data_center_id = u32;
+ break;
+ case TLV_OPT_NODE_STATE:
+ if ((res = tlv_iter_decode_node_state(&tlv_iter,
+ &decoded_msg->node_state)) != 0) {
+ return (res);
+ }
+ break;
+ case TLV_OPT_NODE_INFO:
+ if ((res = tlv_iter_decode_node_info(&tlv_iter, &node_info)) != 0) {
+ return (res);
+ }
+
+ if (node_list_add_from_node_info(&decoded_msg->nodes, &node_info) == NULL) {
+ return (-2);
+ }
+ break;
+ case TLV_OPT_NODE_LIST_TYPE:
+ if ((res = tlv_iter_decode_node_list_type(&tlv_iter,
+ &decoded_msg->node_list_type)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->node_list_type_set = 1;
+ break;
+ case TLV_OPT_VOTE:
+ if ((res = tlv_iter_decode_vote(&tlv_iter, &decoded_msg->vote)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->vote_set = 1;
+ break;
+ case TLV_OPT_QUORATE:
+ if ((res = tlv_iter_decode_quorate(&tlv_iter,
+ &decoded_msg->quorate)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->quorate_set = 1;
+ break;
+ case TLV_OPT_TIE_BREAKER:
+ if ((res = tlv_iter_decode_tie_breaker(&tlv_iter, &tie_breaker)) != 0) {
+ return (res);
+ }
+
+ decoded_msg->tie_breaker_set = 1;
+ memcpy(&decoded_msg->tie_breaker, &tie_breaker, sizeof(tie_breaker));
+ break;
+ case TLV_OPT_HEURISTICS:
+ if ((res = tlv_iter_decode_heuristics(&tlv_iter,
+ &decoded_msg->heuristics)) != 0) {
+ return (res);
+ }
+ break;
+ /*
+ * Default is not defined intentionally. Compiler shows warning when
+ * new tlv option is added. Also protocol ignores unknown options so
+ * no extra work is needed.
+ */
+ }
+ }
+
+ if (iter_res != 0) {
+ return (-3);
+ }
+
+ return (0);
+}
+
+void
+msg_get_supported_messages(enum msg_type **supported_messages, size_t *no_supported_messages)
+{
+
+ *supported_messages = msg_static_supported_messages;
+ *no_supported_messages = MSG_STATIC_SUPPORTED_MESSAGES_SIZE;
+}
+
+const char *
+msg_type_to_str(enum msg_type type)
+{
+
+ switch (type) {
+ case MSG_TYPE_PREINIT: return ("Preinit"); break;
+ case MSG_TYPE_PREINIT_REPLY: return ("Preinit reply"); break;
+ case MSG_TYPE_STARTTLS: return ("StartTLS"); break;
+ case MSG_TYPE_INIT: return ("Init"); break;
+ case MSG_TYPE_INIT_REPLY: return ("Init reply"); break;
+ case MSG_TYPE_SERVER_ERROR: return ("Server error"); break;
+ case MSG_TYPE_SET_OPTION: return ("Set option"); break;
+ case MSG_TYPE_SET_OPTION_REPLY: return ("Set option reply"); break;
+ case MSG_TYPE_ECHO_REQUEST: return ("Echo request"); break;
+ case MSG_TYPE_ECHO_REPLY: return ("Echo reply"); break;
+ case MSG_TYPE_NODE_LIST: return ("Node list"); break;
+ case MSG_TYPE_NODE_LIST_REPLY: return ("Node list reply"); break;
+ case MSG_TYPE_ASK_FOR_VOTE: return ("Ask for vote"); break;
+ case MSG_TYPE_ASK_FOR_VOTE_REPLY: return ("Ask for vote reply"); break;
+ case MSG_TYPE_VOTE_INFO: return ("Vote info"); break;
+ case MSG_TYPE_VOTE_INFO_REPLY: return ("Vote info reply"); break;
+ case MSG_TYPE_HEURISTICS_CHANGE: return ("Heuristics change"); break;
+ case MSG_TYPE_HEURISTICS_CHANGE_REPLY: return ("Heuristics change reply"); break;
+ }
+
+ return ("Unknown message type");
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _MSG_H_
+#define _MSG_H_
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include "dynar.h"
+#include "tlv.h"
+#include "node-list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum msg_type {
+ MSG_TYPE_PREINIT = 0,
+ MSG_TYPE_PREINIT_REPLY = 1,
+ MSG_TYPE_STARTTLS = 2,
+ MSG_TYPE_INIT = 3,
+ MSG_TYPE_INIT_REPLY = 4,
+ MSG_TYPE_SERVER_ERROR = 5,
+ MSG_TYPE_SET_OPTION = 6,
+ MSG_TYPE_SET_OPTION_REPLY = 7,
+ MSG_TYPE_ECHO_REQUEST = 8,
+ MSG_TYPE_ECHO_REPLY = 9,
+ MSG_TYPE_NODE_LIST = 10,
+ MSG_TYPE_NODE_LIST_REPLY = 11,
+ MSG_TYPE_ASK_FOR_VOTE = 12,
+ MSG_TYPE_ASK_FOR_VOTE_REPLY = 13,
+ MSG_TYPE_VOTE_INFO = 14,
+ MSG_TYPE_VOTE_INFO_REPLY = 15,
+ MSG_TYPE_HEURISTICS_CHANGE = 16,
+ MSG_TYPE_HEURISTICS_CHANGE_REPLY = 17,
+};
+
+struct msg_decoded {
+ enum msg_type type;
+ uint8_t seq_number_set;
+ uint32_t seq_number; /* Only valid if seq_number_set != 0 */
+ size_t cluster_name_len;
+ /* Valid only if != NULL. Trailing \0 is added but not counted in cluster_name_len */
+ char *cluster_name;
+ uint8_t tls_supported_set;
+ enum tlv_tls_supported tls_supported; /* Valid only if tls_supported_set != 0. */
+ uint8_t tls_client_cert_required_set;
+ uint8_t tls_client_cert_required; /* Valid only if tls_client_cert_required_set != 0 */
+ size_t no_supported_messages;
+ enum msg_type *supported_messages; /* Valid only if != NULL */
+ size_t no_supported_options;
+ enum tlv_opt_type *supported_options; /* Valid only if != NULL */
+ uint8_t reply_error_code_set;
+ enum tlv_reply_error_code reply_error_code; /* Valid only if reply_error_code_set != 0 */
+ uint8_t server_maximum_request_size_set;
+ /* Valid only if server_maximum_request_size_set != 0 */
+ size_t server_maximum_request_size;
+ uint8_t server_maximum_reply_size_set;
+ size_t server_maximum_reply_size; /* Valid only if server_maximum_reply_size_set != 0 */
+ uint8_t node_id_set;
+ uint32_t node_id;
+ size_t no_supported_decision_algorithms;
+ /* Valid only if != NULL */
+ enum tlv_decision_algorithm_type *supported_decision_algorithms;
+ uint8_t decision_algorithm_set;
+ /* Valid only if decision_algorithm_set != 0 */
+ enum tlv_decision_algorithm_type decision_algorithm;
+ uint8_t heartbeat_interval_set;
+ uint32_t heartbeat_interval; /* Valid only if heartbeat_interval_set != 0 */
+ uint8_t ring_id_set;
+ struct tlv_ring_id ring_id; /* Valid only if ring_id_set != 0 */
+ uint8_t config_version_set;
+ uint64_t config_version; /* Valid only if config_version_set != 0 */
+ uint32_t data_center_id; /* Valid only if != 0 */
+ enum tlv_node_state node_state; /* Valid only if != TLV_NODE_STATE_NOT_SET */
+ struct node_list nodes; /* Valid only if node_list_is_empty(nodes) != 0 */
+ int node_list_type_set;
+ enum tlv_node_list_type node_list_type; /* Valid only if node_list_type_set != 0 */
+ int vote_set;
+ enum tlv_vote vote; /* Valid only if vote_set != 0 */
+ int quorate_set;
+ enum tlv_quorate quorate; /* Valid only if quorate_set != 0 */
+ int tie_breaker_set;
+ struct tlv_tie_breaker tie_breaker;
+ enum tlv_heuristics heuristics; /* Always valid but can be TLV_HEURISTICS_UNDEFINED */
+};
+
+extern size_t msg_create_preinit(struct dynar *msg, const char *cluster_name,
+ int add_msg_seq_number, uint32_t msg_seq_number);
+
+extern size_t msg_create_preinit_reply(struct dynar *msg, int add_msg_seq_number,
+ uint32_t msg_seq_number, enum tlv_tls_supported tls_supported, int tls_client_cert_required);
+
+extern size_t msg_create_starttls(struct dynar *msg, int add_msg_seq_number,
+ uint32_t msg_seq_number);
+
+extern size_t msg_create_init(struct dynar *msg, int add_msg_seq_number,
+ uint32_t msg_seq_number, enum tlv_decision_algorithm_type decision_algorithm,
+ const enum msg_type *supported_msgs, size_t no_supported_msgs,
+ const enum tlv_opt_type *supported_opts, size_t no_supported_opts, uint32_t node_id,
+ uint32_t heartbeat_interval, const struct tlv_tie_breaker *tie_breaker,
+ const struct tlv_ring_id *ring_id);
+
+extern size_t msg_create_server_error(struct dynar *msg, int add_msg_seq_number,
+ uint32_t msg_seq_number, enum tlv_reply_error_code reply_error_code);
+
+extern size_t msg_create_init_reply(struct dynar *msg, int add_msg_seq_number,
+ uint32_t msg_seq_number, enum tlv_reply_error_code reply_error_code,
+ const enum msg_type *supported_msgs, size_t no_supported_msgs,
+ const enum tlv_opt_type *supported_opts, size_t no_supported_opts,
+ size_t server_maximum_request_size, size_t server_maximum_reply_size,
+ const enum tlv_decision_algorithm_type *supported_decision_algorithms,
+ size_t no_supported_decision_algorithms);
+
+extern size_t msg_create_set_option(struct dynar *msg,
+ int add_msg_seq_number, uint32_t msg_seq_number,
+ int add_heartbeat_interval, uint32_t heartbeat_interval);
+
+extern size_t msg_create_set_option_reply(struct dynar *msg,
+ int add_msg_seq_number, uint32_t msg_seq_number, uint32_t heartbeat_interval);
+
+extern size_t msg_create_echo_request(struct dynar *msg, int add_msg_seq_number,
+ uint32_t msg_seq_number);
+
+extern size_t msg_create_echo_reply(struct dynar *msg,
+ const struct dynar *echo_request_msg);
+
+extern size_t msg_create_node_list(struct dynar *msg,
+ uint32_t msg_seq_number, enum tlv_node_list_type node_list_type,
+ int add_ring_id, const struct tlv_ring_id *ring_id,
+ int add_config_version, uint64_t config_version,
+ int add_quorate, enum tlv_quorate quorate,
+ int add_heuristics, enum tlv_heuristics heuristics,
+ const struct node_list *nodes);
+
+extern size_t msg_create_node_list_reply(struct dynar *msg, uint32_t msg_seq_number,
+ enum tlv_node_list_type node_list_type, const struct tlv_ring_id *ring_id,
+ enum tlv_vote vote);
+
+extern size_t msg_create_ask_for_vote(struct dynar *msg, uint32_t msg_seq_number);
+
+extern size_t msg_create_ask_for_vote_reply(struct dynar *msg, uint32_t msg_seq_number,
+ const struct tlv_ring_id *ring_id, enum tlv_vote vote);
+
+extern size_t msg_create_vote_info(struct dynar *msg, uint32_t msg_seq_number,
+ const struct tlv_ring_id *ring_id, enum tlv_vote vote);
+
+extern size_t msg_create_vote_info_reply(struct dynar *msg, uint32_t msg_seq_number);
+
+extern size_t msg_create_heuristics_change(struct dynar *msg, uint32_t msg_seq_number,
+ enum tlv_heuristics heuristics);
+
+extern size_t msg_create_heuristics_change_reply(struct dynar *msg,
+ uint32_t msg_seq_number, const struct tlv_ring_id *ring_id, enum tlv_heuristics heuristics,
+ enum tlv_vote vote);
+
+extern size_t msg_get_header_length(void);
+
+extern uint32_t msg_get_len(const struct dynar *msg);
+
+extern enum msg_type msg_get_type(const struct dynar *msg);
+
+extern int msg_is_valid_msg_type(const struct dynar *msg);
+
+extern void msg_decoded_init(struct msg_decoded *decoded_msg);
+
+extern void msg_decoded_destroy(struct msg_decoded *decoded_msg);
+
+extern int msg_decode(const struct dynar *msg, struct msg_decoded *decoded_msg);
+
+extern void msg_get_supported_messages(enum msg_type **supported_messages,
+ size_t *no_supported_messages);
+
+extern const char * msg_type_to_str(enum msg_type type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MSG_H_ */
--- /dev/null
+/*
+ * 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 "msgio.h"
+#include "msg.h"
+
+#define MSGIO_LOCAL_BUF_SIZE (1 << 10)
+
+ssize_t
+msgio_send(PRFileDesc *sock, const char *msg, size_t msg_len, size_t *start_pos)
+{
+ ssize_t sent_bytes;
+
+ if ((sent_bytes = PR_Send(sock, msg + *start_pos,
+ msg_len - *start_pos, 0, PR_INTERVAL_NO_TIMEOUT)) != -1) {
+ *start_pos += sent_bytes;
+ }
+
+ return (sent_bytes);
+}
+
+ssize_t
+msgio_send_blocking(PRFileDesc *sock, const char *msg, size_t msg_len)
+{
+ PRPollDesc pfd;
+ size_t already_sent_bytes;
+ PRInt32 res;
+ ssize_t ret;
+
+ already_sent_bytes = 0;
+ ret = 0;
+
+ while (ret != -1 && already_sent_bytes < msg_len) {
+ pfd.fd = sock;
+ pfd.in_flags = PR_POLL_WRITE;
+ pfd.out_flags = 0;
+
+ if ((res = PR_Poll(&pfd, 1, PR_INTERVAL_NO_TIMEOUT)) > 0) {
+ if (pfd.out_flags & PR_POLL_WRITE) {
+ if ((msgio_send(sock, msg, msg_len, &already_sent_bytes) == -1) &&
+ PR_GetError() != PR_WOULD_BLOCK_ERROR) {
+ ret = -1;
+ } else {
+ ret = already_sent_bytes;
+ }
+ } else if (pfd.out_flags & (PR_POLL_ERR | PR_POLL_NVAL | PR_POLL_HUP)) {
+ PR_SetError(PR_IO_ERROR, 0);
+ ret = -1;
+ }
+ } else {
+ ret = -1;
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ * -1 = send returned 0,
+ * -2 = unhandled error.
+ * 0 = success but whole buffer is still not sent
+ * 1 = all data was sent
+ */
+int
+msgio_write(PRFileDesc *sock, const struct dynar *msg, size_t *already_sent_bytes)
+{
+ PRInt32 sent;
+ PRInt32 to_send;
+
+ to_send = dynar_size(msg) - *already_sent_bytes;
+ if (to_send > MSGIO_LOCAL_BUF_SIZE) {
+ to_send = MSGIO_LOCAL_BUF_SIZE;
+ }
+
+ sent = PR_Send(sock, dynar_data(msg) + *already_sent_bytes, to_send, 0,
+ PR_INTERVAL_NO_TIMEOUT);
+
+ if (sent > 0) {
+ *already_sent_bytes += sent;
+
+ if (*already_sent_bytes == dynar_size(msg)) {
+ /*
+ * All data sent
+ */
+ return (1);
+ }
+ }
+
+ if (sent == 0) {
+ return (-1);
+ }
+
+ if (sent < 0 && PR_GetError() != PR_WOULD_BLOCK_ERROR) {
+ return (-2);
+ }
+
+ return (0);
+}
+
+/*
+ * 1 Full message received
+ * 0 Partial read (no error)
+ * -1 End of connection
+ * -2 Unhandled error
+ * -3 Fatal error. Unable to store message header
+ * -4 Unable to store message
+ * -5 Invalid msg type
+ * -6 Msg too long
+ */
+int
+msgio_read(PRFileDesc *sock, struct dynar *msg, size_t *already_received_bytes, int *skipping_msg)
+{
+ char local_read_buffer[MSGIO_LOCAL_BUF_SIZE];
+ PRInt32 readed;
+ PRInt32 to_read;
+ int ret;
+
+ ret = 0;
+
+ if (*already_received_bytes < msg_get_header_length()) {
+ /*
+ * Complete reading of header
+ */
+ to_read = msg_get_header_length() - *already_received_bytes;
+ } else {
+ /*
+ * Read rest of message (or at least as much as possible)
+ */
+ to_read = (msg_get_header_length() + msg_get_len(msg)) - *already_received_bytes;
+ }
+
+ if (to_read > MSGIO_LOCAL_BUF_SIZE) {
+ to_read = MSGIO_LOCAL_BUF_SIZE;
+ }
+
+ readed = PR_Recv(sock, local_read_buffer, to_read, 0, PR_INTERVAL_NO_TIMEOUT);
+ if (readed > 0) {
+ *already_received_bytes += readed;
+
+ if (!*skipping_msg) {
+ if (dynar_cat(msg, local_read_buffer, readed) == -1) {
+ *skipping_msg = 1;
+ ret = -4;
+ }
+ }
+
+ if (*skipping_msg && *already_received_bytes < msg_get_header_length()) {
+ /*
+ * Fatal error. We were unable to store even message header
+ */
+ return (-3);
+ }
+
+ if (!*skipping_msg && *already_received_bytes == msg_get_header_length()) {
+ /*
+ * Full header received. Check type, maximum size, ...
+ */
+ if (!msg_is_valid_msg_type(msg)) {
+ *skipping_msg = 1;
+ ret = -5;
+ } else if ((msg_get_header_length() + msg_get_len(msg)) >
+ dynar_max_size(msg)) {
+ *skipping_msg = 1;
+ ret = -6;
+ }
+ }
+
+ if (*already_received_bytes >= msg_get_header_length() &&
+ *already_received_bytes == (msg_get_header_length() + msg_get_len(msg))) {
+ /*
+ * Full message skipped or received
+ */
+ ret = 1;
+ }
+
+ }
+
+ if (readed == 0) {
+ return (-1);
+ }
+
+ if (readed < 0 && PR_GetError() != PR_WOULD_BLOCK_ERROR) {
+ return (-2);
+ }
+
+ return (ret);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _MSGIO_H_
+#define _MSGIO_H_
+
+#include <nspr.h>
+
+#include "dynar.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern ssize_t msgio_send(PRFileDesc *sock, const char *msg, size_t msg_len,
+ size_t *start_pos);
+
+extern ssize_t msgio_send_blocking(PRFileDesc *sock, const char *msg, size_t msg_len);
+
+extern int msgio_write(PRFileDesc *sock, const struct dynar *msg, size_t *already_sent_bytes);
+
+extern int msgio_read(PRFileDesc *sock, struct dynar *msg, size_t *already_received_bytes,
+ int *skipping_msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MSGIO_H_ */
--- /dev/null
+/*
+ * 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 <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "node-list.h"
+
+void
+node_list_init(struct node_list *list)
+{
+
+ TAILQ_INIT(list);
+}
+
+struct node_list_entry *
+node_list_add(struct node_list *list, uint32_t node_id, uint32_t data_center_id,
+ enum tlv_node_state node_state)
+{
+ struct node_list_entry *node;
+
+ node = (struct node_list_entry *)malloc(sizeof(*node));
+ if (node == NULL) {
+ return (NULL);
+ }
+
+ memset(node, 0, sizeof(*node));
+
+ node->node_id = node_id;
+ node->data_center_id = data_center_id;
+ node->node_state = node_state;
+
+ TAILQ_INSERT_TAIL(list, node, entries);
+
+ return (node);
+}
+
+struct node_list_entry *
+node_list_add_from_node_info(struct node_list *list, const struct tlv_node_info *node_info)
+{
+
+ return (node_list_add(list, node_info->node_id, node_info->data_center_id,
+ node_info->node_state));
+}
+
+int
+node_list_clone(struct node_list *dst_list, const struct node_list *src_list)
+{
+ struct node_list_entry *node_entry;
+
+ node_list_init(dst_list);
+
+ TAILQ_FOREACH(node_entry, src_list, entries) {
+ if (node_list_add(dst_list, node_entry->node_id, node_entry->data_center_id,
+ node_entry->node_state) == NULL) {
+ node_list_free(dst_list);
+
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+void
+node_list_entry_to_tlv_node_info(const struct node_list_entry *node,
+ struct tlv_node_info *node_info)
+{
+
+ node_info->node_id = node->node_id;
+ node_info->data_center_id = node->data_center_id;
+ node_info->node_state = node->node_state;
+}
+
+void
+node_list_free(struct node_list *list)
+{
+ struct node_list_entry *node;
+ struct node_list_entry *node_next;
+
+ node = TAILQ_FIRST(list);
+
+ while (node != NULL) {
+ node_next = TAILQ_NEXT(node, entries);
+
+ free(node);
+
+ node = node_next;
+ }
+
+ TAILQ_INIT(list);
+}
+
+void
+node_list_del(struct node_list *list, struct node_list_entry *node)
+{
+
+ TAILQ_REMOVE(list, node, entries);
+
+ free(node);
+}
+
+int
+node_list_is_empty(const struct node_list *list)
+{
+
+ return (TAILQ_EMPTY(list));
+}
+
+struct node_list_entry *
+node_list_find_node_id(const struct node_list *list, uint32_t node_id)
+{
+ struct node_list_entry *node_entry;
+
+ TAILQ_FOREACH(node_entry, list, entries) {
+ if (node_entry->node_id == node_id) {
+ return (node_entry);
+ }
+ }
+
+ return (NULL);
+}
+
+int
+node_list_eq(const struct node_list *list1, const struct node_list *list2)
+{
+ struct node_list_entry *node1_entry;
+ struct node_list_entry *node2_entry;
+ struct node_list tmp_list;
+ int res;
+
+ res = 1;
+
+ if (node_list_clone(&tmp_list, list2) != 0) {
+ return (-1);
+ }
+
+ TAILQ_FOREACH(node1_entry, list1, entries) {
+ node2_entry = node_list_find_node_id(&tmp_list, node1_entry->node_id);
+ if (node2_entry == NULL) {
+ res = 0;
+ goto return_res;
+ }
+
+ if (node1_entry->node_id != node2_entry->node_id ||
+ node1_entry->data_center_id != node2_entry->data_center_id ||
+ node1_entry->node_state != node2_entry->node_state) {
+ res = 0;
+ goto return_res;
+ }
+
+ node_list_del(&tmp_list, node2_entry);
+ }
+
+ if (!node_list_is_empty(&tmp_list)) {
+ res = 0;
+ goto return_res;
+ }
+
+return_res:
+ node_list_free(&tmp_list);
+
+ return (res);
+}
+
+size_t
+node_list_size(const struct node_list *nlist)
+{
+ struct node_list_entry *node_entry;
+ size_t res;
+
+ res = 0;
+
+ TAILQ_FOREACH(node_entry, nlist, entries) {
+ res++;
+ }
+
+ return (res);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _NODE_LIST_H_
+#define _NODE_LIST_H_
+
+#include <sys/types.h>
+
+#include <sys/queue.h>
+#include <inttypes.h>
+
+#include "tlv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct node_list_entry {
+ uint32_t node_id;
+ uint32_t data_center_id;
+ enum tlv_node_state node_state;
+ TAILQ_ENTRY(node_list_entry) entries;
+};
+
+TAILQ_HEAD(node_list, node_list_entry);
+
+extern void node_list_init(struct node_list *list);
+
+extern struct node_list_entry *node_list_add(struct node_list *list,
+ uint32_t node_id, uint32_t data_center_id, enum tlv_node_state node_state);
+
+extern struct node_list_entry *node_list_add_from_node_info(
+ struct node_list *list, const struct tlv_node_info *node_info);
+
+extern int node_list_clone(struct node_list *dst_list,
+ const struct node_list *src_list);
+
+extern void node_list_free(struct node_list *list);
+
+extern void node_list_del(struct node_list *list,
+ struct node_list_entry *node);
+
+extern int node_list_is_empty(const struct node_list *list);
+
+extern void node_list_entry_to_tlv_node_info(
+ const struct node_list_entry *node, struct tlv_node_info *node_info);
+
+extern struct node_list_entry * node_list_find_node_id(const struct node_list *list,
+ uint32_t node_id);
+
+extern int node_list_eq(const struct node_list *list1,
+ const struct node_list *list2);
+
+extern size_t node_list_size(const struct node_list *nlist);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NODE_LIST_H_ */
--- /dev/null
+/*
+ * 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 <limits.h>
+
+#include "nss-sock.h"
+
+int
+nss_sock_init_nss(char *config_dir)
+{
+ if (config_dir == NULL) {
+ if (NSS_NoDB_Init(NULL) != SECSuccess) {
+ return (-1);
+ }
+ } else {
+ if (NSS_Init(config_dir) != SECSuccess) {
+ return (-1);
+ }
+ }
+
+ if (NSS_SetDomesticPolicy() != SECSuccess) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Set NSS socket non-blocking
+ */
+int
+nss_sock_set_non_blocking(PRFileDesc *sock)
+{
+ PRSocketOptionData sock_opt;
+
+ memset(&sock_opt, 0, sizeof(sock_opt));
+ sock_opt.option = PR_SockOpt_Nonblocking;
+ sock_opt.value.non_blocking = PR_TRUE;
+ if (PR_SetSocketOption(sock, &sock_opt) != PR_SUCCESS) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Create TCP socket with af family. If reuse_addr is set, socket option
+ * for reuse address is set.
+ */
+static PRFileDesc *
+nss_sock_create_socket(PRIntn af, int reuse_addr)
+{
+ PRFileDesc *sock;
+ PRSocketOptionData socket_option;
+
+ sock = PR_OpenTCPSocket(af);
+ if (sock == NULL) {
+ return (NULL);
+ }
+
+ if (reuse_addr) {
+ socket_option.option = PR_SockOpt_Reuseaddr;
+ socket_option.value.reuse_addr = PR_TRUE;
+ if (PR_SetSocketOption(sock, &socket_option) != PR_SUCCESS) {
+ return (NULL);
+ }
+ }
+
+ return (sock);
+}
+
+/*
+ * Create listen socket and bind it to address. hostname can be NULL and then
+ * any address is used. Address family (af) can be ether PR_AF_INET6,
+ * PR_AF_INET or PR_AF_UNSPEC.
+ */
+PRFileDesc *
+nss_sock_create_listen_socket(const char *hostname, uint16_t port, PRIntn af)
+{
+ PRNetAddr addr;
+ PRFileDesc *sock;
+ PRAddrInfo *addr_info;
+ void *addr_iter;
+
+ sock = NULL;
+
+ if (hostname == NULL) {
+ memset(&addr, 0, sizeof(addr));
+
+ if (PR_InitializeNetAddr(PR_IpAddrAny, port, &addr) != PR_SUCCESS) {
+ return (NULL);
+ }
+ if (af == PR_AF_UNSPEC) {
+ af = PR_AF_INET6;
+ }
+ addr.raw.family = af;
+
+ sock = nss_sock_create_socket(af, 1);
+ if (sock == NULL) {
+ return (NULL);
+ }
+
+ if (PR_Bind(sock, &addr) != PR_SUCCESS) {
+ PR_Close(sock);
+
+ return (NULL);
+ }
+ } else {
+ addr_info = PR_GetAddrInfoByName(hostname, af, PR_AI_ADDRCONFIG);
+ if (addr_info == NULL) {
+ return (NULL);
+ }
+
+ addr_iter = NULL;
+
+ while ((addr_iter = PR_EnumerateAddrInfo(addr_iter, addr_info, port,
+ &addr)) != NULL) {
+ if (af == PR_AF_UNSPEC || addr.raw.family == af) {
+ sock = nss_sock_create_socket(addr.raw.family, 1);
+ if (sock == NULL) {
+ continue;
+ }
+
+ if (PR_Bind(sock, &addr) != PR_SUCCESS) {
+ PR_Close(sock);
+ sock = NULL;
+
+ continue;
+ }
+
+ /*
+ * Socket is sucesfully bound
+ */
+ break;
+ }
+ }
+
+ PR_FreeAddrInfo(addr_info);
+
+ if (sock == NULL) {
+ /*
+ * No address succeeded
+ */
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, 0);
+
+ return (NULL);
+ }
+ }
+
+ return (sock);
+}
+
+PRFileDesc *
+nss_sock_create_client_socket(const char *hostname, uint16_t port, PRIntn af,
+ PRIntervalTime timeout)
+{
+ PRNetAddr addr;
+ PRFileDesc *sock;
+ PRAddrInfo *addr_info;
+ void *addr_iter;
+ PRStatus res;
+ int connect_failed;
+ PRIntn tmp_af;
+
+ sock = NULL;
+ connect_failed = 0;
+
+ tmp_af = af;
+ if (af == PR_AF_INET6) {
+ tmp_af = PR_AF_UNSPEC;
+ }
+
+ addr_info = PR_GetAddrInfoByName(hostname, tmp_af, PR_AI_ADDRCONFIG);
+ if (addr_info == NULL) {
+ return (NULL);
+ }
+
+ addr_iter = NULL;
+
+ while ((addr_iter = PR_EnumerateAddrInfo(addr_iter, addr_info, port, &addr)) != NULL) {
+ if (af != PR_AF_UNSPEC && addr.raw.family != af) {
+ continue;
+ }
+
+ sock = nss_sock_create_socket(addr.raw.family, 0);
+ if (sock == NULL) {
+ continue;
+ }
+
+ if ((res = PR_Connect(sock, &addr, timeout)) != PR_SUCCESS) {
+ PR_Close(sock);
+ sock = NULL;
+ connect_failed = 1;
+ }
+
+ /*
+ * Connection attempt finished
+ */
+ break;
+ }
+
+ PR_FreeAddrInfo(addr_info);
+
+ if (sock == NULL && !connect_failed) {
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, 0);
+ }
+
+ return (sock);
+}
+
+int
+nss_sock_non_blocking_client_init(const char *host_name, uint16_t port, PRIntn af,
+ struct nss_sock_non_blocking_client *client)
+{
+ PRIntn tmp_af;
+
+ client->destroyed = 1;
+
+ if ((client->host_name = strdup(host_name)) == NULL) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+
+ return (-1);
+ }
+
+ client->port = port;
+ client->af = af;
+
+ tmp_af = af;
+ if (af == PR_AF_INET6) {
+ tmp_af = PR_AF_UNSPEC;
+ }
+
+ client->addr_info = PR_GetAddrInfoByName(client->host_name, tmp_af, PR_AI_ADDRCONFIG);
+ if (client->addr_info == NULL) {
+ free(client->host_name);
+
+ return (-1);
+ }
+ client->addr_iter = NULL;
+ client->connect_attempts = 0;
+ client->socket = NULL;
+ client->destroyed = 0;
+
+ return (0);
+}
+
+int
+nss_sock_non_blocking_client_try_next(struct nss_sock_non_blocking_client *client)
+{
+ PRNetAddr addr;
+ PRStatus res;
+
+ if (client->destroyed) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return (-1);
+ }
+
+ if (client->socket != NULL) {
+ PR_Close(client->socket);
+ client->socket = NULL;
+ }
+
+ while ((client->addr_iter = PR_EnumerateAddrInfo(client->addr_iter, client->addr_info,
+ client->port, &addr)) != NULL) {
+ if (client->af != PR_AF_UNSPEC && addr.raw.family != client->af) {
+ continue;
+ }
+
+ client->socket = nss_sock_create_socket(addr.raw.family, 0);
+ if (client->socket == NULL) {
+ continue;
+ }
+
+ if (nss_sock_set_non_blocking(client->socket) == -1) {
+ PR_Close(client->socket);
+ client->socket = NULL;
+ continue;
+ }
+
+ res = PR_Connect(client->socket, &addr, PR_INTERVAL_NO_TIMEOUT);
+ if (res == PR_SUCCESS || PR_GetError() == PR_IN_PROGRESS_ERROR) {
+ return (0);
+ }
+
+ PR_Close(client->socket);
+ client->socket = NULL;
+
+ if (client->connect_attempts < INT_MAX) {
+ client->connect_attempts++;
+ }
+ }
+
+ if (client->connect_attempts == 0) {
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, 0);
+ }
+
+ return (-1);
+}
+
+void
+nss_sock_non_blocking_client_destroy(struct nss_sock_non_blocking_client *client)
+{
+
+ if (client->destroyed) {
+ return ;
+ }
+
+ if (client->addr_info != NULL) {
+ PR_FreeAddrInfo(client->addr_info);
+ client->addr_info = NULL;
+ }
+
+ free(client->host_name);
+ client->host_name = NULL;
+
+ client->destroyed = 1;
+}
+
+/*
+ * -1 = Client connect failed
+ * 0 = Client connect still in progress
+ * 1 = Client successfuly connected
+ */
+int
+nss_sock_non_blocking_client_succeeded(const PRPollDesc *pfd)
+{
+ int res;
+
+ res = -1;
+
+ if (PR_GetConnectStatus(pfd) == PR_SUCCESS) {
+ res = 1;
+ } else {
+ if (PR_GetError() == PR_IN_PROGRESS_ERROR) {
+ res = 0;
+ } else {
+ res = -1;
+ }
+ }
+
+ return (res);
+}
+
+/*
+ * Start client side SSL connection. This can block.
+ *
+ * ssl_url is expected server URL, bad_cert_hook is callback called when server certificate
+ * verification fails.
+ */
+PRFileDesc *
+nss_sock_start_ssl_as_client(PRFileDesc *input_sock, const char *ssl_url,
+ SSLBadCertHandler bad_cert_hook, SSLGetClientAuthData client_auth_hook,
+ void *client_auth_hook_arg, int force_handshake, int *reset_would_block)
+{
+ PRFileDesc *ssl_sock;
+
+ if (force_handshake) {
+ *reset_would_block = 0;
+ }
+
+ ssl_sock = SSL_ImportFD(NULL, input_sock);
+ if (ssl_sock == NULL) {
+ return (NULL);
+ }
+
+ if (SSL_SetURL(ssl_sock, ssl_url) != SECSuccess) {
+ return (NULL);
+ }
+
+ if ((SSL_OptionSet(ssl_sock, SSL_SECURITY, PR_TRUE) != SECSuccess) ||
+ (SSL_OptionSet(ssl_sock, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess) ||
+ (SSL_OptionSet(ssl_sock, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess)) {
+ return (NULL);
+ }
+ if (bad_cert_hook != NULL && SSL_BadCertHook(ssl_sock, bad_cert_hook, NULL) != SECSuccess) {
+ return (NULL);
+ }
+
+ if (client_auth_hook != NULL &&
+ (SSL_GetClientAuthDataHook(ssl_sock, client_auth_hook,
+ client_auth_hook_arg) != SECSuccess)) {
+ return (NULL);
+ }
+
+ if (SSL_ResetHandshake(ssl_sock, PR_FALSE) != SECSuccess) {
+ return (NULL);
+ }
+
+ if (force_handshake && SSL_ForceHandshake(ssl_sock) != SECSuccess) {
+ if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
+ /*
+ * Mask would block error.
+ */
+ *reset_would_block = 1;
+ } else {
+ return (NULL);
+ }
+ }
+
+ return (ssl_sock);
+}
+
+PRFileDesc *
+nss_sock_start_ssl_as_server(PRFileDesc *input_sock, CERTCertificate *server_cert,
+ SECKEYPrivateKey *server_key, int require_client_cert, int force_handshake,
+ int *reset_would_block)
+{
+ PRFileDesc *ssl_sock;
+
+ if (force_handshake) {
+ *reset_would_block = 0;
+ }
+
+ ssl_sock = SSL_ImportFD(NULL, input_sock);
+ if (ssl_sock == NULL) {
+ return (NULL);
+ }
+
+ if (SSL_ConfigSecureServer(ssl_sock, server_cert, server_key,
+ NSS_FindCertKEAType(server_cert)) != SECSuccess) {
+ return (NULL);
+ }
+
+ if ((SSL_OptionSet(ssl_sock, SSL_SECURITY, PR_TRUE) != SECSuccess) ||
+ (SSL_OptionSet(ssl_sock, SSL_HANDSHAKE_AS_SERVER, PR_TRUE) != SECSuccess) ||
+ (SSL_OptionSet(ssl_sock, SSL_HANDSHAKE_AS_CLIENT, PR_FALSE) != SECSuccess) ||
+ (SSL_OptionSet(ssl_sock, SSL_REQUEST_CERTIFICATE, require_client_cert) != SECSuccess) ||
+ (SSL_OptionSet(ssl_sock, SSL_REQUIRE_CERTIFICATE, require_client_cert) != SECSuccess)) {
+ return (NULL);
+ }
+
+ if (SSL_ResetHandshake(ssl_sock, PR_TRUE) != SECSuccess) {
+ return (NULL);
+ }
+
+ if (force_handshake && SSL_ForceHandshake(ssl_sock) != SECSuccess) {
+ if (PR_GetError() == PR_WOULD_BLOCK_ERROR) {
+ /*
+ * Mask would block error.
+ */
+ *reset_would_block = 1;
+ } else {
+ return (NULL);
+ }
+ }
+
+ return (ssl_sock);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _NSS_SOCK_H_
+#define _NSS_SOCK_H_
+
+#include <nss.h>
+#include <ssl.h>
+#include <prnetdb.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nss_sock_non_blocking_client {
+ char *host_name;
+ uint16_t port;
+ PRIntn af;
+ PRFileDesc *socket;
+ PRAddrInfo *addr_info;
+ void *addr_iter;
+ unsigned int connect_attempts;
+ int destroyed;
+};
+
+extern int nss_sock_init_nss(char *config_dir);
+
+extern PRFileDesc *nss_sock_create_listen_socket(const char *hostname, uint16_t port,
+ PRIntn af);
+
+extern int nss_sock_set_non_blocking(PRFileDesc *sock);
+
+extern PRFileDesc *nss_sock_create_client_socket(const char *hostname, uint16_t port,
+ PRIntn af, PRIntervalTime timeout);
+
+extern PRFileDesc *nss_sock_start_ssl_as_client(PRFileDesc *input_sock, const char *ssl_url,
+ SSLBadCertHandler bad_cert_hook, SSLGetClientAuthData client_auth_hook,
+ void *client_auth_hook_arg, int force_handshake, int *reset_would_block);
+
+extern PRFileDesc *nss_sock_start_ssl_as_server(PRFileDesc *input_sock,
+ CERTCertificate *server_cert, SECKEYPrivateKey *server_key, int require_client_cert,
+ int force_handshake, int *reset_would_block);
+
+extern int nss_sock_non_blocking_client_init(const char *host_name,
+ uint16_t port, PRIntn af, struct nss_sock_non_blocking_client *client);
+
+extern int nss_sock_non_blocking_client_try_next(
+ struct nss_sock_non_blocking_client *client);
+
+extern void nss_sock_non_blocking_client_destroy(
+ struct nss_sock_non_blocking_client *client);
+
+extern int nss_sock_non_blocking_client_succeeded(const PRPollDesc *pfd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NSS_SOCK_H_ */
--- /dev/null
+/*
+ * 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 <sys/types.h>
+#include <arpa/inet.h>
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pr-poll-array.h"
+
+void
+pr_poll_array_init(struct pr_poll_array *poll_array, size_t user_data_size)
+{
+
+ memset(poll_array, 0, sizeof(*poll_array));
+ poll_array->user_data_size = user_data_size;
+}
+
+void
+pr_poll_array_destroy(struct pr_poll_array *poll_array)
+{
+
+ free(poll_array->array);
+ free(poll_array->user_data_array);
+ pr_poll_array_init(poll_array, poll_array->user_data_size);
+}
+
+void
+pr_poll_array_clean(struct pr_poll_array *poll_array)
+{
+
+ poll_array->items = 0;
+}
+
+static int
+pr_poll_array_realloc(struct pr_poll_array *poll_array,
+ ssize_t new_array_size)
+{
+ PRPollDesc *new_array;
+ char *new_user_data_array;
+
+ new_array = realloc(poll_array->array,
+ sizeof(PRPollDesc) * new_array_size);
+
+ if (new_array == NULL) {
+ return (-1);
+ }
+
+ poll_array->allocated = new_array_size;
+ poll_array->array = new_array;
+
+ if (poll_array->user_data_size > 0) {
+ new_user_data_array = realloc(poll_array->user_data_array,
+ poll_array->user_data_size * new_array_size);
+
+ if (new_user_data_array == NULL) {
+ return (-1);
+ }
+
+ poll_array->user_data_array = new_user_data_array;
+ }
+
+ return (0);
+}
+
+ssize_t
+pr_poll_array_size(struct pr_poll_array *poll_array)
+{
+
+ return (poll_array->items);
+}
+
+ssize_t
+pr_poll_array_add(struct pr_poll_array *poll_array, PRPollDesc **pfds, void **user_data)
+{
+
+ if (pr_poll_array_size(poll_array) >= poll_array->allocated) {
+ if (pr_poll_array_realloc(poll_array, (poll_array->allocated * 2) + 1)) {
+ return (-1);
+ }
+ }
+
+ *pfds = &poll_array->array[pr_poll_array_size(poll_array)];
+ memset(*pfds, 0, sizeof(**pfds));
+
+ *user_data = poll_array->user_data_array + (poll_array->items * poll_array->user_data_size);
+ memset(*user_data, 0, poll_array->user_data_size);
+
+ poll_array->items++;
+
+ return (poll_array->items - 1);
+}
+
+void
+pr_poll_array_gc(struct pr_poll_array *poll_array)
+{
+
+ if (poll_array->allocated > (pr_poll_array_size(poll_array) * 3) + 1) {
+ pr_poll_array_realloc(poll_array, (pr_poll_array_size(poll_array) * 2) + 1);
+ }
+}
+
+PRPollDesc *
+pr_poll_array_get(const struct pr_poll_array *poll_array, ssize_t pos)
+{
+
+ if (pos >= poll_array->items) {
+ return (NULL);
+ }
+
+ return (&poll_array->array[pos]);
+}
+
+void *
+pr_poll_array_get_user_data(const struct pr_poll_array *poll_array, ssize_t pos)
+{
+
+ if (pos >= poll_array->items) {
+ return (NULL);
+ }
+
+ return (poll_array->user_data_array + (pos * poll_array->user_data_size));
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _PR_POLL_ARRAY_H_
+#define _PR_POLL_ARRAY_H_
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include <nspr.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct pr_poll_array {
+ PRPollDesc *array;
+ char *user_data_array;
+ size_t user_data_size;
+ ssize_t allocated;
+ ssize_t items;
+};
+
+extern void pr_poll_array_init(struct pr_poll_array *poll_array, size_t user_data_size);
+
+extern void pr_poll_array_destroy(struct pr_poll_array *poll_array);
+
+extern void pr_poll_array_clean(struct pr_poll_array *poll_array);
+
+extern ssize_t pr_poll_array_size(struct pr_poll_array *poll_array);
+
+extern ssize_t pr_poll_array_add(struct pr_poll_array *poll_array, PRPollDesc **pfds,
+ void **user_data);
+
+extern PRPollDesc *pr_poll_array_get(const struct pr_poll_array *poll_array,
+ ssize_t pos);
+
+extern void *pr_poll_array_get_user_data(const struct pr_poll_array *poll_array,
+ ssize_t pos);
+
+extern void pr_poll_array_gc(struct pr_poll_array *poll_array);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PR_POLL_ARRAY_H_ */
--- /dev/null
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include "dynar.h"
+#include "dynar-str.h"
+#include "dynar-simple-lex.h"
+#include "process-list.h"
+
+static void process_list_free_argv(size_t no_params, char **argv);
+
+static void process_list_entry_free(struct process_list_entry *entry);
+
+static char **process_list_parse_command(const char *command, size_t *no_params);
+
+static int process_list_entry_exec(const struct process_list *plist,
+ struct process_list_entry *entry);
+
+void
+process_list_init(struct process_list *plist, size_t max_list_entries, int use_execvp,
+ process_list_notify_fn_t notify_fn, void *notify_fn_user_data)
+{
+
+ memset(plist, 0, sizeof(*plist));
+
+ plist->max_list_entries = max_list_entries;
+ plist->allocated_list_entries = 0;
+ plist->use_execvp = use_execvp;
+ plist->notify_fn = notify_fn;
+ plist->notify_fn_user_data = notify_fn_user_data;
+
+ TAILQ_INIT(&plist->active_list);
+ TAILQ_INIT(&plist->to_kill_list);
+}
+
+static void
+process_list_free_argv(size_t no_params, char **argv)
+{
+ size_t zi;
+
+ for (zi = 0; zi < no_params; zi++) {
+ free(argv[zi]);
+ }
+ free(argv);
+}
+
+static void
+process_list_entry_free(struct process_list_entry *entry)
+{
+
+ process_list_free_argv(entry->exec_argc, entry->exec_argv);
+ free(entry->name);
+ free(entry);
+}
+
+static char **
+process_list_parse_command(const char *command, size_t *no_params)
+{
+ struct dynar command_dstr;
+ struct dynar_simple_lex lex;
+ struct dynar *token;
+ int finished;
+ char **res_argv;
+ size_t zi;
+
+ res_argv = NULL;
+
+ dynar_init(&command_dstr, strlen(command) + 1);
+ if (dynar_str_cpy(&command_dstr, command) != 0) {
+ return (NULL);
+ }
+
+ dynar_simple_lex_init(&lex, &command_dstr, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
+ *no_params = 0;
+ finished = 0;
+
+ while (!finished) {
+ token = dynar_simple_lex_token_next(&lex);
+ if (token == NULL) {
+ goto exit_res;
+ }
+
+ if (strcmp(dynar_data(token), "") == 0) {
+ finished = 1;
+ } else {
+ (*no_params)++;
+ }
+ }
+
+ if (*no_params < 1) {
+ goto exit_res;
+ }
+
+ dynar_simple_lex_destroy(&lex);
+
+ res_argv = malloc(sizeof(char *) * (*no_params + 1));
+ if (res_argv == NULL) {
+ goto exit_res;
+ }
+ memset(res_argv, 0, sizeof(char *) * (*no_params + 1));
+
+ dynar_simple_lex_init(&lex, &command_dstr, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
+
+ finished = 0;
+ zi = 0;
+ while (!finished) {
+ token = dynar_simple_lex_token_next(&lex);
+ if (token == NULL) {
+ process_list_free_argv(*no_params, res_argv);
+ res_argv = NULL;
+ goto exit_res;
+ }
+
+ if (strcmp(dynar_data(token), "") == 0) {
+ finished = 1;
+ } else {
+ res_argv[zi] = strdup(dynar_data(token));
+ if (res_argv[zi] == NULL) {
+ process_list_free_argv(*no_params, res_argv);
+ res_argv = NULL;
+ }
+ zi++;
+ }
+ }
+
+ if (zi != *no_params) {
+ /*
+ * If this happens it means something is seriously broken (memory corrupted)
+ */
+ process_list_free_argv(*no_params, res_argv);
+ res_argv = NULL;
+ goto exit_res;
+ }
+
+exit_res:
+ dynar_simple_lex_destroy(&lex);
+ dynar_destroy(&command_dstr);
+ return (res_argv);
+}
+
+struct process_list_entry *
+process_list_add(struct process_list *plist, const char *name, const char *command)
+{
+ struct process_list_entry *entry;
+
+ if (plist->allocated_list_entries + 1 > plist->max_list_entries) {
+ return (NULL);
+ }
+
+ /*
+ * Alloc new entry
+ */
+ entry = malloc(sizeof(*entry));
+ if (entry == NULL) {
+ return (NULL);
+ }
+
+ memset(entry, 0, sizeof(*entry));
+ entry->name = strdup(name);
+ if (entry->name == NULL) {
+ process_list_entry_free(entry);
+
+ return (NULL);
+ }
+
+ entry->state = PROCESS_LIST_ENTRY_STATE_INITIALIZED;
+ entry->exec_argv = process_list_parse_command(command, &entry->exec_argc);
+ if (entry->exec_argv == NULL) {
+ process_list_entry_free(entry);
+
+ return (NULL);
+ }
+
+ plist->allocated_list_entries++;
+ TAILQ_INSERT_TAIL(&plist->active_list, entry, entries);
+
+ return (entry);
+}
+
+void
+process_list_free(struct process_list *plist)
+{
+ struct process_list_entry *entry;
+ struct process_list_entry *entry_next;
+
+ entry = TAILQ_FIRST(&plist->active_list);
+
+ while (entry != NULL) {
+ entry_next = TAILQ_NEXT(entry, entries);
+
+ process_list_entry_free(entry);
+
+ entry = entry_next;
+ }
+
+ entry = TAILQ_FIRST(&plist->to_kill_list);
+
+ while (entry != NULL) {
+ entry_next = TAILQ_NEXT(entry, entries);
+
+ process_list_entry_free(entry);
+
+ entry = entry_next;
+ }
+
+ plist->allocated_list_entries = 0;
+
+ TAILQ_INIT(&plist->active_list);
+ TAILQ_INIT(&plist->to_kill_list);
+}
+
+static void
+process_list_entry_exec_helper_set_stdfd(void)
+{
+ int devnull;
+
+ devnull = open("/dev/null", O_RDWR);
+ if (devnull == -1) {
+ err(1, "Can't open /dev/null");
+ }
+
+ if (dup2(devnull, 0) < 0 || dup2(devnull, 1) < 0 || dup2(devnull, 2) < 0) {
+ close(devnull);
+ err(1, "Can't dup2 stdin/out/err to /dev/null");
+ }
+
+ close(devnull);
+}
+
+static int
+process_list_entry_exec(const struct process_list *plist, struct process_list_entry *entry)
+{
+ pid_t pid;
+
+ if (entry->state != PROCESS_LIST_ENTRY_STATE_INITIALIZED) {
+ return (-1);
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ return (-1);
+ } else if (pid == 0) {
+ process_list_entry_exec_helper_set_stdfd();
+
+ if (!plist->use_execvp) {
+ execv(entry->exec_argv[0], entry->exec_argv);
+ } else {
+ execvp(entry->exec_argv[0], entry->exec_argv);
+ }
+
+ /*
+ * Exec returned -> exec failed
+ */
+ err(1, "Can't execute command %s (%s)", entry->name, entry->exec_argv[0]);
+ } else {
+ entry->pid = pid;
+ entry->state = PROCESS_LIST_ENTRY_STATE_RUNNING;
+
+ if (plist->notify_fn != NULL) {
+ plist->notify_fn(PROCESS_LIST_NOTIFY_REASON_EXECUTED, entry,
+ plist->notify_fn_user_data);
+ }
+ }
+
+ return (0);
+}
+
+int
+process_list_exec_initialized(struct process_list *plist)
+{
+ struct process_list_entry *entry;
+
+ TAILQ_FOREACH(entry, &plist->active_list, entries) {
+ if (entry->state == PROCESS_LIST_ENTRY_STATE_INITIALIZED) {
+ if (process_list_entry_exec(plist, entry) != 0) {
+ return (-1);
+ }
+ }
+ }
+
+ return (0);
+}
+
+static int
+process_list_entry_waitpid(const struct process_list *plist, struct process_list_entry *entry)
+{
+ pid_t wpid_res;
+ int status;
+
+ if (entry->state == PROCESS_LIST_ENTRY_STATE_INITIALIZED ||
+ entry->state == PROCESS_LIST_ENTRY_STATE_FINISHED) {
+ return (0);
+ }
+
+ wpid_res = waitpid(entry->pid, &status, WNOHANG);
+ if (wpid_res == -1) {
+ return (-1);
+ }
+
+ if (wpid_res == 0) {
+ /*
+ * No change
+ */
+ return (0);
+ }
+
+ entry->exit_status = status;
+
+ if (entry->state == PROCESS_LIST_ENTRY_STATE_RUNNING) {
+ if (plist->notify_fn != NULL) {
+ plist->notify_fn(PROCESS_LIST_NOTIFY_REASON_FINISHED, entry,
+ plist->notify_fn_user_data);
+ }
+ }
+
+ entry->state = PROCESS_LIST_ENTRY_STATE_FINISHED;
+
+ return (0);
+}
+
+int
+process_list_waitpid(struct process_list *plist)
+{
+ struct process_list_entry *entry;
+ struct process_list_entry *entry_next;
+
+ TAILQ_FOREACH(entry, &plist->active_list, entries) {
+ if (process_list_entry_waitpid(plist, entry) != 0) {
+ return (-1);
+ }
+ }
+
+ entry = TAILQ_FIRST(&plist->to_kill_list);
+
+ while (entry != NULL) {
+ entry_next = TAILQ_NEXT(entry, entries);
+
+ if (process_list_entry_waitpid(plist, entry) != 0) {
+ return (-1);
+ }
+
+ if (entry->state == PROCESS_LIST_ENTRY_STATE_FINISHED) {
+ /*
+ * Process finished -> remove it from list
+ */
+ TAILQ_REMOVE(&plist->to_kill_list, entry, entries);
+ process_list_entry_free(entry);
+ plist->allocated_list_entries--;
+ }
+
+ entry = entry_next;
+ }
+
+ return (0);
+}
+
+size_t
+process_list_get_no_running(struct process_list *plist)
+{
+ struct process_list_entry *entry;
+ size_t res;
+
+ res = 0;
+
+ TAILQ_FOREACH(entry, &plist->active_list, entries) {
+ if (entry->state == PROCESS_LIST_ENTRY_STATE_RUNNING) {
+ res++;
+ }
+ }
+
+ return (res);
+}
+
+/*
+ * -1 = Not all processes finished
+ * 0 = All processes finished sucesfully
+ * 1 - All processes finished but some of them not sucesfully
+ */
+int
+process_list_get_summary_result(struct process_list *plist)
+{
+ struct process_list_entry *entry;
+ int res;
+
+ res = 0;
+
+ TAILQ_FOREACH(entry, &plist->active_list, entries) {
+ if (entry->state != PROCESS_LIST_ENTRY_STATE_FINISHED) {
+ return (-1);
+ }
+
+ if (!WIFEXITED(entry->exit_status) || WEXITSTATUS(entry->exit_status) != 0) {
+ res = 1;
+ }
+ }
+
+ return (res);
+}
+
+/*
+ * 0 = All processes finished sucesfully
+ * 1 = Some process finished and failed
+ * -1 = Not all processed finished and none of finished failed
+ */
+int
+process_list_get_summary_result_short(struct process_list *plist)
+{
+ struct process_list_entry *entry;
+ int res;
+
+ res = 0;
+
+ TAILQ_FOREACH(entry, &plist->active_list, entries) {
+ if (entry->state == PROCESS_LIST_ENTRY_STATE_FINISHED) {
+ if (!WIFEXITED(entry->exit_status) || WEXITSTATUS(entry->exit_status) != 0) {
+ return (1);
+ }
+ } else {
+ res = -1;
+ }
+ }
+
+ return (res);
+}
+
+static void
+process_list_move_entry_to_kill_list(struct process_list *plist, struct process_list_entry *entry)
+{
+
+ TAILQ_REMOVE(&plist->active_list, entry, entries);
+ TAILQ_INSERT_TAIL(&plist->to_kill_list, entry, entries);
+}
+
+void
+process_list_move_active_entries_to_kill_list(struct process_list *plist)
+{
+ struct process_list_entry *entry;
+ struct process_list_entry *entry_next;
+
+ entry = TAILQ_FIRST(&plist->active_list);
+
+ while (entry != NULL) {
+ entry_next = TAILQ_NEXT(entry, entries);
+
+ if (entry->state == PROCESS_LIST_ENTRY_STATE_INITIALIZED ||
+ entry->state == PROCESS_LIST_ENTRY_STATE_FINISHED) {
+ TAILQ_REMOVE(&plist->active_list, entry, entries);
+ process_list_entry_free(entry);
+ plist->allocated_list_entries--;
+ } else {
+ process_list_move_entry_to_kill_list(plist, entry);
+ }
+
+ entry = entry_next;
+ }
+}
+
+static int
+process_list_process_kill_list_entry(struct process_list *plist, struct process_list_entry *entry)
+{
+ int sig_to_send;
+ enum process_list_entry_state new_state;
+ int res;
+
+ sig_to_send = 0;
+ new_state = PROCESS_LIST_ENTRY_STATE_INITIALIZED;
+
+ switch (entry->state) {
+ case PROCESS_LIST_ENTRY_STATE_INITIALIZED:
+ /*
+ * This shouldn't happen. If it does, process_list_move_active_entries_to_kill_list
+ * doesn't work as expected or there is some kind of memory corruption.
+ */
+ assert(entry->state != PROCESS_LIST_ENTRY_STATE_INITIALIZED);
+ break;
+ case PROCESS_LIST_ENTRY_STATE_FINISHED:
+ /*
+ * This shouldn't happen. If it does, process_list_waitpid
+ * doesn't work as expected or there is some kind of memory corruption.
+ */
+ assert(entry->state != PROCESS_LIST_ENTRY_STATE_FINISHED);
+ break;
+ case PROCESS_LIST_ENTRY_STATE_RUNNING:
+ sig_to_send = SIGTERM;
+ new_state = PROCESS_LIST_ENTRY_STATE_SIGTERM_SENT;
+ break;
+ case PROCESS_LIST_ENTRY_STATE_SIGTERM_SENT:
+ sig_to_send = SIGKILL;
+ new_state = PROCESS_LIST_ENTRY_STATE_SIGKILL_SENT;
+ break;
+ case PROCESS_LIST_ENTRY_STATE_SIGKILL_SENT:
+ sig_to_send = SIGKILL;
+ new_state = PROCESS_LIST_ENTRY_STATE_SIGKILL_SENT;
+ break;
+ }
+
+ res = 0;
+
+ if (kill(entry->pid, sig_to_send) == -1) {
+ if (errno == EPERM || errno == EINVAL) {
+ res = -1;
+ }
+ }
+
+ entry->state = new_state;
+
+ return (res);
+}
+
+int
+process_list_process_kill_list(struct process_list *plist)
+{
+ struct process_list_entry *entry;
+
+ if (process_list_waitpid(plist) != 0) {
+ return (-1);
+ }
+
+ TAILQ_FOREACH(entry, &plist->to_kill_list, entries) {
+ if (process_list_process_kill_list_entry(plist, entry) != 0) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+size_t
+process_list_get_kill_list_items(struct process_list *plist)
+{
+ struct process_list_entry *entry;
+ size_t res;
+
+ res = 0;
+
+ TAILQ_FOREACH(entry, &plist->to_kill_list, entries) {
+ res++;
+ }
+
+ return (res);
+}
+
+int
+process_list_killall(struct process_list *plist, uint32_t timeout)
+{
+ uint32_t action_timeout;
+ int i;
+
+ process_list_move_active_entries_to_kill_list(plist);
+
+ action_timeout = timeout / 10;
+ if (action_timeout < 1) {
+ action_timeout = 1;
+ }
+
+ for (i = 0; i < 10; i++) {
+ /*
+ * Make sure all process got signal (quick phase)
+ */
+ if (process_list_process_kill_list(plist) != 0) {
+ return (-1);
+ }
+ }
+
+ for (i = 0; i < 10 && process_list_get_kill_list_items(plist) > 0; i++) {
+ if (process_list_process_kill_list(plist) != 0) {
+ return (-1);
+ }
+
+ poll(NULL, 0, action_timeout);
+ }
+
+ if (process_list_get_kill_list_items(plist) > 0) {
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _PROCESS_LIST_H_
+#define _PROCESS_LIST_H_
+
+#include <signal.h>
+#include <sys/queue.h>
+
+#include "dynar.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum process_list_entry_state {
+ PROCESS_LIST_ENTRY_STATE_INITIALIZED,
+ PROCESS_LIST_ENTRY_STATE_RUNNING,
+ PROCESS_LIST_ENTRY_STATE_FINISHED,
+ PROCESS_LIST_ENTRY_STATE_SIGTERM_SENT,
+ PROCESS_LIST_ENTRY_STATE_SIGKILL_SENT,
+};
+
+enum process_list_notify_reason {
+ PROCESS_LIST_NOTIFY_REASON_EXECUTED,
+ PROCESS_LIST_NOTIFY_REASON_FINISHED,
+};
+
+struct process_list_entry {
+ char *name;
+ enum process_list_entry_state state;
+ char **exec_argv;
+ size_t exec_argc;
+ pid_t pid;
+ int exit_status;
+
+ TAILQ_ENTRY(process_list_entry) entries;
+};
+
+typedef void (*process_list_notify_fn_t) (enum process_list_notify_reason reason,
+ const struct process_list_entry *entry, void *user_data);
+
+struct process_list {
+ int use_execvp;
+ size_t max_list_entries;
+ size_t allocated_list_entries;
+ process_list_notify_fn_t notify_fn;
+ void *notify_fn_user_data;
+
+ TAILQ_HEAD(, process_list_entry) active_list;
+ TAILQ_HEAD(, process_list_entry) to_kill_list;
+};
+
+
+extern void process_list_init(struct process_list *plist,
+ size_t max_list_entries, int use_execvp, process_list_notify_fn_t notify_fn,
+ void *notify_fn_user_data);
+
+extern struct process_list_entry *process_list_add(struct process_list *plist,
+ const char *name, const char *command);
+
+extern void process_list_free(struct process_list *plist);
+
+extern int process_list_exec_initialized(struct process_list *plist);
+
+extern int process_list_waitpid(struct process_list *plist);
+
+extern size_t process_list_get_no_running(struct process_list *plist);
+
+extern int process_list_get_summary_result(struct process_list *plist);
+
+extern int process_list_get_summary_result_short(
+ struct process_list *plist);
+
+extern void process_list_move_active_entries_to_kill_list(
+ struct process_list *plist);
+
+extern int process_list_process_kill_list(struct process_list *plist);
+
+extern size_t process_list_get_kill_list_items(struct process_list *plist);
+
+extern int process_list_killall(struct process_list *plist,
+ uint32_t timeout);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PROCESS_LIST_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "dynar.h"
+#include "dynar-getopt-lex.h"
+#include "dynar-str.h"
+#include "qdevice-config.h"
+#include "qnet-config.h"
+#include "qdevice-advanced-settings.h"
+#include "utils.h"
+
+int
+qdevice_advanced_settings_init(struct qdevice_advanced_settings *settings)
+{
+
+ memset(settings, 0, sizeof(*settings));
+ if ((settings->lock_file = strdup(QDEVICE_DEFAULT_LOCK_FILE)) == NULL) {
+ return (-1);
+ }
+ if ((settings->local_socket_file = strdup(QDEVICE_DEFAULT_LOCAL_SOCKET_FILE)) == NULL) {
+ return (-1);
+ }
+ settings->local_socket_backlog = QDEVICE_DEFAULT_LOCAL_SOCKET_BACKLOG;
+ settings->max_cs_try_again = QDEVICE_DEFAULT_MAX_CS_TRY_AGAIN;
+ if ((settings->votequorum_device_name = strdup(QDEVICE_DEFAULT_VOTEQUORUM_DEVICE_NAME)) == NULL) {
+ return (-1);
+ }
+ settings->ipc_max_clients = QDEVICE_DEFAULT_IPC_MAX_CLIENTS;
+ settings->ipc_max_receive_size = QDEVICE_DEFAULT_IPC_MAX_RECEIVE_SIZE;
+ settings->ipc_max_send_size = QDEVICE_DEFAULT_IPC_MAX_SEND_SIZE;
+
+ settings->heuristics_ipc_max_send_buffers = QDEVICE_DEFAULT_HEURISTICS_IPC_MAX_SEND_BUFFERS;
+ settings->heuristics_ipc_max_send_receive_size = QDEVICE_DEFAULT_HEURISTICS_IPC_MAX_SEND_RECEIVE_SIZE;
+
+ settings->heuristics_min_timeout = QDEVICE_DEFAULT_HEURISTICS_MIN_TIMEOUT;
+ settings->heuristics_max_timeout = QDEVICE_DEFAULT_HEURISTICS_MAX_TIMEOUT;
+ settings->heuristics_min_interval = QDEVICE_DEFAULT_HEURISTICS_MIN_INTERVAL;
+ settings->heuristics_max_interval = QDEVICE_DEFAULT_HEURISTICS_MAX_INTERVAL;
+
+ settings->heuristics_max_execs = QDEVICE_DEFAULT_HEURISTICS_MAX_EXECS;
+
+ settings->heuristics_use_execvp = QDEVICE_DEFAULT_HEURISTICS_USE_EXECVP;
+ settings->heuristics_max_processes = QDEVICE_DEFAULT_HEURISTICS_MAX_PROCESSES;
+ settings->heuristics_kill_list_interval = QDEVICE_DEFAULT_HEURISTICS_KILL_LIST_INTERVAL;
+
+ if ((settings->net_nss_db_dir = strdup(QDEVICE_NET_DEFAULT_NSS_DB_DIR)) == NULL) {
+ return (-1);
+ }
+ settings->net_initial_msg_receive_size = QDEVICE_NET_DEFAULT_INITIAL_MSG_RECEIVE_SIZE;
+ settings->net_initial_msg_send_size = QDEVICE_NET_DEFAULT_INITIAL_MSG_SEND_SIZE;
+ settings->net_min_msg_send_size = QDEVICE_NET_DEFAULT_MIN_MSG_SEND_SIZE;
+ settings->net_max_msg_receive_size = QDEVICE_NET_DEFAULT_MAX_MSG_RECEIVE_SIZE;
+ settings->net_max_send_buffers = QDEVICE_NET_DEFAULT_MAX_SEND_BUFFERS;
+ if ((settings->net_nss_qnetd_cn = strdup(QDEVICE_NET_DEFAULT_NSS_QNETD_CN)) == NULL) {
+ return (-1);
+ }
+ if ((settings->net_nss_client_cert_nickname =
+ strdup(QDEVICE_NET_DEFAULT_NSS_CLIENT_CERT_NICKNAME)) == NULL) {
+ return (-1);
+ }
+ settings->net_heartbeat_interval_min = QDEVICE_NET_DEFAULT_HEARTBEAT_INTERVAL_MIN;
+ settings->net_heartbeat_interval_max = QDEVICE_NET_DEFAULT_HEARTBEAT_INTERVAL_MAX;
+ settings->net_min_connect_timeout = QDEVICE_NET_DEFAULT_MIN_CONNECT_TIMEOUT;
+ settings->net_max_connect_timeout = QDEVICE_NET_DEFAULT_MAX_CONNECT_TIMEOUT;
+ settings->net_test_algorithm_enabled = QDEVICE_NET_DEFAULT_TEST_ALGORITHM_ENABLED;
+
+ settings->master_wins = QDEVICE_ADVANCED_SETTINGS_MASTER_WINS_MODEL;
+
+ return (0);
+}
+
+void
+qdevice_advanced_settings_destroy(struct qdevice_advanced_settings *settings)
+{
+
+ free(settings->local_socket_file);
+ free(settings->lock_file);
+ free(settings->votequorum_device_name);
+ free(settings->net_nss_db_dir);
+ free(settings->net_nss_qnetd_cn);
+ free(settings->net_nss_client_cert_nickname);
+}
+
+/*
+ * 0 - No error
+ * -1 - Unknown option
+ * -2 - Incorrect value
+ */
+int
+qdevice_advanced_settings_set(struct qdevice_advanced_settings *settings,
+ const char *option, const char *value)
+{
+ long long int tmpll;
+ char *ep;
+
+ if (strcasecmp(option, "lock_file") == 0) {
+ free(settings->lock_file);
+
+ if ((settings->lock_file = strdup(value)) == NULL) {
+ return (-1);
+ }
+ } else if (strcasecmp(option, "local_socket_file") == 0) {
+ free(settings->local_socket_file);
+
+ if ((settings->local_socket_file = strdup(value)) == NULL) {
+ return (-1);
+ }
+ } else if (strcasecmp(option, "local_socket_backlog") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_MIN_LOCAL_SOCKET_BACKLOG || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->local_socket_backlog = (int)tmpll;
+ } else if (strcasecmp(option, "max_cs_try_again") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_MIN_MAX_CS_TRY_AGAIN || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->max_cs_try_again = (int)tmpll;
+ } else if (strcasecmp(option, "votequorum_device_name") == 0) {
+ free(settings->votequorum_device_name);
+
+ if ((settings->votequorum_device_name = strdup(value)) == NULL) {
+ return (-1);
+ }
+ } else if (strcasecmp(option, "ipc_max_clients") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_MIN_IPC_MAX_CLIENTS || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->ipc_max_clients = (size_t)tmpll;
+ } else if (strcasecmp(option, "ipc_max_receive_size") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_MIN_IPC_RECEIVE_SEND_SIZE || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->ipc_max_receive_size = (size_t)tmpll;
+ } else if (strcasecmp(option, "ipc_max_send_size") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_MIN_IPC_RECEIVE_SEND_SIZE || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->ipc_max_send_size = (size_t)tmpll;
+ } else if (strcasecmp(option, "heuristics_ipc_max_send_buffers") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_MIN_HEURISTICS_IPC_MAX_SEND_BUFFERS || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->heuristics_ipc_max_send_buffers = (size_t)tmpll;
+ } else if (strcasecmp(option, "heuristics_ipc_max_send_receive_size") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_MIN_HEURISTICS_IPC_MAX_SEND_RECEIVE_SIZE || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->heuristics_ipc_max_send_receive_size = (size_t)tmpll;
+ } else if (strcasecmp(option, "heuristics_min_timeout") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_MIN_HEURISTICS_TIMEOUT || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->heuristics_min_timeout = (uint32_t)tmpll;
+ } else if (strcasecmp(option, "heuristics_max_timeout") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_MIN_HEURISTICS_TIMEOUT || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->heuristics_max_timeout = (uint32_t)tmpll;
+ } else if (strcasecmp(option, "heuristics_min_interval") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_MIN_HEURISTICS_INTERVAL || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->heuristics_min_interval = (uint32_t)tmpll;
+ } else if (strcasecmp(option, "heuristics_max_interval") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_MIN_HEURISTICS_INTERVAL || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->heuristics_max_interval = (uint32_t)tmpll;
+ } else if (strcasecmp(option, "heuristics_max_execs") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_MIN_HEURISTICS_MAX_EXECS || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->heuristics_max_execs = (size_t)tmpll;
+ } else if (strcasecmp(option, "heuristics_use_execvp") == 0) {
+ if ((tmpll = utils_parse_bool_str(value)) == -1) {
+ return (-2);
+ }
+
+ settings->heuristics_use_execvp = (uint8_t)tmpll;
+ } else if (strcasecmp(option, "heuristics_max_processes") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_MIN_HEURISTICS_MAX_PROCESSES || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->heuristics_max_processes = (size_t)tmpll;
+ } else if (strcasecmp(option, "heuristics_kill_list_interval") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_MIN_HEURISTICS_KILL_LIST_INTERVAL || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->heuristics_kill_list_interval = (uint32_t)tmpll;
+ } else if (strcasecmp(option, "net_nss_db_dir") == 0) {
+ free(settings->net_nss_db_dir);
+
+ if ((settings->net_nss_db_dir = strdup(value)) == NULL) {
+ return (-1);
+ }
+ } else if (strcasecmp(option, "net_initial_msg_receive_size") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_NET_MIN_MSG_RECEIVE_SEND_SIZE || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->net_initial_msg_receive_size = (size_t)tmpll;
+ } else if (strcasecmp(option, "net_initial_msg_send_size") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_NET_MIN_MSG_RECEIVE_SEND_SIZE || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->net_initial_msg_send_size = (size_t)tmpll;
+ } else if (strcasecmp(option, "net_min_msg_send_size") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_NET_MIN_MSG_RECEIVE_SEND_SIZE || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->net_min_msg_send_size = (size_t)tmpll;
+ } else if (strcasecmp(option, "net_max_msg_receive_size") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_NET_MIN_MSG_RECEIVE_SEND_SIZE || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->net_max_msg_receive_size = (size_t)tmpll;
+ } else if (strcasecmp(option, "net_max_send_buffers") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_NET_MIN_MAX_SEND_BUFFERS || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->net_max_send_buffers = (size_t)tmpll;
+ } else if (strcasecmp(option, "net_nss_qnetd_cn") == 0) {
+ free(settings->net_nss_qnetd_cn);
+
+ if ((settings->net_nss_qnetd_cn = strdup(value)) == NULL) {
+ return (-1);
+ }
+ } else if (strcasecmp(option, "net_nss_client_cert_nickname") == 0) {
+ free(settings->net_nss_client_cert_nickname);
+
+ if ((settings->net_nss_client_cert_nickname = strdup(value)) == NULL) {
+ return (-1);
+ }
+ } else if (strcasecmp(option, "net_heartbeat_interval_min") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_NET_MIN_HEARTBEAT_INTERVAL || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->net_heartbeat_interval_min = (uint32_t)tmpll;
+ } else if (strcasecmp(option, "net_heartbeat_interval_max") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_NET_MIN_HEARTBEAT_INTERVAL || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->net_heartbeat_interval_max = (uint32_t)tmpll;
+ } else if (strcasecmp(option, "net_min_connect_timeout") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_NET_MIN_CONNECT_TIMEOUT || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->net_min_connect_timeout = (uint32_t)tmpll;
+ } else if (strcasecmp(option, "net_max_connect_timeout") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QDEVICE_NET_MIN_CONNECT_TIMEOUT || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->net_max_connect_timeout = (uint32_t)tmpll;
+ } else if (strcasecmp(option, "net_test_algorithm_enabled") == 0) {
+ if ((tmpll = utils_parse_bool_str(value)) == -1) {
+ return (-2);
+ }
+
+ settings->net_test_algorithm_enabled = (uint8_t)tmpll;
+ } else if (strcasecmp(option, "master_wins") == 0) {
+ tmpll = utils_parse_bool_str(value);
+
+ if (tmpll == 0) {
+ settings->master_wins = QDEVICE_ADVANCED_SETTINGS_MASTER_WINS_FORCE_OFF;
+ } else if (tmpll == 1) {
+ settings->master_wins = QDEVICE_ADVANCED_SETTINGS_MASTER_WINS_FORCE_ON;
+ } else if (strcasecmp(value, "model") == 0) {
+ settings->master_wins = QDEVICE_ADVANCED_SETTINGS_MASTER_WINS_MODEL;
+ } else {
+ return (-2);
+ }
+ } else {
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_ADVANCED_SETTINGS_H_
+#define _QDEVICE_ADVANCED_SETTINGS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum qdevice_advanced_settings_master_wins {
+ QDEVICE_ADVANCED_SETTINGS_MASTER_WINS_MODEL,
+ QDEVICE_ADVANCED_SETTINGS_MASTER_WINS_FORCE_ON,
+ QDEVICE_ADVANCED_SETTINGS_MASTER_WINS_FORCE_OFF,
+};
+
+struct qdevice_advanced_settings {
+ char *lock_file;
+ char *local_socket_file;
+ int local_socket_backlog;
+ int max_cs_try_again;
+ char *votequorum_device_name;
+ size_t ipc_max_clients;
+ size_t ipc_max_send_size;
+ size_t ipc_max_receive_size;
+ enum qdevice_advanced_settings_master_wins master_wins;
+ size_t heuristics_ipc_max_send_buffers;
+ size_t heuristics_ipc_max_send_receive_size;
+ uint32_t heuristics_min_timeout;
+ uint32_t heuristics_max_timeout;
+ uint32_t heuristics_min_interval;
+ uint32_t heuristics_max_interval;
+ size_t heuristics_max_execs;
+ int heuristics_use_execvp;
+ size_t heuristics_max_processes;
+ uint32_t heuristics_kill_list_interval;
+
+ /*
+ * Related to model NET
+ */
+ char *net_nss_db_dir;
+ size_t net_initial_msg_receive_size;
+ size_t net_initial_msg_send_size;
+ size_t net_min_msg_send_size;
+ size_t net_max_msg_receive_size;
+ size_t net_max_send_buffers;
+ char *net_nss_qnetd_cn;
+ char *net_nss_client_cert_nickname;
+ uint32_t net_heartbeat_interval_min;
+ uint32_t net_heartbeat_interval_max;
+ uint32_t net_min_connect_timeout;
+ uint32_t net_max_connect_timeout;
+ uint8_t net_test_algorithm_enabled;
+};
+
+extern int qdevice_advanced_settings_init(struct qdevice_advanced_settings *settings);
+
+extern int qdevice_advanced_settings_set(struct qdevice_advanced_settings *settings,
+ const char *option, const char *value);
+
+extern void qdevice_advanced_settings_destroy(struct qdevice_advanced_settings *settings);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_ADVANCED_SETTINGS_H_ */
--- /dev/null
+/*
+ * 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 <sys/types.h>
+#include <sys/socket.h>
+
+#include <err.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <netdb.h>
+
+#include "qdevice-config.h"
+#include "qdevice-cmap.h"
+#include "qdevice-log.h"
+#include "qdevice-log-debug.h"
+#include "qdevice-model.h"
+#include "utils.h"
+
+static uint32_t
+qdevice_cmap_autogenerate_node_id(const char *addr, int clear_node_high_byte)
+{
+ struct addrinfo *ainfo;
+ struct addrinfo ahints;
+ int ret, i;
+
+ memset(&ahints, 0, sizeof(ahints));
+ ahints.ai_socktype = SOCK_DGRAM;
+ ahints.ai_protocol = IPPROTO_UDP;
+ /*
+ * Hardcoded AF_INET because autogenerated nodeid is valid only for ipv4
+ */
+ ahints.ai_family = AF_INET;
+
+ ret = getaddrinfo(addr, NULL, &ahints, &ainfo);
+ if (ret != 0)
+ return (0);
+
+ if (ainfo->ai_family != AF_INET) {
+
+ freeaddrinfo(ainfo);
+
+ return (0);
+ }
+
+ memcpy(&i, &((struct sockaddr_in *)ainfo->ai_addr)->sin_addr, sizeof(struct in_addr));
+ freeaddrinfo(ainfo);
+
+ ret = htonl(i);
+
+ if (clear_node_high_byte) {
+ ret &= 0x7FFFFFFF;
+ }
+
+ return (ret);
+}
+
+int
+qdevice_cmap_get_nodelist(cmap_handle_t cmap_handle, struct node_list *list)
+{
+ cs_error_t cs_err;
+ cmap_iter_handle_t iter_handle;
+ char key_name[CMAP_KEYNAME_MAXLEN + 1];
+ char tmp_key[CMAP_KEYNAME_MAXLEN + 1];
+ int res;
+ int ret_value;
+ unsigned int node_pos;
+ uint32_t node_id;
+ uint32_t data_center_id;
+ char *tmp_str;
+ char *addr0_str;
+ int clear_node_high_byte;
+
+ ret_value = 0;
+
+ node_list_init(list);
+
+ cs_err = cmap_iter_init(cmap_handle, "nodelist.node.", &iter_handle);
+ if (cs_err != CS_OK) {
+ return (-1);
+ }
+
+ while ((cs_err = cmap_iter_next(cmap_handle, iter_handle, key_name, NULL, NULL)) == CS_OK) {
+ res = sscanf(key_name, "nodelist.node.%u.%s", &node_pos, tmp_key);
+ if (res != 2) {
+ continue;
+ }
+
+ if (strcmp(tmp_key, "ring0_addr") != 0) {
+ continue;
+ }
+
+ snprintf(tmp_key, CMAP_KEYNAME_MAXLEN, "nodelist.node.%u.nodeid", node_pos);
+ cs_err = cmap_get_uint32(cmap_handle, tmp_key, &node_id);
+
+ if (cs_err == CS_ERR_NOT_EXIST) {
+ /*
+ * Nodeid doesn't exists -> autogenerate node id
+ */
+ clear_node_high_byte = 0;
+
+ if (cmap_get_string(cmap_handle, "totem.clear_node_high_bit",
+ &tmp_str) == CS_OK) {
+ if (strcmp (tmp_str, "yes") == 0) {
+ clear_node_high_byte = 1;
+ }
+
+ free(tmp_str);
+ }
+
+ if (cmap_get_string(cmap_handle, key_name, &addr0_str) != CS_OK) {
+ return (-1);
+ }
+
+ node_id = qdevice_cmap_autogenerate_node_id(addr0_str,
+ clear_node_high_byte);
+
+ free(addr0_str);
+ } else if (cs_err != CS_OK) {
+ ret_value = -1;
+
+ goto iter_finalize;
+ }
+
+ snprintf(tmp_key, CMAP_KEYNAME_MAXLEN, "nodelist.node.%u.datacenterid", node_pos);
+ if (cmap_get_uint32(cmap_handle, tmp_key, &data_center_id) != CS_OK) {
+ data_center_id = 0;
+ }
+
+ if (node_list_add(list, node_id, data_center_id, TLV_NODE_STATE_NOT_SET) == NULL) {
+ ret_value = -1;
+
+ goto iter_finalize;
+ }
+ }
+
+iter_finalize:
+ cmap_iter_finalize(cmap_handle, iter_handle);
+
+ if (ret_value != 0) {
+ node_list_free(list);
+ }
+
+ return (ret_value);
+}
+
+int
+qdevice_cmap_get_config_version(cmap_handle_t cmap_handle, uint64_t *config_version)
+{
+ int res;
+
+ if (cmap_get_uint64(cmap_handle, "totem.config_version", config_version) == CS_OK) {
+ res = 0;
+ } else {
+ *config_version = 0;
+ res = -1;
+ }
+
+ return (res);
+}
+
+int
+qdevice_cmap_store_config_node_list(struct qdevice_instance *instance)
+{
+ int res;
+
+ node_list_free(&instance->config_node_list);
+
+ if (qdevice_cmap_get_nodelist(instance->cmap_handle, &instance->config_node_list) != 0) {
+ qdevice_log(LOG_ERR, "Can't get configuration node list.");
+
+ return (-1);
+ }
+
+ res = qdevice_cmap_get_config_version(instance->cmap_handle, &instance->config_node_list_version);
+ instance->config_node_list_version_set = (res == 0);
+
+ return (0);
+}
+
+void
+qdevice_cmap_init(struct qdevice_instance *instance)
+{
+ cs_error_t res;
+ int no_retries;
+
+ no_retries = 0;
+
+ while ((res = cmap_initialize(&instance->cmap_handle)) == CS_ERR_TRY_AGAIN &&
+ no_retries++ < instance->advanced_settings->max_cs_try_again) {
+ (void)poll(NULL, 0, 1000);
+ }
+
+ if (res != CS_OK) {
+ errx(1, "Failed to initialize the cmap API. Error %s", cs_strerror(res));
+ }
+
+ if ((res = cmap_context_set(instance->cmap_handle, (void *)instance)) != CS_OK) {
+ errx(1, "Can't set cmap context. Error %s", cs_strerror(res));
+ }
+
+ cmap_fd_get(instance->cmap_handle, &instance->cmap_poll_fd);
+}
+
+static void
+qdevice_cmap_node_list_event(struct qdevice_instance *instance)
+{
+ struct node_list nlist;
+ int config_version_set;
+ uint64_t config_version;
+
+ qdevice_log(LOG_DEBUG, "Node list configuration possibly changed");
+ if (qdevice_cmap_get_nodelist(instance->cmap_handle, &nlist) != 0) {
+ qdevice_log(LOG_ERR, "Can't get configuration node list.");
+
+ if (qdevice_model_get_config_node_list_failed(instance) != 0) {
+ qdevice_log(LOG_DEBUG, "qdevice_model_get_config_node_list_failed returned error -> exit");
+ exit(2);
+ }
+
+ return ;
+ }
+
+ config_version_set = (qdevice_cmap_get_config_version(instance->cmap_handle,
+ &config_version) == 0);
+
+ if (node_list_eq(&instance->config_node_list, &nlist)) {
+ return ;
+ }
+
+ qdevice_log(LOG_DEBUG, "Node list changed");
+ if (config_version_set) {
+ qdevice_log(LOG_DEBUG, " config_version = "UTILS_PRI_CONFIG_VERSION, config_version);
+ }
+ qdevice_log_debug_dump_node_list(&nlist);
+
+ if (qdevice_model_config_node_list_changed(instance, &nlist,
+ config_version_set, config_version) != 0) {
+ qdevice_log(LOG_DEBUG, "qdevice_model_config_node_list_changed returned error -> exit");
+ exit(2);
+ }
+
+ node_list_free(&instance->config_node_list);
+ if (node_list_clone(&instance->config_node_list, &nlist) != 0) {
+ qdevice_log(LOG_ERR, "Can't allocate instance->config_node_list clone");
+
+ node_list_free(&nlist);
+
+ if (qdevice_model_get_config_node_list_failed(instance) != 0) {
+ qdevice_log(LOG_DEBUG, "qdevice_model_get_config_node_list_failed returned error -> exit");
+ exit(2);
+ }
+
+ return ;
+ }
+
+ instance->config_node_list_version_set = config_version_set;
+
+ if (config_version_set) {
+ instance->config_node_list_version = config_version;
+ }
+}
+
+static void
+qdevice_cmap_logging_event(struct qdevice_instance *instance)
+{
+
+ qdevice_log(LOG_DEBUG, "Logging configuration possibly changed");
+ qdevice_log_configure(instance);
+}
+
+static void
+qdevice_cmap_heuristics_event(struct qdevice_instance *instance)
+{
+
+ qdevice_log(LOG_DEBUG, "Heuristics configuration possibly changed");
+ if (qdevice_instance_configure_from_cmap_heuristics(instance) != 0) {
+ qdevice_log(LOG_DEBUG, "qdevice_instance_configure_from_cmap_heuristics returned error -> exit");
+ exit(2);
+ }
+}
+
+static void
+qdevice_cmap_reload_cb(cmap_handle_t cmap_handle, cmap_track_handle_t cmap_track_handle,
+ int32_t event, const char *key_name,
+ struct cmap_notify_value new_value, struct cmap_notify_value old_value,
+ void *user_data)
+{
+ cs_error_t cs_res;
+ uint8_t reload;
+ struct qdevice_instance *instance;
+ const char *node_list_prefix_str;
+ const char *logging_prefix_str;
+ const char *heuristics_prefix_str;
+ struct qdevice_cmap_change_events events;
+
+ memset(&events, 0, sizeof(events));
+ node_list_prefix_str = "nodelist.";
+ logging_prefix_str = "logging.";
+ heuristics_prefix_str = "quorum.device.heuristics.";
+
+ if (cmap_context_get(cmap_handle, (const void **)&instance) != CS_OK) {
+ qdevice_log(LOG_ERR, "Fatal error. Can't get cmap context");
+ exit(1);
+ }
+
+ /*
+ * Wait for full reload
+ */
+ if (strcmp(key_name, "config.totemconfig_reload_in_progress") == 0 &&
+ new_value.type == CMAP_VALUETYPE_UINT8 && new_value.len == sizeof(reload)) {
+ reload = 1;
+ if (memcmp(new_value.data, &reload, sizeof(reload)) == 0) {
+ /*
+ * Ignore nodelist changes
+ */
+ instance->cmap_reload_in_progress = 1;
+ return ;
+ } else {
+ instance->cmap_reload_in_progress = 0;
+ events.node_list = 1;
+ events.logging = 1;
+ events.heuristics = 1;
+ }
+ }
+
+ if (instance->cmap_reload_in_progress) {
+ return ;
+ }
+
+ if (((cs_res = cmap_get_uint8(cmap_handle, "config.totemconfig_reload_in_progress",
+ &reload)) == CS_OK) && reload == 1) {
+ return ;
+ }
+
+ if (strncmp(key_name, node_list_prefix_str, strlen(node_list_prefix_str)) == 0) {
+ events.node_list = 1;
+ }
+
+ if (strncmp(key_name, logging_prefix_str, strlen(logging_prefix_str)) == 0) {
+ events.logging = 1;
+ }
+
+ if (strncmp(key_name, heuristics_prefix_str, strlen(heuristics_prefix_str)) == 0) {
+ events.heuristics = 1;
+ }
+
+ if (events.logging) {
+ qdevice_cmap_logging_event(instance);
+ }
+
+ if (events.node_list) {
+ qdevice_cmap_node_list_event(instance);
+ }
+
+ if (events.heuristics) {
+ qdevice_cmap_heuristics_event(instance);
+ }
+
+ /*
+ * Inform model about change
+ */
+ if (qdevice_model_cmap_changed(instance, &events) != 0) {
+ qdevice_log(LOG_DEBUG, "qdevice_model_cmap_changed returned error -> exit");
+ exit(2);
+ }
+}
+
+int
+qdevice_cmap_add_track(struct qdevice_instance *instance)
+{
+ cs_error_t res;
+
+ res = cmap_track_add(instance->cmap_handle, "config.totemconfig_reload_in_progress",
+ CMAP_TRACK_ADD | CMAP_TRACK_MODIFY, qdevice_cmap_reload_cb,
+ NULL, &instance->cmap_reload_track_handle);
+
+ if (res != CS_OK) {
+ qdevice_log(LOG_ERR, "Can't initialize cmap totemconfig_reload_in_progress tracking");
+ return (-1);
+ }
+
+ res = cmap_track_add(instance->cmap_handle, "nodelist.",
+ CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX,
+ qdevice_cmap_reload_cb,
+ NULL, &instance->cmap_nodelist_track_handle);
+
+ if (res != CS_OK) {
+ qdevice_log(LOG_ERR, "Can't initialize cmap nodelist tracking");
+ return (-1);
+ }
+
+ res = cmap_track_add(instance->cmap_handle, "logging.",
+ CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX,
+ qdevice_cmap_reload_cb,
+ NULL, &instance->cmap_logging_track_handle);
+
+ if (res != CS_OK) {
+ qdevice_log(LOG_ERR, "Can't initialize logging tracking");
+ return (-1);
+ }
+
+ res = cmap_track_add(instance->cmap_handle, "quorum.device.heuristics.",
+ CMAP_TRACK_ADD | CMAP_TRACK_DELETE | CMAP_TRACK_MODIFY | CMAP_TRACK_PREFIX,
+ qdevice_cmap_reload_cb,
+ NULL, &instance->cmap_heuristics_track_handle);
+
+ if (res != CS_OK) {
+ qdevice_log(LOG_ERR, "Can't initialize logging tracking");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qdevice_cmap_del_track(struct qdevice_instance *instance)
+{
+ cs_error_t res;
+
+ res = cmap_track_delete(instance->cmap_handle, instance->cmap_reload_track_handle);
+ if (res != CS_OK) {
+ qdevice_log(LOG_WARNING, "Can't delete cmap totemconfig_reload_in_progress tracking");
+ }
+
+ res = cmap_track_delete(instance->cmap_handle, instance->cmap_nodelist_track_handle);
+ if (res != CS_OK) {
+ qdevice_log(LOG_WARNING, "Can't delete cmap nodelist tracking");
+ }
+
+ res = cmap_track_delete(instance->cmap_handle, instance->cmap_logging_track_handle);
+ if (res != CS_OK) {
+ qdevice_log(LOG_WARNING, "Can't delete cmap logging tracking");
+ }
+
+ res = cmap_track_delete(instance->cmap_handle, instance->cmap_heuristics_track_handle);
+ if (res != CS_OK) {
+ qdevice_log(LOG_WARNING, "Can't delete cmap heuristics tracking");
+ }
+
+ return (0);
+}
+
+void
+qdevice_cmap_destroy(struct qdevice_instance *instance)
+{
+ cs_error_t res;
+
+ res = cmap_finalize(instance->cmap_handle);
+
+ if (res != CS_OK) {
+ qdevice_log(LOG_WARNING, "Can't finalize cmap. Error %s", cs_strerror(res));
+ }
+}
+
+int
+qdevice_cmap_dispatch(struct qdevice_instance *instance)
+{
+ cs_error_t res;
+
+ /*
+ * dispatch can block if corosync is during sync phase
+ */
+ if (instance->sync_in_progress) {
+ return (0);
+ }
+
+ res = cmap_dispatch(instance->cmap_handle, CS_DISPATCH_ALL);
+
+ if (res != CS_OK && res != CS_ERR_TRY_AGAIN) {
+ qdevice_log(LOG_ERR, "Can't dispatch cmap messages");
+
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QDEVICE_CMAP_H_
+#define _QDEVICE_CMAP_H_
+
+#include <corosync/cmap.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include "node-list.h"
+#include "qdevice-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qdevice_cmap_change_events {
+ unsigned int logging : 1;
+ unsigned int node_list : 1;
+ unsigned int heuristics : 1;
+};
+
+extern int qdevice_cmap_get_nodelist(cmap_handle_t cmap_handle,
+ struct node_list *list);
+
+extern int qdevice_cmap_get_config_version(cmap_handle_t cmap_handle,
+ uint64_t *config_version);
+
+extern void qdevice_cmap_init(struct qdevice_instance *instance);
+
+extern int qdevice_cmap_add_track(struct qdevice_instance *instance);
+
+extern int qdevice_cmap_del_track(struct qdevice_instance *instance);
+
+extern void qdevice_cmap_destroy(struct qdevice_instance *instance);
+
+extern int qdevice_cmap_dispatch(struct qdevice_instance *instance);
+
+extern int qdevice_cmap_store_config_node_list(struct qdevice_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_CMAP_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_CONFIG_H_
+#define _QDEVICE_CONFIG_H_
+
+#include <config.h>
+
+#include <qb/qbdefs.h>
+#include <qb/qblog.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <string.h>
+#include "qdevice-heuristics-mode.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * There are "hardcoded" defines for qdevice. It's not so good
+ * idea to change them as long as you are not 100% sure what you are doing. Also
+ * most of them can be changed in CLI via advanced_settings (-S).
+ */
+#define QDEVICE_DEFAULT_LOCK_FILE LOCALSTATEDIR"/run/corosync-qdevice/corosync-qdevice.pid"
+#define QDEVICE_DEFAULT_LOCAL_SOCKET_FILE LOCALSTATEDIR"/run/corosync-qdevice/corosync-qdevice.sock"
+#define QDEVICE_DEFAULT_LOCAL_SOCKET_BACKLOG 10
+#define QDEVICE_MIN_LOCAL_SOCKET_BACKLOG 1
+
+#define QDEVICE_DEFAULT_MAX_CS_TRY_AGAIN 10
+#define QDEVICE_MIN_MAX_CS_TRY_AGAIN 1
+
+#define QDEVICE_PROGRAM_NAME "corosync-qdevice"
+#define QDEVICE_LOG_SUBSYS "QDEVICE"
+#define QDEVICE_LOG_DEFAULT_TO_STDERR 1
+#define QDEVICE_LOG_DEFAULT_TO_SYSLOG 1
+#define QDEVICE_LOG_DEFAULT_TO_LOGFILE 0
+#define QDEVICE_LOG_DEFAULT_SYSLOG_FACILITY LOG_DAEMON
+#define QDEVICE_LOG_DEFAULT_SYSLOG_PRIORITY LOG_INFO
+#define QDEVICE_LOG_DEFAULT_DEBUG 0
+#define QDEVICE_LOG_DEFAULT_FILELINE 0
+#define QDEVICE_LOG_DEFAULT_TIMESTAMP 0
+#define QDEVICE_LOG_DEFAULT_FUNCTION_NAME 0
+
+#define QDEVICE_DEFAULT_VOTEQUORUM_DEVICE_NAME "Qdevice"
+
+#define QDEVICE_DEFAULT_IPC_MAX_CLIENTS 10
+#define QDEVICE_MIN_IPC_MAX_CLIENTS 0
+#define QDEVICE_DEFAULT_IPC_MAX_RECEIVE_SIZE (4*1024)
+#define QDEVICE_DEFAULT_IPC_MAX_SEND_SIZE (64*1024)
+#define QDEVICE_MIN_IPC_RECEIVE_SEND_SIZE 1024
+
+#define QDEVICE_DEFAULT_HEURISTICS_IPC_MAX_SEND_BUFFERS 128
+#define QDEVICE_MIN_HEURISTICS_IPC_MAX_SEND_BUFFERS 10
+#define QDEVICE_DEFAULT_HEURISTICS_IPC_MAX_SEND_RECEIVE_SIZE (4 * 1024)
+#define QDEVICE_MIN_HEURISTICS_IPC_MAX_SEND_RECEIVE_SIZE 1024
+
+#define QDEVICE_DEFAULT_HEURISTICS_MIN_TIMEOUT (1 * 1000)
+#define QDEVICE_DEFAULT_HEURISTICS_MAX_TIMEOUT (2 * 60 * 1000)
+#define QDEVICE_MIN_HEURISTICS_TIMEOUT 250
+#define QDEVICE_DEFAULT_HEURISTICS_MIN_INTERVAL QDEVICE_DEFAULT_HEURISTICS_MIN_TIMEOUT
+#define QDEVICE_DEFAULT_HEURISTICS_MAX_INTERVAL (60 * 60 * 1000)
+#define QDEVICE_MIN_HEURISTICS_INTERVAL QDEVICE_MIN_HEURISTICS_TIMEOUT
+
+#define QDEVICE_DEFAULT_HEURISTICS_MODE QDEVICE_HEURISTICS_MODE_DISABLED
+
+#define QDEVICE_DEFAULT_HEURISTICS_MAX_EXECS 32
+#define QDEVICE_MIN_HEURISTICS_MAX_EXECS 1
+
+#define QDEVICE_DEFAULT_HEURISTICS_USE_EXECVP 0
+
+#define QDEVICE_DEFAULT_HEURISTICS_MAX_PROCESSES (QDEVICE_DEFAULT_HEURISTICS_MAX_EXECS * 5)
+#define QDEVICE_MIN_HEURISTICS_MAX_PROCESSES 1
+
+#define QDEVICE_DEFAULT_HEURISTICS_KILL_LIST_INTERVAL (5 * 1000)
+#define QDEVICE_MIN_HEURISTICS_KILL_LIST_INTERVAL QDEVICE_MIN_HEURISTICS_TIMEOUT
+
+#define QDEVICE_TOOL_PROGRAM_NAME "corosync-qdevice-tool"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_CONFIG_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_HEURISTICS_CMD_STR_H_
+#define _QDEVICE_HEURISTICS_CMD_STR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define QDEVICE_HEURISTICS_CMD_STR_EXEC_LIST_CLEAR "exec-list-clear"
+#define QDEVICE_HEURISTICS_CMD_STR_EXEC_LIST_ADD "exec-list-add"
+#define QDEVICE_HEURISTICS_CMD_STR_EXEC_LIST_ADD_SPACE \
+ QDEVICE_HEURISTICS_CMD_STR_EXEC_LIST_ADD " "
+#define QDEVICE_HEURISTICS_CMD_STR_EXEC "exec"
+#define QDEVICE_HEURISTICS_CMD_STR_EXEC_ADD_SPACE QDEVICE_HEURISTICS_CMD_STR_EXEC " "
+#define QDEVICE_HEURISTICS_CMD_STR_EXEC_RESULT "exec-result"
+#define QDEVICE_HEURISTICS_CMD_STR_EXEC_RESULT_ADD_SPACE \
+ QDEVICE_HEURISTICS_CMD_STR_EXEC_RESULT " "
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_HEURISTICS_CMD_STR_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <stdlib.h>
+
+#include "dynar.h"
+#include "dynar-str.h"
+#include "qdevice-heuristics-exec-result.h"
+#include "qdevice-heuristics-cmd.h"
+#include "qdevice-heuristics-cmd-str.h"
+#include "qdevice-heuristics-io.h"
+#include "qdevice-log.h"
+
+static int
+qdevice_heuristics_cmd_process_exec_result(struct qdevice_heuristics_instance *instance,
+ struct dynar *data)
+{
+ uint32_t seq_number;
+ char *str;
+ enum qdevice_heuristics_exec_result exec_result;
+
+ str = dynar_data(data);
+
+ if (sscanf(str, QDEVICE_HEURISTICS_CMD_STR_EXEC_RESULT_ADD_SPACE "%"PRIu32" %u", &seq_number,
+ &exec_result) != 2) {
+ qdevice_log(LOG_CRIT, "Can't parse exec result command (sscanf)");
+
+ return (-1);
+ }
+
+ qdevice_log(LOG_DEBUG,
+ "Received heuristics exec result command with seq_no \"%"PRIu32"\" and result \"%s\"", seq_number,
+ qdevice_heuristics_exec_result_to_str(exec_result));
+
+ if (!instance->waiting_for_result) {
+ qdevice_log(LOG_DEBUG, "Received exec result is not expected. Ignoring.");
+
+ return (0);
+ }
+
+ if (seq_number != instance->expected_reply_seq_number) {
+ qdevice_log(LOG_DEBUG, "Received heuristics exec result seq number %"PRIu32
+ " is not expected one (expected %"PRIu32"). Ignoring.", seq_number,
+ instance->expected_reply_seq_number);
+
+ return (0);
+ }
+
+ instance->waiting_for_result = 0;
+
+ if (qdevice_heuristics_result_notifier_notify(&instance->exec_result_notifier_list,
+ (void *)instance, seq_number, exec_result) != 0) {
+ qdevice_log(LOG_DEBUG, "qdevice_heuristics_result_notifier_notify returned non-zero result");
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * 1 - Line processed
+ * 0 - No line to process - everything processed
+ * -1 - Error
+ */
+static int
+qdevice_heuristics_cmd_process_one_line(struct qdevice_heuristics_instance *instance,
+ struct dynar *data)
+{
+ char *str;
+ size_t str_len;
+ size_t nl_pos;
+ size_t zi;
+
+ str = dynar_data(data);
+ str_len = dynar_size(data);
+
+ /*
+ * Find valid line
+ */
+ for (zi = 0; zi < str_len && str[zi] != '\r' && str[zi] != '\n'; zi++) ;
+
+ if (zi >= str_len) {
+ /*
+ * Command is not yet fully readed
+ */
+ return (0);
+ }
+
+ nl_pos = zi;
+
+ str[nl_pos] = '\0';
+
+ if (strncmp(str, QDEVICE_HEURISTICS_CMD_STR_EXEC_RESULT_ADD_SPACE,
+ strlen(QDEVICE_HEURISTICS_CMD_STR_EXEC_RESULT_ADD_SPACE)) == 0) {
+ if (qdevice_heuristics_cmd_process_exec_result(instance, data) != 0) {
+ return (-1);
+ }
+ } else {
+ qdevice_log(LOG_CRIT,
+ "Heuristics worker sent unknown command \"%s\"", str);
+
+ return (-1);
+ }
+
+ /*
+ * Find place where is begining of new "valid" line
+ */
+ for (zi = nl_pos + 1; zi < str_len && (str[zi] == '\0' || str[zi] == '\n' || str[zi] == '\r'); zi++) ;
+
+ memmove(str, str + zi, str_len - zi);
+ if (dynar_set_size(data, str_len - zi) == -1) {
+ qdevice_log(LOG_CRIT,
+ "qdevice_heuristics_cmd_process_one_line: Can't set dynar size");
+ return (-1);
+ }
+
+ return (1);
+}
+
+/*
+ * 0 - No error
+ * -1 - Error
+ */
+static int
+qdevice_heuristics_cmd_process(struct qdevice_heuristics_instance *instance)
+{
+ int res;
+
+ while ((res =
+ qdevice_heuristics_cmd_process_one_line(instance, &instance->cmd_in_buffer)) == 1) ;
+
+ return (res);
+}
+
+/*
+ * 0 - No error
+ * 1 - Error
+ */
+int
+qdevice_heuristics_cmd_read_from_pipe(struct qdevice_heuristics_instance *instance)
+{
+ int res;
+ int ret;
+
+ res = qdevice_heuristics_io_read(instance->pipe_cmd_recv, &instance->cmd_in_buffer);
+
+ ret = 0;
+
+ switch (res) {
+ case 0:
+ /*
+ * Partial read
+ */
+ break;
+ case -1:
+ qdevice_log(LOG_ERR, "Lost connection with heuristics worker");
+ ret = -1;
+ break;
+ case -2:
+ qdevice_log(LOG_ERR, "Heuristics worker sent too long cmd.");
+ ret = -1;
+ break;
+ case -3:
+ qdevice_log(LOG_ERR, "Unhandled error when reading from heuristics worker cmd fd");
+ ret = -1;
+ break;
+ case 1:
+ /*
+ * At least one cmd line received
+ */
+ ret = qdevice_heuristics_cmd_process(instance);
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * 0 - No error
+ * 1 - Error
+ */
+int
+qdevice_heuristics_cmd_write(struct qdevice_heuristics_instance *instance)
+{
+ struct send_buffer_list_entry *send_buffer;
+ int res;
+
+ send_buffer = send_buffer_list_get_active(&instance->cmd_out_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_CRIT, "send_buffer_list_get_active in qdevice_heuristics_cmd_write returned NULL");
+
+ return (-1);
+ }
+
+ res = qdevice_heuristics_io_write(instance->pipe_cmd_send, &send_buffer->buffer,
+ &send_buffer->msg_already_sent_bytes);
+
+ if (res == 1) {
+ send_buffer_list_delete(&instance->cmd_out_buffer_list, send_buffer);
+ }
+
+ if (res == -1) {
+ qdevice_log(LOG_CRIT, "qdevice_heuristics_io_write returned -1 (write returned 0)");
+
+ return (-1);
+ }
+
+ if (res == -2) {
+ qdevice_log(LOG_CRIT, "Unhandled error in during sending message to heuristics "
+ "worker (qdevice_heuristics_io_write returned -2)");
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+qdevice_heuristics_cmd_remove_newlines(struct dynar *str)
+{
+ size_t len;
+ size_t zi;
+ char *buf;
+
+ len = dynar_size(str);
+ buf = dynar_data(str);
+
+ for (zi = 0; zi < len ; zi++) {
+ if (buf[zi] == '\n' || buf[zi] == '\r') {
+ buf[zi] = ' ';
+ }
+ }
+
+ return (0);
+}
+
+int
+qdevice_heuristics_cmd_write_exec_list(struct qdevice_heuristics_instance *instance,
+ const struct qdevice_heuristics_exec_list *new_exec_list)
+{
+ struct send_buffer_list_entry *send_buffer;
+ struct qdevice_heuristics_exec_list_entry *entry;
+
+ send_buffer = send_buffer_list_get_new(&instance->cmd_out_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_ERR, "Can't alloc send list for cmd change exec list");
+
+ return (-1);
+ }
+
+ if (dynar_str_cpy(&send_buffer->buffer, QDEVICE_HEURISTICS_CMD_STR_EXEC_LIST_CLEAR) == -1 ||
+ dynar_str_cat(&send_buffer->buffer, "\n") == -1) {
+ qdevice_log(LOG_ERR, "Can't alloc list clear message");
+
+ send_buffer_list_discard_new(&instance->cmd_out_buffer_list, send_buffer);
+
+ return (-1);
+ }
+
+ send_buffer_list_put(&instance->cmd_out_buffer_list, send_buffer);
+
+ if (new_exec_list == NULL) {
+ return (0);
+ }
+
+ /*
+ * new_exec_list is not NULL, send it
+ */
+ TAILQ_FOREACH(entry, new_exec_list, entries) {
+ send_buffer = send_buffer_list_get_new(&instance->cmd_out_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_ERR, "Can't alloc send list for cmd change exec list");
+
+ return (-1);
+ }
+
+ if (dynar_str_cpy(&send_buffer->buffer,
+ QDEVICE_HEURISTICS_CMD_STR_EXEC_LIST_ADD_SPACE) == -1 ||
+ dynar_str_cat(&send_buffer->buffer, entry->name) == -1 ||
+ dynar_str_cat(&send_buffer->buffer, " ") == -1 ||
+ dynar_str_cat(&send_buffer->buffer, entry->command) == -1 ||
+ qdevice_heuristics_cmd_remove_newlines(&send_buffer->buffer) == -1 ||
+ dynar_str_cat(&send_buffer->buffer, "\n") == -1) {
+ qdevice_log(LOG_ERR, "Can't alloc list add message");
+
+ send_buffer_list_discard_new(&instance->cmd_out_buffer_list, send_buffer);
+
+ return (-1);
+ }
+
+ send_buffer_list_put(&instance->cmd_out_buffer_list, send_buffer);
+ }
+
+ return (0);
+}
+
+int
+qdevice_heuristics_cmd_write_exec(struct qdevice_heuristics_instance *instance,
+ uint32_t timeout, uint32_t seq_number)
+{
+ struct send_buffer_list_entry *send_buffer;
+
+ send_buffer = send_buffer_list_get_new(&instance->cmd_out_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_ERR, "Can't alloc send list for cmd change exec list");
+
+ return (-1);
+ }
+
+ if (dynar_str_cpy(&send_buffer->buffer, QDEVICE_HEURISTICS_CMD_STR_EXEC_ADD_SPACE) == -1 ||
+ dynar_str_catf(&send_buffer->buffer, "%"PRIu32" %"PRIu32"\n", timeout, seq_number) == -1) {
+ qdevice_log(LOG_ERR, "Can't alloc exec message");
+
+ send_buffer_list_discard_new(&instance->cmd_out_buffer_list, send_buffer);
+
+ return (-1);
+ }
+
+ send_buffer_list_put(&instance->cmd_out_buffer_list, send_buffer);
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_HEURISTICS_CMD_H_
+#define _QDEVICE_HEURISTICS_CMD_H_
+
+#include "qdevice-heuristics-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_heuristics_cmd_write(
+ struct qdevice_heuristics_instance *instance);
+
+extern int qdevice_heuristics_cmd_write_exec_list(
+ struct qdevice_heuristics_instance *instance, const struct qdevice_heuristics_exec_list *new_exec_list);
+
+extern int qdevice_heuristics_cmd_write_exec(struct qdevice_heuristics_instance *instance,
+ uint32_t timeout, uint32_t seq_number);
+
+extern int qdevice_heuristics_cmd_read_from_pipe(
+ struct qdevice_heuristics_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_HEURISTICS_CMD_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qdevice-heuristics-exec-list.h"
+
+void
+qdevice_heuristics_exec_list_init(struct qdevice_heuristics_exec_list *list)
+{
+
+ TAILQ_INIT(list);
+}
+
+struct qdevice_heuristics_exec_list_entry *
+qdevice_heuristics_exec_list_add(struct qdevice_heuristics_exec_list *list,
+ char *name, char *command)
+{
+ struct qdevice_heuristics_exec_list_entry *entry;
+
+ entry = (struct qdevice_heuristics_exec_list_entry *)malloc(sizeof(*entry));
+ if (entry == NULL) {
+ return (NULL);
+ }
+
+ memset(entry, 0, sizeof(*entry));
+
+ entry->name = strdup(name);
+ if (entry->name == NULL) {
+ free(entry);
+
+ return (NULL);
+ }
+
+ entry->command = strdup(command);
+ if (entry->command == NULL) {
+ free(entry->name);
+ free(entry);
+
+ return (NULL);
+ }
+
+ TAILQ_INSERT_TAIL(list, entry, entries);
+
+ return (entry);
+}
+
+void
+qdevice_heuristics_exec_list_free(struct qdevice_heuristics_exec_list *list)
+{
+ struct qdevice_heuristics_exec_list_entry *entry;
+ struct qdevice_heuristics_exec_list_entry *entry_next;
+
+ entry = TAILQ_FIRST(list);
+
+ while (entry != NULL) {
+ entry_next = TAILQ_NEXT(entry, entries);
+
+ free(entry->name);
+ free(entry->command);
+ free(entry);
+
+ entry = entry_next;
+ }
+
+ TAILQ_INIT(list);
+}
+
+size_t
+qdevice_heuristics_exec_list_size(const struct qdevice_heuristics_exec_list *list)
+{
+ struct qdevice_heuristics_exec_list_entry *entry;
+ size_t res;
+
+ res = 0;
+
+ TAILQ_FOREACH(entry, list, entries) {
+ res++;
+ }
+
+ return (res);
+}
+
+int
+qdevice_heuristics_exec_list_clone(struct qdevice_heuristics_exec_list *dst_list,
+ const struct qdevice_heuristics_exec_list *src_list)
+{
+ struct qdevice_heuristics_exec_list_entry *entry;
+
+ qdevice_heuristics_exec_list_init(dst_list);
+
+ TAILQ_FOREACH(entry, src_list, entries) {
+ if (qdevice_heuristics_exec_list_add(dst_list, entry->name, entry->command) == NULL) {
+ qdevice_heuristics_exec_list_free(dst_list);
+
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+void
+qdevice_heuristics_exec_list_del(struct qdevice_heuristics_exec_list *list,
+ struct qdevice_heuristics_exec_list_entry *entry)
+{
+
+ TAILQ_REMOVE(list, entry, entries);
+
+ free(entry->name);
+ free(entry->command);
+ free(entry);
+}
+
+int
+qdevice_heuristics_exec_list_is_empty(const struct qdevice_heuristics_exec_list *list)
+{
+
+ return (TAILQ_EMPTY(list));
+}
+
+struct qdevice_heuristics_exec_list_entry *
+qdevice_heuristics_exec_list_find_name(const struct qdevice_heuristics_exec_list *list,
+ const char *name)
+{
+ struct qdevice_heuristics_exec_list_entry *entry;
+
+ TAILQ_FOREACH(entry, list, entries) {
+ if (strcmp(entry->name, name) == 0) {
+ return (entry);
+ }
+ }
+
+ return (NULL);
+}
+
+int
+qdevice_heuristics_exec_list_eq(const struct qdevice_heuristics_exec_list *list1,
+ const struct qdevice_heuristics_exec_list *list2)
+{
+ struct qdevice_heuristics_exec_list_entry *entry1;
+ struct qdevice_heuristics_exec_list_entry *entry2;
+ struct qdevice_heuristics_exec_list tmp_list;
+ int res;
+
+ res = 1;
+
+ if (qdevice_heuristics_exec_list_clone(&tmp_list, list2) != 0) {
+ return (-1);
+ }
+
+ TAILQ_FOREACH(entry1, list1, entries) {
+ entry2 = qdevice_heuristics_exec_list_find_name(&tmp_list, entry1->name);
+ if (entry2 == NULL) {
+ res = 0;
+ goto return_res;
+ }
+
+ if (strcmp(entry1->command, entry2->command) != 0) {
+ res = 0;
+ goto return_res;
+ }
+
+ qdevice_heuristics_exec_list_del(&tmp_list, entry2);
+ }
+
+ if (!qdevice_heuristics_exec_list_is_empty(&tmp_list)) {
+ res = 0;
+ goto return_res;
+ }
+
+return_res:
+ qdevice_heuristics_exec_list_free(&tmp_list);
+
+ return (res);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_HEURISTICS_EXEC_LIST_H_
+#define _QDEVICE_HEURISTICS_EXEC_LIST_H_
+
+#include <sys/types.h>
+
+#include <sys/queue.h>
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qdevice_heuristics_exec_list_entry {
+ char *name;
+ char *command;
+ TAILQ_ENTRY(qdevice_heuristics_exec_list_entry) entries;
+};
+
+TAILQ_HEAD(qdevice_heuristics_exec_list, qdevice_heuristics_exec_list_entry);
+
+extern void qdevice_heuristics_exec_list_init(
+ struct qdevice_heuristics_exec_list *list);
+
+extern struct qdevice_heuristics_exec_list_entry *qdevice_heuristics_exec_list_add(
+ struct qdevice_heuristics_exec_list *list, char *name, char *command);
+
+extern void qdevice_heuristics_exec_list_free(
+ struct qdevice_heuristics_exec_list *list);
+
+extern size_t qdevice_heuristics_exec_list_size(
+ const struct qdevice_heuristics_exec_list *list);
+
+extern int qdevice_heuristics_exec_list_clone(
+ struct qdevice_heuristics_exec_list *dst_list,
+ const struct qdevice_heuristics_exec_list *src_list);
+
+extern void qdevice_heuristics_exec_list_del(
+ struct qdevice_heuristics_exec_list *list, struct qdevice_heuristics_exec_list_entry *entry);
+
+extern int qdevice_heuristics_exec_list_is_empty(
+ const struct qdevice_heuristics_exec_list *list);
+
+extern struct qdevice_heuristics_exec_list_entry *qdevice_heuristics_exec_list_find_name(
+ const struct qdevice_heuristics_exec_list *list, const char *name);
+
+extern int qdevice_heuristics_exec_list_eq(
+ const struct qdevice_heuristics_exec_list *list1,
+ const struct qdevice_heuristics_exec_list *list2);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_HEURISTICS_EXEC_LIST_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 "qdevice-heuristics-exec-result.h"
+
+const char *
+qdevice_heuristics_exec_result_to_str(enum qdevice_heuristics_exec_result exec_result)
+{
+
+ switch (exec_result) {
+ case QDEVICE_HEURISTICS_EXEC_RESULT_FAIL: return("Fail"); break;
+ case QDEVICE_HEURISTICS_EXEC_RESULT_PASS: return("Pass"); break;
+ case QDEVICE_HEURISTICS_EXEC_RESULT_DISABLED: return("Disabled"); break;
+ }
+
+ return ("Unknown heuristics exec result value");
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_HEURISTICS_EXEC_RESULT_H_
+#define _QDEVICE_HEURISTICS_EXEC_RESULT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum qdevice_heuristics_exec_result {
+ /*
+ * Heuristics worker received command to exec heuristics but list is empty. This
+ * is happening when heuristics is disabled.
+ */
+ QDEVICE_HEURISTICS_EXEC_RESULT_DISABLED = 0,
+ /*
+ * All executed commands passed
+ */
+ QDEVICE_HEURISTICS_EXEC_RESULT_PASS = 1,
+ /*
+ * One (or more) commands failed or timed-out
+ */
+ QDEVICE_HEURISTICS_EXEC_RESULT_FAIL = 2,
+};
+
+extern const char * qdevice_heuristics_exec_result_to_str(
+ enum qdevice_heuristics_exec_result exec_result);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_HEURISTICS_EXEC_RESULT_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <string.h>
+
+#include "qdevice-heuristics-instance.h"
+#include "qdevice-heuristics-exec-list.h"
+
+int
+qdevice_heuristics_instance_init(struct qdevice_heuristics_instance *instance)
+{
+
+ memset(instance, 0, sizeof(*instance));
+
+ qdevice_heuristics_exec_list_init(&instance->exec_list);
+ qdevice_heuristics_result_notifier_list_init(&instance->exec_result_notifier_list);
+
+ return (0);
+}
+
+int
+qdevice_heuristics_instance_destroy(struct qdevice_heuristics_instance *instance)
+{
+
+ qdevice_heuristics_result_notifier_list_free(&instance->exec_result_notifier_list);
+ qdevice_heuristics_exec_list_free(&instance->exec_list);
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_HEURISTICS_INSTANCE_H_
+#define _QDEVICE_HEURISTICS_INSTANCE_H_
+
+#include "dynar.h"
+#include "send-buffer-list.h"
+#include "qdevice-heuristics-mode.h"
+#include "qdevice-heuristics-exec-list.h"
+#include "qdevice-heuristics-exec-result.h"
+#include "qdevice-heuristics-result-notifier.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qdevice_heuristics_instance {
+ int pipe_cmd_send;
+ int pipe_cmd_recv;
+ int pipe_log_recv;
+ pid_t worker_pid;
+ struct send_buffer_list cmd_out_buffer_list;
+ struct dynar log_in_buffer;
+ struct dynar cmd_in_buffer;
+
+ uint32_t timeout;
+ uint32_t sync_timeout;
+ uint32_t interval;
+
+ enum qdevice_heuristics_mode mode;
+
+ int waiting_for_result;
+ uint32_t expected_reply_seq_number;
+
+ struct qdevice_heuristics_exec_list exec_list;
+
+ struct qdevice_instance *qdevice_instance_ptr;
+
+ struct qdevice_heuristics_result_notifier_list exec_result_notifier_list;
+};
+
+extern int qdevice_heuristics_instance_init(struct qdevice_heuristics_instance *instance);
+
+extern int qdevice_heuristics_instance_destroy(struct qdevice_heuristics_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_HEURISTICS_INSTANCE_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <limits.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "qdevice-heuristics-io.h"
+
+#define QDEVICE_HEURISTICS_IO_BUFFER_SIZE 256
+
+ssize_t
+qdevice_heuristics_io_blocking_write(int fd, const void *buf, size_t count)
+{
+ ssize_t bytes_written;
+ ssize_t tmp_bytes_written;
+
+ bytes_written = 0;
+
+ do {
+ tmp_bytes_written = write(fd, (const char *)buf + bytes_written,
+ (count - bytes_written > SSIZE_MAX) ? SSIZE_MAX : count - bytes_written);
+ if (tmp_bytes_written == -1) {
+ if (errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK) {
+ return (-1);
+ }
+ } else {
+ bytes_written += tmp_bytes_written;
+ }
+ } while ((size_t)bytes_written != count);
+
+ return (bytes_written);
+}
+
+/*
+ * 1 Full line readed (at least one \n found)
+ * 0 Partial read (no error)
+ * -1 End of connection
+ * -2 Buffer too long
+ * -3 Unhandled error
+ */
+int
+qdevice_heuristics_io_read(int fd, struct dynar *dest)
+{
+ char buf[QDEVICE_HEURISTICS_IO_BUFFER_SIZE];
+ ssize_t readed;
+ int res;
+ size_t zi;
+
+ res = 0;
+ readed = read(fd, buf, sizeof(buf));
+ if (readed > 0) {
+ if (dynar_cat(dest, buf, readed) == -1) {
+ res = -2;
+ goto exit_err;
+ }
+
+ for (zi = 0; zi < (size_t)readed; zi++) {
+ if (buf[zi] == '\n') {
+ res = 1;
+ }
+ }
+ }
+
+ if (readed == 0) {
+ res = -1;
+ }
+
+ if (readed < 0 && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
+ res = -3;
+ }
+
+exit_err:
+ return (res);
+}
+
+/*
+ * 1 All data succesfully sent
+ * 0 Partial send (no error)
+ * -1 send returned 0,
+ * -2 Unhandled error
+ */
+int
+qdevice_heuristics_io_write(int fd, const struct dynar *msg, size_t *already_sent_bytes)
+{
+ ssize_t sent;
+ size_t to_send;
+ int res;
+
+ res = 0;
+
+ to_send = dynar_size(msg) - *already_sent_bytes;
+ if (to_send > QDEVICE_HEURISTICS_IO_BUFFER_SIZE) {
+ to_send = QDEVICE_HEURISTICS_IO_BUFFER_SIZE;
+ }
+
+ sent = write(fd, dynar_data(msg) + *already_sent_bytes,
+ to_send);
+
+ if (sent > 0) {
+ *already_sent_bytes += sent;
+
+ if (*already_sent_bytes == dynar_size(msg)) {
+ return (1);
+ }
+ }
+
+ if (sent == 0) {
+ res = -1;
+ }
+
+ if (sent < 0 && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
+ res = -2;
+ }
+
+ return (res);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_HEURISTICS_IO_H_
+#define _QDEVICE_HEURISTICS_IO_H_
+
+#include "dynar.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern ssize_t qdevice_heuristics_io_blocking_write(int fd, const void *buf,
+ size_t count);
+
+extern int qdevice_heuristics_io_read(int fd, struct dynar *dest);
+
+extern int qdevice_heuristics_io_write(int fd, const struct dynar *msg,
+ size_t *already_sent_bytes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_HEURISTICS_IO_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 "qdevice-heuristics-io.h"
+#include "qdevice-heuristics-log.h"
+#include "qdevice-log.h"
+
+/*
+ * 1 - Line logged
+ * 0 - No line to log - everything processed
+ * -1 - Error
+ */
+static int
+qdevice_heuristics_log_process_one_line(struct dynar *data)
+{
+ char *str;
+ char *log_str_start;
+ size_t str_len;
+ size_t nl_pos;
+ size_t zi;
+ int status;
+ unsigned int log_priority;
+
+ str = dynar_data(data);
+ str_len = dynar_size(data);
+ log_str_start = str;
+
+ status = 0;
+ log_priority = 0;
+ /*
+ * Find start of log message and end of line
+ */
+ for (zi = 0; zi < str_len && status != -1; zi++) {
+ switch (status) {
+ case 0:
+ if (str[zi] >= '0' && str[zi] <= '9') {
+ log_priority = log_priority * 10 + (str[zi] - '0');
+ } else if (str[zi] == ' ') {
+ status = 1;
+ } else {
+ qdevice_log(LOG_ERR, "Parsing of heuristics log line failed. "
+ "Unexpected char '%c'", str[zi]);
+ return (-1);
+ }
+ break;
+ case 1:
+ if (str[zi] != ' ') {
+ status = 2;
+ log_str_start = str + zi;
+ }
+ break;
+ case 2:
+ if (str[zi] == '\n' || str[zi] == '\r') {
+ str[zi] = '\0';
+ nl_pos = zi;
+ status = -1;
+ }
+ break;
+ }
+ }
+
+ if (status != -1) {
+ return (0);
+ }
+
+ /*
+ * Do actual logging
+ */
+ qb_log_from_external_source(__func__, __FILE__, "worker: %s", log_priority, __LINE__, 0, log_str_start);
+
+ /*
+ * Find place where is begining of new "valid" line
+ */
+ for (zi = nl_pos + 1; zi < str_len && (str[zi] == '\0' || str[zi] == '\n' || str[zi] == '\r'); zi++) ;
+
+ memmove(str, str + zi, str_len - zi);
+ if (dynar_set_size(data, str_len - zi) == -1) {
+ qdevice_log(LOG_ERR, "qdevice_heuristics_log_process_one_line: Can't set dynar size");
+ return (-1);
+ }
+
+ return (1);
+
+}
+
+
+/*
+ * 0 - No error
+ * -1 - Error
+ */
+static int
+qdevice_heuristics_log_process(struct qdevice_heuristics_instance *instance)
+{
+ int res;
+
+ while ((res = qdevice_heuristics_log_process_one_line(&instance->log_in_buffer)) == 1) ;
+
+ return (res);
+}
+
+/*
+ * 0 - No error
+ * 1 - Error
+ */
+int
+qdevice_heuristics_log_read_from_pipe(struct qdevice_heuristics_instance *instance)
+{
+ int res;
+ int ret;
+
+ res = qdevice_heuristics_io_read(instance->pipe_log_recv, &instance->log_in_buffer);
+
+ ret = 0;
+
+ switch (res) {
+ case 0:
+ /*
+ * Partial read
+ */
+ break;
+ case -1:
+ qdevice_log(LOG_ERR, "Lost connection with heuristics worker");
+ ret = -1;
+ break;
+ case -2:
+ qdevice_log(LOG_ERR, "Heuristics worker sent too long log. Ignoring line");
+ dynar_clean(&instance->log_in_buffer);
+ break;
+ case -3:
+ qdevice_log(LOG_ERR, "Unhandled error when reading from heuristics worker log fd");
+ ret = -1;
+ break;
+ case 1:
+ /*
+ * At least one log line received
+ */
+ ret = qdevice_heuristics_log_process(instance);
+ break;
+ }
+
+ return (ret);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_HEURISTICS_LOG_H_
+#define _QDEVICE_HEURISTICS_LOG_H_
+
+#include "qdevice-heuristics-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_heuristics_log_read_from_pipe(
+ struct qdevice_heuristics_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_HEURISTICS_LOG_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2017 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 <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qdevice-heuristics-mode.h"
+
+const char*
+qdevice_heuristics_mode_to_str(enum qdevice_heuristics_mode mode)
+{
+ switch (mode) {
+ case QDEVICE_HEURISTICS_MODE_DISABLED: return ("Disabled"); break;
+ case QDEVICE_HEURISTICS_MODE_ENABLED: return ("Enabled"); break;
+ case QDEVICE_HEURISTICS_MODE_SYNC: return ("Enabled only on sync"); break;
+ }
+
+ return ("Undefined");
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_HEURISTICS_MODE_H_
+#define _QDEVICE_HEURISTICS_MODE_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum qdevice_heuristics_mode {
+ QDEVICE_HEURISTICS_MODE_DISABLED = 0,
+ QDEVICE_HEURISTICS_MODE_ENABLED = 1,
+ QDEVICE_HEURISTICS_MODE_SYNC = 2,
+};
+
+extern const char *qdevice_heuristics_mode_to_str(enum qdevice_heuristics_mode mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_HEURISTICS_MODE_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <stdlib.h>
+#include <string.h>
+
+#include "qdevice-heuristics-result-notifier.h"
+
+void
+qdevice_heuristics_result_notifier_list_init(struct qdevice_heuristics_result_notifier_list *notifier_list)
+{
+
+ TAILQ_INIT(notifier_list);
+}
+
+struct qdevice_heuristics_result_notifier_item *
+qdevice_heuristics_result_notifier_list_get(struct qdevice_heuristics_result_notifier_list *notifier_list,
+ qdevice_heuristics_result_notifier_callback callback)
+{
+ struct qdevice_heuristics_result_notifier_item *item;
+
+ TAILQ_FOREACH(item, notifier_list, entries) {
+ if (item->callback == callback) {
+ return (item);
+ }
+ }
+
+ return (NULL);
+}
+
+struct qdevice_heuristics_result_notifier_item *
+qdevice_heuristics_result_notifier_list_add(struct qdevice_heuristics_result_notifier_list *notifier_list,
+ qdevice_heuristics_result_notifier_callback callback)
+{
+ struct qdevice_heuristics_result_notifier_item *item;
+
+ item = qdevice_heuristics_result_notifier_list_get(notifier_list, callback);
+ if (item != NULL) {
+ return (item);
+ }
+
+ item = (struct qdevice_heuristics_result_notifier_item *)malloc(sizeof(*item));
+ if (item == NULL) {
+ return (NULL);
+ }
+ memset(item, 0, sizeof(*item));
+ item->callback = callback;
+ item->active = 0;
+
+ TAILQ_INSERT_TAIL(notifier_list, item, entries);
+
+ return (item);
+}
+
+int
+qdevice_heuristics_result_notifier_list_set_active(struct qdevice_heuristics_result_notifier_list *notifier_list,
+ qdevice_heuristics_result_notifier_callback callback, int active)
+{
+ struct qdevice_heuristics_result_notifier_item *item;
+
+ item = qdevice_heuristics_result_notifier_list_get(notifier_list, callback);
+ if (item == NULL) {
+ return (-1);
+ }
+
+ item->active = active;
+
+ return (0);
+}
+
+void
+qdevice_heuristics_result_notifier_list_free(struct qdevice_heuristics_result_notifier_list *notifier_list)
+{
+ struct qdevice_heuristics_result_notifier_item *item;
+ struct qdevice_heuristics_result_notifier_item *item_next;
+
+ item = TAILQ_FIRST(notifier_list);
+ while (item != NULL) {
+ item_next = TAILQ_NEXT(item, entries);
+
+ free(item);
+ item = item_next;
+ }
+}
+
+int
+qdevice_heuristics_result_notifier_notify(struct qdevice_heuristics_result_notifier_list *notifier_list,
+ void *heuristics_instance, uint32_t seq_number, enum qdevice_heuristics_exec_result exec_result)
+{
+ struct qdevice_heuristics_result_notifier_item *item;
+
+ TAILQ_FOREACH(item, notifier_list, entries) {
+ if (!item->active) {
+ continue ;
+ }
+
+ if (item->callback(heuristics_instance, seq_number, exec_result) != 0) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_HEURISTICS_RESULT_NOTIFIER_H_
+#define _QDEVICE_HEURISTICS_RESULT_NOTIFIER_H_
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "qdevice-heuristics-exec-result.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*qdevice_heuristics_result_notifier_callback)(void *heuristics_instance, uint32_t seq_number,
+ enum qdevice_heuristics_exec_result exec_result);
+
+struct qdevice_heuristics_result_notifier_item {
+ qdevice_heuristics_result_notifier_callback callback;
+ int active;
+ TAILQ_ENTRY(qdevice_heuristics_result_notifier_item) entries;
+};
+
+TAILQ_HEAD(qdevice_heuristics_result_notifier_list, qdevice_heuristics_result_notifier_item);
+
+extern void qdevice_heuristics_result_notifier_list_init(
+ struct qdevice_heuristics_result_notifier_list *notifier_list);
+
+extern struct qdevice_heuristics_result_notifier_item *qdevice_heuristics_result_notifier_list_add(
+ struct qdevice_heuristics_result_notifier_list *notifier_list,
+ qdevice_heuristics_result_notifier_callback callback);
+
+extern struct qdevice_heuristics_result_notifier_item *qdevice_heuristics_result_notifier_list_get(
+ struct qdevice_heuristics_result_notifier_list *notifier_list,
+ qdevice_heuristics_result_notifier_callback callback);
+
+extern int qdevice_heuristics_result_notifier_list_set_active(
+ struct qdevice_heuristics_result_notifier_list *notifier_list,
+ qdevice_heuristics_result_notifier_callback callback, int active);
+
+extern void qdevice_heuristics_result_notifier_list_free(
+ struct qdevice_heuristics_result_notifier_list *notifier_list);
+
+extern int qdevice_heuristics_result_notifier_notify(
+ struct qdevice_heuristics_result_notifier_list *notifier_list,
+ void *heuristics_instance, uint32_t seq_number,
+ enum qdevice_heuristics_exec_result exec_result);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_HEURISTICS_RESULT_NOTIFIER_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "dynar.h"
+#include "dynar-str.h"
+#include "qdevice-heuristics-io.h"
+#include "qdevice-heuristics-worker.h"
+#include "qdevice-heuristics-worker-cmd.h"
+#include "qdevice-heuristics-cmd-str.h"
+#include "qdevice-heuristics-worker-log.h"
+
+static int
+qdevice_heuristics_worker_cmd_process_exec_list_add(struct qdevice_heuristics_worker_instance *instance,
+ struct dynar *data)
+{
+ size_t zi;
+ char *exec_name;
+ char *exec_command;
+ char *str;
+
+ str = dynar_data(data);
+
+ /*
+ * Skip to first space
+ */
+ for (zi = 0; str[zi] != ' ' && str[zi] != '\0'; zi++) ;
+
+ if (str[zi] == '\0') {
+ qdevice_heuristics_worker_log_printf(instance, LOG_CRIT,
+ "qdevice_heuristics_worker_cmd_process_exec_list_add: Can't find first space");
+ return (-1);
+ }
+
+ /*
+ * Skip to the end of spaces
+ */
+ for (; str[zi] == ' '; zi++) ;
+
+ if (str[zi] == '\0') {
+ qdevice_heuristics_worker_log_printf(instance, LOG_CRIT,
+ "qdevice_heuristics_worker_cmd_process_exec_list_add: Can't find start of exec name");
+ return (-1);
+ }
+
+ /*
+ * Found exec name
+ */
+ exec_name = str + zi;
+
+ /*
+ * Skip to the next spaces
+ */
+ for (; str[zi] != ' ' && str[zi] != '\0'; zi++) ;
+
+ if (str[zi] == '\0') {
+ qdevice_heuristics_worker_log_printf(instance, LOG_CRIT,
+ "qdevice_heuristics_worker_cmd_process_exec_list_add: Can't find second space");
+ return (-1);
+ }
+
+ /*
+ * Put trailing \0 into exec_name
+ */
+ str[zi] = '\0';
+ zi++;
+
+ /*
+ * Skip to the end of next spaces
+ */
+ for (; str[zi] == ' '; zi++) ;
+
+ if (str[zi] == '\0') {
+ qdevice_heuristics_worker_log_printf(instance, LOG_CRIT,
+ "qdevice_heuristics_worker_cmd_process_exec_list_add: Can't find start of exec command");
+ return (-1);
+ }
+
+ /*
+ * Found exec_command
+ */
+ exec_command = str + zi;
+
+ qdevice_heuristics_worker_log_printf(instance, LOG_DEBUG,
+ "qdevice_heuristics_worker_cmd_process_one_line: Received exec-list-add command "
+ "with name \"%s\" and command \"%s\"", exec_name, exec_command);
+
+ if (qdevice_heuristics_exec_list_add(&instance->exec_list, exec_name, exec_command) == NULL) {
+ qdevice_heuristics_worker_log_printf(instance, LOG_CRIT,
+ "qdevice_heuristics_worker_cmd_process_exec_list_add: Can't alloc exec list entry");
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+qdevice_heuristics_worker_cmd_process_exec(struct qdevice_heuristics_worker_instance *instance,
+ struct dynar *data)
+{
+ uint32_t timeout;
+ uint32_t seq_number;
+ char *str;
+ struct qdevice_heuristics_exec_list_entry *exec_list_entry;
+ struct process_list_entry *plist_entry;
+
+ str = dynar_data(data);
+
+ if (sscanf(str, QDEVICE_HEURISTICS_CMD_STR_EXEC_ADD_SPACE "%"PRIu32" %"PRIu32, &timeout,
+ &seq_number) != 2) {
+ qdevice_heuristics_worker_log_printf(instance, LOG_CRIT,
+ "qdevice_heuristics_worker_cmd_process_exec: Can't parse command (sscanf)");
+ return (-1);
+ }
+
+ qdevice_heuristics_worker_log_printf(instance, LOG_DEBUG,
+ "qdevice_heuristics_worker_cmd_process_exec: Received exec command "
+ "with seq_no \"%"PRIu32"\" and timeout \"%"PRIu32"\"", seq_number, timeout);
+
+ if (instance->exec_timeout_timer != NULL) {
+ process_list_move_active_entries_to_kill_list(&instance->main_process_list);
+
+ timer_list_delete(&instance->main_timer_list, instance->exec_timeout_timer);
+ instance->exec_timeout_timer = NULL;
+ }
+
+ instance->last_exec_seq_number = seq_number;
+
+ if (qdevice_heuristics_exec_list_is_empty(&instance->exec_list)) {
+ if (qdevice_heuristics_worker_cmd_write_exec_result(instance,
+ instance->last_exec_seq_number,
+ QDEVICE_HEURISTICS_EXEC_RESULT_DISABLED) != 0) {
+ return (-1);
+ }
+ } else {
+ /*
+ * Initialize process list (from exec list)
+ */
+ TAILQ_FOREACH(exec_list_entry, &instance->exec_list, entries) {
+ plist_entry = process_list_add(&instance->main_process_list,
+ exec_list_entry->name, exec_list_entry->command);
+
+ if (plist_entry == NULL) {
+ qdevice_heuristics_worker_log_printf(instance, LOG_ERR,
+ "qdevice_heuristics_worker_cmd_process_exec: Can't allocate "
+ "process list entry");
+
+ process_list_move_active_entries_to_kill_list(
+ &instance->main_process_list);
+
+ if (qdevice_heuristics_worker_cmd_write_exec_result(instance,
+ instance->last_exec_seq_number,
+ QDEVICE_HEURISTICS_EXEC_RESULT_FAIL) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+ }
+
+ if (process_list_exec_initialized(&instance->main_process_list) != 0) {
+ qdevice_heuristics_worker_log_printf(instance, LOG_ERR,
+ "qdevice_heuristics_worker_cmd_process_exec: Can't execute "
+ "process list");
+
+ process_list_move_active_entries_to_kill_list(&instance->main_process_list);
+
+ if (qdevice_heuristics_worker_cmd_write_exec_result(instance,
+ instance->last_exec_seq_number,
+ QDEVICE_HEURISTICS_EXEC_RESULT_FAIL) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ instance->exec_timeout_timer = timer_list_add(&instance->main_timer_list,
+ timeout, qdevice_heuristics_worker_exec_timeout_timer_callback,
+ (void *)instance, NULL);
+ if (instance->exec_timeout_timer == NULL) {
+ qdevice_heuristics_worker_log_printf(instance, LOG_ERR,
+ "qdevice_heuristics_worker_cmd_process_exec: Can't add exec timeout "
+ "timer to timer list");
+
+ process_list_move_active_entries_to_kill_list(&instance->main_process_list);
+
+ if (qdevice_heuristics_worker_cmd_write_exec_result(instance,
+ instance->last_exec_seq_number,
+ QDEVICE_HEURISTICS_EXEC_RESULT_FAIL) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * 1 - Line processed
+ * 0 - No line to process - everything processed
+ * -1 - Error
+ */
+static int
+qdevice_heuristics_worker_cmd_process_one_line(struct qdevice_heuristics_worker_instance *instance,
+ struct dynar *data)
+{
+ char *str;
+ size_t str_len;
+ size_t nl_pos;
+ size_t zi;
+
+ str = dynar_data(data);
+ str_len = dynar_size(data);
+
+ /*
+ * Find valid line
+ */
+ for (zi = 0; zi < str_len && str[zi] != '\r' && str[zi] != '\n'; zi++) ;
+
+ if (zi >= str_len) {
+ /*
+ * Command is not yet fully readed
+ */
+ return (0);
+ }
+
+ nl_pos = zi;
+
+ str[nl_pos] = '\0';
+
+ if (strcmp(str, QDEVICE_HEURISTICS_CMD_STR_EXEC_LIST_CLEAR) == 0) {
+ qdevice_heuristics_worker_log_printf(instance, LOG_DEBUG,
+ "qdevice_heuristics_worker_cmd_process_one_line: Received exec-list-clear command");
+
+ qdevice_heuristics_exec_list_free(&instance->exec_list);
+ } else if (strncmp(str, QDEVICE_HEURISTICS_CMD_STR_EXEC_LIST_ADD_SPACE,
+ strlen(QDEVICE_HEURISTICS_CMD_STR_EXEC_LIST_ADD)) == 0) {
+ if (qdevice_heuristics_worker_cmd_process_exec_list_add(instance, data) != 0) {
+ return (-1);
+ }
+ } else if (strncmp(str, QDEVICE_HEURISTICS_CMD_STR_EXEC_ADD_SPACE,
+ strlen(QDEVICE_HEURISTICS_CMD_STR_EXEC_ADD_SPACE)) == 0) {
+ if (qdevice_heuristics_worker_cmd_process_exec(instance, data) != 0) {
+ return (-1);
+ }
+ } else {
+ qdevice_heuristics_worker_log_printf(instance, LOG_CRIT,
+ "qdevice_heuristics_worker_cmd_process_one_line: Unknown command \"%s\" "
+ "received from main qdevice process", str);
+
+ return (-1);
+ }
+
+ /*
+ * Find place where is begining of new "valid" line
+ */
+ for (zi = nl_pos + 1; zi < str_len && (str[zi] == '\0' || str[zi] == '\n' || str[zi] == '\r'); zi++) ;
+
+ memmove(str, str + zi, str_len - zi);
+ if (dynar_set_size(data, str_len - zi) == -1) {
+ qdevice_heuristics_worker_log_printf(instance, LOG_CRIT,
+ "qdevice_heuristics_worker_cmd_process_one_line: Can't set dynar size");
+ return (-1);
+ }
+
+ return (1);
+}
+
+/*
+ * 0 - No error
+ * -1 - Error
+ */
+static int
+qdevice_heuristics_worker_cmd_process(struct qdevice_heuristics_worker_instance *instance)
+{
+ int res;
+
+ while ((res =
+ qdevice_heuristics_worker_cmd_process_one_line(instance, &instance->cmd_in_buffer)) == 1) ;
+
+ return (res);
+}
+
+/*
+ * 0 - No error
+ * 1 - Error
+ */
+int
+qdevice_heuristics_worker_cmd_read_from_pipe(struct qdevice_heuristics_worker_instance *instance)
+{
+ int res;
+ int ret;
+
+ res = qdevice_heuristics_io_read(QDEVICE_HEURISTICS_WORKER_CMD_IN_FD, &instance->cmd_in_buffer);
+
+ ret = 0;
+
+ switch (res) {
+ case 0:
+ /*
+ * Partial read
+ */
+ break;
+ case -1:
+ qdevice_heuristics_worker_log_printf(instance, LOG_ERR,
+ "Lost connection with main qdevice process");
+ ret = -1;
+ break;
+ case -2:
+ qdevice_heuristics_worker_log_printf(instance, LOG_ERR,
+ "Heuristics sent too long command line");
+ ret = -1;
+ break;
+ case -3:
+ qdevice_heuristics_worker_log_printf(instance, LOG_ERR,
+ "Unhandled error when reading from heuristics command in fd");
+ ret = -1;
+ break;
+ case 1:
+ /*
+ * At least one log line received
+ */
+ ret = qdevice_heuristics_worker_cmd_process(instance);
+ break;
+ }
+
+ return (ret);
+}
+
+int
+qdevice_heuristics_worker_cmd_write_exec_result(struct qdevice_heuristics_worker_instance *instance,
+ uint32_t seq_number, enum qdevice_heuristics_exec_result exec_result)
+{
+ if (dynar_str_cpy(&instance->cmd_out_buffer,
+ QDEVICE_HEURISTICS_CMD_STR_EXEC_RESULT_ADD_SPACE) != -1 &&
+ dynar_str_catf(&instance->cmd_out_buffer, "%"PRIu32" %u\n", seq_number,
+ (int)exec_result) != -1) {
+ (void)qdevice_heuristics_io_blocking_write(QDEVICE_HEURISTICS_WORKER_CMD_OUT_FD,
+ dynar_data(&instance->cmd_out_buffer), dynar_size(&instance->cmd_out_buffer));
+ } else {
+ qdevice_heuristics_worker_log_printf(instance, LOG_CRIT,
+ "Can't alloc memory for exec result");
+
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_HEURISTICS_WORKER_CMD_H_
+#define _QDEVICE_HEURISTICS_WORKER_CMD_H_
+
+#include "qdevice-heuristics-worker-instance.h"
+#include "qdevice-heuristics-exec-result.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_heuristics_worker_cmd_read_from_pipe(
+ struct qdevice_heuristics_worker_instance *instance);
+
+extern int qdevice_heuristics_worker_cmd_write_exec_result(
+ struct qdevice_heuristics_worker_instance *instance, uint32_t seq_number,
+ enum qdevice_heuristics_exec_result exec_result);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_HEURISTICS_WORKER_CMD_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_HEURISTICS_WORKER_INSTANCE_H_
+#define _QDEVICE_HEURISTICS_WORKER_INSTANCE_H_
+
+#include "dynar.h"
+
+#include "qdevice-heuristics-exec-list.h"
+#include "process-list.h"
+#include "timer-list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qdevice_heuristics_worker_instance {
+ struct dynar cmd_in_buffer;
+ struct dynar cmd_out_buffer;
+ struct dynar log_out_buffer;
+
+ struct qdevice_heuristics_exec_list exec_list;
+ struct process_list main_process_list;
+ struct timer_list main_timer_list;
+
+ struct timer_list_entry *kill_list_timer;
+ struct timer_list_entry *exec_timeout_timer;
+
+ uint32_t last_exec_seq_number;
+
+ int schedule_exit;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_HEURISTICS_WORKER_INSTANCE_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <stdarg.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include "dynar.h"
+#include "dynar-str.h"
+#include "qdevice-heuristics-io.h"
+#include "qdevice-heuristics-worker.h"
+#include "qdevice-heuristics-worker-log.h"
+
+static int
+qdevice_heuristics_worker_log_remove_newlines(struct dynar *str)
+{
+ size_t len;
+ size_t zi;
+ char *buf;
+
+ len = dynar_size(str);
+ buf = dynar_data(str);
+
+ for (zi = 0; zi < len ; zi++) {
+ if (buf[zi] == '\n' || buf[zi] == '\r') {
+ buf[zi] = ' ';
+ }
+ }
+
+ return (0);
+}
+
+void
+qdevice_heuristics_worker_log_printf(struct qdevice_heuristics_worker_instance *instance,
+ int priority, const char *format, ...)
+{
+ va_list ap;
+ va_list ap_copy;
+
+ va_start(ap, format);
+
+ if (dynar_str_cpy(&instance->log_out_buffer, "") != -1 &&
+ dynar_str_catf(&instance->log_out_buffer, "%u ", priority) != -1 &&
+ dynar_str_vcatf(&instance->log_out_buffer, format, ap) != -1 &&
+ qdevice_heuristics_worker_log_remove_newlines(&instance->log_out_buffer) != -1 &&
+ dynar_str_cat(&instance->log_out_buffer, "\n") != -1) {
+ /*
+ * It was possible to log everything
+ */
+ (void)qdevice_heuristics_io_blocking_write(QDEVICE_HEURISTICS_WORKER_LOG_OUT_FD,
+ dynar_data(&instance->log_out_buffer), dynar_size(&instance->log_out_buffer));
+ } else {
+ /*
+ * As a fallback try to log to syslog
+ */
+ va_copy(ap_copy, ap);
+ openlog("qdevice_heuristics_worker", LOG_PID, LOG_DAEMON);
+ syslog(LOG_ERR, "Log entry sent to syslog instead of parent process");
+ vsyslog(priority, format, ap_copy);
+ closelog();
+ va_end(ap_copy);
+ }
+
+ va_end(ap);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_HEURISTICS_WORKER_LOG_H_
+#define _QDEVICE_HEURISTICS_WORKER_LOG_H_
+
+#include <syslog.h>
+
+#include "qdevice-heuristics-worker-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void qdevice_heuristics_worker_log_printf(
+ struct qdevice_heuristics_worker_instance *instance,
+ int priority, const char *format, ...) __attribute__((__format__(__printf__, 3, 0)));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_HEURISTICS_WORKER_LOG_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <limits.h>
+#include <errno.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "dynar-str.h"
+#include "qdevice-config.h"
+#include "qdevice-heuristics-io.h"
+#include "qdevice-heuristics-worker.h"
+#include "qdevice-heuristics-worker-instance.h"
+#include "qdevice-heuristics-worker-log.h"
+#include "qdevice-heuristics-worker-cmd.h"
+
+/*
+ * Declarations
+ */
+static int qdevice_heuristics_worker_kill_list_timer_callback(void *data1,
+ void *data2);
+
+static void qdevice_heuristics_worker_process_list_notify(
+ enum process_list_notify_reason reason, const struct process_list_entry *entry,
+ void *user_data);
+
+static void qdevice_heuristics_worker_signal_handlers_register(void);
+
+
+/*
+ * Definitions
+ */
+static void
+qdevice_heuristics_worker_process_list_notify(enum process_list_notify_reason reason,
+ const struct process_list_entry *entry, void *user_data)
+{
+ struct qdevice_heuristics_worker_instance *instance;
+
+ instance = (struct qdevice_heuristics_worker_instance *)user_data;
+
+ switch (reason) {
+ case PROCESS_LIST_NOTIFY_REASON_EXECUTED:
+ qdevice_heuristics_worker_log_printf(instance, LOG_DEBUG,
+ "process %s executed", entry->name);
+ break;
+ case PROCESS_LIST_NOTIFY_REASON_FINISHED:
+ if (!WIFEXITED(entry->exit_status) || WEXITSTATUS(entry->exit_status) != 0) {
+ if (WIFEXITED(entry->exit_status)) {
+ qdevice_heuristics_worker_log_printf(instance, LOG_WARNING,
+ "process %s finished with status %d", entry->name,
+ WEXITSTATUS(entry->exit_status));
+ } else if (WIFSIGNALED(entry->exit_status)) {
+ qdevice_heuristics_worker_log_printf(instance, LOG_WARNING,
+ "process %s killed by signal %d", entry->name,
+ WTERMSIG(entry->exit_status));
+ } else {
+ qdevice_heuristics_worker_log_printf(instance, LOG_WARNING,
+ "process %s finished with non zero status", entry->name);
+ }
+ } else {
+ qdevice_heuristics_worker_log_printf(instance, LOG_DEBUG,
+ "process %s sucesfully finished", entry->name);
+ }
+ break;
+ }
+}
+
+static void
+qdevice_heuristics_worker_signal_handlers_register(void)
+{
+ struct sigaction act;
+
+ act.sa_handler = SIG_DFL;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+
+ sigaction(SIGCHLD, &act, NULL);
+
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+
+ sigaction(SIGPIPE, &act, NULL);
+
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+
+ sigaction(SIGINT, &act, NULL);
+}
+
+static int
+qdevice_heuristics_worker_kill_list_timer_callback(void *data1, void *data2)
+{
+ struct qdevice_heuristics_worker_instance *instance;
+ size_t kill_list_size;
+
+ instance = (struct qdevice_heuristics_worker_instance *)data1;
+
+ if (process_list_process_kill_list(&instance->main_process_list) != 0) {
+ qdevice_heuristics_worker_log_printf(instance, LOG_CRIT,
+ "qdevice_heuristics_worker_kill_list_timer_callback: process kill list failed. "
+ "Shutting down worker");
+
+ instance->schedule_exit = 1;
+ return (0);
+ }
+
+ kill_list_size = process_list_get_kill_list_items(&instance->main_process_list);
+
+ if (kill_list_size > 0) {
+ qdevice_heuristics_worker_log_printf(instance, LOG_DEBUG,
+ "Still waiting for %zu processes exit", kill_list_size);
+ }
+
+ /*
+ * Schedule this timer again
+ */
+ return (-1);
+}
+
+int
+qdevice_heuristics_worker_exec_timeout_timer_callback(void *data1, void *data2)
+{
+ struct qdevice_heuristics_worker_instance *instance;
+
+ instance = (struct qdevice_heuristics_worker_instance *)data1;
+
+ qdevice_heuristics_worker_log_printf(instance, LOG_WARNING,
+ "Not all heuristics execs finished on time");
+
+ process_list_move_active_entries_to_kill_list(&instance->main_process_list);
+
+ instance->exec_timeout_timer = NULL;
+
+ if (qdevice_heuristics_worker_cmd_write_exec_result(instance, instance->last_exec_seq_number,
+ QDEVICE_HEURISTICS_EXEC_RESULT_FAIL) != 0) {
+ instance->schedule_exit = 1;
+
+ return (0);
+ }
+
+ return (0);
+}
+
+static int
+qdevice_heuristics_worker_poll(struct qdevice_heuristics_worker_instance *instance)
+{
+ int poll_res;
+ struct pollfd poll_input_fd;
+ uint32_t timeout;
+ int plist_summary;
+
+ /*
+ * Poll command input
+ */
+ poll_input_fd.fd = QDEVICE_HEURISTICS_WORKER_CMD_IN_FD;
+ poll_input_fd.events = POLLIN;
+ poll_input_fd.revents = 0;
+
+ timeout = timer_list_time_to_expire_ms(&instance->main_timer_list);
+ if (timeout > QDEVICE_MIN_HEURISTICS_TIMEOUT) {
+ timeout = QDEVICE_MIN_HEURISTICS_TIMEOUT;
+ }
+
+ if ((poll_res = poll(&poll_input_fd, 1, timeout)) >= 0) {
+ if (poll_input_fd.revents & POLLIN) {
+ /*
+ * POLLIN
+ */
+ if (qdevice_heuristics_worker_cmd_read_from_pipe(instance) != 0) {
+ return (-1);
+ }
+ }
+
+ if (poll_input_fd.revents & POLLOUT) {
+ /*
+ * Pollout shouldn't happen (critical error)
+ */
+ qdevice_heuristics_worker_log_printf(instance, LOG_CRIT,
+ "qdevice_heuristics_worker_poll: POLLOUT set. Shutting down worker");
+
+ return (-1);
+ }
+
+ if (poll_input_fd.revents & (POLLERR|POLLHUP|POLLNVAL) &&
+ !(poll_input_fd.revents & (POLLIN|POLLOUT))) {
+ /*
+ * Qdevice closed pipe
+ */
+
+ return (-1);
+ }
+ }
+
+ if (process_list_waitpid(&instance->main_process_list) != 0) {
+ qdevice_heuristics_worker_log_printf(instance, LOG_CRIT,
+ "qdevice_heuristics_worker_poll: Waitpid failed. Shutting down worker");
+
+ return (-1);
+ }
+
+ if (instance->exec_timeout_timer != NULL) {
+ plist_summary = process_list_get_summary_result_short(&instance->main_process_list);
+
+ switch (plist_summary) {
+ case -1:
+ /*
+ * Processes not finished -> continue
+ */
+ break;
+ case 0:
+ /*
+ * All processes finished sucesfully
+ */
+ if (qdevice_heuristics_worker_cmd_write_exec_result(instance,
+ instance->last_exec_seq_number, QDEVICE_HEURISTICS_EXEC_RESULT_PASS) != 0) {
+ return (-1);
+ }
+
+ process_list_move_active_entries_to_kill_list(&instance->main_process_list);
+
+ timer_list_delete(&instance->main_timer_list, instance->exec_timeout_timer);
+ instance->exec_timeout_timer = NULL;
+
+ break;
+ case 1:
+ /*
+ * Some processes failed
+ */
+ if (qdevice_heuristics_worker_cmd_write_exec_result(instance,
+ instance->last_exec_seq_number, QDEVICE_HEURISTICS_EXEC_RESULT_FAIL) != 0) {
+ return (-1);
+ }
+
+ process_list_move_active_entries_to_kill_list(&instance->main_process_list);
+
+ timer_list_delete(&instance->main_timer_list, instance->exec_timeout_timer);
+ instance->exec_timeout_timer = NULL;
+ break;
+ default:
+ qdevice_heuristics_worker_log_printf(instance, LOG_CRIT,
+ "qdevice_heuristics_worker_poll: Unhandled "
+ "process_list_get_summary_result. Shutting down worker");
+
+ return (-1);
+ break;
+ }
+ }
+
+ timer_list_expire(&instance->main_timer_list);
+
+ if (instance->schedule_exit) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+qdevice_heuristics_worker_start(size_t ipc_max_send_receive_size, int use_execvp,
+ size_t max_processes, uint32_t kill_list_interval)
+{
+ struct qdevice_heuristics_worker_instance instance;
+
+ memset(&instance, 0, sizeof(instance));
+
+ instance.schedule_exit = 0;
+
+ dynar_init(&instance.cmd_in_buffer, ipc_max_send_receive_size);
+ dynar_init(&instance.cmd_out_buffer, ipc_max_send_receive_size);
+ dynar_init(&instance.log_out_buffer, ipc_max_send_receive_size);
+
+ process_list_init(&instance.main_process_list, max_processes, use_execvp,
+ qdevice_heuristics_worker_process_list_notify, (void *)&instance);
+
+ timer_list_init(&instance.main_timer_list);
+ instance.kill_list_timer = timer_list_add(&instance.main_timer_list,
+ kill_list_interval, qdevice_heuristics_worker_kill_list_timer_callback,
+ (void *)&instance, NULL);
+
+ if (instance.kill_list_timer == NULL) {
+ qdevice_heuristics_worker_log_printf(&instance, LOG_CRIT,
+ "Can't create kill list timer");
+ return ;
+ }
+
+ instance.exec_timeout_timer = NULL;
+
+ qdevice_heuristics_exec_list_init(&instance.exec_list);
+
+ qdevice_heuristics_worker_signal_handlers_register();
+
+ qdevice_heuristics_worker_log_printf(&instance, LOG_DEBUG, "Heuristic worker initialized");
+
+ while (qdevice_heuristics_worker_poll(&instance) == 0) {
+ }
+
+ qdevice_heuristics_worker_log_printf(&instance, LOG_DEBUG, "Heuristic worker shutdown "
+ "requested");
+
+ qdevice_heuristics_exec_list_free(&instance.exec_list);
+
+ timer_list_free(&instance.main_timer_list);
+
+ qdevice_heuristics_worker_log_printf(&instance, LOG_DEBUG,
+ "Waiting for all processes to exit");
+
+ if (process_list_killall(&instance.main_process_list, kill_list_interval) != 0) {
+ qdevice_heuristics_worker_log_printf(&instance, LOG_WARNING,
+ "Not all process exited");
+ }
+
+ process_list_free(&instance.main_process_list);
+
+ dynar_destroy(&instance.cmd_in_buffer);
+ dynar_destroy(&instance.cmd_out_buffer);
+ dynar_destroy(&instance.log_out_buffer);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_HEURISTICS_WORKER_H_
+#define _QDEVICE_HEURISTICS_WORKER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define QDEVICE_HEURISTICS_WORKER_CMD_IN_FD 0
+#define QDEVICE_HEURISTICS_WORKER_CMD_OUT_FD 1
+#define QDEVICE_HEURISTICS_WORKER_LOG_OUT_FD 2
+
+void qdevice_heuristics_worker_start(size_t ipc_max_send_receive_size,
+ int use_execvp, size_t max_processes, uint32_t kill_list_interval);
+
+int qdevice_heuristics_worker_exec_timeout_timer_callback(void *data1, void *data2);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_HEURISTICS_WORKER_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <sys/types.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "qdevice-log.h"
+#include "qdevice-heuristics.h"
+#include "qdevice-heuristics-cmd.h"
+#include "qdevice-heuristics-worker.h"
+#include "qdevice-heuristics-io.h"
+#include "qdevice-votequorum.h"
+#include "utils.h"
+
+#define QDEVICE_HEURISTICS_WAIT_FOR_INITIAL_EXEC_RESULT_MAX_PFDS 5
+
+void
+qdevice_heuristics_init(struct qdevice_heuristics_instance *instance,
+ struct qdevice_advanced_settings *advanced_settings)
+{
+ int pipe_cmd_in[2], pipe_cmd_out[2], pipe_log_out[2];
+ pid_t pid;
+
+ if (pipe(pipe_cmd_in) != 0) {
+ err(1, "Can't create command input pipe");
+ }
+
+ if (pipe(pipe_cmd_out) != 0) {
+ err(1, "Can't create command output pipe");
+ }
+
+ if (pipe(pipe_log_out) != 0) {
+ err(1, "Can't create logging output pipe");
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ err(1, "Can't create child process");
+ } else if (pid == 0) {
+ /*
+ * Child
+ */
+ (void)setsid();
+ if (dup2(pipe_cmd_in[0], 0) == -1) {
+ err(1, "Can't dup2 command input pipe");
+ }
+ close(pipe_cmd_in[1]);
+ close(pipe_cmd_in[0]);
+ if (utils_fd_set_non_blocking(0) == -1) {
+ err(1, "Can't set non blocking flag on command input pipe");
+ }
+
+ if (dup2(pipe_cmd_out[1], 1) == -1) {
+ err(1, "Can't dup2 command output pipe");
+ }
+ close(pipe_cmd_out[0]);
+ close(pipe_cmd_out[1]);
+
+ if (dup2(pipe_log_out[1], 2) == -1) {
+ err(1, "Can't dup2 logging output pipe");
+ }
+ close(pipe_log_out[0]);
+ close(pipe_log_out[1]);
+
+ qdevice_heuristics_worker_start(advanced_settings->heuristics_ipc_max_send_receive_size,
+ advanced_settings->heuristics_use_execvp, advanced_settings->heuristics_max_processes,
+ advanced_settings->heuristics_kill_list_interval);
+
+ qdevice_advanced_settings_destroy(advanced_settings);
+
+ exit(0);
+ } else {
+ close(pipe_cmd_in[0]);
+ close(pipe_cmd_out[1]);
+ close(pipe_log_out[1]);
+
+ qdevice_heuristics_instance_init(instance);
+
+ instance->pipe_cmd_send = pipe_cmd_in[1];
+ if (utils_fd_set_non_blocking(instance->pipe_cmd_send) == -1) {
+ err(1, "Can't set non blocking flag on command input pipe");
+ }
+ instance->pipe_cmd_recv = pipe_cmd_out[0];
+ if (utils_fd_set_non_blocking(instance->pipe_cmd_recv) == -1) {
+ err(1, "Can't set non blocking flag on command output pipe");
+ }
+ instance->pipe_log_recv = pipe_log_out[0];
+ if (utils_fd_set_non_blocking(instance->pipe_cmd_recv) == -1) {
+ err(1, "Can't set non blocking flag on logging output pipe");
+ }
+ instance->worker_pid = pid;
+
+ send_buffer_list_init(&instance->cmd_out_buffer_list,
+ advanced_settings->heuristics_ipc_max_send_buffers,
+ advanced_settings->heuristics_ipc_max_send_receive_size);
+ dynar_init(&instance->log_in_buffer,
+ advanced_settings->heuristics_ipc_max_send_receive_size);
+ dynar_init(&instance->cmd_in_buffer,
+ advanced_settings->heuristics_ipc_max_send_receive_size);
+ }
+}
+
+void
+qdevice_heuristics_destroy(struct qdevice_heuristics_instance *instance)
+{
+ int status;
+
+ /*
+ * Close of pipe_cmd_send result is correct and almost instant exit of worker
+ */
+ close(instance->pipe_cmd_send);
+
+ qdevice_log(LOG_DEBUG, "Waiting for heuristics worker to finish");
+ if (waitpid(instance->worker_pid, &status, 0) == -1) {
+ qdevice_log_err(LOG_ERR, "Heuristics worker waitpid failed");
+ } else {
+ /*
+ * Log what left in worker log buffer. Errors can be ignored
+ */
+ (void)qdevice_heuristics_log_read_from_pipe(instance);
+ }
+
+ close(instance->pipe_cmd_recv);
+ close(instance->pipe_log_recv);
+
+ dynar_destroy(&instance->log_in_buffer);
+ dynar_destroy(&instance->cmd_in_buffer);
+ send_buffer_list_free(&instance->cmd_out_buffer_list);
+
+ qdevice_heuristics_instance_destroy(instance);
+}
+
+int
+qdevice_heuristics_exec(struct qdevice_heuristics_instance *instance, int sync_in_progress)
+{
+ uint32_t timeout;
+
+ instance->expected_reply_seq_number++;
+ instance->waiting_for_result = 1;
+
+ if (sync_in_progress) {
+ timeout = instance->sync_timeout;
+ } else {
+ timeout = instance->timeout;
+ }
+
+ return (qdevice_heuristics_cmd_write_exec(instance, timeout,
+ instance->expected_reply_seq_number));
+}
+
+int
+qdevice_heuristics_waiting_for_result(const struct qdevice_heuristics_instance *instance)
+{
+
+ return (instance->waiting_for_result);
+}
+
+int
+qdevice_heuristics_change_exec_list(struct qdevice_heuristics_instance *instance,
+ const struct qdevice_heuristics_exec_list *new_exec_list, int sync_in_progress)
+{
+
+ if (qdevice_heuristics_cmd_write_exec_list(instance, new_exec_list) != 0) {
+ return (-1);
+ }
+
+ qdevice_heuristics_exec_list_free(&instance->exec_list);
+
+ if (new_exec_list != NULL) {
+ if (qdevice_heuristics_exec_list_clone(&instance->exec_list, new_exec_list) != 0) {
+ qdevice_log(LOG_ERR, "Can't clone exec list");
+
+ return (-1);
+ }
+ }
+
+ if (qdevice_heuristics_waiting_for_result(instance)) {
+ if (qdevice_heuristics_exec(instance, sync_in_progress) != 0) {
+ qdevice_log(LOG_ERR, "Can't execute heuristics");
+
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+
+int
+qdevice_heuristics_wait_for_initial_exec_result(struct qdevice_heuristics_instance *instance)
+{
+ struct pollfd pfds[QDEVICE_HEURISTICS_WAIT_FOR_INITIAL_EXEC_RESULT_MAX_PFDS];
+ int no_pfds;
+ int poll_res;
+ int timeout;
+ int i;
+ int case_processed;
+ int res;
+
+ while (!instance->qdevice_instance_ptr->vq_node_list_initial_heuristics_finished) {
+ no_pfds = 0;
+
+ assert(no_pfds < QDEVICE_HEURISTICS_WAIT_FOR_INITIAL_EXEC_RESULT_MAX_PFDS);
+ pfds[no_pfds].fd = instance->pipe_log_recv;
+ pfds[no_pfds].events = POLLIN;
+ pfds[no_pfds].revents = 0;
+ no_pfds++;
+
+ assert(no_pfds < QDEVICE_HEURISTICS_WAIT_FOR_INITIAL_EXEC_RESULT_MAX_PFDS);
+ pfds[no_pfds].fd = instance->pipe_cmd_recv;
+ pfds[no_pfds].events = POLLIN;
+ pfds[no_pfds].revents = 0;
+ no_pfds++;
+
+ assert(no_pfds < QDEVICE_HEURISTICS_WAIT_FOR_INITIAL_EXEC_RESULT_MAX_PFDS);
+ pfds[no_pfds].fd = instance->qdevice_instance_ptr->votequorum_poll_fd;
+ pfds[no_pfds].events = POLLIN;
+ pfds[no_pfds].revents = 0;
+ no_pfds++;
+
+ if (!send_buffer_list_empty(&instance->cmd_out_buffer_list)) {
+ assert(no_pfds < QDEVICE_HEURISTICS_WAIT_FOR_INITIAL_EXEC_RESULT_MAX_PFDS);
+ pfds[no_pfds].fd = instance->pipe_cmd_send;
+ pfds[no_pfds].events = POLLOUT;
+ pfds[no_pfds].revents = 0;
+ no_pfds++;
+ }
+
+ /*
+ * We know this is never larger than QDEVICE_DEFAULT_HEURISTICS_MAX_TIMEOUT * 2
+ */
+ timeout = (int)instance->sync_timeout * 2;
+
+ poll_res = poll(pfds, no_pfds, timeout);
+ if (poll_res > 0) {
+ for (i = 0; i < no_pfds; i++) {
+ if (pfds[i].revents & POLLIN) {
+ case_processed = 0;
+ switch (i) {
+ case 0:
+ case_processed = 1;
+
+ res = qdevice_heuristics_log_read_from_pipe(instance);
+ if (res == -1) {
+ return (-1);
+ }
+ break;
+ case 1:
+ case_processed = 1;
+ res = qdevice_heuristics_cmd_read_from_pipe(instance);
+ if (res == -1) {
+ return (-1);
+ }
+ break;
+ case 2:
+ case_processed = 1;
+ res = qdevice_votequorum_dispatch(instance->qdevice_instance_ptr);
+ if (res == -1) {
+ return (-1);
+ }
+ case 3:
+ /*
+ * Read on heuristics cmd send fs shouldn't happen
+ */
+ break;
+ }
+
+ if (!case_processed) {
+ qdevice_log(LOG_CRIT, "Unhandled read on poll descriptor %u", i);
+ exit(1);
+ }
+ }
+
+ if (pfds[i].revents & POLLOUT) {
+ case_processed = 0;
+ switch (i) {
+ case 0:
+ case 1:
+ case 2:
+ /*
+ * Write on heuristics log, cmd recv or vq shouldn't happen
+ */
+ break;
+ case 3:
+ case_processed = 1;
+ res = qdevice_heuristics_cmd_write(instance);
+ if (res == -1) {
+ return (-1);
+ }
+ break;
+ }
+
+ if (!case_processed) {
+ qdevice_log(LOG_CRIT, "Unhandled write on poll descriptor %u", i);
+ exit(1);
+ }
+ }
+
+ if ((pfds[i].revents & (POLLERR|POLLHUP|POLLNVAL)) &&
+ !(pfds[i].revents & (POLLIN|POLLOUT))) {
+ switch (i) {
+ case 0:
+ case 1:
+ case 3:
+ /*
+ * Closed pipe doesn't mean return of POLLIN. To display
+ * better log message, we call read log as if POLLIN would
+ * be set.
+ */
+ res = qdevice_heuristics_log_read_from_pipe(instance);
+ if (res == -1) {
+ return (-1);
+ }
+
+ qdevice_log(LOG_ERR, "POLLERR (%u) on heuristics pipe. Exiting");
+ return (-1);
+ break;
+ case 2:
+ qdevice_log(LOG_ERR, "POLLERR (%u) on corosync socket. Exiting");
+ return (-1);
+ break;
+ }
+ }
+ }
+ } else if (poll_res == 0) {
+ qdevice_log(LOG_ERR, "Timeout waiting for initial heuristics exec result");
+ return (-1);
+ } else {
+ qdevice_log_err(LOG_ERR, "Initial heuristics exec result poll failed");
+ return (-1);
+ }
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_HEURISTICS_H_
+#define _QDEVICE_HEURISTICS_H_
+
+#include "dynar.h"
+#include "qdevice-advanced-settings.h"
+#include "send-buffer-list.h"
+#include "qdevice-heuristics-instance.h"
+#include "qdevice-heuristics-log.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void qdevice_heuristics_init(struct qdevice_heuristics_instance *instance,
+ struct qdevice_advanced_settings *advanced_settings);
+
+extern void qdevice_heuristics_destroy(struct qdevice_heuristics_instance *instance);
+
+extern int qdevice_heuristics_exec(struct qdevice_heuristics_instance *instance,
+ int sync_in_progress);
+
+extern int qdevice_heuristics_waiting_for_result(
+ const struct qdevice_heuristics_instance *instance);
+
+extern int qdevice_heuristics_change_exec_list(
+ struct qdevice_heuristics_instance *instance,
+ const struct qdevice_heuristics_exec_list *new_exec_list, int sync_in_progress);
+
+extern int qdevice_heuristics_wait_for_initial_exec_result(
+ struct qdevice_heuristics_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_HEURISTICS_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 "qdevice-config.h"
+#include "qdevice-instance.h"
+#include "qdevice-heuristics-exec-list.h"
+#include "qdevice-log.h"
+#include "qdevice-model.h"
+#include "utils.h"
+
+int
+qdevice_instance_init(struct qdevice_instance *instance,
+ const struct qdevice_advanced_settings *advanced_settings)
+{
+
+ memset(instance, 0, sizeof(*instance));
+
+ node_list_init(&instance->config_node_list);
+
+ instance->vq_last_poll = ((time_t) -1);
+ instance->advanced_settings = advanced_settings;
+
+ return (0);
+}
+
+int
+qdevice_instance_destroy(struct qdevice_instance *instance)
+{
+
+ node_list_free(&instance->config_node_list);
+
+ return (0);
+}
+
+int
+qdevice_instance_configure_from_cmap_heuristics(struct qdevice_instance *instance)
+{
+ char *str;
+ long int li;
+ char *ep;
+ int i;
+ int res;
+ cs_error_t cs_err;
+ cmap_iter_handle_t iter_handle;
+ char key_name[CMAP_KEYNAME_MAXLEN + 1];
+ size_t value_len;
+ cmap_value_types_t type;
+ struct qdevice_heuristics_exec_list tmp_exec_list;
+ struct qdevice_heuristics_exec_list *exec_list;
+ char *command;
+ char exec_name[CMAP_KEYNAME_MAXLEN + 1];
+ char tmp_key[CMAP_KEYNAME_MAXLEN + 1];
+ size_t no_execs;
+ int send_exec_list;
+
+ instance->heuristics_instance.timeout = instance->heartbeat_interval / 2;
+ if (cmap_get_string(instance->cmap_handle,
+ "quorum.device.heuristics.timeout", &str) == CS_OK) {
+ li = strtol(str, &ep, 10);
+ if (li < instance->advanced_settings->heuristics_min_timeout ||
+ li > instance->advanced_settings->heuristics_max_timeout || *ep != '\0') {
+ qdevice_log(LOG_ERR, "heuristics.timeout must be valid number in "
+ "range <%"PRIu32",%"PRIu32">",
+ instance->advanced_settings->heuristics_min_timeout,
+ instance->advanced_settings->heuristics_max_timeout);
+
+ free(str);
+ return (-1);
+ } else {
+ instance->heuristics_instance.timeout = li;
+ }
+
+ free(str);
+ }
+
+ instance->heuristics_instance.sync_timeout = instance->sync_heartbeat_interval / 2;
+ if (cmap_get_string(instance->cmap_handle,
+ "quorum.device.heuristics.sync_timeout", &str) == CS_OK) {
+ li = strtol(str, &ep, 10);
+ if (li < instance->advanced_settings->heuristics_min_timeout ||
+ li > instance->advanced_settings->heuristics_max_timeout || *ep != '\0') {
+ qdevice_log(LOG_ERR, "heuristics.sync_timeout must be valid number in "
+ "range <%"PRIu32",%"PRIu32">",
+ instance->advanced_settings->heuristics_min_timeout,
+ instance->advanced_settings->heuristics_max_timeout);
+
+ free(str);
+ return (-1);
+ } else {
+ instance->heuristics_instance.sync_timeout = li;
+ }
+
+ free(str);
+ }
+
+ instance->heuristics_instance.interval = instance->heartbeat_interval * 3;
+ if (cmap_get_string(instance->cmap_handle,
+ "quorum.device.heuristics.interval", &str) == CS_OK) {
+ li = strtol(str, &ep, 10);
+ if (li < instance->advanced_settings->heuristics_min_interval ||
+ li > instance->advanced_settings->heuristics_max_interval || *ep != '\0') {
+ qdevice_log(LOG_ERR, "heuristics.interval must be valid number in "
+ "range <%"PRIu32",%"PRIu32">",
+ instance->advanced_settings->heuristics_min_interval,
+ instance->advanced_settings->heuristics_max_interval);
+
+ free(str);
+ return (-1);
+ } else {
+ instance->heuristics_instance.interval = li;
+ }
+
+ free(str);
+ }
+
+ instance->heuristics_instance.mode = QDEVICE_DEFAULT_HEURISTICS_MODE;
+
+ if (cmap_get_string(instance->cmap_handle, "quorum.device.heuristics.mode", &str) == CS_OK) {
+ if ((i = utils_parse_bool_str(str)) == -1) {
+ if (strcasecmp(str, "sync") != 0) {
+ qdevice_log(LOG_ERR, "quorum.device.heuristics.mode value is not valid.");
+
+ free(str);
+ return (-1);
+ } else {
+ instance->heuristics_instance.mode = QDEVICE_HEURISTICS_MODE_SYNC;
+ }
+ } else {
+ if (i == 1) {
+ instance->heuristics_instance.mode = QDEVICE_HEURISTICS_MODE_ENABLED;
+ } else {
+ instance->heuristics_instance.mode = QDEVICE_HEURISTICS_MODE_DISABLED;
+ }
+ }
+
+ free(str);
+ }
+
+ send_exec_list = 0;
+ exec_list = NULL;
+ qdevice_heuristics_exec_list_init(&tmp_exec_list);
+
+ if (instance->heuristics_instance.mode == QDEVICE_HEURISTICS_MODE_DISABLED) {
+ exec_list = NULL;
+ send_exec_list = 1;
+ } else if (instance->heuristics_instance.mode == QDEVICE_HEURISTICS_MODE_ENABLED ||
+ instance->heuristics_instance.mode == QDEVICE_HEURISTICS_MODE_SYNC) {
+ /*
+ * Walk thru list of commands to exec
+ */
+ cs_err = cmap_iter_init(instance->cmap_handle, "quorum.device.heuristics.exec_", &iter_handle);
+ if (cs_err != CS_OK) {
+ qdevice_log(LOG_ERR, "Can't iterate quorum.device.heuristics.exec_ keys. "
+ "Error %s", cs_strerror(cs_err));
+
+ return (-1);
+ }
+
+ while ((cs_err = cmap_iter_next(instance->cmap_handle, iter_handle, key_name,
+ &value_len, &type)) == CS_OK) {
+ if (type != CMAP_VALUETYPE_STRING) {
+ qdevice_log(LOG_WARNING, "%s key is not of string type. Ignoring");
+ continue ;
+ }
+
+ res = sscanf(key_name, "quorum.device.heuristics.exec_%[^.]%s", exec_name, tmp_key);
+ if (res != 1) {
+ qdevice_log(LOG_WARNING, "%s key is not correct heuristics exec name. Ignoring");
+ continue ;
+ }
+
+ cs_err = cmap_get_string(instance->cmap_handle, key_name, &command);
+ if (cs_err != CS_OK) {
+ qdevice_log(LOG_WARNING, "Can't get value of %s key. Ignoring");
+ continue ;
+ }
+
+ if (qdevice_heuristics_exec_list_add(&tmp_exec_list, exec_name, command) == NULL) {
+ qdevice_log(LOG_WARNING, "Can't store value of %s key into list. Ignoring");
+ }
+
+ free(command);
+ }
+
+ no_execs = qdevice_heuristics_exec_list_size(&tmp_exec_list);
+
+ if (no_execs == 0) {
+ qdevice_log(LOG_INFO, "No valid heuristics execs defined. Disabling heuristics.");
+ instance->heuristics_instance.mode = QDEVICE_HEURISTICS_MODE_DISABLED;
+ exec_list = NULL;
+ send_exec_list = 1;
+ } else if (no_execs > instance->advanced_settings->heuristics_max_execs) {
+ qdevice_log(LOG_ERR, "Too much (%zu) heuristics execs defined (max is %zu)."
+ " Disabling heuristics.", no_execs,
+ instance->advanced_settings->heuristics_max_execs);
+ instance->heuristics_instance.mode = QDEVICE_HEURISTICS_MODE_DISABLED;
+ exec_list = NULL;
+ send_exec_list = 1;
+ } else if (qdevice_heuristics_exec_list_eq(&tmp_exec_list,
+ &instance->heuristics_instance.exec_list) == 1) {
+ qdevice_log(LOG_DEBUG, "Heuristics list is unchanged");
+ send_exec_list = 0;
+ } else {
+ qdevice_log(LOG_DEBUG, "Heuristics list changed");
+ exec_list = &tmp_exec_list;
+ send_exec_list = 1;
+ }
+
+ } else {
+ qdevice_log(LOG_CRIT, "Undefined heuristics mode");
+ exit(1);
+ }
+
+ if (send_exec_list) {
+ if (qdevice_heuristics_change_exec_list(&instance->heuristics_instance,
+ exec_list, instance->sync_in_progress) != 0) {
+ return (-1);
+ }
+ }
+
+ qdevice_heuristics_exec_list_free(&tmp_exec_list);
+
+ return (0);
+}
+
+int
+qdevice_instance_configure_from_cmap(struct qdevice_instance *instance)
+{
+ char *str;
+
+ if (cmap_get_string(instance->cmap_handle, "quorum.device.model", &str) != CS_OK) {
+ qdevice_log(LOG_ERR, "Can't read quorum.device.model cmap key.");
+
+ return (-1);
+ }
+
+ if (qdevice_model_str_to_type(str, &instance->model_type) != 0) {
+ qdevice_log(LOG_ERR, "Configured device model %s is not supported.", str);
+ free(str);
+
+ return (-1);
+ }
+ free(str);
+
+ if (cmap_get_uint32(instance->cmap_handle, "runtime.votequorum.this_node_id",
+ &instance->node_id) != CS_OK) {
+ qdevice_log(LOG_ERR, "Unable to retrieve this node nodeid.");
+
+ return (-1);
+ }
+
+ if (cmap_get_uint32(instance->cmap_handle, "quorum.device.timeout", &instance->heartbeat_interval) != CS_OK) {
+ instance->heartbeat_interval = VOTEQUORUM_QDEVICE_DEFAULT_TIMEOUT;
+ }
+
+ if (cmap_get_uint32(instance->cmap_handle, "quorum.device.sync_timeout",
+ &instance->sync_heartbeat_interval) != CS_OK) {
+ instance->sync_heartbeat_interval = VOTEQUORUM_QDEVICE_DEFAULT_SYNC_TIMEOUT;
+ }
+
+ if (qdevice_instance_configure_from_cmap_heuristics(instance) != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_INSTANCE_H_
+#define _QDEVICE_INSTANCE_H_
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <corosync/cmap.h>
+#include <corosync/votequorum.h>
+
+#include "qdevice-advanced-settings.h"
+#include "qdevice-heuristics.h"
+#include "qdevice-model-type.h"
+#include "node-list.h"
+#include "unix-socket-ipc.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qdevice_instance {
+ cmap_handle_t cmap_handle;
+ int cmap_poll_fd;
+ int cmap_reload_in_progress;
+ cmap_track_handle_t cmap_reload_track_handle;
+ cmap_track_handle_t cmap_nodelist_track_handle;
+ cmap_track_handle_t cmap_logging_track_handle;
+ cmap_track_handle_t cmap_heuristics_track_handle;
+
+ votequorum_handle_t votequorum_handle;
+ int votequorum_poll_fd;
+
+ struct unix_socket_ipc local_ipc;
+
+ enum qdevice_model_type model_type;
+
+ uint32_t node_id;
+ uint32_t heartbeat_interval; /* Heartbeat interval during normal operation */
+ uint32_t sync_heartbeat_interval; /* Heartbeat interval during corosync sync */
+
+ struct node_list config_node_list;
+ int config_node_list_version_set;
+ uint64_t config_node_list_version;
+
+ /*
+ * Copy of votequorum_quorum_notify_fn callback paramters.
+ * Set after model callback is called.
+ */
+ uint32_t vq_quorum_quorate;
+ uint32_t vq_quorum_node_list_entries;
+ votequorum_node_t *vq_quorum_node_list;
+
+ /*
+ * Copy of current votequorum_nodelist_notify_fn callback parameters.
+ * Set after model callback qdevice_votequorum_node_list_notify_callback is called.
+ */
+ uint8_t vq_node_list_initial_ring_id_set;
+ votequorum_ring_id_t vq_node_list_ring_id;
+ uint32_t vq_node_list_entries;
+ uint32_t *vq_node_list;
+ uint8_t vq_node_list_initial_heuristics_finished;
+ enum qdevice_heuristics_exec_result vq_node_list_heuristics_result;
+
+ /*
+ * Copy of current votequorum_nodelist_notify_fn callback ring id
+ * It's set before any callback is called and used for qdevice_votequorum_poll
+ */
+ votequorum_ring_id_t vq_poll_ring_id;
+
+ /*
+ * Copy of votequorum_expectedvotes_notify_fn callback parameters.
+ * Set after model callback is called.
+ */
+ uint32_t vq_expected_votes;
+
+ time_t vq_last_poll;
+ int vq_last_poll_cast_vote;
+
+ void *model_data;
+
+ const struct qdevice_advanced_settings *advanced_settings;
+
+ int sync_in_progress;
+
+ struct qdevice_heuristics_instance heuristics_instance;
+};
+
+extern int qdevice_instance_init(struct qdevice_instance *instance,
+ const struct qdevice_advanced_settings *advanced_settings);
+
+extern int qdevice_instance_destroy(struct qdevice_instance *instance);
+
+extern int qdevice_instance_configure_from_cmap(struct qdevice_instance *instance);
+
+extern int qdevice_instance_configure_from_cmap_heuristics(struct qdevice_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_INSTANCE_H_ */
--- /dev/null
+/*
+ * 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 "qdevice-ipc-cmd.h"
+#include "qdevice-log.h"
+#include "qdevice-model.h"
+#include "dynar-str.h"
+#include "utils.h"
+
+static int
+qdevice_ipc_cmd_status_add_header(struct qdevice_instance *instance, struct dynar *outbuf,
+ int verbose)
+{
+
+ return ((dynar_str_catf(outbuf, "Qdevice information\n") != -1) &&
+ (dynar_str_catf(outbuf, "-------------------\n") != -1));
+}
+
+static int
+qdevice_ipc_cmd_status_add_model(struct qdevice_instance *instance, struct dynar *outbuf,
+ int verbose)
+{
+
+ return (dynar_str_catf(outbuf, "Model:\t\t\t%s\n",
+ qdevice_model_type_to_str(instance->model_type)) != -1);
+}
+
+static int
+qdevice_ipc_cmd_status_add_nodeid(struct qdevice_instance *instance, struct dynar *outbuf,
+ int verbose)
+{
+
+ return (dynar_str_catf(outbuf, "Node ID:\t\t"UTILS_PRI_NODE_ID"\n",
+ instance->node_id) != -1);
+}
+
+static int
+qdevice_ipc_cmd_status_add_intervals(struct qdevice_instance *instance, struct dynar *outbuf,
+ int verbose)
+{
+
+ if (!verbose) {
+ return (1);
+ }
+
+ return ((dynar_str_catf(outbuf, "HB interval:\t\t%"PRIu32"ms\n",
+ instance->heartbeat_interval) != -1) &&
+ (dynar_str_catf(outbuf, "Sync HB interval:\t%"PRIu32"ms\n",
+ instance->sync_heartbeat_interval) != -1));
+}
+
+static int
+qdevice_ipc_cmd_status_add_config_node_list(struct qdevice_instance *instance, struct dynar *outbuf,
+ int verbose)
+{
+ struct node_list_entry *node_info;
+ size_t zi;
+
+ if (instance->config_node_list_version_set) {
+ if (dynar_str_catf(outbuf, "Configuration version:\t"UTILS_PRI_CONFIG_VERSION"\n",
+ instance->config_node_list_version) == -1) {
+ return (0);
+ }
+ }
+
+ if (dynar_str_catf(outbuf, "Configured node list:\n") == -1) {
+ return (0);
+ }
+
+ zi = 0;
+
+ TAILQ_FOREACH(node_info, &instance->config_node_list, entries) {
+ if ((dynar_str_catf(outbuf, " %zu\tNode ID = "UTILS_PRI_NODE_ID, zi,
+ node_info->node_id) == -1) ||
+ (node_info->data_center_id != 0 && dynar_str_catf(outbuf, ", Data center ID = "
+ UTILS_PRI_DATACENTER_ID, node_info->data_center_id) == -1) ||
+ (dynar_str_catf(outbuf, "\n") == -1)) {
+ return (0);
+ }
+
+ zi++;
+ }
+
+ return (1);
+}
+
+static int
+qdevice_ipc_cmd_status_add_membership_node_list(struct qdevice_instance *instance, struct dynar *outbuf,
+ int verbose)
+{
+ uint32_t u32;
+
+ if (verbose && dynar_str_catf(outbuf, "Ring ID:\t\t"UTILS_PRI_RING_ID"\n",
+ instance->vq_node_list_ring_id.nodeid, instance->vq_node_list_ring_id.seq) == -1) {
+ return (0);
+ }
+
+ if (dynar_str_catf(outbuf, "Membership node list:\t") == -1) {
+ return (0);
+ }
+
+ for (u32 = 0; u32 < instance->vq_node_list_entries; u32++) {
+ if (u32 != 0) {
+ if (dynar_str_catf(outbuf, ", ") == -1) {
+ return (0);
+ }
+ }
+
+ if (dynar_str_catf(outbuf, UTILS_PRI_NODE_ID, instance->vq_node_list[u32]) == -1) {
+ return (0);
+ }
+ }
+
+ if (dynar_str_catf(outbuf, "\n") == -1) {
+ return (0);
+ }
+
+ return (1);
+}
+
+static const char *
+qdevice_ipc_cmd_vq_nodestate_to_str(uint32_t state)
+{
+
+ switch (state) {
+ case VOTEQUORUM_NODESTATE_MEMBER: return ("member"); break;
+ case VOTEQUORUM_NODESTATE_DEAD: return ("dead"); break;
+ case VOTEQUORUM_NODESTATE_LEAVING: return ("leaving"); break;
+ default:
+ qdevice_log(LOG_ERR, "qdevice_ipc_cmd_vq_nodestate_to_str: Unhandled votequorum "
+ "node state %"PRIu32, state);
+ exit(1);
+ break;
+ }
+
+ return ("Unhandled votequorum node state");
+}
+
+static int
+qdevice_ipc_cmd_status_add_quorum_node_list(struct qdevice_instance *instance, struct dynar *outbuf,
+ int verbose)
+{
+ uint32_t u32;
+ votequorum_node_t *node;
+
+ if (!verbose) {
+ return (1);
+ }
+
+ if (dynar_str_catf(outbuf, "Quorate:\t\t%s\n",
+ (instance->vq_quorum_quorate ? "Yes" : "No")) == -1) {
+ return (0);
+ }
+
+ if (dynar_str_catf(outbuf, "Quorum node list:\n") == -1) {
+ return (0);
+ }
+
+ for (u32 = 0; u32 < instance->vq_quorum_node_list_entries; u32++) {
+ node = &instance->vq_quorum_node_list[u32];
+
+ if (node->nodeid == 0) {
+ continue;
+ }
+
+ if (dynar_str_catf(outbuf, " %"PRIu32"\tNode ID = "UTILS_PRI_NODE_ID
+ ", State = %s\n", u32, node->nodeid,
+ qdevice_ipc_cmd_vq_nodestate_to_str(node->state)) == -1) {
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+static int
+qdevice_ipc_cmd_status_add_expected_votes(struct qdevice_instance *instance, struct dynar *outbuf,
+ int verbose)
+{
+
+ if (!verbose) {
+ return (1);
+ }
+
+ return (dynar_str_catf(outbuf, "Expected votes:\t\t"UTILS_PRI_EXPECTED_VOTES"\n",
+ instance->vq_expected_votes) != -1);
+}
+
+static int
+qdevice_ipc_cmd_status_add_last_poll(struct qdevice_instance *instance, struct dynar *outbuf,
+ int verbose)
+{
+ struct tm tm_res;
+
+ if (!verbose) {
+ return (1);
+ }
+
+ if (instance->vq_last_poll == ((time_t) -1)) {
+ return (dynar_str_catf(outbuf, "Last poll call:\t\tNever\n") != -1);
+ }
+
+ localtime_r(&instance->vq_last_poll, &tm_res);
+
+ if (dynar_str_catf(outbuf, "Last poll call:\t\t%04d-%02d-%02dT%02d:%02d:%02d%s\n",
+ tm_res.tm_year + 1900, tm_res.tm_mon + 1, tm_res.tm_mday,
+ tm_res.tm_hour, tm_res.tm_min, tm_res.tm_sec,
+ (instance->vq_last_poll_cast_vote ? " (cast vote)" : "")) == -1) {
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+qdevice_ipc_cmd_status_add_heuristics(struct qdevice_instance *instance, struct dynar *outbuf,
+ int verbose)
+{
+
+ if (!verbose) {
+ return (1);
+ }
+
+ return (dynar_str_catf(outbuf, "Heuristics:\t\t%s\n",
+ qdevice_heuristics_mode_to_str(instance->heuristics_instance.mode)) != 0);
+}
+
+int
+qdevice_ipc_cmd_status(struct qdevice_instance *instance, struct dynar *outbuf, int verbose)
+{
+
+ if (qdevice_ipc_cmd_status_add_header(instance, outbuf, verbose) &&
+ qdevice_ipc_cmd_status_add_model(instance, outbuf, verbose) &&
+ qdevice_ipc_cmd_status_add_nodeid(instance, outbuf, verbose) &&
+ qdevice_ipc_cmd_status_add_intervals(instance, outbuf, verbose) &&
+ qdevice_ipc_cmd_status_add_config_node_list(instance, outbuf, verbose) &&
+ qdevice_ipc_cmd_status_add_heuristics(instance, outbuf, verbose) &&
+ qdevice_ipc_cmd_status_add_membership_node_list(instance, outbuf, verbose) &&
+ qdevice_ipc_cmd_status_add_quorum_node_list(instance, outbuf, verbose) &&
+ qdevice_ipc_cmd_status_add_expected_votes(instance, outbuf, verbose) &&
+ qdevice_ipc_cmd_status_add_last_poll(instance, outbuf, verbose) &&
+ dynar_str_catf(outbuf, "\n") != -1 &&
+ qdevice_model_ipc_cmd_status(instance, outbuf, verbose) != -1) {
+ return (0);
+ }
+
+ return (-1);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QDEVICE_IPC_CMD_H_
+#define _QDEVICE_IPC_CMD_H_
+
+#include "dynar.h"
+#include "qdevice-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_ipc_cmd_status(struct qdevice_instance *instance, struct dynar *outbuf,
+ int verbose);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_IPC_CMD_H_ */
--- /dev/null
+/*
+ * 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 "qdevice-config.h"
+#include "qdevice-ipc.h"
+#include "qdevice-log.h"
+#include "unix-socket-ipc.h"
+#include "dynar-simple-lex.h"
+#include "dynar-str.h"
+#include "qdevice-ipc-cmd.h"
+
+int
+qdevice_ipc_init(struct qdevice_instance *instance)
+{
+ if (unix_socket_ipc_init(&instance->local_ipc,
+ instance->advanced_settings->local_socket_file,
+ instance->advanced_settings->local_socket_backlog,
+ instance->advanced_settings->ipc_max_clients,
+ instance->advanced_settings->ipc_max_receive_size,
+ instance->advanced_settings->ipc_max_send_size) != 0) {
+ qdevice_log_err(LOG_ERR, "Can't create unix socket");
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qdevice_ipc_close(struct qdevice_instance *instance)
+{
+ int res;
+
+ res = unix_socket_ipc_close(&instance->local_ipc);
+ if (res != 0) {
+ qdevice_log_err(LOG_WARNING, "Can't close local IPC");
+ }
+
+ return (res);
+}
+
+int
+qdevice_ipc_is_closed(struct qdevice_instance *instance)
+{
+
+ return (unix_socket_ipc_is_closed(&instance->local_ipc));
+}
+
+int
+qdevice_ipc_destroy(struct qdevice_instance *instance)
+{
+ int res;
+ struct unix_socket_client *client;
+ const struct unix_socket_client_list *ipc_client_list;
+
+ ipc_client_list = &instance->local_ipc.clients;
+
+ TAILQ_FOREACH(client, ipc_client_list, entries) {
+ free(client->user_data);
+ }
+
+ res = unix_socket_ipc_destroy(&instance->local_ipc);
+ if (res != 0) {
+ qdevice_log_err(LOG_WARNING, "Can't destroy local IPC");
+ }
+
+ return (res);
+}
+
+int
+qdevice_ipc_accept(struct qdevice_instance *instance, struct unix_socket_client **res_client)
+{
+ int res;
+ int accept_res;
+
+ accept_res = unix_socket_ipc_accept(&instance->local_ipc, res_client);
+
+ switch (accept_res) {
+ case -1:
+ qdevice_log_err(LOG_ERR, "Can't accept local IPC connection");
+ res = -1;
+ goto return_res;
+ break;
+ case -2:
+ qdevice_log(LOG_ERR, "Maximum IPC clients reached. Not accepting connection");
+ res = -1;
+ goto return_res;
+ break;
+ case -3:
+ qdevice_log(LOG_ERR, "Can't add client to list");
+ res = -1;
+ goto return_res;
+ break;
+ default:
+ unix_socket_client_read_line(*res_client, 1);
+ res = 0;
+ break;
+ }
+
+ (*res_client)->user_data = malloc(sizeof(struct qdevice_ipc_user_data));
+ if ((*res_client)->user_data == NULL) {
+ qdevice_log(LOG_ERR, "Can't alloc IPC client user data");
+ res = -1;
+ qdevice_ipc_client_disconnect(instance, *res_client);
+ } else {
+ memset((*res_client)->user_data, 0, sizeof(struct qdevice_ipc_user_data));
+ }
+
+return_res:
+ return (res);
+}
+
+void
+qdevice_ipc_client_disconnect(struct qdevice_instance *instance, struct unix_socket_client *client)
+{
+
+ free(client->user_data);
+ unix_socket_ipc_client_disconnect(&instance->local_ipc, client);
+}
+
+int
+qdevice_ipc_send_error(struct qdevice_instance *instance, struct unix_socket_client *client,
+ const char *error_fmt, ...)
+{
+ va_list ap;
+ int res;
+
+ va_start(ap, error_fmt);
+ res = ((dynar_str_cpy(&client->send_buffer, "Error\n") == 0) &&
+ (dynar_str_vcatf(&client->send_buffer, error_fmt, ap) > 0) &&
+ (dynar_str_cat(&client->send_buffer, "\n") == 0));
+
+ va_end(ap);
+
+ if (res) {
+ unix_socket_client_write_buffer(client, 1);
+ } else {
+ qdevice_log(LOG_ERR, "Can't send ipc error to client (buffer too small)");
+ }
+
+ return (res ? 0 : -1);
+}
+
+int
+qdevice_ipc_send_buffer(struct qdevice_instance *instance, struct unix_socket_client *client)
+{
+
+ if (dynar_str_prepend(&client->send_buffer, "OK\n") != 0) {
+ qdevice_log(LOG_ERR, "Can't send ipc message to client (buffer too small)");
+
+ if (qdevice_ipc_send_error(instance, client, "Internal IPC buffer too small") != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ unix_socket_client_write_buffer(client, 1);
+
+ return (0);
+}
+
+static void
+qdevice_ipc_parse_line(struct qdevice_instance *instance, struct unix_socket_client *client)
+{
+ struct dynar_simple_lex lex;
+ struct dynar *token;
+ char *str;
+ struct qdevice_ipc_user_data *ipc_user_data;
+ int verbose;
+
+ ipc_user_data = (struct qdevice_ipc_user_data *)client->user_data;
+
+ dynar_simple_lex_init(&lex, &client->receive_buffer, DYNAR_SIMPLE_LEX_TYPE_PLAIN);
+ token = dynar_simple_lex_token_next(&lex);
+
+ verbose = 0;
+
+ if (token == NULL) {
+ qdevice_log(LOG_ERR, "Can't alloc memory for simple lex");
+
+ if (qdevice_ipc_send_error(instance, client, "Command too long") != 0) {
+ client->schedule_disconnect = 1;
+ }
+
+ return;
+ }
+
+ str = dynar_data(token);
+ if (strcasecmp(str, "") == 0) {
+ qdevice_log(LOG_DEBUG, "IPC client doesn't send command");
+ if (qdevice_ipc_send_error(instance, client, "No command specified") != 0) {
+ client->schedule_disconnect = 1;
+ }
+ } else if (strcasecmp(str, "shutdown") == 0) {
+ qdevice_log(LOG_DEBUG, "IPC client requested shutdown");
+
+ ipc_user_data->shutdown_requested = 1;
+
+ if (qdevice_ipc_send_buffer(instance, client) != 0) {
+ client->schedule_disconnect = 1;
+ }
+ } else if (strcasecmp(str, "status") == 0) {
+ token = dynar_simple_lex_token_next(&lex);
+
+ if (token != NULL && (str = dynar_data(token), strcmp(str, "")) != 0) {
+ if (strcasecmp(str, "verbose") == 0) {
+ verbose = 1;
+ }
+ }
+
+ if (qdevice_ipc_cmd_status(instance, &client->send_buffer, verbose) != 0) {
+ if (qdevice_ipc_send_error(instance, client, "Can't get QDevice status") != 0) {
+ client->schedule_disconnect = 1;
+ }
+ } else {
+ if (qdevice_ipc_send_buffer(instance, client) != 0) {
+ client->schedule_disconnect = 1;
+ }
+ }
+ } else {
+ qdevice_log(LOG_DEBUG, "IPC client sent unknown command");
+ if (qdevice_ipc_send_error(instance, client, "Unknown command '%s'", str) != 0) {
+ client->schedule_disconnect = 1;
+ }
+ }
+
+ dynar_simple_lex_destroy(&lex);
+}
+
+void
+qdevice_ipc_io_read(struct qdevice_instance *instance, struct unix_socket_client *client)
+{
+ int res;
+
+ res = unix_socket_client_io_read(client);
+
+ switch (res) {
+ case 0:
+ /*
+ * Partial read
+ */
+ break;
+ case -1:
+ qdevice_log(LOG_DEBUG, "IPC client closed connection");
+ client->schedule_disconnect = 1;
+ break;
+ case -2:
+ qdevice_log(LOG_ERR, "Can't store message from IPC client. Disconnecting client.");
+ client->schedule_disconnect = 1;
+ break;
+ case -3:
+ qdevice_log_err(LOG_ERR, "Can't receive message from IPC client. Disconnecting client.");
+ client->schedule_disconnect = 1;
+ break;
+ case 1:
+ /*
+ * Full message received
+ */
+ unix_socket_client_read_line(client, 0);
+
+ qdevice_ipc_parse_line(instance, client);
+ break;
+ }
+}
+
+void
+qdevice_ipc_io_write(struct qdevice_instance *instance, struct unix_socket_client *client)
+{
+ int res;
+ struct qdevice_ipc_user_data *ipc_user_data;
+
+ ipc_user_data = (struct qdevice_ipc_user_data *)client->user_data;
+
+ res = unix_socket_client_io_write(client);
+
+ switch (res) {
+ case 0:
+ /*
+ * Partial send
+ */
+ break;
+ case -1:
+ qdevice_log(LOG_DEBUG, "IPC client closed connection");
+ client->schedule_disconnect = 1;
+ break;
+ case -2:
+ qdevice_log_err(LOG_ERR, "Can't send message to IPC client. Disconnecting client");
+ client->schedule_disconnect = 1;
+ break;
+ case 1:
+ /*
+ * Full message sent
+ */
+ unix_socket_client_write_buffer(client, 0);
+ client->schedule_disconnect = 1;
+
+ if (ipc_user_data->shutdown_requested) {
+ qdevice_ipc_close(instance);
+ }
+
+ break;
+ }
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QDEVICE_IPC_H_
+#define _QDEVICE_IPC_H_
+
+#include "qdevice-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qdevice_ipc_user_data {
+ void *model_data;
+ int shutdown_requested;
+};
+
+extern int qdevice_ipc_init(struct qdevice_instance *instance);
+
+extern int qdevice_ipc_close(struct qdevice_instance *instance);
+
+extern int qdevice_ipc_destroy(struct qdevice_instance *instance);
+
+extern int qdevice_ipc_accept(struct qdevice_instance *instance,
+ struct unix_socket_client **res_client);
+
+extern void qdevice_ipc_client_disconnect(struct qdevice_instance *instance,
+ struct unix_socket_client *client);
+
+extern void qdevice_ipc_io_read(struct qdevice_instance *instance,
+ struct unix_socket_client *client);
+
+extern void qdevice_ipc_io_write(struct qdevice_instance *instance,
+ struct unix_socket_client *client);
+
+extern int qdevice_ipc_send_error(struct qdevice_instance *instance,
+ struct unix_socket_client *client, const char *error_fmt, ...)
+ __attribute__((__format__(__printf__, 3, 4)));
+
+extern int qdevice_ipc_send_buffer(struct qdevice_instance *instance,
+ struct unix_socket_client *client);
+
+extern int qdevice_ipc_is_closed(struct qdevice_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_IPC_H_ */
--- /dev/null
+/*
+ * 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 "qdevice-log-debug.h"
+#include "qdevice-log.h"
+#include "utils.h"
+
+void
+qdevice_log_debug_dump_node_list(const struct node_list *nlist)
+{
+ struct node_list_entry *node_info;
+ size_t zi;
+
+ qdevice_log(LOG_DEBUG, " Node list:");
+
+ zi = 0;
+
+ TAILQ_FOREACH(node_info, nlist, entries) {
+ qdevice_log(LOG_DEBUG, " %zu node_id = "UTILS_PRI_NODE_ID", "
+ "data_center_id = "UTILS_PRI_DATACENTER_ID", node_state = %s",
+ zi, node_info->node_id, node_info->data_center_id,
+ tlv_node_state_to_str(node_info->node_state));
+ zi++;
+ }
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QDEVICE_LOG_DEBUG_H_
+#define _QDEVICE_LOG_DEBUG_H_
+
+#include "node-list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void qdevice_log_debug_dump_node_list(const struct node_list *nlist);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_LOG_DEBUG_H_ */
--- /dev/null
+/*
+ * 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 "qdevice-log.h"
+#include "qdevice-config.h"
+#include "utils.h"
+
+static int qdevice_log_global_force_debug;
+
+struct qdevice_log_syslog_names {
+ const char *prio_name;
+ int priority;
+};
+
+static struct qdevice_log_syslog_names qdevice_log_priority_names[] = {
+ { "alert", LOG_ALERT },
+ { "crit", LOG_CRIT },
+ { "debug", LOG_DEBUG },
+ { "emerg", LOG_EMERG },
+ { "err", LOG_ERR },
+ { "error", LOG_ERR },
+ { "info", LOG_INFO },
+ { "notice", LOG_NOTICE },
+ { "warning", LOG_WARNING },
+ { NULL, -1 }};
+
+static int
+qdevice_log_priority_str_to_int(const char *priority_str)
+{
+ unsigned int i;
+
+ for (i = 0; qdevice_log_priority_names[i].prio_name != NULL; i++) {
+ if (strcasecmp(priority_str, qdevice_log_priority_names[i].prio_name) == 0) {
+ return (qdevice_log_priority_names[i].priority);
+ }
+ }
+
+ return (-1);
+}
+
+void
+qdevice_log_configure(struct qdevice_instance *instance)
+{
+ int to_stderr;
+ int to_syslog;
+ int syslog_facility;
+ int syslog_priority;
+ int logfile_priority;
+ int debug;
+ char *str;
+ int i;
+ int fileline;
+ int timestamp;
+ int function_name;
+ char log_format_syslog[64];
+ char log_format_stderr[64];
+
+ to_stderr = QDEVICE_LOG_DEFAULT_TO_STDERR;
+ if (cmap_get_string(instance->cmap_handle, "logging.to_stderr", &str) == CS_OK) {
+ if ((i = utils_parse_bool_str(str)) == -1) {
+ qdevice_log(LOG_WARNING, "logging.to_stderr value is not valid");
+ } else {
+ to_stderr = i;
+ }
+ free(str);
+ }
+
+ if (cmap_get_string(instance->cmap_handle,
+ "logging.logger_subsys." QDEVICE_LOG_SUBSYS ".to_stderr", &str) == CS_OK) {
+ if ((i = utils_parse_bool_str(str)) == -1) {
+ qdevice_log(LOG_WARNING,
+ "logging.logger_subsys." QDEVICE_LOG_SUBSYS ".to_stderr value is not valid.");
+ } else {
+ to_stderr = i;
+ }
+ free(str);
+ }
+
+ to_syslog = QDEVICE_LOG_DEFAULT_TO_SYSLOG;
+ if (cmap_get_string(instance->cmap_handle, "logging.to_syslog", &str) == CS_OK) {
+ if ((i = utils_parse_bool_str(str)) == -1) {
+ qdevice_log(LOG_WARNING, "logging.to_syslog value is not valid");
+ } else {
+ to_syslog = i;
+ }
+ free(str);
+ }
+
+ if (cmap_get_string(instance->cmap_handle,
+ "logging.logger_subsys." QDEVICE_LOG_SUBSYS ".to_syslog", &str) == CS_OK) {
+ if ((i = utils_parse_bool_str(str)) == -1) {
+ qdevice_log(LOG_WARNING,
+ "logging.logger_subsys." QDEVICE_LOG_SUBSYS ".to_syslog value is not valid.");
+ } else {
+ to_syslog = i;
+ }
+ free(str);
+ }
+
+ syslog_facility = QDEVICE_LOG_DEFAULT_SYSLOG_FACILITY;
+ if (cmap_get_string(instance->cmap_handle, "logging.syslog_facility", &str) == CS_OK) {
+ if ((i = qb_log_facility2int(str)) < 0) {
+ qdevice_log(LOG_WARNING, "logging.syslog_facility value is not valid");
+ } else {
+ syslog_facility = i;
+ }
+
+ free(str);
+ }
+
+ if (cmap_get_string(instance->cmap_handle,
+ "logging.logger_subsys." QDEVICE_LOG_SUBSYS ".syslog_facility", &str) == CS_OK) {
+ if ((i = qb_log_facility2int(str)) < 0) {
+ qdevice_log(LOG_WARNING,
+ "logging.logger_subsys." QDEVICE_LOG_SUBSYS ".syslog_facility value is not valid.");
+ } else {
+ syslog_facility = i;
+ }
+ free(str);
+ }
+
+ syslog_priority = QDEVICE_LOG_DEFAULT_SYSLOG_PRIORITY;
+ if (cmap_get_string(instance->cmap_handle, "logging.syslog_priority", &str) == CS_OK) {
+ if ((i = qdevice_log_priority_str_to_int(str)) < 0) {
+ qdevice_log(LOG_WARNING, "logging.syslog_priority value is not valid");
+ } else {
+ syslog_priority = i;
+ }
+
+ free(str);
+ }
+
+ if (cmap_get_string(instance->cmap_handle,
+ "logging.logger_subsys." QDEVICE_LOG_SUBSYS ".syslog_priority", &str) == CS_OK) {
+ if ((i = qdevice_log_priority_str_to_int(str)) < 0) {
+ qdevice_log(LOG_WARNING,
+ "logging.logger_subsys." QDEVICE_LOG_SUBSYS ".syslog_priority value is not valid.");
+ } else {
+ syslog_priority = i;
+ }
+ free(str);
+ }
+
+ logfile_priority = QDEVICE_LOG_DEFAULT_SYSLOG_PRIORITY;
+ if (cmap_get_string(instance->cmap_handle, "logging.logfile_priority", &str) == CS_OK) {
+ if ((i = qdevice_log_priority_str_to_int(str)) < 0) {
+ qdevice_log(LOG_WARNING, "logging.logfile_priority value is not valid");
+ } else {
+ logfile_priority = i;
+ }
+
+ free(str);
+ }
+
+ if (cmap_get_string(instance->cmap_handle,
+ "logging.logger_subsys." QDEVICE_LOG_SUBSYS ".logfile_priority", &str) == CS_OK) {
+ if ((i = qdevice_log_priority_str_to_int(str)) < 0) {
+ qdevice_log(LOG_WARNING,
+ "logging.logger_subsys." QDEVICE_LOG_SUBSYS ".logfile_priority value is not valid.");
+ } else {
+ logfile_priority = i;
+ }
+ free(str);
+ }
+
+ debug = QDEVICE_LOG_DEFAULT_DEBUG;
+ if (cmap_get_string(instance->cmap_handle, "logging.debug", &str) == CS_OK) {
+ if ((i = utils_parse_bool_str(str)) == -1) {
+ if (strcasecmp(str, "trace") == 0) {
+ debug = 1;
+ } else {
+ qdevice_log(LOG_WARNING, "logging.debug value is not valid");
+ }
+ } else {
+ debug = i;
+ }
+ free(str);
+ }
+
+ if (cmap_get_string(instance->cmap_handle,
+ "logging.logger_subsys." QDEVICE_LOG_SUBSYS ".debug", &str) == CS_OK) {
+ if ((i = utils_parse_bool_str(str)) == -1) {
+ if (strcasecmp(str, "trace") == 0) {
+ debug = 1;
+ } else {
+ qdevice_log(LOG_WARNING,
+ "logging.logger_subsys." QDEVICE_LOG_SUBSYS ".debug value is not valid.");
+ }
+ } else {
+ debug = i;
+ }
+ free(str);
+ }
+
+ fileline = QDEVICE_LOG_DEFAULT_FILELINE;
+ if (cmap_get_string(instance->cmap_handle, "logging.fileline", &str) == CS_OK) {
+ if ((i = utils_parse_bool_str(str)) == -1) {
+ qdevice_log(LOG_WARNING, "logging.fileline value is not valid");
+ } else {
+ fileline = i;
+ }
+ free(str);
+ }
+
+ timestamp = QDEVICE_LOG_DEFAULT_TIMESTAMP;
+ if (cmap_get_string(instance->cmap_handle, "logging.timestamp", &str) == CS_OK) {
+ if ((i = utils_parse_bool_str(str)) == -1) {
+ qdevice_log(LOG_WARNING, "logging.timestamp value is not valid");
+ } else {
+ timestamp = i;
+ }
+ free(str);
+ }
+
+ function_name = QDEVICE_LOG_DEFAULT_FUNCTION_NAME;
+ if (cmap_get_string(instance->cmap_handle, "logging.function_name", &str) == CS_OK) {
+ if ((i = utils_parse_bool_str(str)) == -1) {
+ qdevice_log(LOG_WARNING, "logging.function_name value is not valid");
+ } else {
+ function_name = i;
+ }
+ free(str);
+ }
+
+ strcpy(log_format_syslog, "");
+
+ if (fileline) {
+ strcat(log_format_syslog, "%f:");
+
+ if (function_name) {
+ strcat(log_format_syslog, "%n:");
+ }
+
+ strcat(log_format_syslog, "%l ");
+ }
+
+ strcat(log_format_syslog, "%b");
+
+ strcpy(log_format_stderr, "");
+ if (timestamp) {
+ strcpy(log_format_stderr, "%t %7p ");
+ }
+ strcat(log_format_stderr, log_format_syslog);
+
+ if (qdevice_log_global_force_debug) {
+ debug = 1;
+ }
+
+ /*
+ * Finally reconfigure log system
+ */
+ qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, to_stderr);
+ qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, to_syslog);
+ qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_FACILITY, syslog_facility);
+ qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+ qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*",
+ (debug ? LOG_DEBUG : syslog_priority));
+ qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_CLEAR_ALL, QB_LOG_FILTER_FILE, "*", LOG_TRACE);
+ qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*",
+ (debug ? LOG_DEBUG : logfile_priority));
+
+ qb_log_format_set(QB_LOG_STDERR, log_format_stderr);
+ qb_log_format_set(QB_LOG_SYSLOG, log_format_syslog);
+}
+
+void
+qdevice_log_init(struct qdevice_instance *instance, int force_debug)
+{
+ qdevice_log_global_force_debug = force_debug;
+
+ qb_log_init(QDEVICE_PROGRAM_NAME, QDEVICE_LOG_DEFAULT_SYSLOG_FACILITY,
+ QDEVICE_LOG_DEFAULT_SYSLOG_PRIORITY);
+
+ qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
+ qb_log_ctl(QB_LOG_STDOUT, QB_LOG_CONF_ENABLED, QB_FALSE);
+ qb_log_ctl(QB_LOG_BLACKBOX, QB_LOG_CONF_ENABLED, QB_FALSE);
+ qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
+
+ qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_INFO);
+ qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, "*", LOG_INFO);
+ qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_PRIORITY_BUMP, LOG_INFO - LOG_DEBUG);
+ qb_log_format_set(QB_LOG_STDERR, "%t %7p %b");
+
+ qdevice_log_configure(instance);
+}
+
+void
+qdevice_log_close(struct qdevice_instance *instance)
+{
+
+ qb_log_fini();
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QDEVICE_LOG_H_
+#define _QDEVICE_LOG_H_
+
+#include <qb/qbdefs.h>
+#include <qb/qblog.h>
+#include <prerror.h>
+
+#include "qdevice-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define qdevice_log qb_log
+#define qdevice_log_nss(priority, str) qdevice_log(priority, "%s (%d): %s", \
+ str, PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT));
+
+#define qdevice_log_err(priority, str) qdevice_log(priority, "%s (%d): %s", \
+ str, errno, strerror(errno));
+
+extern void qdevice_log_init(struct qdevice_instance *instance, int force_debug);
+
+extern void qdevice_log_configure(struct qdevice_instance *instance);
+
+extern void qdevice_log_close(struct qdevice_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_LOG_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <poll.h>
+
+#include "qdevice-model.h"
+#include "qdevice-model-net.h"
+#include "qdevice-log.h"
+#include "qdevice-net-cast-vote-timer.h"
+#include "qdevice-net-instance.h"
+#include "qdevice-net-ipc-cmd.h"
+#include "qdevice-net-algorithm.h"
+#include "qdevice-net-heuristics.h"
+#include "qdevice-net-poll.h"
+#include "qdevice-net-send.h"
+#include "qdevice-net-votequorum.h"
+#include "qnet-config.h"
+#include "nss-sock.h"
+
+int
+qdevice_model_net_init(struct qdevice_instance *instance)
+{
+
+ struct qdevice_net_instance *net_instance;
+
+ qdevice_log(LOG_DEBUG, "Initializing qdevice_net_instance");
+ if (qdevice_net_instance_init_from_cmap(instance) != 0) {
+ return (-1);
+ }
+
+ net_instance = instance->model_data;
+
+ qdevice_log(LOG_DEBUG, "Registering algorithms");
+ if (qdevice_net_algorithm_register_all() != 0) {
+ return (-1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Initializing NSS");
+ if (nss_sock_init_nss((net_instance->tls_supported != TLV_TLS_UNSUPPORTED ?
+ instance->advanced_settings->net_nss_db_dir : NULL)) != 0) {
+ qdevice_log_nss(LOG_ERR, "Can't init nss");
+ return (-1);
+ }
+
+ if (qdevice_net_cast_vote_timer_update(net_instance, TLV_VOTE_ASK_LATER) != 0) {
+ qdevice_log(LOG_ERR, "Can't update cast vote timer");
+ return (-1);
+ }
+
+ if (qdevice_net_algorithm_init(net_instance) != 0) {
+ qdevice_log(LOG_ERR, "Algorithm init failed");
+ return (-1);
+ }
+
+ if (qdevice_net_heuristics_init(net_instance) != 0) {
+ qdevice_log(LOG_ERR, "Can't initialize net heuristics");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qdevice_model_net_destroy(struct qdevice_instance *instance)
+{
+ struct qdevice_net_instance *net_instance;
+
+ net_instance = instance->model_data;
+
+ qdevice_log(LOG_DEBUG, "Destroying algorithm");
+ qdevice_net_algorithm_destroy(net_instance);
+
+ qdevice_log(LOG_DEBUG, "Destroying qdevice_net_instance");
+ qdevice_net_instance_destroy(net_instance);
+
+ qdevice_log(LOG_DEBUG, "Shutting down NSS");
+ SSL_ClearSessionCache();
+
+ if (NSS_Shutdown() != SECSuccess) {
+ qdevice_log_nss(LOG_WARNING, "Can't shutdown NSS");
+ }
+
+ if (PR_Cleanup() != PR_SUCCESS) {
+ qdevice_log_nss(LOG_WARNING, "Can't shutdown NSPR");
+ }
+
+ free(net_instance);
+
+ return (0);
+}
+
+static int
+qdevice_model_net_timer_connect_timeout(void *data1, void *data2)
+{
+ struct qdevice_net_instance *instance;
+
+ instance = (struct qdevice_net_instance *)data1;
+
+ qdevice_log(LOG_ERR, "Connect timeout");
+
+ instance->schedule_disconnect = 1;
+
+ instance->connect_timer = NULL;
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_CONNECT_TO_THE_SERVER;
+
+ return (0);
+}
+
+static PRIntn
+qdevice_model_net_get_af(const struct qdevice_net_instance *instance)
+{
+ PRIntn af;
+
+ af = PR_AF_UNSPEC;
+ if (instance->force_ip_version == 4) {
+ af = PR_AF_INET;
+ }
+
+ if (instance->force_ip_version == 6) {
+ af = PR_AF_INET6;
+ }
+
+ return (af);
+}
+
+int
+qdevice_model_net_run(struct qdevice_instance *instance)
+{
+ struct qdevice_net_instance *net_instance;
+ int try_connect;
+ int res;
+ enum tlv_vote vote;
+ int delay_before_reconnect;
+
+ net_instance = instance->model_data;
+
+ qdevice_log(LOG_DEBUG, "Executing qdevice-net");
+
+ try_connect = 1;
+ while (try_connect) {
+ net_instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT;
+ net_instance->socket = NULL;
+
+ net_instance->connect_timer = timer_list_add(&net_instance->main_timer_list,
+ net_instance->connect_timeout, qdevice_model_net_timer_connect_timeout,
+ (void *)net_instance, NULL);
+
+ if (net_instance->connect_timer == NULL) {
+ qdevice_log(LOG_CRIT, "Can't schedule connect timer");
+
+ try_connect = 0;
+ break;
+ }
+
+ qdevice_log(LOG_DEBUG, "Trying connect to qnetd server %s:%u (timeout = %ums)",
+ net_instance->host_addr, net_instance->host_port, net_instance->connect_timeout);
+
+ res = nss_sock_non_blocking_client_init(net_instance->host_addr,
+ net_instance->host_port, qdevice_model_net_get_af(net_instance),
+ &net_instance->non_blocking_client);
+ if (res == -1) {
+ qdevice_log_nss(LOG_ERR, "Can't initialize non blocking client connection");
+ }
+
+ res = nss_sock_non_blocking_client_try_next(&net_instance->non_blocking_client);
+ if (res == -1) {
+ qdevice_log_nss(LOG_ERR, "Can't connect to qnetd host");
+ nss_sock_non_blocking_client_destroy(&net_instance->non_blocking_client);
+ }
+
+ while (qdevice_net_poll(net_instance) == 0) {
+ };
+
+ if (net_instance->connect_timer != NULL) {
+ timer_list_delete(&net_instance->main_timer_list, net_instance->connect_timer);
+ net_instance->connect_timer = NULL;
+ }
+
+ if (net_instance->echo_request_timer != NULL) {
+ timer_list_delete(&net_instance->main_timer_list, net_instance->echo_request_timer);
+ net_instance->echo_request_timer = NULL;
+ }
+
+ try_connect = qdevice_net_disconnect_reason_try_reconnect(net_instance->disconnect_reason);
+
+ /*
+ * Unpause cast vote timer, because if it is paused we cannot remove tracking
+ */
+ qdevice_net_cast_vote_timer_set_paused(net_instance, 0);
+
+ vote = TLV_VOTE_NO_CHANGE;
+
+ if (qdevice_net_algorithm_disconnected(net_instance,
+ net_instance->disconnect_reason, &try_connect, &vote) != 0) {
+ qdevice_log(LOG_ERR, "Algorithm returned error, force exit");
+ return (-1);
+ } else {
+ qdevice_log(LOG_DEBUG, "Algorithm result vote is %s",
+ tlv_vote_to_str(vote));
+ }
+
+ if (qdevice_net_cast_vote_timer_update(net_instance, vote) != 0) {
+ qdevice_log(LOG_ERR, "qdevice_model_net_run fatal error. "
+ " Can't update cast vote timer vote");
+ }
+
+ if (qdevice_net_disconnect_reason_force_disconnect(net_instance->disconnect_reason)) {
+ try_connect = 0;
+ }
+
+ if (net_instance->socket != NULL) {
+ if (PR_Close(net_instance->socket) != PR_SUCCESS) {
+ qdevice_log_nss(LOG_WARNING, "Unable to close connection");
+ }
+ net_instance->socket = NULL;
+ }
+
+ if (!net_instance->non_blocking_client.destroyed) {
+ nss_sock_non_blocking_client_destroy(&net_instance->non_blocking_client);
+ }
+
+ if (net_instance->non_blocking_client.socket != NULL) {
+ if (PR_Close(net_instance->non_blocking_client.socket) != PR_SUCCESS) {
+ qdevice_log_nss(LOG_WARNING, "Unable to close non-blocking client connection");
+ }
+ net_instance->non_blocking_client.socket = NULL;
+ }
+
+ if (try_connect &&
+ net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT) {
+ /*
+ * Give qnetd server a little time before reconnect
+ */
+ delay_before_reconnect = random() %
+ (int)(net_instance->cast_vote_timer_interval * 0.9);
+
+ qdevice_log(LOG_DEBUG, "Sleeping for %u ms before reconnect",
+ delay_before_reconnect);
+ (void)poll(NULL, 0, delay_before_reconnect);
+ }
+
+ qdevice_net_instance_clean(net_instance);
+ }
+
+ return (0);
+}
+
+/*
+ * Called when cmap reload (or nodelist) was requested.
+ *
+ * nlist is node list
+ * config_version is valid only if config_version_set != 0
+ *
+ * Should return 0 if processing should continue or -1 to call exit
+ */
+int
+qdevice_model_net_config_node_list_changed(struct qdevice_instance *instance,
+ const struct node_list *nlist, int config_version_set, uint64_t config_version)
+{
+ struct qdevice_net_instance *net_instance;
+ int send_node_list;
+ enum tlv_vote vote;
+
+ net_instance = instance->model_data;
+
+ if (net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
+ /*
+ * Nodelist changed, but connection to qnetd not initiated yet.
+ */
+ send_node_list = 0;
+
+ if (net_instance->cast_vote_timer_vote == TLV_VOTE_ACK) {
+ vote = TLV_VOTE_NACK;
+ } else {
+ vote = TLV_VOTE_NO_CHANGE;
+ }
+ } else {
+ send_node_list = 1;
+ vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ if (qdevice_net_algorithm_config_node_list_changed(net_instance, nlist, config_version_set,
+ config_version, &send_node_list, &vote) != 0) {
+ qdevice_log(LOG_ERR, "Algorithm returned error, Disconnecting");
+
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_CONFIG_NODE_LIST_CHANGED_ERR;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ } else {
+ qdevice_log(LOG_DEBUG, "Algorithm decided to %s node list and result vote is %s",
+ (send_node_list ? "send" : "not send"), tlv_vote_to_str(vote));
+ }
+
+ if (qdevice_net_cast_vote_timer_update(net_instance, vote) != 0) {
+ qdevice_log(LOG_CRIT, "qdevice_model_net_config_node_list_changed fatal error. "
+ " Can't update cast vote timer vote");
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+
+ if (send_node_list) {
+ if (qdevice_net_send_config_node_list(net_instance, nlist, config_version_set,
+ config_version, 0) != 0) {
+ net_instance->schedule_disconnect = 1;
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+
+ return (0);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Called when cmap reload (or nodelist) was requested, but it was not possible to
+ * get node list.
+ *
+ * Should return 0 if processing should continue or -1 to call exit
+ */
+int
+qdevice_model_net_get_config_node_list_failed(struct qdevice_instance *instance)
+{
+ struct qdevice_net_instance *net_instance;
+
+ net_instance = instance->model_data;
+
+ net_instance->schedule_disconnect = 1;
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+
+ return (0);
+}
+
+int
+qdevice_model_net_votequorum_quorum_notify(struct qdevice_instance *instance,
+ uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[])
+{
+ struct qdevice_net_instance *net_instance;
+ int send_node_list;
+ enum tlv_vote vote;
+
+ net_instance = instance->model_data;
+
+ if (net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
+ /*
+ * Nodelist changed, but connection to qnetd not initiated yet.
+ */
+ send_node_list = 0;
+
+ if (net_instance->cast_vote_timer_vote == TLV_VOTE_ACK) {
+ vote = TLV_VOTE_NACK;
+ } else {
+ vote = TLV_VOTE_NO_CHANGE;
+ }
+ } else {
+ send_node_list = 1;
+ vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ if (qdevice_net_algorithm_votequorum_quorum_notify(net_instance, quorate,
+ node_list_entries, node_list, &send_node_list, &vote) != 0) {
+ qdevice_log(LOG_ERR, "Algorithm returned error. Disconnecting.");
+
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_QUORUM_NOTIFY_ERR;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ } else {
+ qdevice_log(LOG_DEBUG, "Algorithm decided to %s list and result vote is %s",
+ (send_node_list ? "send" : "not send"), tlv_vote_to_str(vote));
+ }
+
+ if (qdevice_net_cast_vote_timer_update(net_instance, vote) != 0) {
+ qdevice_log(LOG_CRIT, "qdevice_model_net_votequorum_quorum_notify fatal error. "
+ " Can't update cast vote timer vote");
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+
+ if (send_node_list) {
+ if (qdevice_net_send_quorum_node_list(net_instance,
+ (quorate ? TLV_QUORATE_QUORATE : TLV_QUORATE_INQUORATE),
+ node_list_entries, node_list) != 0) {
+ /*
+ * Fatal error -> schedule disconnect
+ */
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+ }
+
+ return (0);
+}
+
+int
+qdevice_model_net_votequorum_node_list_heuristics_notify(struct qdevice_instance *instance,
+ votequorum_ring_id_t votequorum_ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ enum qdevice_heuristics_exec_result heuristics_exec_result)
+{
+ struct qdevice_net_instance *net_instance;
+ struct tlv_ring_id tlv_rid;
+ enum tlv_vote vote;
+ enum tlv_heuristics heuristics;
+ int send_node_list;
+
+ net_instance = instance->model_data;
+
+ qdevice_net_votequorum_ring_id_to_tlv(&tlv_rid, &votequorum_ring_id);
+ heuristics = qdevice_net_heuristics_exec_result_to_tlv(heuristics_exec_result);
+
+ if (net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
+ /*
+ * Nodelist changed, but connection to qnetd not initiated yet.
+ */
+ send_node_list = 0;
+
+ if (net_instance->cast_vote_timer_vote == TLV_VOTE_ACK) {
+ vote = TLV_VOTE_NACK;
+ } else {
+ vote = TLV_VOTE_NO_CHANGE;
+ }
+ } else {
+ send_node_list = 1;
+ vote = TLV_VOTE_WAIT_FOR_REPLY;
+ }
+
+ if (qdevice_net_algorithm_votequorum_node_list_heuristics_notify(net_instance, &tlv_rid,
+ node_list_entries, node_list, &send_node_list, &vote, &heuristics) != 0) {
+ qdevice_log(LOG_ERR, "Algorithm returned error. Disconnecting.");
+
+ net_instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_NODE_LIST_HEURISTICS_NOTIFY_ERR;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ } else {
+ qdevice_log(LOG_DEBUG, "Algorithm decided to %s list, result vote is %s and heuristics is %s",
+ (send_node_list ? "send" : "not send"), tlv_vote_to_str(vote),
+ tlv_heuristics_to_str(heuristics));
+ }
+
+ if (send_node_list) {
+ if (qdevice_net_send_membership_node_list(net_instance, &tlv_rid,
+ node_list_entries, node_list, heuristics) != 0) {
+ /*
+ * Fatal error -> schedule disconnect
+ */
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+ }
+
+ /*
+ * Unpause cast vote timer
+ */
+ qdevice_net_cast_vote_timer_set_paused(net_instance, 0);
+
+ if (qdevice_net_cast_vote_timer_update(net_instance, vote) != 0) {
+ qdevice_log(LOG_CRIT, "qdevice_model_net_votequorum_node_list_notify fatal error "
+ "Can't update cast vote timer");
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+
+ net_instance->latest_vq_heuristics_result = heuristics;
+ net_instance->latest_heuristics_result = heuristics;
+
+ if (qdevice_net_heuristics_schedule_timer(net_instance) != 0) {
+ return (0);
+ }
+
+ return (0);
+}
+
+int
+qdevice_model_net_votequorum_node_list_notify(struct qdevice_instance *instance,
+ votequorum_ring_id_t votequorum_ring_id, uint32_t node_list_entries, uint32_t node_list[])
+{
+ struct qdevice_net_instance *net_instance;
+ struct tlv_ring_id tlv_rid;
+ enum tlv_vote vote;
+ int pause_cast_vote_timer;
+
+ net_instance = instance->model_data;
+
+ /*
+ * Stop regular heuristics till qdevice_model_net_votequorum_node_list_heuristics_notify
+ * is called
+ */
+ if (qdevice_net_heuristics_stop_timer(net_instance) != 0) {
+ return (0);
+ }
+
+ pause_cast_vote_timer = 1;
+ vote = TLV_VOTE_NO_CHANGE;
+
+ if (net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS &&
+ net_instance->cast_vote_timer_vote == TLV_VOTE_ACK) {
+ /*
+ * Nodelist changed and vote timer still votes ACK. It's needed to start voting
+ * NACK.
+ */
+ if (net_instance->cast_vote_timer_vote == TLV_VOTE_ACK) {
+ vote = TLV_VOTE_NACK;
+ }
+ }
+
+ qdevice_net_votequorum_ring_id_to_tlv(&tlv_rid, &votequorum_ring_id);
+
+ if (qdevice_net_algorithm_votequorum_node_list_notify(net_instance, &tlv_rid,
+ node_list_entries, node_list, &pause_cast_vote_timer, &vote) != 0) {
+ qdevice_log(LOG_ERR, "Algorithm returned error. Disconnecting.");
+
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_NODE_LIST_NOTIFY_ERR;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ } else {
+ qdevice_log(LOG_DEBUG, "Algorithm decided to %s cast vote timer and result vote is %s ",
+ (pause_cast_vote_timer ? "pause" : "not pause"), tlv_vote_to_str(vote));
+ }
+
+ if (qdevice_net_cast_vote_timer_update(net_instance, vote) != 0) {
+ qdevice_log(LOG_CRIT, "qdevice_model_net_votequorum_node_list_notify fatal error "
+ "Can't update cast vote timer");
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+
+ qdevice_net_cast_vote_timer_set_paused(net_instance, pause_cast_vote_timer);
+
+ return (0);
+}
+
+int
+qdevice_model_net_votequorum_expected_votes_notify(struct qdevice_instance *instance,
+ uint32_t expected_votes)
+{
+ struct qdevice_net_instance *net_instance;
+ enum tlv_vote vote;
+
+ net_instance = instance->model_data;
+
+ qdevice_log(LOG_DEBUG, "qdevice_model_net_votequorum_expected_votes_notify"
+ " (expected votes old=%"PRIu32" / new=%"PRIu32")",
+ net_instance->qdevice_instance_ptr->vq_expected_votes, expected_votes);
+
+ vote = TLV_VOTE_NO_CHANGE;
+
+ if (qdevice_net_algorithm_votequorum_expected_votes_notify(net_instance, expected_votes,
+ &vote) != 0) {
+ qdevice_log(LOG_DEBUG, "Algorithm returned error. Disconnecting.");
+
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_EXPECTED_VOTES_NOTIFY_ERR;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ } else {
+ qdevice_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(vote));
+ }
+
+ if (qdevice_net_cast_vote_timer_update(net_instance, vote) != 0) {
+ qdevice_log(LOG_CRIT, "qdevice_model_net_votequorum_expected_votes_notify fatal error. "
+ " Can't update cast vote timer vote");
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+
+ return (0);
+}
+
+int
+qdevice_model_net_cmap_changed(struct qdevice_instance *instance,
+ const struct qdevice_cmap_change_events *events)
+{
+ struct qdevice_net_instance *net_instance;
+ enum qdevice_heuristics_mode active_heuristics_mode;
+ int heuristics_enabled;
+
+ net_instance = instance->model_data;
+
+ if (events->heuristics) {
+ active_heuristics_mode = instance->heuristics_instance.mode;
+ heuristics_enabled = (active_heuristics_mode == QDEVICE_HEURISTICS_MODE_ENABLED ||
+ active_heuristics_mode == QDEVICE_HEURISTICS_MODE_SYNC);
+
+ if (net_instance->state == QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS &&
+ !net_instance->server_supports_heuristics && heuristics_enabled) {
+ qdevice_log(LOG_ERR, "Heuristics are enabled but not supported by the server");
+
+ net_instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_SERVER_DOESNT_SUPPORT_REQUIRED_OPT;
+
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+
+ if (qdevice_net_heuristics_schedule_timer(net_instance) != 0) {
+ return (0);
+ }
+ }
+
+ return (0);
+}
+
+int
+qdevice_model_net_ipc_cmd_status(struct qdevice_instance *instance,
+ struct dynar *outbuf, int verbose)
+{
+ struct qdevice_net_instance *net_instance;
+
+ net_instance = instance->model_data;
+
+ if (!qdevice_net_ipc_cmd_status(net_instance, outbuf, verbose)) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+static struct qdevice_model qdevice_model_net = {
+ .name = "net",
+ .init = qdevice_model_net_init,
+ .destroy = qdevice_model_net_destroy,
+ .run = qdevice_model_net_run,
+ .get_config_node_list_failed = qdevice_model_net_get_config_node_list_failed,
+ .config_node_list_changed = qdevice_model_net_config_node_list_changed,
+ .votequorum_quorum_notify = qdevice_model_net_votequorum_quorum_notify,
+ .votequorum_node_list_notify = qdevice_model_net_votequorum_node_list_notify,
+ .votequorum_node_list_heuristics_notify = qdevice_model_net_votequorum_node_list_heuristics_notify,
+ .votequorum_expected_votes_notify = qdevice_model_net_votequorum_expected_votes_notify,
+ .cmap_changed = qdevice_model_net_cmap_changed,
+ .ipc_cmd_status = qdevice_model_net_ipc_cmd_status,
+};
+
+int
+qdevice_model_net_register(void)
+{
+ return (qdevice_model_register(QDEVICE_MODEL_TYPE_NET, &qdevice_model_net));
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_MODEL_NET_H_
+#define _QDEVICE_MODEL_NET_H_
+
+#include "qdevice-instance.h"
+#include "qdevice-model.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_model_net_init(struct qdevice_instance *instance);
+
+extern int qdevice_model_net_destroy(struct qdevice_instance *instance);
+
+extern int qdevice_model_net_run(struct qdevice_instance *instance);
+
+extern int qdevice_model_net_get_config_node_list_failed(struct qdevice_instance *instance);
+
+extern int qdevice_model_net_config_node_list_changed(struct qdevice_instance *instance,
+ const struct node_list *nlist, int config_version_set, uint64_t config_version);
+
+extern int qdevice_model_net_votequorum_quorum_notify(struct qdevice_instance *instance,
+ uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[]);
+
+extern int qdevice_model_net_votequorum_node_list_notify(struct qdevice_instance *instance,
+ votequorum_ring_id_t votequorum_ring_id, uint32_t node_list_entries, uint32_t node_list[]);
+
+extern int qdevice_model_net_votequorum_node_list_heuristics_notify(
+ struct qdevice_instance *instance,
+ votequorum_ring_id_t votequorum_ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ enum qdevice_heuristics_exec_result heuristics_exec_result);
+
+extern int qdevice_model_net_votequorum_expected_votes_notify(struct qdevice_instance *instance,
+ uint32_t expected_votes);
+
+extern int qdevice_model_net_cmap_changed(struct qdevice_instance *instance,
+ const struct qdevice_cmap_change_events *events);
+
+extern int qdevice_model_net_ipc_cmd_status(struct qdevice_instance *instance,
+ struct dynar *outbuf, int verbose);
+
+extern int qdevice_model_net_register(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_MODEL_NET_H_ */
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QDEVICE_MODEL_TYPE_H_
+#define _QDEVICE_MODEL_TYPE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum qdevice_model_type {
+ QDEVICE_MODEL_TYPE_NET = 0,
+ QDEVICE_MODEL_TYPE_ARRAY_SIZE,
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_MODEL_TYPE_H_ */
--- /dev/null
+/*
+ * 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 "qdevice-log.h"
+#include "qdevice-model.h"
+#include "qdevice-model-net.h"
+
+static struct qdevice_model *qdevice_model_array[QDEVICE_MODEL_TYPE_ARRAY_SIZE];
+
+int
+qdevice_model_init(struct qdevice_instance *instance)
+{
+
+ if (instance->model_type >= QDEVICE_MODEL_TYPE_ARRAY_SIZE ||
+ qdevice_model_array[instance->model_type] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_model_init unhandled model");
+ exit(1);
+ }
+
+ return (qdevice_model_array[instance->model_type]->init(instance));
+}
+
+int
+qdevice_model_destroy(struct qdevice_instance *instance)
+{
+
+ if (instance->model_type >= QDEVICE_MODEL_TYPE_ARRAY_SIZE ||
+ qdevice_model_array[instance->model_type] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_model_destroy unhandled model");
+ exit(1);
+ }
+
+ return (qdevice_model_array[instance->model_type]->destroy(instance));
+}
+
+int
+qdevice_model_run(struct qdevice_instance *instance)
+{
+
+ if (instance->model_type >= QDEVICE_MODEL_TYPE_ARRAY_SIZE ||
+ qdevice_model_array[instance->model_type] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_model_run unhandled model");
+ exit(1);
+ }
+
+ return (qdevice_model_array[instance->model_type]->run(instance));
+}
+
+int
+qdevice_model_get_config_node_list_failed(struct qdevice_instance *instance)
+{
+
+ if (instance->model_type >= QDEVICE_MODEL_TYPE_ARRAY_SIZE ||
+ qdevice_model_array[instance->model_type] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_model_run unhandled model");
+ exit(1);
+ }
+
+ return (qdevice_model_array[instance->model_type]->get_config_node_list_failed(instance));
+}
+
+int
+qdevice_model_config_node_list_changed(struct qdevice_instance *instance,
+ const struct node_list *nlist, int config_version_set, uint64_t config_version)
+{
+
+ if (instance->model_type >= QDEVICE_MODEL_TYPE_ARRAY_SIZE ||
+ qdevice_model_array[instance->model_type] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_model_run unhandled model");
+ exit(1);
+ }
+
+ return (qdevice_model_array[instance->model_type]->
+ config_node_list_changed(instance, nlist, config_version_set, config_version));
+}
+
+int
+qdevice_model_votequorum_quorum_notify(struct qdevice_instance *instance,
+ uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[])
+{
+
+ if (instance->model_type >= QDEVICE_MODEL_TYPE_ARRAY_SIZE ||
+ qdevice_model_array[instance->model_type] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_model_votequorum_quorum_notify unhandled model");
+ exit(1);
+ }
+
+ return (qdevice_model_array[instance->model_type]->
+ votequorum_quorum_notify(instance, quorate, node_list_entries, node_list));
+}
+
+int
+qdevice_model_votequorum_node_list_notify(struct qdevice_instance *instance,
+ votequorum_ring_id_t votequorum_ring_id, uint32_t node_list_entries, uint32_t node_list[])
+{
+
+ if (instance->model_type >= QDEVICE_MODEL_TYPE_ARRAY_SIZE ||
+ qdevice_model_array[instance->model_type] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_model_votequorum_node_list_notify unhandled model");
+ exit(1);
+ }
+
+ return (qdevice_model_array[instance->model_type]->
+ votequorum_node_list_notify(instance, votequorum_ring_id, node_list_entries, node_list));
+}
+
+int
+qdevice_model_votequorum_node_list_heuristics_notify(struct qdevice_instance *instance,
+ votequorum_ring_id_t votequorum_ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ enum qdevice_heuristics_exec_result heuristics_exec_result)
+{
+
+ if (instance->model_type >= QDEVICE_MODEL_TYPE_ARRAY_SIZE ||
+ qdevice_model_array[instance->model_type] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_model_votequorum_node_list_heuristics_notify unhandled model");
+ exit(1);
+ }
+
+ return (qdevice_model_array[instance->model_type]->
+ votequorum_node_list_heuristics_notify(instance, votequorum_ring_id, node_list_entries,
+ node_list, heuristics_exec_result));
+}
+
+int
+qdevice_model_votequorum_expected_votes_notify(struct qdevice_instance *instance,
+ uint32_t expected_votes)
+{
+
+ if (instance->model_type >= QDEVICE_MODEL_TYPE_ARRAY_SIZE ||
+ qdevice_model_array[instance->model_type] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_model_votequorum_expected_votes_notify unhandled model");
+ exit(1);
+ }
+
+ return (qdevice_model_array[instance->model_type]->
+ votequorum_expected_votes_notify(instance, expected_votes));
+}
+
+int
+qdevice_model_ipc_cmd_status(struct qdevice_instance *instance, struct dynar *outbuf, int verbose)
+{
+
+ if (instance->model_type >= QDEVICE_MODEL_TYPE_ARRAY_SIZE ||
+ qdevice_model_array[instance->model_type] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_model_ipc_cmd_status unhandled model");
+ exit(1);
+ }
+
+ return (qdevice_model_array[instance->model_type]->
+ ipc_cmd_status(instance, outbuf, verbose));
+}
+
+int
+qdevice_model_cmap_changed(struct qdevice_instance *instance,
+ const struct qdevice_cmap_change_events *events)
+{
+
+ if (instance->model_type >= QDEVICE_MODEL_TYPE_ARRAY_SIZE ||
+ qdevice_model_array[instance->model_type] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_model_cmap_chaged unhandled model");
+ exit(1);
+ }
+
+ return (qdevice_model_array[instance->model_type]->
+ cmap_changed(instance, events));
+}
+
+int
+qdevice_model_register(enum qdevice_model_type model_type,
+ struct qdevice_model *model)
+{
+
+ if (model_type >= QDEVICE_MODEL_TYPE_ARRAY_SIZE) {
+ return (-1);
+ }
+
+ if (qdevice_model_array[model_type] != NULL) {
+ return (-1);
+ }
+
+ qdevice_model_array[model_type] = model;
+
+ return (0);
+}
+
+void
+qdevice_model_register_all(void)
+{
+
+ if (qdevice_model_net_register() != 0) {
+ qdevice_log(LOG_CRIT, "Failed to register model 'net' ");
+ exit(1);
+ }
+}
+
+int
+qdevice_model_str_to_type(const char *str, enum qdevice_model_type *model_type)
+{
+ int i;
+
+ for (i = 0; i < QDEVICE_MODEL_TYPE_ARRAY_SIZE; i++) {
+ if (qdevice_model_array[i] != NULL &&
+ strcmp(qdevice_model_array[i]->name, str) == 0) {
+ *model_type = i;
+
+ return (0);
+ }
+ }
+
+ return (-1);
+}
+
+const char *
+qdevice_model_type_to_str(enum qdevice_model_type model_type)
+{
+
+ switch (model_type) {
+ case QDEVICE_MODEL_TYPE_NET: return ("Net"); break;
+ case QDEVICE_MODEL_TYPE_ARRAY_SIZE: return ("Unknown model"); break;
+ /*
+ * Default is not defined intentionally. Compiler shows warning when new model is added
+ */
+ }
+
+ return ("Unknown model");
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_MODEL_H_
+#define _QDEVICE_MODEL_H_
+
+#include "qdevice-cmap.h"
+#include "qdevice-instance.h"
+#include "qdevice-model-type.h"
+#include "qdevice-heuristics-exec-result.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_model_init(struct qdevice_instance *instance);
+
+extern int qdevice_model_destroy(struct qdevice_instance *instance);
+
+extern int qdevice_model_run(struct qdevice_instance *instance);
+
+extern int qdevice_model_get_config_node_list_failed(struct qdevice_instance *instance);
+
+extern int qdevice_model_config_node_list_changed(struct qdevice_instance *instance,
+ const struct node_list *nlist, int config_version_set, uint64_t config_version);
+
+extern int qdevice_model_votequorum_quorum_notify(struct qdevice_instance *instance,
+ uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[]);
+
+extern int qdevice_model_votequorum_node_list_notify(struct qdevice_instance *instance,
+ votequorum_ring_id_t votequorum_ring_id, uint32_t node_list_entries, uint32_t node_list[]);
+
+extern int qdevice_model_votequorum_node_list_heuristics_notify(struct qdevice_instance *instance,
+ votequorum_ring_id_t votequorum_ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ enum qdevice_heuristics_exec_result heuristics_exec_result);
+
+extern int qdevice_model_votequorum_expected_votes_notify(struct qdevice_instance *instance,
+ uint32_t expected_votes);
+
+extern int qdevice_model_ipc_cmd_status(struct qdevice_instance *instance,
+ struct dynar *outbuf, int verbose);
+
+extern int qdevice_model_cmap_changed(struct qdevice_instance *instance,
+ const struct qdevice_cmap_change_events *events);
+
+struct qdevice_model {
+ const char *name;
+ int (*init)(struct qdevice_instance *instance);
+ int (*destroy)(struct qdevice_instance *instance);
+ int (*run)(struct qdevice_instance *instance);
+ int (*get_config_node_list_failed)(struct qdevice_instance *instance);
+ int (*config_node_list_changed)(struct qdevice_instance *instance,
+ const struct node_list *nlist, int config_version_set, uint64_t config_version);
+ int (*votequorum_quorum_notify)(struct qdevice_instance *instance,
+ uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[]);
+ int (*votequorum_node_list_notify)(struct qdevice_instance *instance,
+ votequorum_ring_id_t votequorum_ring_id,uint32_t node_list_entries,
+ uint32_t node_list[]);
+ int (*votequorum_node_list_heuristics_notify)(struct qdevice_instance *instance,
+ votequorum_ring_id_t votequorum_ring_id,uint32_t node_list_entries,
+ uint32_t node_list[], enum qdevice_heuristics_exec_result heuristics_exec_result);
+ int (*votequorum_expected_votes_notify)(struct qdevice_instance *instance,
+ uint32_t expected_votes);
+ int (*ipc_cmd_status)(struct qdevice_instance *instance, struct dynar *outbuf, int verbose);
+ int (*cmap_changed)(struct qdevice_instance *instance,
+ const struct qdevice_cmap_change_events *events);
+};
+
+extern int qdevice_model_register(
+ enum qdevice_model_type model_type, struct qdevice_model *model);
+
+extern void qdevice_model_register_all(void);
+
+extern int qdevice_model_str_to_type(const char *str,
+ enum qdevice_model_type *model_type);
+
+extern const char *qdevice_model_type_to_str(enum qdevice_model_type model_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_MODEL_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <sys/types.h>
+
+#include <string.h>
+
+#include "qdevice-net-algo-2nodelms.h"
+#include "qdevice-log.h"
+#include "qdevice-net-send.h"
+#include "qdevice-net-cast-vote-timer.h"
+
+int
+qdevice_net_algo_2nodelms_init(struct qdevice_net_instance *instance)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_connected(struct qdevice_net_instance *instance, enum tlv_heuristics *heuristics,
+ int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_config_node_list_changed(struct qdevice_net_instance *instance,
+ const struct node_list *nlist, int config_version_set, uint64_t config_version,
+ int *send_node_list, enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_votequorum_node_list_notify(struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ int *pause_cast_vote_timer, enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_votequorum_node_list_heuristics_notify(struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ int *send_node_list, enum tlv_vote *vote, enum tlv_heuristics *heuristics)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_votequorum_quorum_notify(struct qdevice_net_instance *instance,
+ uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list,
+ enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_votequorum_expected_votes_notify(struct qdevice_net_instance *instance,
+ uint32_t expected_votes, enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_config_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, int initial, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_membership_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_quorum_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_ask_for_vote_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_vote_info_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_echo_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, int is_expected_seq_number)
+{
+
+ return (is_expected_seq_number ? 0 : -1);
+}
+
+int
+qdevice_net_algo_2nodelms_echo_reply_not_received(struct qdevice_net_instance *instance)
+{
+
+ return (-1);
+}
+
+int
+qdevice_net_algo_2nodelms_heuristics_change(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_msg, enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_heuristics_change_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_heuristics heuristics, enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_2nodelms_disconnected(struct qdevice_net_instance *instance,
+ enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+void
+qdevice_net_algo_2nodelms_destroy(struct qdevice_net_instance *instance)
+{
+
+}
+
+static struct qdevice_net_algorithm qdevice_net_algo_2nodelms = {
+ .init = qdevice_net_algo_2nodelms_init,
+ .connected = qdevice_net_algo_2nodelms_connected,
+ .config_node_list_changed = qdevice_net_algo_2nodelms_config_node_list_changed,
+ .votequorum_node_list_notify = qdevice_net_algo_2nodelms_votequorum_node_list_notify,
+ .votequorum_node_list_heuristics_notify = qdevice_net_algo_2nodelms_votequorum_node_list_heuristics_notify,
+ .votequorum_quorum_notify = qdevice_net_algo_2nodelms_votequorum_quorum_notify,
+ .votequorum_expected_votes_notify = qdevice_net_algo_2nodelms_votequorum_expected_votes_notify,
+ .config_node_list_reply_received = qdevice_net_algo_2nodelms_config_node_list_reply_received,
+ .membership_node_list_reply_received = qdevice_net_algo_2nodelms_membership_node_list_reply_received,
+ .quorum_node_list_reply_received = qdevice_net_algo_2nodelms_quorum_node_list_reply_received,
+ .ask_for_vote_reply_received = qdevice_net_algo_2nodelms_ask_for_vote_reply_received,
+ .vote_info_received = qdevice_net_algo_2nodelms_vote_info_received,
+ .echo_reply_received = qdevice_net_algo_2nodelms_echo_reply_received,
+ .echo_reply_not_received = qdevice_net_algo_2nodelms_echo_reply_not_received,
+ .heuristics_change = qdevice_net_algo_2nodelms_heuristics_change,
+ .heuristics_change_reply_received = qdevice_net_algo_2nodelms_heuristics_change_reply_received,
+ .disconnected = qdevice_net_algo_2nodelms_disconnected,
+ .destroy = qdevice_net_algo_2nodelms_destroy,
+};
+
+int
+qdevice_net_algo_2nodelms_register(void)
+{
+ return (qdevice_net_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_2NODELMS, &qdevice_net_algo_2nodelms));
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_NET_ALGO_2NODELMS_H_
+#define _QDEVICE_NET_ALGO_2NODELMS_H_
+
+#include "qdevice-net-algorithm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_net_algo_2nodelms_init(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_algo_2nodelms_connected(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_config_node_list, int *send_membership_node_list,
+ int *send_quorum_node_list, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_2nodelms_config_node_list_changed(
+ struct qdevice_net_instance *instance, const struct node_list *nlist,
+ int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_2nodelms_votequorum_node_list_notify(
+ struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id,
+ uint32_t node_list_entries, uint32_t node_list[], int *pause_cast_vote_timer,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algo_2nodelms_votequorum_node_list_heuristics_notify(
+ struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id,
+ uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote,
+ enum tlv_heuristics *heuristics);
+
+extern int qdevice_net_algo_2nodelms_votequorum_quorum_notify(
+ struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries,
+ votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_2nodelms_votequorum_expected_votes_notify(
+ struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_2nodelms_config_node_list_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number, int initial,
+ const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_2nodelms_membership_node_list_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number, const struct tlv_ring_id *ring_id,
+ int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_2nodelms_quorum_node_list_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number,
+ const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_2nodelms_ask_for_vote_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number,
+ const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_2nodelms_vote_info_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algo_2nodelms_echo_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number);
+
+extern int qdevice_net_algo_2nodelms_echo_reply_not_received(
+ struct qdevice_net_instance *instance);
+
+extern int qdevice_net_algo_2nodelms_heuristics_change(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_msg, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_2nodelms_heuristics_change_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_heuristics heuristics,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algo_2nodelms_disconnected(struct qdevice_net_instance *instance,
+ enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote);
+
+extern void qdevice_net_algo_2nodelms_destroy(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_algo_2nodelms_register(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_ALGO_2NODELMS_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <sys/types.h>
+
+#include <string.h>
+
+#include "qdevice-net-algo-ffsplit.h"
+#include "qdevice-log.h"
+#include "qdevice-net-send.h"
+#include "qdevice-net-cast-vote-timer.h"
+#include "qdevice-votequorum.h"
+#include "utils.h"
+
+static int
+check_vqinfo_validity(struct qdevice_net_instance *instance)
+{
+ struct qdevice_instance *qdev_instance;
+ struct votequorum_info vq_info;
+ cs_error_t cs_res;
+ struct node_list_entry *node;
+ uint32_t node_id;
+
+ qdev_instance = instance->qdevice_instance_ptr;
+
+ TAILQ_FOREACH(node, &qdev_instance->config_node_list, entries) {
+ node_id = node->node_id;
+
+ cs_res = votequorum_getinfo(qdev_instance->votequorum_handle, node_id, &vq_info);
+
+ if (cs_res == CS_ERR_NOT_EXIST) {
+ continue;
+ } else if (cs_res != CS_OK) {
+ qdevice_log(LOG_CRIT, "Can't get votequorum information for node "
+ UTILS_PRI_NODE_ID ". Error %s", node_id, cs_strerror(cs_res));
+
+ return (-1);
+ }
+
+ if (vq_info.node_votes != 1) {
+ qdevice_log(LOG_CRIT, "50:50 split algorithm works only if all nodes have "
+ "exactly 1 vote. Node " UTILS_PRI_NODE_ID " has %u votes!",
+ node_id, vq_info.node_votes);
+
+ return (-1);
+ }
+
+ if (vq_info.qdevice_votes != 1) {
+ qdevice_log(LOG_CRIT, "50:50 split algorithm works only if qdevice has "
+ "exactly 1 vote. Node "UTILS_PRI_NODE_ID" has %u votes!",
+ node_id, vq_info.qdevice_votes);
+
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+check_cmap_validity(struct qdevice_net_instance *instance)
+{
+ struct qdevice_instance *qdev_instance;
+ uint32_t qdevice_votes;
+
+ qdev_instance = instance->qdevice_instance_ptr;
+
+ if (cmap_get_uint32(qdev_instance->cmap_handle, "quorum.device.votes", &qdevice_votes) != CS_OK ||
+ qdevice_votes != 1) {
+ qdevice_log(LOG_CRIT, "50:50 split algorithm works only if quorum.device.votes"
+ " configuration key is set to 1!");
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_init(struct qdevice_net_instance *instance)
+{
+
+ if (check_cmap_validity(instance) != 0 ||
+ check_vqinfo_validity(instance) != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_connected(struct qdevice_net_instance *instance, enum tlv_heuristics *heuristics,
+ int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_config_node_list_changed(struct qdevice_net_instance *instance,
+ const struct node_list *nlist, int config_version_set, uint64_t config_version,
+ int *send_node_list, enum tlv_vote *vote)
+{
+
+ if (check_vqinfo_validity(instance) != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_votequorum_node_list_notify(struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ int *pause_cast_vote_timer, enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_votequorum_node_list_heuristics_notify(struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ int *send_node_list, enum tlv_vote *vote, enum tlv_heuristics *heuristics)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_votequorum_quorum_notify(struct qdevice_net_instance *instance,
+ uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list,
+ enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_votequorum_expected_votes_notify(struct qdevice_net_instance *instance,
+ uint32_t expected_votes, enum tlv_vote *vote)
+{
+
+ if (check_vqinfo_validity(instance) != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_config_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, int initial, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_membership_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_quorum_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_ask_for_vote_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_vote_info_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_echo_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, int is_expected_seq_number)
+{
+
+ return (is_expected_seq_number ? 0 : -1);
+}
+
+int
+qdevice_net_algo_ffsplit_echo_reply_not_received(struct qdevice_net_instance *instance)
+{
+
+ return (-1);
+}
+
+int
+qdevice_net_algo_ffsplit_heuristics_change(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_msg, enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_heuristics_change_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_heuristics heuristics, enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_ffsplit_disconnected(struct qdevice_net_instance *instance,
+ enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote)
+{
+
+ /*
+ * We cannot depend on default behavior (until there is no change -> use old vote).
+ * This could create two quorate clusters (2:2 -> first half get ACK -> first half
+ * disconnects from qnetd -> second half get ACK -> two quorate clusters)
+ */
+ *vote = TLV_VOTE_NACK;
+
+ return (0);
+}
+
+void
+qdevice_net_algo_ffsplit_destroy(struct qdevice_net_instance *instance)
+{
+
+}
+
+static struct qdevice_net_algorithm qdevice_net_algo_ffsplit = {
+ .init = qdevice_net_algo_ffsplit_init,
+ .connected = qdevice_net_algo_ffsplit_connected,
+ .config_node_list_changed = qdevice_net_algo_ffsplit_config_node_list_changed,
+ .votequorum_node_list_notify = qdevice_net_algo_ffsplit_votequorum_node_list_notify,
+ .votequorum_node_list_heuristics_notify = qdevice_net_algo_ffsplit_votequorum_node_list_heuristics_notify,
+ .votequorum_quorum_notify = qdevice_net_algo_ffsplit_votequorum_quorum_notify,
+ .votequorum_expected_votes_notify = qdevice_net_algo_ffsplit_votequorum_expected_votes_notify,
+ .config_node_list_reply_received = qdevice_net_algo_ffsplit_config_node_list_reply_received,
+ .membership_node_list_reply_received = qdevice_net_algo_ffsplit_membership_node_list_reply_received,
+ .quorum_node_list_reply_received = qdevice_net_algo_ffsplit_quorum_node_list_reply_received,
+ .ask_for_vote_reply_received = qdevice_net_algo_ffsplit_ask_for_vote_reply_received,
+ .vote_info_received = qdevice_net_algo_ffsplit_vote_info_received,
+ .echo_reply_received = qdevice_net_algo_ffsplit_echo_reply_received,
+ .echo_reply_not_received = qdevice_net_algo_ffsplit_echo_reply_not_received,
+ .heuristics_change = qdevice_net_algo_ffsplit_heuristics_change,
+ .heuristics_change_reply_received = qdevice_net_algo_ffsplit_heuristics_change_reply_received,
+ .disconnected = qdevice_net_algo_ffsplit_disconnected,
+ .destroy = qdevice_net_algo_ffsplit_destroy,
+};
+
+int
+qdevice_net_algo_ffsplit_register(void)
+{
+ return (qdevice_net_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_FFSPLIT, &qdevice_net_algo_ffsplit));
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_NET_ALGO_FFSPLIT_H_
+#define _QDEVICE_NET_ALGO_FFSPLIT_H_
+
+#include "qdevice-net-algorithm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_net_algo_ffsplit_init(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_algo_ffsplit_connected(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_config_node_list, int *send_membership_node_list,
+ int *send_quorum_node_list, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_ffsplit_config_node_list_changed(
+ struct qdevice_net_instance *instance, const struct node_list *nlist,
+ int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_ffsplit_votequorum_node_list_notify(
+ struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id,
+ uint32_t node_list_entries, uint32_t node_list[], int *pause_cast_vote_timer,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algo_ffsplit_votequorum_node_list_heuristics_notify(
+ struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id,
+ uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote,
+ enum tlv_heuristics *heuristics);
+
+extern int qdevice_net_algo_ffsplit_votequorum_quorum_notify(
+ struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries,
+ votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_ffsplit_votequorum_expected_votes_notify(
+ struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_ffsplit_config_node_list_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number, int initial,
+ const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_ffsplit_membership_node_list_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number, const struct tlv_ring_id *ring_id,
+ int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_ffsplit_quorum_node_list_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number,
+ const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_ffsplit_ask_for_vote_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number,
+ const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_ffsplit_vote_info_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algo_ffsplit_echo_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number);
+
+extern int qdevice_net_algo_ffsplit_echo_reply_not_received(
+ struct qdevice_net_instance *instance);
+
+extern int qdevice_net_algo_ffsplit_heuristics_change(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_msg, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_ffsplit_heuristics_change_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_heuristics heuristics,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algo_ffsplit_disconnected(struct qdevice_net_instance *instance,
+ enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote);
+
+extern void qdevice_net_algo_ffsplit_destroy(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_algo_ffsplit_register(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_ALGO_FFSPLIT_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <sys/types.h>
+
+#include <string.h>
+
+#include "qdevice-net-algo-lms.h"
+#include "qdevice-log.h"
+#include "qdevice-net-send.h"
+#include "qdevice-net-cast-vote-timer.h"
+
+struct algo_lms_instance_data {
+ uint32_t quorate;
+ uint8_t have_wfa;
+ enum tlv_vote vote;
+};
+
+int
+qdevice_net_algo_lms_init(struct qdevice_net_instance *instance)
+{
+ struct algo_lms_instance_data *data;
+ int res;
+
+ data = malloc(sizeof(struct algo_lms_instance_data));
+ if (!data) {
+ return (-1);
+ }
+ instance->algorithm_data = data;
+
+ data->quorate = 0;
+ data->vote = TLV_VOTE_ASK_LATER;
+ res = cmap_get_uint8(instance->qdevice_instance_ptr->cmap_handle, "quorum.wait_for_all", &data->have_wfa);
+ if (res != CS_OK) {
+ qdevice_log(LOG_DEBUG, "algo-lms: Can't get WFA res = %d", res);
+ data->have_wfa = 0;
+ }
+
+ qdevice_log(LOG_DEBUG, "algo-lms: initialised. WFA = %d", data->have_wfa);
+ return (0);
+}
+
+
+int
+qdevice_net_algo_lms_connected(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_config_node_list, int *send_membership_node_list,
+ int *send_quorum_node_list, enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_lms_config_node_list_changed(struct qdevice_net_instance *instance,
+ const struct node_list *nlist, int config_version_set, uint64_t config_version,
+ int *send_node_list, enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_lms_votequorum_node_list_notify(struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ int *pause_cast_vote_timer, enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_lms_votequorum_node_list_heuristics_notify(struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ int *send_node_list, enum tlv_vote *vote, enum tlv_heuristics *heuristics)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_lms_votequorum_quorum_notify(struct qdevice_net_instance *instance,
+ uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list,
+ enum tlv_vote *vote)
+{
+ struct algo_lms_instance_data *data = instance->algorithm_data;
+
+ data->quorate = quorate;
+ qdevice_log(LOG_DEBUG, "algo-lms: quorum_notify. quorate = %d", data->quorate);
+
+ return (0);
+}
+
+int
+qdevice_net_algo_lms_config_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, int initial, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_lms_membership_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_lms_quorum_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_lms_votequorum_expected_votes_notify(struct qdevice_net_instance *instance,
+ uint32_t expected_votes, enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_lms_ask_for_vote_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_lms_vote_info_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_lms_echo_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, int is_expected_seq_number)
+{
+
+ return (is_expected_seq_number ? 0 : -1);
+}
+
+int
+qdevice_net_algo_lms_echo_reply_not_received(struct qdevice_net_instance *instance)
+{
+ struct algo_lms_instance_data *data = instance->algorithm_data;
+
+ qdevice_log(LOG_DEBUG, "algo-lms: echo_not_recvd. quorate = %d, WFA = %d", data->quorate, data->have_wfa);
+
+ /* qnetd server is disconnected, if we were already quorate AND WFA is enabled
+ then we can continue to provide our vote.
+ Otherwise ... no
+ */
+ if (data->quorate && data->have_wfa) {
+ return (0);
+ }
+
+ return (-1);
+}
+
+int
+qdevice_net_algo_lms_heuristics_change(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_msg, enum tlv_vote *vote)
+{
+
+ return (0);
+}
+
+int
+qdevice_net_algo_lms_heuristics_change_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_heuristics heuristics, enum tlv_vote *vote)
+{
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_algo_lms_disconnected(struct qdevice_net_instance *instance,
+ enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote)
+{
+ struct algo_lms_instance_data *data = instance->algorithm_data;
+
+ qdevice_log(LOG_DEBUG, "algo-lms: disconnected. quorate = %d, WFA = %d", data->quorate, data->have_wfa);
+ qdevice_log(LOG_DEBUG, "algo-lms: disconnected. reason = %d, WFA = %d", disconnect_reason, data->have_wfa);
+
+ if (!data->quorate || !data->have_wfa) {
+ *vote = TLV_VOTE_NACK;
+ }
+ *try_reconnect = 1;
+ return (0);
+}
+
+void
+qdevice_net_algo_lms_destroy(struct qdevice_net_instance *instance)
+{
+ free(instance->algorithm_data);
+}
+
+static struct qdevice_net_algorithm qdevice_net_algo_lms = {
+ .init = qdevice_net_algo_lms_init,
+ .connected = qdevice_net_algo_lms_connected,
+ .config_node_list_changed = qdevice_net_algo_lms_config_node_list_changed,
+ .votequorum_node_list_notify = qdevice_net_algo_lms_votequorum_node_list_notify,
+ .votequorum_node_list_heuristics_notify = qdevice_net_algo_lms_votequorum_node_list_heuristics_notify,
+ .votequorum_quorum_notify = qdevice_net_algo_lms_votequorum_quorum_notify,
+ .votequorum_expected_votes_notify = qdevice_net_algo_lms_votequorum_expected_votes_notify,
+ .config_node_list_reply_received = qdevice_net_algo_lms_config_node_list_reply_received,
+ .membership_node_list_reply_received = qdevice_net_algo_lms_membership_node_list_reply_received,
+ .quorum_node_list_reply_received = qdevice_net_algo_lms_quorum_node_list_reply_received,
+ .ask_for_vote_reply_received = qdevice_net_algo_lms_ask_for_vote_reply_received,
+ .vote_info_received = qdevice_net_algo_lms_vote_info_received,
+ .echo_reply_received = qdevice_net_algo_lms_echo_reply_received,
+ .echo_reply_not_received = qdevice_net_algo_lms_echo_reply_not_received,
+ .heuristics_change = qdevice_net_algo_lms_heuristics_change,
+ .heuristics_change_reply_received = qdevice_net_algo_lms_heuristics_change_reply_received,
+ .disconnected = qdevice_net_algo_lms_disconnected,
+ .destroy = qdevice_net_algo_lms_destroy,
+};
+
+int
+qdevice_net_algo_lms_register(void)
+{
+ return (qdevice_net_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_LMS, &qdevice_net_algo_lms));
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_NET_ALGO_LMS_H_
+#define _QDEVICE_NET_ALGO_LMS_H_
+
+#include "qdevice-net-algorithm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_net_algo_lms_init(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_algo_lms_connected(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_config_node_list, int *send_membership_node_list,
+ int *send_quorum_node_list, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_lms_config_node_list_changed(
+ struct qdevice_net_instance *instance, const struct node_list *nlist,
+ int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_lms_votequorum_node_list_notify(
+ struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id,
+ uint32_t node_list_entries, uint32_t node_list[], int *pause_cast_vote_timer,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algo_lms_votequorum_node_list_heuristics_notify(
+ struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id,
+ uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote,
+ enum tlv_heuristics *heuristics);
+
+extern int qdevice_net_algo_lms_votequorum_quorum_notify(
+ struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries,
+ votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_lms_votequorum_expected_votes_notify(
+ struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_lms_config_node_list_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number, int initial,
+ const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_lms_membership_node_list_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number, const struct tlv_ring_id *ring_id,
+ int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_lms_quorum_node_list_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number,
+ const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_lms_ask_for_vote_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number,
+ const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_lms_vote_info_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algo_lms_echo_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number);
+
+extern int qdevice_net_algo_lms_echo_reply_not_received(
+ struct qdevice_net_instance *instance);
+
+extern int qdevice_net_algo_lms_heuristics_change(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_msg, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_lms_heuristics_change_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_heuristics heuristics,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algo_lms_disconnected(struct qdevice_net_instance *instance,
+ enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote);
+
+extern void qdevice_net_algo_lms_destroy(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_algo_lms_register(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_ALGO_LMS_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <sys/types.h>
+
+#include <string.h>
+
+#include "qdevice-net-algo-test.h"
+#include "qdevice-log.h"
+#include "qdevice-net-send.h"
+#include "qdevice-net-cast-vote-timer.h"
+
+/*
+ * Called after qdevice_net_instance is initialized. Connection to server is not yet
+ * established. Used mainly for allocating instance->algorithm_data.
+ *
+ * Callback should return 0 on success or -1 on failure.
+ */
+int
+qdevice_net_algo_test_init(struct qdevice_net_instance *instance)
+{
+
+ instance->algorithm_data = NULL;
+ qdevice_log(LOG_INFO, "algo-test: Initialized");
+
+ return (0);
+}
+
+/*
+ * Called after qdevice connected to qnetd.
+ * send_config_node_list, send_membership_node_list and send_quorum_node_list can be set to
+ * nonzero (default) to make qdevice-net send given lists to qnetd
+ * vote (default TLV_VOTE_WAIT_FOR_REPLY) can be set to update voting timer
+ * heuristics is result of pre connect heuristics and can be updated
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_connected(struct qdevice_net_instance *instance, enum tlv_heuristics *heuristics,
+ int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Connected");
+
+ return (0);
+}
+
+/*
+ * Called after config node list changed.
+ *
+ * Callback can override send_node_list and vote.
+ * Depending on net_instance->state, they are set acordingly:
+ * If net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS
+ * send_node_list = 0
+ * if cast_vote_timer_vote != TLV_VOTE_ACK
+ * vote = TLV_VOTE_NO_CHANGE
+ * if cast_vote_timer_vote = TLV_VOTE_ACK
+ * vote = TLV_VOTE_NACK.
+ * Otherwise send_node_list = 1 and vote = TLV_VOTE_NO_CHANGE
+ * If send_node_list is set to non zero, node list is sent to qnetd
+ */
+int
+qdevice_net_algo_test_config_node_list_changed(struct qdevice_net_instance *instance,
+ const struct node_list *nlist, int config_version_set, uint64_t config_version,
+ int *send_node_list, enum tlv_vote *vote)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Config node list changed");
+
+ return (0);
+}
+
+/*
+ * Called after votequorum node list notify is dispatched but heuristics result is not
+ * yet known (heuristics were just executed). Full list together with result of heuristics
+ * are received in qdevice_net_algo_test_votequorum_node_list_heuristics_notify.
+ * This applies also if heuristics are disabled.
+ *
+ * pause_cast_vote_timer is preset to 1. If after callback this value is still != 0,
+ * cast vote timer is paused until heuristics finishes.
+ *
+ * vote is by default set to TLV_VOTE_NO_CHANGE with exception of situation when
+ * net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS and
+ * cast_vote_timer_vote = TLV_VOTE_ACK
+ * then vote is set to TLV_VOTE_NACK. This is to allow qnetd disconnection and still vote
+ * ACK as long as no change happens.
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_votequorum_node_list_notify(struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ int *pause_cast_vote_timer, enum tlv_vote *vote)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Votequorum list notify");
+
+ return (0);
+}
+
+/*
+ * Called after votequorum node list notify is dispatched and heuristics result is known.
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ *
+ * If net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS
+ * send_node_list = 0
+ * if cast_vote_timer_vote != TLV_VOTE_ACK
+ * vote = TLV_VOTE_NO_CHANGE
+ * if cast_vote_timer_vote = TLV_VOTE_ACK
+ * vote = TLV_VOTE_NACK.
+ * Otherwise send_node_list = 1 and vote = TLV_VOTE_WAIT_FOR_REPLY
+ * If send_node_list is set to non zero, node list is sent to qnetd
+ * heuristics results can be used (and overwrite)
+ */
+int
+qdevice_net_algo_test_votequorum_node_list_heuristics_notify(struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ int *send_node_list, enum tlv_vote *vote, enum tlv_heuristics *heuristics)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Votequorum heuristics list notify");
+
+ return (0);
+}
+
+/*
+ * Called after votequorum quorum notify is dispatched.
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ *
+ * Callback can override send_node_list and vote.
+ * Depending on net_instance->state, they are set acordingly:
+ * If net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS
+ * send_node_list = 0
+ * if cast_vote_timer_vote != TLV_VOTE_ACK
+ * vote = TLV_VOTE_NO_CHANGE
+ * if cast_vote_timer_vote = TLV_VOTE_ACK
+ * vote = TLV_VOTE_NACK.
+ * Otherwise send_node_list = 1 and vote = TLV_VOTE_NO_CHANGE
+ *
+ * If send_node_list is set to non zero, node list is sent to qnetd
+ */
+int
+qdevice_net_algo_test_votequorum_quorum_notify(struct qdevice_net_instance *instance,
+ uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list,
+ enum tlv_vote *vote)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Votequorum quorum notify");
+
+ return (0);
+}
+/*
+ * Called after votequorum expected_votes notify is dispatched.
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ *
+ * Vote is set to TLV_VOTE_NO_CHANGE
+ */
+int
+qdevice_net_algo_test_votequorum_expected_votes_notify(struct qdevice_net_instance *instance,
+ uint32_t expected_votes, enum tlv_vote *vote)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Votequorum expected votes notify");
+
+ return (0);
+}
+
+/*
+ * Called when config node list reply is received. Vote is set to value returned by server (and can
+ * be overwriten by algorithm). ring_id is returned by server. ring_id_is_valid is set to 1 only if
+ * received ring id matches last sent ring id. If it is 0, then it usually makes sense to not
+ * update timer.
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_config_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, int initial, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Config node list reply");
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+/*
+ * Called when membership node list reply (reply for votequorum votequorum_nodelist_notify_fn)
+ * is received. Vote is set to value returned by server (and can be overwriten by algorithm).
+ *
+ * Also if server returned TLV_VOTE_ASK_LATER, it's good idea to create timer (call timer_list_add
+ * with instance->main_timer_list parameter) and ask for reply (qdevice_net_send_ask_for_vote).
+ * Another option may be to wait for vote_info message (if server algorithm is configured so).
+ *
+ * ring_id and ring_id_is_valid have same meaning as for
+ * qdevice_net_algo_test_config_node_list_reply_received
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_membership_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Membership node list reply");
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+/*
+ * Called when quorum node list reply (reply for votequorum votequorum_quorum_notify_fn)
+ * is received. Vote is set to value returned by server (and can be overwriten by algorithm).
+ *
+ * ring_id and ring_id_is_valid have same meaning as for
+ * qdevice_net_algo_test_config_node_list_reply_received
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_quorum_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Quorum node list reply");
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+/*
+ * Called when reply for ask for vote message was received.
+ * Vote is set to value returned by server (and can be overwriten by algorithm).
+ *
+ * ring_id and ring_id_is_valid have same meaning as for
+ * qdevice_net_algo_test_config_node_list_reply_received
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_ask_for_vote_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Ask for vote reply received");
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+/*
+ * Called when vote info message from server was received.
+ * Vote is set to value sent by server (and can be overwriten by algorithm).
+ *
+ * ring_id and ring_id_is_valid have same meaning as for
+ * qdevice_net_algo_test_config_node_list_reply_received
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_vote_info_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Vote info received");
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+/*
+ * Called when echo reply message was received.
+ * is_expected_seq_number is set to 1 if received seq_number was equal to last sent echo request.
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_echo_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, int is_expected_seq_number)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Echo reply received");
+
+ return (is_expected_seq_number ? 0 : -1);
+}
+
+/*
+ * Called when client is about to send echo request but echo reply to previous echo request
+ * was not yet received.
+ *
+ * Callback should return 0 if processing should continue (echo request is not sent but timer is
+ * scheduled again) otherwise -1 (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_echo_reply_not_received(struct qdevice_net_instance *instance)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Echo reply not received");
+
+ return (-1);
+}
+
+/*
+ * Called when regular heuristics finished and it's result differs from previous heuristics.
+ *
+ * heuristics contains result of heuristics
+ * send_msg distinquish if message should be sent to qnetd. Default value is 0 if qnetd is not
+ * connected and 1 otherwise
+ * vote can be set to change cast_vote_timer voting value (default is TLV_VOTE_NO_CHANGE)
+ *
+ * It's not possible to set send_msg to 1 and heuristics to TLV_HEURISTICS_UNDEFINED
+ *
+ * Callback should return 0 on success, -1 on failure (-> force exit)
+ */
+int
+qdevice_net_algo_test_heuristics_change(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_msg, enum tlv_vote *vote)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Heuristics change");
+
+ return (0);
+}
+
+/*
+ * Called when reply for heuristics change was received.
+ * Vote is set to value returned by server (and can be overwriten by algorithm).
+ *
+ * ring_id and ring_id_is_valid have same meaning as for
+ * qdevice_net_algo_test_config_node_list_reply_received
+ *
+ * heuristics is unmodified value sent to server (and set by qdevice_net_algo_test_heuristics_change)
+ *
+ * Callback should return 0 on success or -1 on failure (-> disconnect client).
+ */
+int
+qdevice_net_algo_test_heuristics_change_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_heuristics heuristics, enum tlv_vote *vote)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: heuristics change reply received");
+
+ if (!ring_id_is_valid) {
+ *vote = TLV_VOTE_NO_CHANGE;
+ }
+
+ return (0);
+}
+
+/*
+ * Called when client disconnects from server.
+ *
+ * disconnect_reason contains one of QDEVICE_NET_DISCONNECT_REASON_
+ * try_reconnect can be set to non zero value if reconnect to server should be tried
+ * vote (default TLV_VOTE_NO_CHANGE) can be set to update voting timer
+ *
+ * Callback should return 0 on success, -1 on failure (-> force exit)
+ */
+int
+qdevice_net_algo_test_disconnected(struct qdevice_net_instance *instance,
+ enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Disconnected");
+
+ return (0);
+}
+
+/*
+ * Called when qdevice-net is going down.
+ */
+void
+qdevice_net_algo_test_destroy(struct qdevice_net_instance *instance)
+{
+
+ qdevice_log(LOG_INFO, "algo-test: Destroy");
+}
+
+static struct qdevice_net_algorithm qdevice_net_algo_test = {
+ .init = qdevice_net_algo_test_init,
+ .connected = qdevice_net_algo_test_connected,
+ .config_node_list_changed = qdevice_net_algo_test_config_node_list_changed,
+ .votequorum_node_list_notify = qdevice_net_algo_test_votequorum_node_list_notify,
+ .votequorum_node_list_heuristics_notify = qdevice_net_algo_test_votequorum_node_list_heuristics_notify,
+ .votequorum_quorum_notify = qdevice_net_algo_test_votequorum_quorum_notify,
+ .votequorum_expected_votes_notify = qdevice_net_algo_test_votequorum_expected_votes_notify,
+ .config_node_list_reply_received = qdevice_net_algo_test_config_node_list_reply_received,
+ .membership_node_list_reply_received = qdevice_net_algo_test_membership_node_list_reply_received,
+ .quorum_node_list_reply_received = qdevice_net_algo_test_quorum_node_list_reply_received,
+ .ask_for_vote_reply_received = qdevice_net_algo_test_ask_for_vote_reply_received,
+ .vote_info_received = qdevice_net_algo_test_vote_info_received,
+ .echo_reply_received = qdevice_net_algo_test_echo_reply_received,
+ .echo_reply_not_received = qdevice_net_algo_test_echo_reply_not_received,
+ .heuristics_change = qdevice_net_algo_test_heuristics_change,
+ .heuristics_change_reply_received = qdevice_net_algo_test_heuristics_change_reply_received,
+ .disconnected = qdevice_net_algo_test_disconnected,
+ .destroy = qdevice_net_algo_test_destroy,
+};
+
+int
+qdevice_net_algo_test_register(void)
+{
+ return (qdevice_net_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_TEST, &qdevice_net_algo_test));
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_NET_ALGO_TEST_H_
+#define _QDEVICE_NET_ALGO_TEST_H_
+
+#include "qdevice-net-algorithm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_net_algo_test_init(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_algo_test_connected(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_config_node_list, int *send_membership_node_list,
+ int *send_quorum_node_list, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_test_config_node_list_changed(
+ struct qdevice_net_instance *instance, const struct node_list *nlist,
+ int config_version_set, uint64_t config_version, int *send_node_list, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_test_votequorum_node_list_notify(
+ struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id,
+ uint32_t node_list_entries, uint32_t node_list[], int *pause_cast_vote_timer,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algo_test_votequorum_node_list_heuristics_notify(
+ struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id,
+ uint32_t node_list_entries, uint32_t node_list[], int *send_node_list, enum tlv_vote *vote,
+ enum tlv_heuristics *heuristics);
+
+extern int qdevice_net_algo_test_votequorum_quorum_notify(
+ struct qdevice_net_instance *instance, uint32_t quorate, uint32_t node_list_entries,
+ votequorum_node_t node_list[], int *send_node_list, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_test_votequorum_expected_votes_notify(
+ struct qdevice_net_instance *instance, uint32_t expected_votes, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_test_config_node_list_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number, int initial,
+ const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_test_membership_node_list_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number, const struct tlv_ring_id *ring_id,
+ int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_test_quorum_node_list_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number,
+ const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_test_ask_for_vote_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number,
+ const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_test_vote_info_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algo_test_echo_reply_received(
+ struct qdevice_net_instance *instance, uint32_t seq_number, int is_expected_seq_number);
+
+extern int qdevice_net_algo_test_echo_reply_not_received(
+ struct qdevice_net_instance *instance);
+
+extern int qdevice_net_algo_test_heuristics_change(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_msg, enum tlv_vote *vote);
+
+extern int qdevice_net_algo_test_heuristics_change_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_heuristics heuristics,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algo_test_disconnected(struct qdevice_net_instance *instance,
+ enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote);
+
+extern void qdevice_net_algo_test_destroy(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_algo_test_register(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_ALGO_TEST_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <sys/types.h>
+
+#include "qnet-config.h"
+#include "qdevice-net-algorithm.h"
+#include "qdevice-log.h"
+
+#include "qdevice-net-algo-test.h"
+#include "qdevice-net-algo-ffsplit.h"
+#include "qdevice-net-algo-2nodelms.h"
+#include "qdevice-net-algo-lms.h"
+
+static struct qdevice_net_algorithm *qdevice_net_algorithm_array[QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE];
+
+int
+qdevice_net_algorithm_init(struct qdevice_net_instance *instance)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_init unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->init(instance));
+}
+
+int
+qdevice_net_algorithm_connected(struct qdevice_net_instance *instance, enum tlv_heuristics *heuristics,
+ int *send_config_node_list, int *send_membership_node_list, int *send_quorum_node_list, enum tlv_vote *vote)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_connected unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->connected(instance,
+ heuristics, send_config_node_list, send_membership_node_list, send_quorum_node_list, vote));
+}
+
+int
+qdevice_net_algorithm_config_node_list_changed(struct qdevice_net_instance *instance,
+ const struct node_list *nlist, int config_version_set, uint64_t config_version,
+ int *send_node_list, enum tlv_vote *vote)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_connected unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ config_node_list_changed(instance, nlist, config_version_set, config_version,
+ send_node_list, vote));
+}
+
+int
+qdevice_net_algorithm_votequorum_node_list_notify(struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ int *pause_cast_vote_timer, enum tlv_vote *vote)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_votequorum_node_list_notify "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->votequorum_node_list_notify(
+ instance, ring_id, node_list_entries, node_list, pause_cast_vote_timer, vote));
+}
+
+int
+qdevice_net_algorithm_votequorum_node_list_heuristics_notify(struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ int *send_node_list, enum tlv_vote *vote, enum tlv_heuristics *heuristics)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_votequorum_node_list_heuristics_notify "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ votequorum_node_list_heuristics_notify(
+ instance, ring_id, node_list_entries, node_list, send_node_list, vote, heuristics));
+}
+
+int
+qdevice_net_algorithm_votequorum_quorum_notify(struct qdevice_net_instance *instance,
+ uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list,
+ enum tlv_vote *vote)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_votequorum_quorum_notify "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ votequorum_quorum_notify(instance, quorate, node_list_entries, node_list,
+ send_node_list, vote));
+}
+
+int
+qdevice_net_algorithm_votequorum_expected_votes_notify(struct qdevice_net_instance *instance,
+ uint32_t expected_votes, enum tlv_vote *vote)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_votequorum_expected_votes_notify "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ votequorum_expected_votes_notify(instance, expected_votes, vote));
+}
+
+int
+qdevice_net_algorithm_config_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, int initial, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_config_node_list_reply_received "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ config_node_list_reply_received(instance, seq_number, initial, ring_id,
+ ring_id_is_valid, vote));
+}
+
+int
+qdevice_net_algorithm_membership_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_membership_node_list_reply_received "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ membership_node_list_reply_received(instance, seq_number, ring_id, ring_id_is_valid,
+ vote));
+}
+
+int
+qdevice_net_algorithm_quorum_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_quorum_node_list_reply_received "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ quorum_node_list_reply_received(instance, seq_number, ring_id, ring_id_is_valid,
+ vote));
+}
+
+int
+qdevice_net_algorithm_ask_for_vote_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_ask_for_vote_reply_received "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ ask_for_vote_reply_received(instance, seq_number, ring_id, ring_id_is_valid, vote));
+}
+
+int
+qdevice_net_algorithm_vote_info_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_vote_info_received "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ vote_info_received(instance, seq_number, ring_id, ring_id_is_valid, vote));
+}
+
+int
+qdevice_net_algorithm_echo_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, int is_expected_seq_number)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_echo_reply_received "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ echo_reply_received(instance, seq_number, is_expected_seq_number));
+}
+
+int
+qdevice_net_algorithm_echo_reply_not_received(struct qdevice_net_instance *instance)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_echo_reply_not_received "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ echo_reply_not_received(instance));
+}
+
+int
+qdevice_net_algorithm_heuristics_change(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_msg, enum tlv_vote *vote)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_heuristics_change "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ heuristics_change(instance, heuristics, send_msg, vote));
+}
+
+int
+qdevice_net_algorithm_heuristics_change_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_heuristics heuristics, enum tlv_vote *vote)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_heuristics_change_reply_received "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ heuristics_change_reply_received(instance, seq_number, ring_id, ring_id_is_valid,
+ heuristics, vote));
+}
+
+int
+qdevice_net_algorithm_disconnected(struct qdevice_net_instance *instance,
+ enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_disconnected "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ disconnected(instance, disconnect_reason, try_reconnect, vote));
+}
+
+void
+qdevice_net_algorithm_destroy(struct qdevice_net_instance *instance)
+{
+
+ if (instance->decision_algorithm >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qdevice_net_algorithm_array[instance->decision_algorithm] == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_algorithm_destroy "
+ "unhandled decision algorithm");
+ exit(1);
+ }
+
+ return (qdevice_net_algorithm_array[instance->decision_algorithm]->
+ destroy(instance));
+}
+
+int
+qdevice_net_algorithm_register(enum tlv_decision_algorithm_type algorithm_number,
+ struct qdevice_net_algorithm *algorithm)
+{
+
+ if (algorithm_number >= QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE) {
+ return (-1);
+ }
+
+ if (qdevice_net_algorithm_array[algorithm_number] != NULL) {
+ return (-1);
+ }
+
+ qdevice_net_algorithm_array[algorithm_number] = algorithm;
+
+ return (0);
+}
+
+int
+qdevice_net_algorithm_register_all(void)
+{
+
+ if (qdevice_net_algo_test_register() != 0) {
+ qdevice_log(LOG_CRIT, "Failed to register decision algorithm 'test' ");
+ return (-1);
+ }
+
+ if (qdevice_net_algo_ffsplit_register() != 0) {
+ qdevice_log(LOG_CRIT, "Failed to register decision algorithm 'ffsplit' ");
+ return (-1);
+ }
+
+ if (qdevice_net_algo_2nodelms_register() != 0) {
+ qdevice_log(LOG_CRIT, "Failed to register decision algorithm '2nodelms' ");
+ return (-1);
+ }
+
+ if (qdevice_net_algo_lms_register() != 0) {
+ qdevice_log(LOG_CRIT, "Failed to register decision algorithm 'lms' ");
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_NET_ALGORITHM_H_
+#define _QDEVICE_NET_ALGORITHM_H_
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include "node-list.h"
+#include "qdevice-net-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_net_algorithm_init(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_algorithm_connected(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_config_node_list, int *send_membership_node_list,
+ int *send_quorum_node_list, enum tlv_vote *vote);
+
+extern int qdevice_net_algorithm_config_node_list_changed(struct qdevice_net_instance *instance,
+ const struct node_list *nlist, int config_version_set, uint64_t config_version,
+ int *send_node_list, enum tlv_vote *vote);
+
+extern int qdevice_net_algorithm_votequorum_node_list_notify(struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ int *pause_cast_vote_timer, enum tlv_vote *vote);
+
+extern int qdevice_net_algorithm_votequorum_node_list_heuristics_notify(
+ struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ int *send_node_list, enum tlv_vote *vote, enum tlv_heuristics *heuristics);
+
+extern int qdevice_net_algorithm_votequorum_quorum_notify(struct qdevice_net_instance *instance,
+ uint32_t quorate, uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algorithm_votequorum_expected_votes_notify(struct qdevice_net_instance *instance,
+ uint32_t expected_votes, enum tlv_vote *vote);
+
+extern int qdevice_net_algorithm_config_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, int initial, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algorithm_membership_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algorithm_quorum_node_list_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algorithm_ask_for_vote_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_vote *vote);
+
+extern int qdevice_net_algorithm_vote_info_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algorithm_echo_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, int is_expected_seq_number);
+
+extern int qdevice_net_algorithm_echo_reply_not_received(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_algorithm_heuristics_change(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_msg, enum tlv_vote *vote);
+
+extern int qdevice_net_algorithm_heuristics_change_reply_received(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid, enum tlv_heuristics heuristics,
+ enum tlv_vote *vote);
+
+extern int qdevice_net_algorithm_disconnected(struct qdevice_net_instance *instance,
+ enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect, enum tlv_vote *vote);
+
+extern void qdevice_net_algorithm_destroy(struct qdevice_net_instance *instance);
+
+struct qdevice_net_algorithm {
+ int (*init)(struct qdevice_net_instance *instance);
+ int (*connected)(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_config_node_list, int *send_membership_node_list,
+ int *send_quorum_node_list, enum tlv_vote *vote);
+ int (*config_node_list_changed)(struct qdevice_net_instance *instance,
+ const struct node_list *nlist, int config_version_set, uint64_t config_version,
+ int *send_node_list, enum tlv_vote *vote);
+ int (*votequorum_node_list_notify)(struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ int *pause_cast_vote_timer, enum tlv_vote *vote);
+ int (*votequorum_node_list_heuristics_notify)(struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id, uint32_t node_list_entries, uint32_t node_list[],
+ int *send_node_list, enum tlv_vote *vote, enum tlv_heuristics *heuristics);
+ int (*votequorum_quorum_notify)(struct qdevice_net_instance *instance, uint32_t quorate,
+ uint32_t node_list_entries, votequorum_node_t node_list[], int *send_node_list,
+ enum tlv_vote *vote);
+ int (*votequorum_expected_votes_notify)(struct qdevice_net_instance *instance,
+ uint32_t expected_votes, enum tlv_vote *vote);
+ int (*config_node_list_reply_received)(struct qdevice_net_instance *instance,
+ uint32_t seq_number, int initial, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote);
+ int (*membership_node_list_reply_received)(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote);
+ int (*quorum_node_list_reply_received)(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote);
+ int (*ask_for_vote_reply_received)(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote);
+ int (*vote_info_received)(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_vote *vote);
+ int (*echo_reply_received)(struct qdevice_net_instance *instance,
+ uint32_t seq_number, int is_expected_seq_number);
+ int (*echo_reply_not_received)(struct qdevice_net_instance *instance);
+ int (*heuristics_change)(struct qdevice_net_instance *instance,
+ enum tlv_heuristics *heuristics, int *send_msg, enum tlv_vote *vote);
+ int (*heuristics_change_reply_received)(struct qdevice_net_instance *instance,
+ uint32_t seq_number, const struct tlv_ring_id *ring_id, int ring_id_is_valid,
+ enum tlv_heuristics heuristics, enum tlv_vote *vote);
+ int (*disconnected)(struct qdevice_net_instance *instance,
+ enum qdevice_net_disconnect_reason disconnect_reason, int *try_reconnect,
+ enum tlv_vote *vote);
+ void (*destroy)(struct qdevice_net_instance *instance);
+};
+
+extern int qdevice_net_algorithm_register(
+ enum tlv_decision_algorithm_type algorithm_number, struct qdevice_net_algorithm *algorithm);
+
+extern int qdevice_net_algorithm_register_all(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_ALGORITHM_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 "qnet-config.h"
+#include "qdevice-log.h"
+#include "qdevice-net-cast-vote-timer.h"
+#include "qdevice-votequorum.h"
+
+static int
+qdevice_net_cast_vote_timer_callback(void *data1, void *data2)
+{
+ struct qdevice_net_instance *instance;
+ int cast_vote;
+ int case_processed;
+
+ instance = (struct qdevice_net_instance *)data1;
+
+ if (instance->cast_vote_timer_paused) {
+ return (-1);
+ }
+
+ case_processed = 0;
+
+ switch (instance->cast_vote_timer_vote) {
+ case TLV_VOTE_ACK:
+ case_processed = 1;
+ cast_vote = 1;
+ break;
+ case TLV_VOTE_NACK:
+ case_processed = 1;
+ cast_vote = 0;
+ break;
+ case TLV_VOTE_ASK_LATER:
+ case TLV_VOTE_WAIT_FOR_REPLY:
+ case TLV_VOTE_NO_CHANGE:
+ case TLV_VOTE_UNDEFINED:
+ /*
+ * Shouldn't happen
+ */
+ break;
+ /*
+ * Default is not defined intentionally. Compiler shows warning when
+ * new tlv_vote is added.
+ */
+ }
+
+ if (!case_processed) {
+ qdevice_log(LOG_CRIT, "qdevice_net_timer_cast_vote: Unhandled cast_vote_timer_vote %u\n",
+ instance->cast_vote_timer_vote);
+ exit(1);
+ }
+
+ if (qdevice_votequorum_poll(instance->qdevice_instance_ptr, cast_vote) != 0) {
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
+ instance->schedule_disconnect = 1;
+ instance->cast_vote_timer = NULL;
+ return (0);
+ }
+
+ /*
+ * Schedule this function callback again
+ */
+ return (-1);
+}
+
+int
+qdevice_net_cast_vote_timer_update(struct qdevice_net_instance *instance, enum tlv_vote vote)
+{
+ int timer_needs_running;
+ int case_processed;
+
+ case_processed = 0;
+
+ switch (vote) {
+ case TLV_VOTE_UNDEFINED:
+ break;
+ case TLV_VOTE_ACK:
+ case TLV_VOTE_NACK:
+ case_processed = 1;
+ timer_needs_running = 1;
+ break;
+ case TLV_VOTE_WAIT_FOR_REPLY:
+ case TLV_VOTE_ASK_LATER:
+ case_processed = 1;
+ timer_needs_running = 0;
+ break;
+ case TLV_VOTE_NO_CHANGE:
+ case_processed = 1;
+ return (0);
+
+ break;
+ /*
+ * Default is not defined intentionally. Compiler shows warning when
+ * new tlv_vote is added.
+ */
+ }
+
+ if (!case_processed) {
+ qdevice_log(LOG_CRIT, "qdevice_net_cast_vote_timer_update_vote: Unhandled vote parameter %u\n",
+ vote);
+ exit(1);
+ }
+
+ instance->cast_vote_timer_vote = vote;
+
+ if (timer_needs_running) {
+ if (instance->cast_vote_timer == NULL) {
+ instance->cast_vote_timer = timer_list_add(&instance->main_timer_list,
+ instance->cast_vote_timer_interval,
+ qdevice_net_cast_vote_timer_callback, (void *)instance, NULL);
+
+ if (instance->cast_vote_timer == NULL) {
+ qdevice_log(LOG_ERR, "Can't schedule sending of "
+ "votequorum poll");
+
+ return (-1);
+ } else {
+ qdevice_log(LOG_DEBUG, "Cast vote timer is now scheduled every "
+ "%"PRIu32"ms voting %s.", instance->cast_vote_timer_interval,
+ tlv_vote_to_str(instance->cast_vote_timer_vote));
+ }
+ } else {
+ qdevice_log(LOG_DEBUG, "Cast vote timer remains scheduled every "
+ "%"PRIu32"ms voting %s.", instance->cast_vote_timer_interval,
+ tlv_vote_to_str(instance->cast_vote_timer_vote));
+ }
+
+ if (qdevice_net_cast_vote_timer_callback((void *)instance, NULL) != -1) {
+ return (-1);
+ }
+ } else {
+ if (instance->cast_vote_timer != NULL) {
+ timer_list_delete(&instance->main_timer_list, instance->cast_vote_timer);
+ instance->cast_vote_timer = NULL;
+ qdevice_log(LOG_DEBUG, "Cast vote timer is now stopped.");
+ } else {
+ qdevice_log(LOG_DEBUG, "Cast vote timer remains stopped.");
+ }
+ }
+
+ return (0);
+}
+
+void
+qdevice_net_cast_vote_timer_set_paused(struct qdevice_net_instance *instance, int paused)
+{
+
+ if (paused != instance->cast_vote_timer_paused) {
+ instance->cast_vote_timer_paused = paused;
+
+ if (instance->cast_vote_timer_paused) {
+ qdevice_log(LOG_DEBUG, "Cast vote timer is now paused.");
+ } else {
+ qdevice_log(LOG_DEBUG, "Cast vote timer is no longer paused.");
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_NET_CAST_VOTE_TIMER_H_
+#define _QDEVICE_NET_CAST_VOTE_TIMER_H_
+
+#include "qdevice-net-instance.h"
+#include "tlv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_net_cast_vote_timer_update(
+ struct qdevice_net_instance *instance, enum tlv_vote vote);
+
+extern void qdevice_net_cast_vote_timer_set_paused(
+ struct qdevice_net_instance *instance, int paused);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_CAST_VOTE_TIMER_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_NET_DISCONNECT_REASON_H_
+#define _QDEVICE_NET_DISCONNECT_REASON_H_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum qdevice_net_disconnect_reason {
+ /* Undefined reason. If this error appears, it's error in source code */
+ QDEVICE_NET_DISCONNECT_REASON_UNDEFINED,
+
+ /* Received known message, but it was not expected */
+ QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG,
+ /* Received unknown message */
+ QDEVICE_NET_DISCONNECT_REASON_UNSUPPORTED_MSG,
+ /* TLS setting of server and client are incompatible */
+ QDEVICE_NET_DISCONNECT_REASON_INCOMPATIBLE_TLS,
+ /* MSG setting of server and client are incompatible */
+ QDEVICE_NET_DISCONNECT_REASON_INCOMPATIBLE_MSG_SIZE,
+ /* Message doesn't contain required option */
+ QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING,
+
+ /* Can't allocate send list item or message is too long to fit into send buffer */
+ QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER,
+ /* Impossible to create or update heartbeat sending timer */
+ QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_HB_TIMER,
+ /* Impossible to create or update votequorum poll timer */
+ QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER,
+ /* Impossible to create or update regular heuristics timer */
+ QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_HEURISTICS_TIMER,
+ /* Impossible to exec heuristics */
+ QDEVICE_NET_DISCONNECT_REASON_CANT_START_HEURISTICS,
+ /* Impossible to activate/deactive heuristics result notifier */
+ QDEVICE_NET_DISCONNECT_REASON_CANT_ACTIVATE_HEURISTICS_RESULT_NOTIFIER,
+ /* Impossible to register votequorum callback */
+ QDEVICE_NET_DISCONNECT_REASON_CANT_REGISTER_VOTEQUORUM_CALLBACK,
+ /* Impossible to register cmap callback */
+ QDEVICE_NET_DISCONNECT_REASON_CANT_REGISTER_CMAP_CALLBACK,
+ /* Impossible to start TLS session */
+ QDEVICE_NET_DISCONNECT_REASON_CANT_START_TLS,
+
+ /* Received message with error field set to non TLV_REPLY_ERROR_CODE_NO_ERROR value */
+ QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_ERROR,
+ /* Received message with error field set to TLV_REPLY_ERROR_CODE_DUPLICATE_NODE_ID value */
+ QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_DUPLICATE_NODE_ID_ERROR,
+ /* Received message with error field set to TLV_REPLY_ERROR_CODE_TIE_BREAKER_DIFFERS_FROM_OTHER_NODES value */
+ QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_TIE_BREAKER_DIFFERS_FROM_OTHER_NODES_ERROR,
+ /* Received message with error field set to TLV_REPLY_ERROR_CODE_ALGORITHM_DIFFERS_FROM_OTHER_NODES value */
+ QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_ALGORITHM_DIFFERS_FROM_OTHER_NODES_ERROR,
+
+ /* Server doesn't support client selected decision algorithm */
+ QDEVICE_NET_DISCONNECT_REASON_SERVER_DOESNT_SUPPORT_REQUIRED_ALGORITHM,
+ /* Server doesn't support client required option */
+ QDEVICE_NET_DISCONNECT_REASON_SERVER_DOESNT_SUPPORT_REQUIRED_OPT,
+
+ /* Can't decode message sent by server */
+ QDEVICE_NET_DISCONNECT_REASON_MSG_DECODE_ERROR,
+ /* Server closed connection */
+ QDEVICE_NET_DISCONNECT_REASON_SERVER_CLOSED_CONNECTION,
+ /* Can't read or store message received from server */
+ QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE,
+ /* Can't send message to server */
+ QDEVICE_NET_DISCONNECT_REASON_CANT_SEND_MESSAGE,
+
+ /* Can't dispatch cmap or votequroum. This cannot be overwritten and always means end of qdevice-net */
+ QDEVICE_NET_DISCONNECT_REASON_COROSYNC_CONNECTION_CLOSED,
+
+ /* Local socket closed is reasult of sigint */
+ QDEVICE_NET_DISCONNECT_REASON_LOCAL_SOCKET_CLOSED,
+
+ /* It was not possible to establish connection with qnetd */
+ QDEVICE_NET_DISCONNECT_REASON_CANT_CONNECT_TO_THE_SERVER,
+
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_CONNECTED_ERR,
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_CONFIG_NODE_LIST_CHANGED_ERR,
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_QUORUM_NOTIFY_ERR,
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_NODE_LIST_NOTIFY_ERR,
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_NODE_LIST_HEURISTICS_NOTIFY_ERR,
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTEQUORUM_EXPECTED_VOTES_NOTIFY_ERR,
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_NODE_LIST_REPLY_ERR,
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_ASK_FOR_VOTE_REPLY_ERR,
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTE_INFO_ERR,
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_ECHO_REPLY_RECEIVED_ERR,
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_ECHO_REPLY_NOT_RECEIVED_ERR,
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_HEURISTICS_CHANGE_ERR,
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_HEURISTICS_CHANGE_REPLY_ERR,
+
+ QDEVICE_NET_DISCONNECT_REASON_HEURISTICS_WORKER_CLOSED,
+ QDEVICE_NET_DISCONNECT_REASON_HEURISTICS_CANT_SEND_RECEIVE_MSG,
+};
+
+#define qdevice_net_disconnect_reason_try_reconnect(reason) ( \
+ reason == QDEVICE_NET_DISCONNECT_REASON_MSG_DECODE_ERROR || \
+ reason == QDEVICE_NET_DISCONNECT_REASON_SERVER_CLOSED_CONNECTION || \
+ reason == QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE || \
+ reason == QDEVICE_NET_DISCONNECT_REASON_CANT_SEND_MESSAGE || \
+ reason == QDEVICE_NET_DISCONNECT_REASON_CANT_CONNECT_TO_THE_SERVER || \
+ reason == QDEVICE_NET_DISCONNECT_REASON_ALGO_ECHO_REPLY_NOT_RECEIVED_ERR || \
+ reason == QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_DUPLICATE_NODE_ID_ERROR || \
+ reason == QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_TIE_BREAKER_DIFFERS_FROM_OTHER_NODES_ERROR || \
+ reason == QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_ALGORITHM_DIFFERS_FROM_OTHER_NODES_ERROR)
+
+
+#define qdevice_net_disconnect_reason_force_disconnect(reason) ( \
+ reason == QDEVICE_NET_DISCONNECT_REASON_COROSYNC_CONNECTION_CLOSED || \
+ reason == QDEVICE_NET_DISCONNECT_REASON_LOCAL_SOCKET_CLOSED || \
+ reason == QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER || \
+ reason == QDEVICE_NET_DISCONNECT_REASON_CANT_REGISTER_VOTEQUORUM_CALLBACK || \
+ reason == QDEVICE_NET_DISCONNECT_REASON_CANT_REGISTER_CMAP_CALLBACK || \
+ reason == QDEVICE_NET_DISCONNECT_REASON_HEURISTICS_WORKER_CLOSED || \
+ reason == QDEVICE_NET_DISCONNECT_REASON_HEURISTICS_CANT_SEND_RECEIVE_MSG)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_DISCONNECT_REASON_H_ */
--- /dev/null
+/*
+ * 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 "qdevice-net-algorithm.h"
+#include "qdevice-net-echo-request-timer.h"
+#include "qdevice-net-send.h"
+#include "qdevice-log.h"
+
+static int
+qdevice_net_echo_request_timer_callback(void *data1, void *data2)
+{
+ struct qdevice_net_instance *instance;
+
+ instance = (struct qdevice_net_instance *)data1;
+
+ if (instance->echo_reply_received_msg_seq_num !=
+ instance->echo_request_expected_msg_seq_num) {
+ qdevice_log(LOG_ERR, "Server didn't send echo reply message on time");
+
+ if (qdevice_net_algorithm_echo_reply_not_received(instance) != 0) {
+ qdevice_log(LOG_DEBUG, "Algorithm decided to disconnect");
+
+ instance->schedule_disconnect = 1;
+ instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_ECHO_REPLY_NOT_RECEIVED_ERR;
+
+ instance->echo_request_timer = NULL;
+ return (0);
+ } else {
+ qdevice_log(LOG_DEBUG, "Algorithm decided to continue send heartbeat");
+
+ return (-1);
+ }
+ }
+
+ if (qdevice_net_send_echo_request(instance) == -1) {
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+
+ instance->schedule_disconnect = 1;
+ instance->echo_request_timer = NULL;
+ return (0);
+ }
+
+ /*
+ * Schedule this function callback again
+ */
+ return (-1);
+}
+
+int
+qdevice_net_echo_request_timer_schedule(struct qdevice_net_instance *instance)
+{
+ instance->echo_request_expected_msg_seq_num = 0;
+ instance->echo_reply_received_msg_seq_num = 0;
+
+ if (instance->echo_request_timer != NULL) {
+ timer_list_delete(&instance->main_timer_list, instance->echo_request_timer);
+ instance->echo_request_timer = NULL;
+ }
+
+ qdevice_log(LOG_DEBUG, "Scheduling send of heartbeat every %"PRIu32"ms", instance->heartbeat_interval);
+ instance->echo_request_timer = timer_list_add(&instance->main_timer_list,
+ instance->heartbeat_interval, qdevice_net_echo_request_timer_callback,
+ (void *)instance, NULL);
+
+ if (instance->echo_request_timer == NULL) {
+ qdevice_log(LOG_ERR, "Can't schedule regular sending of heartbeat.");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_HB_TIMER;
+
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2016-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.
+ */
+
+#ifndef _QDEVICE_NET_ECHO_REQUEST_TIMER_H_
+#define _QDEVICE_NET_ECHO_REQUEST_TIMER_H_
+
+#include "qdevice-net-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_net_echo_request_timer_schedule(struct qdevice_net_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_ECHO_REQUEST_TIMER_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2017 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 "qdevice-log.h"
+#include "qdevice-net-algorithm.h"
+#include "qdevice-net-cast-vote-timer.h"
+#include "qdevice-net-heuristics.h"
+#include "qdevice-net-send.h"
+#include "qdevice-net-votequorum.h"
+
+enum tlv_heuristics
+qdevice_net_heuristics_exec_result_to_tlv(enum qdevice_heuristics_exec_result exec_result)
+{
+ enum tlv_heuristics res;
+
+ switch (exec_result) {
+ case QDEVICE_HEURISTICS_EXEC_RESULT_DISABLED: res = TLV_HEURISTICS_UNDEFINED; break;
+ case QDEVICE_HEURISTICS_EXEC_RESULT_PASS: res = TLV_HEURISTICS_PASS; break;
+ case QDEVICE_HEURISTICS_EXEC_RESULT_FAIL: res = TLV_HEURISTICS_FAIL; break;
+ default:
+ qdevice_log(LOG_ERR, "qdevice_net_heuristics_exec_result_to_tlv: Unhandled "
+ "heuristics exec result %s",
+ qdevice_heuristics_exec_result_to_str(exec_result));
+ exit(1);
+ break;
+ }
+
+ return (res);
+}
+
+static int
+qdevice_net_regular_heuristics_exec_result_callback(void *heuristics_instance_ptr,
+ uint32_t seq_number, enum qdevice_heuristics_exec_result exec_result)
+{
+ struct qdevice_heuristics_instance *heuristics_instance;
+ struct qdevice_instance *instance;
+ struct qdevice_net_instance *net_instance;
+ int send_msg;
+ enum tlv_vote vote;
+ enum tlv_heuristics heuristics;
+
+ heuristics_instance = (struct qdevice_heuristics_instance *)heuristics_instance_ptr;
+ instance = heuristics_instance->qdevice_instance_ptr;
+ net_instance = instance->model_data;
+
+ if (qdevice_heuristics_result_notifier_list_set_active(&heuristics_instance->exec_result_notifier_list,
+ qdevice_net_regular_heuristics_exec_result_callback, 0) != 0) {
+ qdevice_log(LOG_ERR, "Can't deactivate net regular heuristics exec callback notifier");
+
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ACTIVATE_HEURISTICS_RESULT_NOTIFIER;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+
+ heuristics = qdevice_net_heuristics_exec_result_to_tlv(exec_result);
+
+ if (exec_result == QDEVICE_HEURISTICS_EXEC_RESULT_DISABLED) {
+ /*
+ * Can happen when user disables heuristics during runtime
+ */
+ return (0);
+ }
+
+ if (net_instance->latest_heuristics_result != heuristics) {
+ qdevice_log(LOG_ERR, "Heuristics result changed from %s to %s",
+ tlv_heuristics_to_str(net_instance->latest_heuristics_result),
+ tlv_heuristics_to_str(heuristics));
+
+ if (net_instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
+ /*
+ * Not connected to qnetd
+ */
+ send_msg = 0;
+ } else {
+ send_msg = 1;
+ }
+
+ vote = TLV_VOTE_NO_CHANGE;
+
+ if (qdevice_net_algorithm_heuristics_change(net_instance, &heuristics, &send_msg,
+ &vote) == -1) {
+ qdevice_log(LOG_ERR, "Algorithm returned error. Disconnecting.");
+
+ net_instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_HEURISTICS_CHANGE_ERR;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ } else {
+ qdevice_log(LOG_DEBUG, "Algorithm decided to %s message with heuristics result "
+ "%s and result vote is %s", (send_msg ? "send" : "not send"),
+ tlv_heuristics_to_str(heuristics), tlv_vote_to_str(vote));
+ }
+
+ if (send_msg) {
+ if (heuristics == TLV_HEURISTICS_UNDEFINED) {
+ qdevice_log(LOG_ERR, "Inconsistent algorithm result. "
+ "It's not possible to send message with undefined heuristics. "
+ "Disconnecting.");
+
+ net_instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_ALGO_HEURISTICS_CHANGE_ERR;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+
+ if (!net_instance->server_supports_heuristics) {
+ qdevice_log(LOG_ERR, "Server doesn't support heuristics. "
+ "Disconnecting.");
+
+ net_instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_SERVER_DOESNT_SUPPORT_REQUIRED_OPT;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+
+ if (qdevice_net_send_heuristics_change(net_instance, heuristics) != 0) {
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+ }
+
+ if (qdevice_net_cast_vote_timer_update(net_instance, vote) != 0) {
+ qdevice_log(LOG_CRIT, "qdevice_net_heuristics_exec_result_callback "
+ "Can't update cast vote timer");
+
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+ }
+
+ net_instance->latest_regular_heuristics_result = heuristics;
+ net_instance->latest_heuristics_result = heuristics;
+
+ if (qdevice_net_heuristics_schedule_timer(net_instance) != 0) {
+ return (0);
+ }
+
+ return (0);
+}
+
+static int
+qdevice_net_connect_heuristics_exec_result_callback(void *heuristics_instance_ptr,
+ uint32_t seq_number, enum qdevice_heuristics_exec_result exec_result)
+{
+ struct qdevice_heuristics_instance *heuristics_instance;
+ struct qdevice_instance *instance;
+ struct qdevice_net_instance *net_instance;
+ enum tlv_vote vote;
+ enum tlv_heuristics heuristics;
+ int send_config_node_list;
+ int send_membership_node_list;
+ int send_quorum_node_list;
+ struct tlv_ring_id tlv_rid;
+ enum tlv_quorate quorate;
+
+ heuristics_instance = (struct qdevice_heuristics_instance *)heuristics_instance_ptr;
+ instance = heuristics_instance->qdevice_instance_ptr;
+ net_instance = instance->model_data;
+
+
+ if (qdevice_heuristics_result_notifier_list_set_active(&heuristics_instance->exec_result_notifier_list,
+ qdevice_net_connect_heuristics_exec_result_callback, 0) != 0) {
+ qdevice_log(LOG_ERR, "Can't deactivate net connect heuristics exec callback notifier");
+
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ACTIVATE_HEURISTICS_RESULT_NOTIFIER;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+
+ heuristics = qdevice_net_heuristics_exec_result_to_tlv(exec_result);
+
+ send_config_node_list = 1;
+ send_membership_node_list = 1;
+ send_quorum_node_list = 1;
+ vote = TLV_VOTE_WAIT_FOR_REPLY;
+
+ if (qdevice_net_algorithm_connected(net_instance, &heuristics, &send_config_node_list,
+ &send_membership_node_list, &send_quorum_node_list, &vote) != 0) {
+ qdevice_log(LOG_DEBUG, "Algorithm returned error. Disconnecting.");
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_CONNECTED_ERR;
+ return (0);
+ } else {
+ qdevice_log(LOG_DEBUG, "Algorithm decided to %s config node list, %s membership "
+ "node list, %s quorum node list, heuristics is %s and result vote is %s",
+ (send_config_node_list ? "send" : "not send"),
+ (send_membership_node_list ? "send" : "not send"),
+ (send_quorum_node_list ? "send" : "not send"),
+ tlv_heuristics_to_str(heuristics),
+ tlv_vote_to_str(vote));
+ }
+
+ /*
+ * Now we can finally really send node list, votequorum node list and update timer
+ */
+ if (send_config_node_list) {
+ if (qdevice_net_send_config_node_list(net_instance,
+ &instance->config_node_list,
+ instance->config_node_list_version_set,
+ instance->config_node_list_version, 1) != 0) {
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+ return (0);
+ }
+ }
+
+ if (send_membership_node_list) {
+ qdevice_net_votequorum_ring_id_to_tlv(&tlv_rid,
+ &instance->vq_node_list_ring_id);
+
+ if (qdevice_net_send_membership_node_list(net_instance, &tlv_rid,
+ instance->vq_node_list_entries,
+ instance->vq_node_list,
+ heuristics) != 0) {
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+ return (0);
+ }
+ }
+
+ if (send_quorum_node_list) {
+ quorate = (instance->vq_quorum_quorate ?
+ TLV_QUORATE_QUORATE : TLV_QUORATE_INQUORATE);
+
+ if (qdevice_net_send_quorum_node_list(net_instance,
+ quorate,
+ instance->vq_quorum_node_list_entries,
+ instance->vq_quorum_node_list) != 0) {
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+ return (0);
+ }
+ }
+
+ if (qdevice_net_cast_vote_timer_update(net_instance, vote) != 0) {
+ qdevice_log(LOG_CRIT, "qdevice_net_msg_received_set_option_reply fatal error. "
+ " Can't update cast vote timer vote");
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
+ }
+
+ net_instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS;
+ net_instance->connected_since_time = time(NULL);
+
+ net_instance->latest_connect_heuristics_result = heuristics;
+ net_instance->latest_heuristics_result = heuristics;
+
+ return (0);
+}
+
+static int
+qdevice_net_heuristics_timer_callback(void *data1, void *data2)
+{
+ struct qdevice_net_instance *net_instance;
+ struct qdevice_heuristics_instance *heuristics_instance;
+
+ net_instance = (struct qdevice_net_instance *)data1;
+ heuristics_instance = &net_instance->qdevice_instance_ptr->heuristics_instance;
+
+ if (qdevice_heuristics_waiting_for_result(heuristics_instance)) {
+ qdevice_log(LOG_DEBUG, "Not executing regular heuristics because other heuristics is already running.");
+
+ return (1);
+ }
+
+ net_instance->regular_heuristics_timer = NULL;
+
+ qdevice_log(LOG_DEBUG, "Executing regular heuristics.");
+
+ if (qdevice_heuristics_result_notifier_list_set_active(&heuristics_instance->exec_result_notifier_list,
+ qdevice_net_regular_heuristics_exec_result_callback, 1) != 0) {
+ qdevice_log(LOG_ERR, "Can't activate net regular heuristics exec callback notifier");
+
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ACTIVATE_HEURISTICS_RESULT_NOTIFIER;
+ net_instance->schedule_disconnect = 1;
+
+ return (0);
+ }
+
+ if (qdevice_heuristics_exec(heuristics_instance,
+ net_instance->qdevice_instance_ptr->sync_in_progress) != 0) {
+ qdevice_log(LOG_ERR, "Can't execute regular heuristics.");
+
+ net_instance->schedule_disconnect = 1;
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_START_HEURISTICS;
+
+ return (0);
+ }
+
+ /*
+ * Do not schedule this callback again. It's going to be scheduled in the
+ * qdevice_net_heuristics_exec_result_callback
+ */
+ return (0);
+}
+
+int
+qdevice_net_heuristics_stop_timer(struct qdevice_net_instance *net_instance)
+{
+ struct qdevice_instance *instance;
+ struct qdevice_heuristics_instance *heuristics_instance;
+
+ instance = net_instance->qdevice_instance_ptr;
+ heuristics_instance = &instance->heuristics_instance;
+
+ if (net_instance->regular_heuristics_timer != NULL) {
+ qdevice_log(LOG_DEBUG, "Regular heuristics timer stopped");
+
+ timer_list_delete(&net_instance->main_timer_list, net_instance->regular_heuristics_timer);
+ net_instance->regular_heuristics_timer = NULL;
+
+ if (qdevice_heuristics_result_notifier_list_set_active(&heuristics_instance->exec_result_notifier_list,
+ qdevice_net_regular_heuristics_exec_result_callback, 0) != 0) {
+ qdevice_log(LOG_ERR, "Can't deactivate net regular heuristics exec callback notifier");
+
+ net_instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_CANT_ACTIVATE_HEURISTICS_RESULT_NOTIFIER;
+ net_instance->schedule_disconnect = 1;
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_heuristics_schedule_timer(struct qdevice_net_instance *net_instance)
+{
+ uint32_t interval;
+ struct qdevice_instance *instance;
+ struct qdevice_heuristics_instance *heuristics_instance;
+
+ instance = net_instance->qdevice_instance_ptr;
+ heuristics_instance = &instance->heuristics_instance;
+
+ if (heuristics_instance->mode != QDEVICE_HEURISTICS_MODE_ENABLED) {
+ qdevice_log(LOG_DEBUG, "Not scheduling heuristics timer because mode is not enabled");
+
+ if (qdevice_net_heuristics_stop_timer(net_instance) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ if (net_instance->regular_heuristics_timer != NULL) {
+ qdevice_log(LOG_DEBUG, "Not scheduling heuristics timer because it is already scheduled");
+
+ return (0);
+ }
+
+ interval = heuristics_instance->interval;
+
+ qdevice_log(LOG_DEBUG, "Scheduling next regular heuristics in %"PRIu32"ms", interval);
+
+ net_instance->regular_heuristics_timer = timer_list_add(&net_instance->main_timer_list,
+ interval,
+ qdevice_net_heuristics_timer_callback,
+ (void *)net_instance, NULL);
+
+ if (net_instance->regular_heuristics_timer == NULL) {
+ qdevice_log(LOG_ERR, "Can't schedule regular heuristics.");
+
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_HEURISTICS_TIMER;
+ net_instance->schedule_disconnect = 1;
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_heuristics_init(struct qdevice_net_instance *net_instance)
+{
+
+ if (qdevice_heuristics_result_notifier_list_add(
+ &net_instance->qdevice_instance_ptr->heuristics_instance.exec_result_notifier_list,
+ qdevice_net_regular_heuristics_exec_result_callback) == NULL) {
+ qdevice_log(LOG_ERR, "Can't add net regular heuristics exec callback into notifier");
+
+ return (-1);
+ }
+
+ if (qdevice_heuristics_result_notifier_list_add(
+ &net_instance->qdevice_instance_ptr->heuristics_instance.exec_result_notifier_list,
+ qdevice_net_connect_heuristics_exec_result_callback) == NULL) {
+ qdevice_log(LOG_ERR, "Can't add net connect heuristics exec callback into notifier");
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_heuristics_exec_after_connect(struct qdevice_net_instance *net_instance)
+{
+ struct qdevice_instance *instance;
+ struct qdevice_heuristics_instance *heuristics_instance;
+
+ instance = net_instance->qdevice_instance_ptr;
+ heuristics_instance = &instance->heuristics_instance;
+
+ qdevice_log(LOG_DEBUG, "Executing after-connect heuristics.");
+
+ if (qdevice_heuristics_result_notifier_list_set_active(&heuristics_instance->exec_result_notifier_list,
+ qdevice_net_connect_heuristics_exec_result_callback, 1) != 0) {
+ qdevice_log(LOG_ERR, "Can't activate net connect heuristics exec callback notifier");
+
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ACTIVATE_HEURISTICS_RESULT_NOTIFIER;
+ net_instance->schedule_disconnect = 1;
+
+ return (-1);
+ }
+
+ if (qdevice_heuristics_exec(heuristics_instance,
+ instance->sync_in_progress) != 0) {
+ qdevice_log(LOG_ERR, "Can't execute connect heuristics.");
+
+ net_instance->schedule_disconnect = 1;
+ net_instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_START_HEURISTICS;
+
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#ifndef _QDEVICE_NET_HEURISTICS_H_
+#define _QDEVICE_NET_HEURISTICS_H_
+
+#include "qdevice-heuristics-exec-result.h"
+#include "qdevice-net-instance.h"
+#include "tlv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern enum tlv_heuristics qdevice_net_heuristics_exec_result_to_tlv(
+ enum qdevice_heuristics_exec_result exec_result);
+
+extern int qdevice_net_heuristics_stop_timer(struct qdevice_net_instance *net_instance);
+
+extern int qdevice_net_heuristics_schedule_timer(struct qdevice_net_instance *net_instance);
+
+extern int qdevice_net_heuristics_init(struct qdevice_net_instance *net_instance);
+
+extern int qdevice_net_heuristics_exec_after_connect(struct qdevice_net_instance *net_instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_HEURISTICS_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 "qdevice-config.h"
+#include "qdevice-log.h"
+#include "qdevice-net-instance.h"
+#include "qnet-config.h"
+#include "utils.h"
+#include "qdevice-net-poll-array-user-data.h"
+#include "qdevice-ipc.h"
+
+/*
+ * Needed for creating nspr handle from unix fd
+ */
+#include <private/pprio.h>
+
+int
+qdevice_net_instance_init(struct qdevice_net_instance *instance,
+ enum tlv_tls_supported tls_supported,
+ enum tlv_decision_algorithm_type decision_algorithm, uint32_t heartbeat_interval,
+ uint32_t sync_heartbeat_interval, uint32_t cast_vote_timer_interval,
+ const char *host_addr, uint16_t host_port, const char *cluster_name,
+ const struct tlv_tie_breaker *tie_breaker, uint32_t connect_timeout,
+ int force_ip_version, int cmap_fd, int votequorum_fd, int local_socket_fd,
+ const struct qdevice_advanced_settings *advanced_settings,
+ int heuristics_pipe_cmd_send_fd, int heuristics_pipe_cmd_recv_fd,
+ int heuristics_pipe_log_recv_fd)
+{
+
+ memset(instance, 0, sizeof(*instance));
+
+ instance->advanced_settings = advanced_settings;
+ instance->decision_algorithm = decision_algorithm;
+ instance->heartbeat_interval = heartbeat_interval;
+ instance->sync_heartbeat_interval = sync_heartbeat_interval;
+ instance->cast_vote_timer_interval = cast_vote_timer_interval;
+ instance->cast_vote_timer = NULL;
+ instance->host_addr = host_addr;
+ instance->host_port = host_port;
+ instance->cluster_name = cluster_name;
+ instance->connect_timeout = connect_timeout;
+ instance->last_msg_seq_num = 1;
+ instance->echo_request_expected_msg_seq_num = 1;
+ instance->echo_reply_received_msg_seq_num = 1;
+ instance->force_ip_version = force_ip_version;
+ instance->last_echo_reply_received_time = ((time_t) -1);
+ instance->connected_since_time = ((time_t) -1);
+
+ memcpy(&instance->tie_breaker, tie_breaker, sizeof(*tie_breaker));
+
+ dynar_init(&instance->receive_buffer, advanced_settings->net_initial_msg_receive_size);
+
+ send_buffer_list_init(&instance->send_buffer_list, advanced_settings->net_max_send_buffers,
+ advanced_settings->net_initial_msg_send_size);
+
+ timer_list_init(&instance->main_timer_list);
+
+ pr_poll_array_init(&instance->poll_array, sizeof(struct qdevice_net_poll_array_user_data));
+
+ instance->tls_supported = tls_supported;
+
+ if ((instance->cmap_poll_fd = PR_CreateSocketPollFd(cmap_fd)) == NULL) {
+ qdevice_log_nss(LOG_CRIT, "Can't create NSPR cmap poll fd");
+ return (-1);
+ }
+
+ if ((instance->votequorum_poll_fd = PR_CreateSocketPollFd(votequorum_fd)) == NULL) {
+ qdevice_log_nss(LOG_CRIT, "Can't create NSPR votequorum poll fd");
+ return (-1);
+ }
+
+ if ((instance->ipc_socket_poll_fd = PR_CreateSocketPollFd(local_socket_fd)) == NULL) {
+ qdevice_log_nss(LOG_CRIT, "Can't create NSPR IPC socket poll fd");
+ return (-1);
+ }
+
+ if ((instance->heuristics_pipe_cmd_send_poll_fd =
+ PR_CreateSocketPollFd(heuristics_pipe_cmd_send_fd)) == NULL) {
+ qdevice_log_nss(LOG_CRIT, "Can't create NSPR heuristics pipe command send poll fd");
+ return (-1);
+ }
+
+ if ((instance->heuristics_pipe_cmd_recv_poll_fd =
+ PR_CreateSocketPollFd(heuristics_pipe_cmd_recv_fd)) == NULL) {
+ qdevice_log_nss(LOG_CRIT, "Can't create NSPR heuristics pipe command recv poll fd");
+ return (-1);
+ }
+
+ if ((instance->heuristics_pipe_log_recv_poll_fd =
+ PR_CreateSocketPollFd(heuristics_pipe_log_recv_fd)) == NULL) {
+ qdevice_log_nss(LOG_CRIT, "Can't create NSPR heuristics pipe log recv poll fd");
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+qdevice_net_instance_clean(struct qdevice_net_instance *instance)
+{
+
+ dynar_clean(&instance->receive_buffer);
+
+ send_buffer_list_free(&instance->send_buffer_list);
+
+ instance->skipping_msg = 0;
+ instance->msg_already_received_bytes = 0;
+ instance->echo_request_expected_msg_seq_num = instance->echo_reply_received_msg_seq_num;
+ instance->using_tls = 0;
+ instance->tls_client_cert_sent = 0;
+ instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT;
+
+ instance->schedule_disconnect = 0;
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNDEFINED;
+ instance->last_echo_reply_received_time = ((time_t) -1);
+ instance->connected_since_time = ((time_t) -1);
+}
+
+int
+qdevice_net_instance_destroy(struct qdevice_net_instance *instance)
+{
+ struct unix_socket_client *ipc_client;
+ const struct unix_socket_client_list *ipc_client_list;
+ struct qdevice_ipc_user_data *qdevice_ipc_user_data;
+ PRFileDesc *prfd;
+
+ ipc_client_list = &instance->qdevice_instance_ptr->local_ipc.clients;
+
+ TAILQ_FOREACH(ipc_client, ipc_client_list, entries) {
+ qdevice_ipc_user_data = (struct qdevice_ipc_user_data *)ipc_client->user_data;
+ prfd = (PRFileDesc *)qdevice_ipc_user_data->model_data;
+
+ if (PR_DestroySocketPollFd(prfd) != PR_SUCCESS) {
+ qdevice_log_nss(LOG_WARNING, "Unable to destroy client IPC poll socket fd");
+ }
+ }
+
+ dynar_destroy(&instance->receive_buffer);
+
+ send_buffer_list_free(&instance->send_buffer_list);
+
+ pr_poll_array_destroy(&instance->poll_array);
+
+ timer_list_free(&instance->main_timer_list);
+
+ free((void *)instance->cluster_name);
+ free((void *)instance->host_addr);
+
+ if (PR_DestroySocketPollFd(instance->votequorum_poll_fd) != PR_SUCCESS) {
+ qdevice_log_nss(LOG_WARNING, "Unable to close votequorum connection fd");
+ }
+
+ if (PR_DestroySocketPollFd(instance->cmap_poll_fd) != PR_SUCCESS) {
+ qdevice_log_nss(LOG_WARNING, "Unable to close votequorum connection fd");
+ }
+
+ if (PR_DestroySocketPollFd(instance->ipc_socket_poll_fd) != PR_SUCCESS) {
+ qdevice_log_nss(LOG_WARNING, "Unable to close local socket poll fd");
+ }
+
+ if (PR_DestroySocketPollFd(instance->heuristics_pipe_cmd_send_poll_fd) != PR_SUCCESS) {
+ qdevice_log_nss(LOG_WARNING, "Unable to close heuristics pipe command send poll fd");
+ return (-1);
+ }
+
+ if (PR_DestroySocketPollFd(instance->heuristics_pipe_cmd_recv_poll_fd) != PR_SUCCESS) {
+ qdevice_log_nss(LOG_WARNING, "Unable to close heuristics pipe command recv poll fd");
+ return (-1);
+ }
+
+ if (PR_DestroySocketPollFd(instance->heuristics_pipe_log_recv_poll_fd) != PR_SUCCESS) {
+ qdevice_log_nss(LOG_WARNING, "Unable to close heuristics pipe log recv poll fd");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_instance_init_from_cmap(struct qdevice_instance *instance)
+{
+ char *str;
+ cmap_handle_t cmap_handle;
+ enum tlv_tls_supported tls_supported;
+ int i;
+ long int li;
+ enum tlv_decision_algorithm_type decision_algorithm;
+ struct tlv_tie_breaker tie_breaker;
+ uint32_t heartbeat_interval;
+ uint32_t sync_heartbeat_interval;
+ uint32_t cast_vote_timer_interval;
+ char *host_addr;
+ int host_port;
+ char *ep;
+ char *cluster_name;
+ uint32_t connect_timeout;
+ struct qdevice_net_instance *net_instance;
+ int force_ip_version;
+
+ cmap_handle = instance->cmap_handle;
+
+ net_instance = malloc(sizeof(*net_instance));
+ if (net_instance == NULL) {
+ qdevice_log(LOG_ERR, "Can't alloc qdevice_net_instance");
+ return (-1);
+ }
+
+ /*
+ * Check tls
+ */
+ tls_supported = QDEVICE_NET_DEFAULT_TLS_SUPPORTED;
+
+ if (cmap_get_string(cmap_handle, "quorum.device.net.tls", &str) == CS_OK) {
+ if ((i = utils_parse_bool_str(str)) == -1) {
+ if (strcasecmp(str, "required") != 0) {
+ free(str);
+ qdevice_log(LOG_ERR, "quorum.device.net.tls value is not valid.");
+
+ goto error_free_instance;
+ } else {
+ tls_supported = TLV_TLS_REQUIRED;
+ }
+ } else {
+ if (i == 1) {
+ tls_supported = TLV_TLS_SUPPORTED;
+ } else {
+ tls_supported = TLV_TLS_UNSUPPORTED;
+ }
+ }
+
+ free(str);
+ }
+
+ /*
+ * Host
+ */
+ if (cmap_get_string(cmap_handle, "quorum.device.net.host", &str) != CS_OK) {
+ qdevice_log(LOG_ERR, "Qdevice net daemon address is not defined (quorum.device.net.host)");
+ goto error_free_instance;
+ }
+ host_addr = str;
+
+ if (cmap_get_string(cmap_handle, "quorum.device.net.port", &str) == CS_OK) {
+ host_port = strtol(str, &ep, 10);
+
+ if (host_port <= 0 || host_port > ((uint16_t)~0) || *ep != '\0') {
+ qdevice_log(LOG_ERR, "quorum.device.net.port must be in range 0-65535");
+ free(str);
+ goto error_free_host_addr;
+ }
+ free(str);
+ } else {
+ host_port = QNETD_DEFAULT_HOST_PORT;
+ }
+
+ /*
+ * Cluster name
+ */
+ if (cmap_get_string(cmap_handle, "totem.cluster_name", &str) != CS_OK) {
+ qdevice_log(LOG_ERR, "Cluster name (totem.cluster_name) has to be defined.");
+ goto error_free_host_addr;
+ }
+ cluster_name = str;
+
+ /*
+ * Adjust qdevice timeouts to better suit qnetd
+ */
+ cast_vote_timer_interval = instance->heartbeat_interval * 0.5;
+ heartbeat_interval = instance->heartbeat_interval * 0.8;
+ if (heartbeat_interval < instance->advanced_settings->net_heartbeat_interval_min) {
+ qdevice_log(LOG_WARNING, "Heartbeat interval too small %"PRIu32". Adjusting to %"PRIu32".",
+ heartbeat_interval, instance->advanced_settings->net_heartbeat_interval_min);
+ heartbeat_interval = instance->advanced_settings->net_heartbeat_interval_min;
+ }
+ if (heartbeat_interval > instance->advanced_settings->net_heartbeat_interval_max) {
+ qdevice_log(LOG_WARNING, "Heartbeat interval too big %"PRIu32". Adjusting to %"PRIu32".",
+ heartbeat_interval, instance->advanced_settings->net_heartbeat_interval_max);
+ heartbeat_interval = instance->advanced_settings->net_heartbeat_interval_max;
+ }
+ sync_heartbeat_interval = instance->sync_heartbeat_interval * 0.8;
+
+ /*
+ * Choose decision algorithm
+ */
+ if (cmap_get_string(cmap_handle, "quorum.device.net.algorithm", &str) != CS_OK) {
+ decision_algorithm = QDEVICE_NET_DEFAULT_ALGORITHM;
+ } else {
+ if (strcmp(str, "test") == 0) {
+ decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_TEST;
+ } else if (strcmp(str, "ffsplit") == 0) {
+ decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_FFSPLIT;
+ } else if (strcmp(str, "2nodelms") == 0) {
+ decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_2NODELMS;
+ } else if (strcmp(str, "lms") == 0) {
+ decision_algorithm = TLV_DECISION_ALGORITHM_TYPE_LMS;
+ } else {
+ qdevice_log(LOG_ERR, "Unknown decision algorithm %s", str);
+ free(str);
+ goto error_free_cluster_name;
+ }
+
+ free(str);
+ }
+
+ if (decision_algorithm == TLV_DECISION_ALGORITHM_TYPE_TEST &&
+ !instance->advanced_settings->net_test_algorithm_enabled) {
+ qdevice_log(LOG_ERR, "Test algorithm is not enabled. You can force enable it by "
+ "passing -S net_test_algorithm_enabled=on to %s command", QDEVICE_PROGRAM_NAME);
+
+ goto error_free_cluster_name;
+ }
+ /*
+ * Load tie_breaker mode
+ */
+ memset(&tie_breaker, 0, sizeof(tie_breaker));
+
+ if (cmap_get_string(cmap_handle, "quorum.device.net.tie_breaker", &str) != CS_OK) {
+ tie_breaker.mode = QDEVICE_NET_DEFAULT_TIE_BREAKER_MODE;
+ } else {
+ if (strcmp(str, "lowest") == 0) {
+ tie_breaker.mode = TLV_TIE_BREAKER_MODE_LOWEST;
+ } else if (strcmp(str, "highest") == 0) {
+ tie_breaker.mode = TLV_TIE_BREAKER_MODE_HIGHEST;
+ } else {
+ li = strtol(str, &ep, 10);
+ if (li <= 0 || li > ((uint32_t)~0) || *ep != '\0') {
+ qdevice_log(LOG_ERR, "tie_breaker must be lowest|highest|valid_node_id");
+ free(str);
+ goto error_free_cluster_name;
+ }
+
+ tie_breaker.mode = TLV_TIE_BREAKER_MODE_NODE_ID;
+ tie_breaker.node_id = li;
+ }
+
+ free(str);
+ }
+
+ /*
+ * Get connect timeout
+ */
+ if (cmap_get_string(cmap_handle, "quorum.device.net.connect_timeout", &str) != CS_OK) {
+ connect_timeout = heartbeat_interval;
+ } else {
+ li = strtol(str, &ep, 10);
+ if (li < instance->advanced_settings->net_min_connect_timeout ||
+ li > instance->advanced_settings->net_max_connect_timeout || *ep != '\0') {
+ qdevice_log(LOG_ERR, "connect_timeout must be valid number in "
+ "range <%"PRIu32",%"PRIu32">",
+ instance->advanced_settings->net_min_connect_timeout,
+ instance->advanced_settings->net_max_connect_timeout);
+ free(str);
+ goto error_free_cluster_name;
+ }
+
+ connect_timeout = li;
+
+ free(str);
+ }
+
+ if (cmap_get_string(cmap_handle, "quorum.device.net.force_ip_version", &str) != CS_OK) {
+ force_ip_version = 0;
+ } else {
+ li = strtol(str, &ep, 10);
+ if ((li != 0 && li != 4 && li != 6) || *ep != '\0') {
+ qdevice_log(LOG_ERR, "force_ip_version must be one of 0|4|6");
+ free(str);
+ goto error_free_cluster_name;
+ }
+
+ force_ip_version = li;
+
+ free(str);
+ }
+
+ /*
+ * Really initialize instance
+ */
+ if (qdevice_net_instance_init(net_instance,
+ tls_supported, decision_algorithm,
+ heartbeat_interval, sync_heartbeat_interval, cast_vote_timer_interval,
+ host_addr, host_port, cluster_name, &tie_breaker, connect_timeout,
+ force_ip_version,
+ instance->cmap_poll_fd, instance->votequorum_poll_fd,
+ instance->local_ipc.socket, instance->advanced_settings,
+ instance->heuristics_instance.pipe_cmd_send,
+ instance->heuristics_instance.pipe_cmd_recv,
+ instance->heuristics_instance.pipe_log_recv) == -1) {
+ qdevice_log(LOG_ERR, "Can't initialize qdevice-net instance");
+ goto error_free_instance;
+ }
+
+ net_instance->qdevice_instance_ptr = instance;
+ instance->model_data = net_instance;
+
+ return (0);
+
+error_free_cluster_name:
+ free(cluster_name);
+error_free_host_addr:
+ free(host_addr);
+error_free_instance:
+ free(net_instance);
+ return (-1);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_NET_INSTANCE_H_
+#define _QDEVICE_NET_INSTANCE_H_
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "nss-sock.h"
+
+#include "qdevice-instance.h"
+
+#include "dynar.h"
+#include "node-list.h"
+#include "pr-poll-array.h"
+#include "qdevice-net-disconnect-reason.h"
+#include "send-buffer-list.h"
+#include "tlv.h"
+#include "timer-list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum qdevice_net_instance_state {
+ QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT,
+ QDEVICE_NET_INSTANCE_STATE_SENDING_PREINIT_REPLY,
+ QDEVICE_NET_INSTANCE_STATE_WAITING_PREINIT_REPLY,
+ QDEVICE_NET_INSTANCE_STATE_WAITING_STARTTLS_BEING_SENT,
+ QDEVICE_NET_INSTANCE_STATE_WAITING_INIT_REPLY,
+ QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS,
+};
+
+struct qdevice_net_instance {
+ PRFileDesc *socket;
+ struct dynar receive_buffer;
+ struct send_buffer_list send_buffer_list;
+ int skipping_msg;
+ size_t msg_already_received_bytes;
+ enum qdevice_net_instance_state state;
+ uint32_t last_msg_seq_num;
+ uint32_t echo_request_expected_msg_seq_num;
+ uint32_t echo_reply_received_msg_seq_num;
+ enum tlv_tls_supported tls_supported;
+ int using_tls;
+ int tls_client_cert_sent;
+ uint32_t heartbeat_interval; /* Adjusted heartbeat interval during normal operation */
+ uint32_t sync_heartbeat_interval; /* Adjusted heartbeat interval during corosync sync */
+ uint32_t cast_vote_timer_interval; /* Timer for cast vote */
+ uint32_t connect_timeout;
+ struct timer_list_entry *cast_vote_timer;
+ enum tlv_vote cast_vote_timer_vote;
+ int cast_vote_timer_paused;
+ const char *host_addr;
+ uint16_t host_port;
+ const char *cluster_name;
+ enum tlv_decision_algorithm_type decision_algorithm;
+ struct timer_list main_timer_list;
+ struct timer_list_entry *echo_request_timer;
+ int schedule_disconnect;
+ PRFileDesc *votequorum_poll_fd;
+ PRFileDesc *cmap_poll_fd;
+ PRFileDesc *ipc_socket_poll_fd;
+ struct tlv_ring_id last_sent_ring_id;
+ struct tlv_tie_breaker tie_breaker;
+ void *algorithm_data;
+ enum qdevice_net_disconnect_reason disconnect_reason;
+ struct qdevice_instance *qdevice_instance_ptr;
+ struct nss_sock_non_blocking_client non_blocking_client;
+ struct timer_list_entry *connect_timer;
+ int force_ip_version;
+ struct pr_poll_array poll_array;
+ time_t last_echo_reply_received_time;
+ time_t connected_since_time;
+ const struct qdevice_advanced_settings *advanced_settings;
+ PRFileDesc *heuristics_pipe_cmd_send_poll_fd;
+ PRFileDesc *heuristics_pipe_cmd_recv_poll_fd;
+ PRFileDesc *heuristics_pipe_log_recv_poll_fd;
+ struct timer_list_entry *regular_heuristics_timer;
+ int server_supports_heuristics;
+ enum tlv_heuristics latest_regular_heuristics_result;
+ enum tlv_heuristics latest_connect_heuristics_result;
+ enum tlv_heuristics latest_vq_heuristics_result;
+ enum tlv_heuristics latest_heuristics_result;
+};
+
+extern int qdevice_net_instance_init(struct qdevice_net_instance *instance,
+ enum tlv_tls_supported tls_supported,
+ enum tlv_decision_algorithm_type decision_algorithm, uint32_t heartbeat_interval,
+ uint32_t sync_heartbeat_interval, uint32_t cast_vote_timer_interval,
+ const char *host_addr, uint16_t host_port, const char *cluster_name,
+ const struct tlv_tie_breaker *tie_breaker, uint32_t connect_timeout, int force_ip_version,
+ int cmap_fd, int votequorum_fd, int local_socket_fd,
+ const struct qdevice_advanced_settings *advanced_settings,
+ int heuristics_pipe_cmd_send_fd, int heuristics_pipe_cmd_recv_fd,
+ int heuristics_pipe_log_recv_fd);
+
+extern void qdevice_net_instance_clean(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_instance_destroy(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_instance_init_from_cmap(struct qdevice_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_INSTANCE_H_ */
--- /dev/null
+/*
+ * 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 "qdevice-net-ipc-cmd.h"
+#include "qdevice-log.h"
+#include "dynar-str.h"
+#include "qdevice-net-algorithm.h"
+#include "utils.h"
+
+static int
+qdevice_net_ipc_cmd_status_add_header(struct dynar *outbuf, int verbose)
+{
+
+ return ((dynar_str_catf(outbuf, "Qdevice-net information\n") != -1) &&
+ (dynar_str_catf(outbuf, "----------------------\n") != -1));
+}
+
+static int
+qdevice_net_ipc_cmd_status_add_tie_breaker(struct qdevice_net_instance *instance,
+ struct dynar *outbuf, int verbose)
+{
+
+ if (dynar_str_catf(outbuf, "Tie-breaker:\t\t") == -1) {
+ return (0);
+ }
+
+ switch (instance->tie_breaker.mode) {
+ case TLV_TIE_BREAKER_MODE_LOWEST:
+ if (dynar_str_catf(outbuf, "Node with lowest node ID") == -1) {
+ return (0);
+ }
+ break;
+ case TLV_TIE_BREAKER_MODE_HIGHEST:
+ if (dynar_str_catf(outbuf, "Node with highest node ID") == -1) {
+ return (0);
+ }
+ break;
+ case TLV_TIE_BREAKER_MODE_NODE_ID:
+ if (dynar_str_catf(outbuf, "Node with node ID "UTILS_PRI_NODE_ID,
+ instance->tie_breaker.node_id) == -1) {
+ return (0);
+ }
+ break;
+ }
+
+ return (dynar_str_catf(outbuf, "\n") != -1);
+}
+
+static int
+qdevice_net_ipc_cmd_status_add_basic_info(struct qdevice_net_instance *instance,
+ struct dynar *outbuf, int verbose)
+{
+
+ if (dynar_str_catf(outbuf, "Cluster name:\t\t%s\n", instance->cluster_name) == -1) {
+ return (0);
+ }
+
+ if (dynar_str_catf(outbuf, "QNetd host:\t\t%s:%"PRIu16"\n",
+ instance->host_addr, instance->host_port) == -1) {
+ return (0);
+ }
+
+ if (verbose && instance->force_ip_version != 0) {
+ if (dynar_str_catf(outbuf, "Force IP version:\t%u\n",
+ instance->force_ip_version) == -1) {
+ return (0);
+ }
+ }
+
+ if (verbose) {
+ if ((dynar_str_catf(outbuf, "Connect timeout:\t%"PRIu32"ms\n",
+ instance->connect_timeout) == -1) ||
+ (dynar_str_catf(outbuf, "HB interval:\t\t%"PRIu32"ms\n",
+ instance->heartbeat_interval) == -1) ||
+ (dynar_str_catf(outbuf, "VQ vote timer interval:\t%"PRIu32"ms\n",
+ instance->cast_vote_timer_interval) == -1)) {
+ return (0);
+ }
+
+ if (dynar_str_catf(outbuf, "TLS:\t\t\t%s\n",
+ tlv_tls_supported_to_str(instance->tls_supported)) == -1) {
+ return (0);
+ }
+ }
+
+ if (dynar_str_catf(outbuf, "Algorithm:\t\t%s\n",
+ tlv_decision_algorithm_type_to_str(instance->decision_algorithm)) == -1) {
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+qdevice_net_ipc_cmd_status_add_poll_timer_status(struct qdevice_net_instance *instance,
+ struct dynar *outbuf, int verbose)
+{
+
+ if (!verbose) {
+ return (1);
+ }
+
+ if (dynar_str_catf(outbuf, "Poll timer running:\t%s",
+ (instance->cast_vote_timer != NULL ? "Yes" : "No")) == -1) {
+ return (0);
+ }
+
+ if (instance->cast_vote_timer != NULL && instance->cast_vote_timer_vote == TLV_VOTE_ACK) {
+ if (dynar_str_catf(outbuf, " (cast vote)") == -1) {
+ return (0);
+ }
+ }
+
+ return (dynar_str_catf(outbuf, "\n") != -1);
+}
+
+static int
+qdevice_net_ipc_cmd_status_add_state(struct qdevice_net_instance *instance,
+ struct dynar *outbuf, int verbose)
+{
+ const char *state;
+
+ if (instance->schedule_disconnect) {
+ state = "Disconnected";
+ } else {
+ if (instance->state == QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
+ state = "Connected";
+ } else {
+ if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT ||
+ !instance->non_blocking_client.destroyed) {
+ state = "Connecting";
+ } else {
+ state = "Connect failed";
+ }
+ }
+ }
+
+ return (dynar_str_catf(outbuf, "State:\t\t\t%s\n", state) != -1);
+}
+
+static int
+qdevice_net_ipc_cmd_status_add_heuristics(struct qdevice_net_instance *instance,
+ struct dynar *outbuf, int verbose)
+{
+ enum qdevice_heuristics_mode active_heuristics_mode;
+ int heuristics_enabled;
+
+ active_heuristics_mode = instance->qdevice_instance_ptr->heuristics_instance.mode;
+ heuristics_enabled = (active_heuristics_mode == QDEVICE_HEURISTICS_MODE_ENABLED ||
+ active_heuristics_mode == QDEVICE_HEURISTICS_MODE_SYNC);
+
+ if (!heuristics_enabled) {
+ return (1);
+ }
+
+ if (dynar_str_catf(outbuf, "Heuristics result:\t%s",
+ tlv_heuristics_to_str(instance->latest_heuristics_result)) == -1) {
+ return (0);
+ }
+
+ if (verbose) {
+ if (dynar_str_catf(outbuf, " (regular: %s, membership: %s, connect: %s)",
+ tlv_heuristics_to_str(instance->latest_regular_heuristics_result),
+ tlv_heuristics_to_str(instance->latest_vq_heuristics_result),
+ tlv_heuristics_to_str(instance->latest_connect_heuristics_result)) == -1) {
+ return (0);
+ }
+ }
+
+ return (dynar_str_catf(outbuf, "\n") != -1);
+}
+
+static int
+qdevice_net_ipc_cmd_status_add_tls_state(struct qdevice_net_instance *instance,
+ struct dynar *outbuf, int verbose)
+{
+
+ if (!verbose || instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
+ return (1);
+ }
+
+ if (dynar_str_catf(outbuf, "TLS active:\t\t%s", (instance->using_tls ? "Yes" : "No")) == -1) {
+ return (0);
+ }
+
+ if (instance->using_tls && instance->tls_client_cert_sent) {
+ if (dynar_str_catf(outbuf, " (client certificate sent)") == -1) {
+ return (0);
+ }
+ }
+
+ return (dynar_str_catf(outbuf, "\n") != -1);
+}
+
+static int
+qdevice_net_ipc_cmd_status_add_times(struct qdevice_net_instance *instance,
+ struct dynar *outbuf, int verbose)
+{
+ struct tm tm_res;
+
+ if (!verbose || instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
+ return (1);
+ }
+
+ if (instance->connected_since_time != ((time_t) -1)) {
+ localtime_r(&instance->connected_since_time, &tm_res);
+ if (dynar_str_catf(outbuf, "Connected since:\t%04d-%02d-%02dT%02d:%02d:%02d\n",
+ tm_res.tm_year + 1900, tm_res.tm_mon + 1, tm_res.tm_mday,
+ tm_res.tm_hour, tm_res.tm_min, tm_res.tm_sec) == -1) {
+ return (0);
+ }
+ }
+
+ if (instance->last_echo_reply_received_time != ((time_t) -1)) {
+ localtime_r(&instance->last_echo_reply_received_time, &tm_res);
+ if (dynar_str_catf(outbuf, "Echo reply received:\t%04d-%02d-%02dT%02d:%02d:%02d\n",
+ tm_res.tm_year + 1900, tm_res.tm_mon + 1, tm_res.tm_mday,
+ tm_res.tm_hour, tm_res.tm_min, tm_res.tm_sec) == -1) {
+ return (0);
+ }
+ }
+
+ return (1);
+}
+
+int
+qdevice_net_ipc_cmd_status(struct qdevice_net_instance *instance, struct dynar *outbuf, int verbose)
+{
+
+ if (qdevice_net_ipc_cmd_status_add_header(outbuf, verbose) &&
+ qdevice_net_ipc_cmd_status_add_basic_info(instance, outbuf, verbose) &&
+ qdevice_net_ipc_cmd_status_add_tie_breaker(instance, outbuf, verbose) &&
+ qdevice_net_ipc_cmd_status_add_poll_timer_status(instance, outbuf, verbose) &&
+ qdevice_net_ipc_cmd_status_add_state(instance, outbuf, verbose) &&
+ qdevice_net_ipc_cmd_status_add_heuristics(instance, outbuf, verbose) &&
+ qdevice_net_ipc_cmd_status_add_tls_state(instance, outbuf, verbose) &&
+ qdevice_net_ipc_cmd_status_add_times(instance, outbuf, verbose)) {
+ return (1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QDEVICE_NET_IPC_CMD_H_
+#define _QDEVICE_NET_IPC_CMD_H_
+
+#include "dynar.h"
+#include "qdevice-net-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_net_ipc_cmd_status(struct qdevice_net_instance *instance,
+ struct dynar *outbuf, int verbose);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_IPC_CMD_H_ */
--- /dev/null
+/*
+ * 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 "qdevice-log.h"
+#include "qdevice-net-algorithm.h"
+#include "qdevice-net-cast-vote-timer.h"
+#include "qdevice-net-heuristics.h"
+#include "qdevice-net-msg-received.h"
+#include "qdevice-net-send.h"
+#include "qdevice-net-votequorum.h"
+#include "qdevice-net-echo-request-timer.h"
+#include "msg.h"
+#include "utils.h"
+
+/*
+ * -1 - Incompatible tls combination
+ * 0 - Don't use TLS
+ * 1 - Use TLS
+ */
+static int
+qdevice_net_msg_received_check_tls_compatibility(enum tlv_tls_supported server_tls,
+ enum tlv_tls_supported client_tls)
+{
+ int res;
+
+ res = -1;
+
+ switch (server_tls) {
+ case TLV_TLS_UNSUPPORTED:
+ switch (client_tls) {
+ case TLV_TLS_UNSUPPORTED: res = 0; break;
+ case TLV_TLS_SUPPORTED: res = 0; break;
+ case TLV_TLS_REQUIRED: res = -1; break;
+ }
+ break;
+ case TLV_TLS_SUPPORTED:
+ switch (client_tls) {
+ case TLV_TLS_UNSUPPORTED: res = 0; break;
+ case TLV_TLS_SUPPORTED: res = 1; break;
+ case TLV_TLS_REQUIRED: res = 1; break;
+ }
+ break;
+ case TLV_TLS_REQUIRED:
+ switch (client_tls) {
+ case TLV_TLS_UNSUPPORTED: res = -1; break;
+ case TLV_TLS_SUPPORTED: res = 1; break;
+ case TLV_TLS_REQUIRED: res = 1; break;
+ }
+ break;
+ }
+
+ return (res);
+}
+
+static void
+qdevice_net_msg_received_log_msg_decode_error(int ret)
+{
+
+ switch (ret) {
+ case -1:
+ qdevice_log(LOG_WARNING, "Received message with option with invalid length");
+ break;
+ case -2:
+ qdevice_log(LOG_CRIT, "Can't allocate memory");
+ break;
+ case -3:
+ qdevice_log(LOG_WARNING, "Received inconsistent msg (tlv len > msg size)");
+ break;
+ case -4:
+ qdevice_log(LOG_ERR, "Received message with option with invalid value");
+ break;
+ default:
+ qdevice_log(LOG_ERR, "Unknown error occurred when decoding message");
+ break;
+ }
+}
+
+static int
+qdevice_net_msg_received_unexpected_msg(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg, const char *msg_str)
+{
+
+ qdevice_log(LOG_ERR, "Received unexpected %s message. Disconnecting from server",
+ msg_str);
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
+
+ return (-1);
+}
+
+static int
+qdevice_net_msg_received_init(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+
+ return (qdevice_net_msg_received_unexpected_msg(instance, msg, "init"));
+}
+
+static int
+qdevice_net_msg_received_preinit(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+
+ return (qdevice_net_msg_received_unexpected_msg(instance, msg, "preinit"));
+}
+
+static int
+qdevice_net_msg_check_seq_number(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+
+ if (!msg->seq_number_set || msg->seq_number != instance->last_msg_seq_num) {
+ qdevice_log(LOG_ERR, "Received message doesn't contain seq_number or "
+ "it's not expected one.");
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+qdevice_net_msg_received_preinit_reply(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+ int res;
+ struct send_buffer_list_entry *send_buffer;
+
+ qdevice_log(LOG_DEBUG, "Received preinit reply msg");
+
+ if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_PREINIT_REPLY) {
+ qdevice_log(LOG_ERR, "Received unexpected preinit reply message. "
+ "Disconnecting from server");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
+
+ return (-1);
+ }
+
+ if (qdevice_net_msg_check_seq_number(instance, msg) != 0) {
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
+ return (-1);
+ }
+
+ /*
+ * Check TLS support
+ */
+ if (!msg->tls_supported_set || !msg->tls_client_cert_required_set) {
+ qdevice_log(LOG_ERR, "Required tls_supported or tls_client_cert_required "
+ "option is unset");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
+ return (-1);
+ }
+
+ res = qdevice_net_msg_received_check_tls_compatibility(msg->tls_supported, instance->tls_supported);
+ if (res == -1) {
+ qdevice_log(LOG_ERR, "Incompatible tls configuration (server %u client %u)",
+ msg->tls_supported, instance->tls_supported);
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_INCOMPATIBLE_TLS;
+
+ return (-1);
+ } else if (res == 1) {
+ /*
+ * Start TLS
+ */
+ send_buffer = send_buffer_list_get_new(&instance->send_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_ERR, "Can't allocate send list buffer for "
+ "starttls msg");
+
+ instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+
+ return (-1);
+ }
+
+ instance->last_msg_seq_num++;
+ if (msg_create_starttls(&send_buffer->buffer, 1,
+ instance->last_msg_seq_num) == 0) {
+ qdevice_log(LOG_ERR, "Can't allocate send buffer for starttls msg");
+
+ instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+
+ send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer);
+ return (-1);
+ }
+
+ send_buffer_list_put(&instance->send_buffer_list, send_buffer);
+
+ instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_STARTTLS_BEING_SENT;
+ } else if (res == 0) {
+ if (qdevice_net_send_init(instance) != 0) {
+ instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+qdevice_net_msg_received_init_reply(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+ size_t zi;
+ int res;
+ enum qdevice_heuristics_mode active_heuristics_mode;
+
+ qdevice_log(LOG_DEBUG, "Received init reply msg");
+
+ if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_INIT_REPLY) {
+ qdevice_log(LOG_ERR, "Received unexpected init reply message. "
+ "Disconnecting from server");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
+
+ return (-1);
+ }
+
+ if (qdevice_net_msg_check_seq_number(instance, msg) != 0) {
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
+ return (-1);
+ }
+
+ if (!msg->reply_error_code_set) {
+ qdevice_log(LOG_ERR, "Received init reply message without error code."
+ "Disconnecting from server");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
+ return (-1);
+ }
+
+ if (msg->reply_error_code != TLV_REPLY_ERROR_CODE_NO_ERROR) {
+ qdevice_log(LOG_ERR, "Received init reply message with error code %"PRIu16". "
+ "Disconnecting from server", msg->reply_error_code);
+
+ if (msg->reply_error_code == TLV_REPLY_ERROR_CODE_DUPLICATE_NODE_ID) {
+ qdevice_log(LOG_ERR, "Duplicate node id may be result of server not yet "
+ "accepted this node disconnect. Retry again.");
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_DUPLICATE_NODE_ID_ERROR;
+ } else if (msg->reply_error_code == TLV_REPLY_ERROR_CODE_TIE_BREAKER_DIFFERS_FROM_OTHER_NODES) {
+ qdevice_log(LOG_ERR, "Configured tie-breaker differs in cluster. This may be "
+ "result of server not yet accepted this node disconnect. Retry again.");
+ instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_TIE_BREAKER_DIFFERS_FROM_OTHER_NODES_ERROR;
+ } else if (msg->reply_error_code == TLV_REPLY_ERROR_CODE_ALGORITHM_DIFFERS_FROM_OTHER_NODES) {
+ qdevice_log(LOG_ERR, "Configured algorithm differs in cluster. This may be "
+ "result of server not yet accepted this node disconnect. Retry again.");
+ instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_ALGORITHM_DIFFERS_FROM_OTHER_NODES_ERROR;
+ } else {
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_ERROR;
+ }
+
+ return (-1);
+ }
+
+ if (!msg->server_maximum_request_size_set || !msg->server_maximum_reply_size_set) {
+ qdevice_log(LOG_ERR, "Required maximum_request_size or maximum_reply_size "
+ "option is unset");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
+ return (-1);
+ }
+
+ if (msg->supported_messages == NULL || msg->supported_options == NULL) {
+ qdevice_log(LOG_ERR, "Required supported messages or supported options "
+ "option is unset");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
+ return (-1);
+ }
+
+ if (msg->supported_decision_algorithms == NULL) {
+ qdevice_log(LOG_ERR, "Required supported decision algorithms option is unset");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
+ return (-1);
+ }
+
+ if (msg->server_maximum_request_size < instance->advanced_settings->net_min_msg_send_size) {
+ qdevice_log(LOG_ERR,
+ "Server accepts maximum %zu bytes message but this client minimum "
+ "is %zu bytes.", msg->server_maximum_request_size,
+ instance->advanced_settings->net_min_msg_send_size);
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_INCOMPATIBLE_MSG_SIZE;
+ return (-1);
+ }
+
+ if (msg->server_maximum_reply_size > instance->advanced_settings->net_max_msg_receive_size) {
+ qdevice_log(LOG_ERR,
+ "Server may send message up to %zu bytes message but this client maximum "
+ "is %zu bytes.", msg->server_maximum_reply_size,
+ instance->advanced_settings->net_max_msg_receive_size);
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_INCOMPATIBLE_MSG_SIZE;
+ return (-1);
+ }
+
+ /*
+ * Change buffer sizes
+ */
+ dynar_set_max_size(&instance->receive_buffer, msg->server_maximum_reply_size);
+ send_buffer_list_set_max_buffer_size(&instance->send_buffer_list,
+ msg->server_maximum_request_size);
+
+
+ /*
+ * Check if server supports decision algorithm we need
+ */
+ res = 0;
+
+ for (zi = 0; zi < msg->no_supported_decision_algorithms && !res; zi++) {
+ if (msg->supported_decision_algorithms[zi] == instance->decision_algorithm) {
+ res = 1;
+ }
+ }
+
+ if (!res) {
+ qdevice_log(LOG_ERR, "Server doesn't support required decision algorithm");
+
+ instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_SERVER_DOESNT_SUPPORT_REQUIRED_ALGORITHM;
+
+ return (-1);
+ }
+
+ /*
+ * Check if server supports heuristics
+ */
+ res = 0;
+ for (zi = 0; zi < msg->no_supported_options; zi++) {
+ if (msg->supported_options[zi] == TLV_OPT_HEURISTICS) {
+ res = 1;
+ }
+ }
+
+ instance->server_supports_heuristics = res;
+
+ if (!res) {
+ active_heuristics_mode = instance->qdevice_instance_ptr->heuristics_instance.mode;
+
+ if (active_heuristics_mode == QDEVICE_HEURISTICS_MODE_ENABLED ||
+ active_heuristics_mode == QDEVICE_HEURISTICS_MODE_SYNC) {
+ qdevice_log(LOG_ERR, "Heuristics are enabled but not supported by server");
+
+ instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_SERVER_DOESNT_SUPPORT_REQUIRED_OPT;
+
+ return (-1);
+ }
+ }
+
+ /*
+ * Finally fully connected so it's possible to remove connection timer
+ */
+ if (instance->connect_timer != NULL) {
+ timer_list_delete(&instance->main_timer_list, instance->connect_timer);
+ instance->connect_timer = NULL;
+ }
+
+ /*
+ * Server accepted heartbeat interval -> schedule regular sending of echo request
+ */
+ if (qdevice_net_echo_request_timer_schedule(instance) != 0) {
+ return (-1);
+ }
+
+ /*
+ * Run heuristics (even when it is disabled, undefined result is ok, rest of sending
+ * is handled by qdevice_net_connect_heuristics_exec_result_callback
+ */
+ if (qdevice_net_heuristics_exec_after_connect(instance) != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+qdevice_net_msg_received_starttls(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+
+ return (qdevice_net_msg_received_unexpected_msg(instance, msg, "starttls"));
+}
+
+static int
+qdevice_net_msg_received_server_error(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+
+ if (!msg->reply_error_code_set) {
+ qdevice_log(LOG_ERR, "Received server error without error code set. "
+ "Disconnecting from server");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+ } else {
+ qdevice_log(LOG_ERR, "Received server error %"PRIu16". "
+ "Disconnecting from server", msg->reply_error_code);
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_SERVER_SENT_ERROR;
+ }
+
+ return (-1);
+}
+
+static int
+qdevice_net_msg_received_set_option(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+
+ return (qdevice_net_msg_received_unexpected_msg(instance, msg, "set option"));
+}
+
+static int
+qdevice_net_msg_received_set_option_reply(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+
+ if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
+ qdevice_log(LOG_ERR, "Received unexpected set option reply message. "
+ "Disconnecting from server");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
+
+ return (-1);
+ }
+
+ if (qdevice_net_msg_check_seq_number(instance, msg) != 0) {
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+
+ return (-1);
+ }
+
+ if (qdevice_net_echo_request_timer_schedule(instance) != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+qdevice_net_msg_received_echo_request(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+
+ return (qdevice_net_msg_received_unexpected_msg(instance, msg, "echo request"));
+}
+
+static int
+qdevice_net_msg_received_echo_reply(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+
+ if (!msg->seq_number_set) {
+ qdevice_log(LOG_ERR, "Received echo reply message doesn't contain seq_number.");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+ return (-1);
+ }
+
+ if (msg->seq_number != instance->echo_request_expected_msg_seq_num) {
+ qdevice_log(LOG_WARNING, "Received echo reply message seq_number is not expected one.");
+ }
+
+ if (qdevice_net_algorithm_echo_reply_received(instance, msg->seq_number,
+ msg->seq_number == instance->echo_request_expected_msg_seq_num) != 0) {
+ qdevice_log(LOG_DEBUG, "Algorithm returned error. Disconnecting");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_ECHO_REPLY_RECEIVED_ERR;
+ return (-1);
+ }
+
+ instance->echo_reply_received_msg_seq_num = msg->seq_number;
+ instance->last_echo_reply_received_time = time(NULL);
+
+ return (0);
+}
+
+static int
+qdevice_net_msg_received_node_list(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+
+ return (qdevice_net_msg_received_unexpected_msg(instance, msg, "node list"));
+}
+
+static int
+qdevice_net_msg_received_node_list_reply(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+ const char *str;
+ enum tlv_vote result_vote;
+ int res;
+ int case_processed;
+ int ring_id_is_valid;
+
+ if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
+ qdevice_log(LOG_ERR, "Received unexpected node list reply message. "
+ "Disconnecting from server");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
+ return (-1);
+ }
+
+ if (!msg->vote_set || !msg->seq_number_set || !msg->node_list_type_set) {
+ qdevice_log(LOG_ERR, "Received node list reply message without "
+ "required options. Disconnecting from server");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+ return (-1);
+ }
+
+ if (!msg->ring_id_set) {
+ qdevice_log(LOG_ERR, "Received node list reply message "
+ "without ring id set. Disconnecting from server");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+ return (-1);
+ }
+
+ str = NULL;
+
+ switch (msg->node_list_type) {
+ case TLV_NODE_LIST_TYPE_INITIAL_CONFIG: str = "initial config"; break;
+ case TLV_NODE_LIST_TYPE_CHANGED_CONFIG: str = "changed config"; break;
+ case TLV_NODE_LIST_TYPE_MEMBERSHIP: str ="membership"; break;
+ case TLV_NODE_LIST_TYPE_QUORUM: str ="quorum"; break;
+ /*
+ * Default is not defined intentionally. Compiler shows warning when new node list type
+ * is added
+ */
+ }
+
+ if (str == NULL) {
+ qdevice_log(LOG_CRIT, "qdevice_net_msg_received_node_list_reply fatal error. "
+ "Unhandled node_list_type (debug output)");
+ exit(1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Received %s node list reply", str);
+ qdevice_log(LOG_DEBUG, " seq = "UTILS_PRI_MSG_SEQ, msg->seq_number);
+ qdevice_log(LOG_DEBUG, " vote = %s", tlv_vote_to_str(msg->vote));
+ qdevice_log(LOG_DEBUG, " ring id = ("UTILS_PRI_RING_ID")",
+ msg->ring_id.node_id, msg->ring_id.seq);
+
+ /*
+ * Call algorithm
+ */
+ result_vote = msg->vote;
+
+ if (!tlv_ring_id_eq(&msg->ring_id, &instance->last_sent_ring_id)) {
+ ring_id_is_valid = 0;
+ qdevice_log(LOG_DEBUG, "Received node list reply with old ring id.");
+ } else {
+ ring_id_is_valid = 1;
+ }
+
+ case_processed = 0;
+ switch (msg->node_list_type) {
+ case TLV_NODE_LIST_TYPE_INITIAL_CONFIG:
+ case TLV_NODE_LIST_TYPE_CHANGED_CONFIG:
+ case_processed = 1;
+ res = qdevice_net_algorithm_config_node_list_reply_received(instance,
+ msg->seq_number, (msg->node_list_type == TLV_NODE_LIST_TYPE_INITIAL_CONFIG),
+ &msg->ring_id, ring_id_is_valid, &result_vote);
+ break;
+ case TLV_NODE_LIST_TYPE_MEMBERSHIP:
+ case_processed = 1;
+ res = qdevice_net_algorithm_membership_node_list_reply_received(instance,
+ msg->seq_number, &msg->ring_id, ring_id_is_valid, &result_vote);
+ break;
+ case TLV_NODE_LIST_TYPE_QUORUM:
+ case_processed = 1;
+ res = qdevice_net_algorithm_quorum_node_list_reply_received(instance,
+ msg->seq_number, &msg->ring_id, ring_id_is_valid, &result_vote);
+ break;
+ /*
+ * Default is not defined intentionally. Compiler shows warning when new node list type
+ * is added
+ */
+ }
+
+ if (!case_processed) {
+ qdevice_log(LOG_CRIT, "qdevice_net_msg_received_node_list_reply fatal error. "
+ "Unhandled node_list_type (algorithm call)");
+ exit(1);
+ }
+
+ if (res != 0) {
+ qdevice_log(LOG_DEBUG, "Algorithm returned error. Disconnecting.");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_NODE_LIST_REPLY_ERR;
+ return (-1);
+ } else {
+ qdevice_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote));
+ }
+
+ if (qdevice_net_cast_vote_timer_update(instance, result_vote) != 0) {
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+qdevice_net_msg_received_ask_for_vote(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+
+ return (qdevice_net_msg_received_unexpected_msg(instance, msg, "ask for vote"));
+}
+
+static int
+qdevice_net_msg_received_ask_for_vote_reply(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+ enum tlv_vote result_vote;
+ int ring_id_is_valid;
+
+ if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
+ qdevice_log(LOG_ERR, "Received unexpected ask for vote reply message. "
+ "Disconnecting from server");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
+ return (-1);
+ }
+
+ if (!msg->vote_set || !msg->seq_number_set || !msg->ring_id_set) {
+ qdevice_log(LOG_ERR, "Received ask for vote reply message without "
+ "required options. Disconnecting from server");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+ return (-1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Received ask for vote reply");
+ qdevice_log(LOG_DEBUG, " seq = "UTILS_PRI_MSG_SEQ, msg->seq_number);
+ qdevice_log(LOG_DEBUG, " vote = %s", tlv_vote_to_str(msg->vote));
+ qdevice_log(LOG_DEBUG, " ring id = ("UTILS_PRI_RING_ID")",
+ msg->ring_id.node_id, msg->ring_id.seq);
+
+ result_vote = msg->vote;
+
+ if (!tlv_ring_id_eq(&msg->ring_id, &instance->last_sent_ring_id)) {
+ ring_id_is_valid = 0;
+ qdevice_log(LOG_DEBUG, "Received ask for vote reply with old ring id.");
+ } else {
+ ring_id_is_valid = 1;
+ }
+
+ if (qdevice_net_algorithm_ask_for_vote_reply_received(instance, msg->seq_number,
+ &msg->ring_id, ring_id_is_valid, &result_vote) != 0) {
+ qdevice_log(LOG_DEBUG, "Algorithm returned error. Disconnecting.");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_ASK_FOR_VOTE_REPLY_ERR;
+ return (-1);
+ } else {
+ qdevice_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote));
+ }
+
+ if (qdevice_net_cast_vote_timer_update(instance, result_vote) != 0) {
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+qdevice_net_msg_received_vote_info(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+ struct send_buffer_list_entry *send_buffer;
+ enum tlv_vote result_vote;
+ int ring_id_is_valid;
+
+ if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
+ qdevice_log(LOG_ERR, "Received unexpected vote info message. "
+ "Disconnecting from server");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
+ return (-1);
+ }
+
+ if (!msg->vote_set || !msg->seq_number_set || !msg->ring_id_set) {
+ qdevice_log(LOG_ERR, "Received node list reply message without "
+ "required options. Disconnecting from server");
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+ return (-1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Received vote info");
+ qdevice_log(LOG_DEBUG, " seq = "UTILS_PRI_MSG_SEQ, msg->seq_number);
+ qdevice_log(LOG_DEBUG, " vote = %s", tlv_vote_to_str(msg->vote));
+ qdevice_log(LOG_DEBUG, " ring id = ("UTILS_PRI_RING_ID")",
+ msg->ring_id.node_id, msg->ring_id.seq);
+
+ result_vote = msg->vote;
+
+ if (!tlv_ring_id_eq(&msg->ring_id, &instance->last_sent_ring_id)) {
+ ring_id_is_valid = 0;
+ qdevice_log(LOG_DEBUG, "Received vote info with old ring id.");
+ } else {
+ ring_id_is_valid = 1;
+ }
+
+ if (qdevice_net_algorithm_vote_info_received(instance, msg->seq_number,
+ &msg->ring_id, ring_id_is_valid, &result_vote) != 0) {
+ qdevice_log(LOG_DEBUG, "Algorithm returned error. Disconnecting.");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_VOTE_INFO_ERR;
+ return (-1);
+ } else {
+ qdevice_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote));
+ }
+
+ if (qdevice_net_cast_vote_timer_update(instance, result_vote) != 0) {
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
+ return (-1);
+ }
+
+ /*
+ * Create reply message
+ */
+ send_buffer = send_buffer_list_get_new(&instance->send_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_ERR, "Can't allocate send list buffer for "
+ "vote info reply msg");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+ return (-1);
+ }
+
+ if (msg_create_vote_info_reply(&send_buffer->buffer, msg->seq_number) == 0) {
+ qdevice_log(LOG_ERR, "Can't allocate send buffer for "
+ "vote info reply list msg");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+ send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer);
+ return (-1);
+ }
+
+ send_buffer_list_put(&instance->send_buffer_list, send_buffer);
+
+ return (0);
+}
+
+static int
+qdevice_net_msg_received_vote_info_reply(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+
+ return (qdevice_net_msg_received_unexpected_msg(instance, msg, "vote info reply"));
+}
+
+static int
+qdevice_net_msg_received_heuristics_change(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+
+ return (qdevice_net_msg_received_unexpected_msg(instance, msg, "heuristics change"));
+}
+
+static int
+qdevice_net_msg_received_heuristics_change_reply(struct qdevice_net_instance *instance,
+ const struct msg_decoded *msg)
+{
+ enum tlv_vote result_vote;
+ int ring_id_is_valid;
+
+ if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_VOTEQUORUM_CMAP_EVENTS) {
+ qdevice_log(LOG_ERR, "Received unexpected heuristics change reply message. "
+ "Disconnecting from server");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
+ return (-1);
+ }
+
+ if (!msg->vote_set || !msg->seq_number_set || !msg->ring_id_set ||
+ msg->heuristics == TLV_HEURISTICS_UNDEFINED) {
+ qdevice_log(LOG_ERR, "Received heuristics change reply message without "
+ "required options. Disconnecting from server");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_REQUIRED_OPTION_MISSING;
+ return (-1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Received heuristics change reply");
+ qdevice_log(LOG_DEBUG, " seq = "UTILS_PRI_MSG_SEQ, msg->seq_number);
+ qdevice_log(LOG_DEBUG, " vote = %s", tlv_vote_to_str(msg->vote));
+ qdevice_log(LOG_DEBUG, " ring id = ("UTILS_PRI_RING_ID")",
+ msg->ring_id.node_id, msg->ring_id.seq);
+ qdevice_log(LOG_DEBUG, " heuristics = %s", tlv_heuristics_to_str(msg->heuristics));
+
+ result_vote = msg->vote;
+
+ if (!tlv_ring_id_eq(&msg->ring_id, &instance->last_sent_ring_id)) {
+ ring_id_is_valid = 0;
+ qdevice_log(LOG_DEBUG, "Received heuristics change reply with old ring id.");
+ } else {
+ ring_id_is_valid = 1;
+ }
+
+ if (qdevice_net_algorithm_heuristics_change_reply_received(instance, msg->seq_number,
+ &msg->ring_id, ring_id_is_valid, msg->heuristics, &result_vote) != 0) {
+ qdevice_log(LOG_DEBUG, "Algorithm returned error. Disconnecting.");
+
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_ALGO_HEURISTICS_CHANGE_REPLY_ERR;
+ return (-1);
+ } else {
+ qdevice_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote));
+ }
+
+ if (qdevice_net_cast_vote_timer_update(instance, result_vote) != 0) {
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SCHEDULE_VOTING_TIMER;
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_msg_received(struct qdevice_net_instance *instance)
+{
+ struct msg_decoded msg;
+ int res;
+ int ret_val;
+ int msg_processed;
+
+ msg_decoded_init(&msg);
+
+ res = msg_decode(&instance->receive_buffer, &msg);
+ if (res != 0) {
+ /*
+ * Error occurred. Disconnect.
+ */
+ qdevice_net_msg_received_log_msg_decode_error(res);
+ qdevice_log(LOG_ERR, "Disconnecting from server");
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_MSG_DECODE_ERROR;
+
+ return (-1);
+ }
+
+ ret_val = 0;
+
+ msg_processed = 0;
+
+ switch (msg.type) {
+ case MSG_TYPE_INIT:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_init(instance, &msg);
+ break;
+ case MSG_TYPE_PREINIT:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_preinit(instance, &msg);
+ break;
+ case MSG_TYPE_PREINIT_REPLY:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_preinit_reply(instance, &msg);
+ break;
+ case MSG_TYPE_STARTTLS:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_starttls(instance, &msg);
+ break;
+ case MSG_TYPE_SERVER_ERROR:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_server_error(instance, &msg);
+ break;
+ case MSG_TYPE_INIT_REPLY:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_init_reply(instance, &msg);
+ break;
+ case MSG_TYPE_SET_OPTION:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_set_option(instance, &msg);
+ break;
+ case MSG_TYPE_SET_OPTION_REPLY:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_set_option_reply(instance, &msg);
+ break;
+ case MSG_TYPE_ECHO_REQUEST:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_echo_request(instance, &msg);
+ break;
+ case MSG_TYPE_ECHO_REPLY:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_echo_reply(instance, &msg);
+ break;
+ case MSG_TYPE_NODE_LIST:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_node_list(instance, &msg);
+ break;
+ case MSG_TYPE_NODE_LIST_REPLY:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_node_list_reply(instance, &msg);
+ break;
+ case MSG_TYPE_ASK_FOR_VOTE:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_ask_for_vote(instance, &msg);
+ break;
+ case MSG_TYPE_ASK_FOR_VOTE_REPLY:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_ask_for_vote_reply(instance, &msg);
+ break;
+ case MSG_TYPE_VOTE_INFO:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_vote_info(instance, &msg);
+ break;
+ case MSG_TYPE_VOTE_INFO_REPLY:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_vote_info_reply(instance, &msg);
+ break;
+ case MSG_TYPE_HEURISTICS_CHANGE:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_heuristics_change(instance, &msg);
+ break;
+ case MSG_TYPE_HEURISTICS_CHANGE_REPLY:
+ msg_processed = 1;
+ ret_val = qdevice_net_msg_received_heuristics_change_reply(instance, &msg);
+ /*
+ * Default is not defined intentionally. Compiler shows warning when msg type is added
+ */
+ }
+
+ if (!msg_processed) {
+ qdevice_log(LOG_ERR, "Received unsupported message %u. "
+ "Disconnecting from server", msg.type);
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNEXPECTED_MSG;
+
+ ret_val = -1;
+ }
+
+ msg_decoded_destroy(&msg);
+
+ return (ret_val);
+}
--- /dev/null
+/*
+ * Copyright (c) 2016-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.
+ */
+
+#ifndef _QDEVICE_NET_MSG_RECEIVED_H_
+#define _QDEVICE_NET_MSG_RECEIVED_H_
+
+#include "qdevice-net-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_net_msg_received(struct qdevice_net_instance *instance);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_MSG_RECEIVED_H_ */
--- /dev/null
+/*
+ * 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 <sslerr.h>
+#include <secerr.h>
+
+#include "qdevice-log.h"
+#include "qdevice-net-nss.h"
+#include "qdevice-net-instance.h"
+#include "qnet-config.h"
+
+SECStatus
+qdevice_net_nss_bad_cert_hook(void *arg, PRFileDesc *fd) {
+ if (PR_GetError() == SEC_ERROR_EXPIRED_CERTIFICATE ||
+ PR_GetError() == SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE ||
+ PR_GetError() == SEC_ERROR_CRL_EXPIRED ||
+ PR_GetError() == SEC_ERROR_KRL_EXPIRED ||
+ PR_GetError() == SSL_ERROR_EXPIRED_CERT_ALERT) {
+ qdevice_log(LOG_WARNING, "Server certificate is expired.");
+
+ return (SECSuccess);
+ }
+
+ qdevice_log_nss(LOG_ERR, "Server certificate verification failure.");
+
+ return (SECFailure);
+}
+
+SECStatus
+qdevice_net_nss_get_client_auth_data(void *arg, PRFileDesc *sock, struct CERTDistNamesStr *caNames,
+ struct CERTCertificateStr **pRetCert, struct SECKEYPrivateKeyStr **pRetKey)
+{
+ struct qdevice_net_instance *instance;
+
+ qdevice_log(LOG_DEBUG, "Sending client auth data.");
+
+ instance = (struct qdevice_net_instance *)arg;
+
+ instance->tls_client_cert_sent = 1;
+
+ return (NSS_GetClientAuthData((void *)instance->advanced_settings->net_nss_client_cert_nickname,
+ sock, caNames, pRetCert, pRetKey));
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QDEVICE_NET_NSS_H_
+#define _QDEVICE_NET_NSS_H_
+
+#include <nspr.h>
+#include <nss.h>
+#include <ssl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern SECStatus qdevice_net_nss_bad_cert_hook(void *arg, PRFileDesc *fd);
+
+extern SECStatus qdevice_net_nss_get_client_auth_data(void *arg,
+ PRFileDesc *sock, struct CERTDistNamesStr *caNames,
+ struct CERTCertificateStr **pRetCert, struct SECKEYPrivateKeyStr **pRetKey);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_NSS_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QDEVICE_NET_POLL_ARRAY_USER_DATA_H_
+#define _QDEVICE_NET_POLL_ARRAY_USER_DATA_H_
+
+#include "unix-socket-client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum qdevice_net_poll_array_user_data_type {
+ QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_VOTEQUORUM,
+ QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_CMAP,
+ QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET,
+ QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_SOCKET,
+ QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT,
+ QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_CMD_SEND,
+ QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_CMD_RECV,
+ QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_LOG_RECV,
+};
+
+struct qdevice_net_poll_array_user_data {
+ enum qdevice_net_poll_array_user_data_type type;
+ struct unix_socket_client *ipc_client;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_POLL_ARRAY_USER_DATA_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 "qdevice-cmap.h"
+#include "qdevice-net-poll.h"
+#include "qdevice-log.h"
+#include "qdevice-net-send.h"
+#include "qdevice-net-socket.h"
+#include "qdevice-votequorum.h"
+#include "qdevice-ipc.h"
+#include "qdevice-net-poll-array-user-data.h"
+#include "qdevice-heuristics.h"
+#include "qdevice-heuristics-cmd.h"
+
+/*
+ * Needed for creating nspr handle from unix fd
+ */
+#include <private/pprio.h>
+
+static void
+qdevice_net_poll_read_socket(struct qdevice_net_instance *instance)
+{
+
+ if (qdevice_net_socket_read(instance) == -1) {
+ instance->schedule_disconnect = 1;
+ }
+}
+
+static void
+qdevice_net_poll_read_votequorum(struct qdevice_net_instance *instance)
+{
+
+ if (qdevice_votequorum_dispatch(instance->qdevice_instance_ptr) == -1) {
+ instance->schedule_disconnect = 1;
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_COROSYNC_CONNECTION_CLOSED;
+ }
+}
+
+static void
+qdevice_net_poll_read_cmap(struct qdevice_net_instance *instance)
+{
+
+ if (qdevice_cmap_dispatch(instance->qdevice_instance_ptr) == -1) {
+ instance->schedule_disconnect = 1;
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_COROSYNC_CONNECTION_CLOSED;
+ }
+}
+
+static void
+qdevice_net_poll_write_socket(struct qdevice_net_instance *instance, const PRPollDesc *pfd)
+{
+ int res;
+
+ if (instance->state == QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT) {
+ res = nss_sock_non_blocking_client_succeeded(pfd);
+ if (res == -1) {
+ /*
+ * Connect failed -> try next
+ */
+ res = nss_sock_non_blocking_client_try_next(&instance->non_blocking_client);
+ if (res == -1) {
+ qdevice_log_nss(LOG_ERR, "Can't connect to qnetd host.");
+ nss_sock_non_blocking_client_destroy(&instance->non_blocking_client);
+ }
+ } else if (res == 0) {
+ /*
+ * Poll again
+ */
+ } else if (res == 1) {
+ /*
+ * Connect success
+ */
+ instance->socket = instance->non_blocking_client.socket;
+ nss_sock_non_blocking_client_destroy(&instance->non_blocking_client);
+ instance->non_blocking_client.socket = NULL;
+
+ instance->state = QDEVICE_NET_INSTANCE_STATE_SENDING_PREINIT_REPLY;
+
+ qdevice_log(LOG_DEBUG, "Sending preinit msg to qnetd");
+ if (qdevice_net_send_preinit(instance) != 0) {
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+ instance->schedule_disconnect = 1;
+ }
+ } else {
+ qdevice_log(LOG_CRIT, "Unhandled nss_sock_non_blocking_client_succeeded");
+ exit(1);
+ }
+ } else {
+ if (qdevice_net_socket_write(instance) == -1) {
+ instance->schedule_disconnect = 1;
+ }
+ }
+}
+
+static void
+qdevice_net_poll_err_socket(struct qdevice_net_instance *instance, const PRPollDesc *pfd)
+{
+
+ if (instance->state == QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT) {
+ /*
+ * Workaround for RHEL<7. Pollout is never set for nonblocking connect (doesn't work
+ * only with poll, select works as expected!???).
+ * So test if client is still valid and if pollout was not already called (ensured
+ * by default because of order in PR_Poll).
+ * If both applies it's possible to emulate pollout set by calling poll_write.
+ */
+ if (!instance->non_blocking_client.destroyed) {
+ qdevice_net_poll_write_socket(instance, pfd);
+ }
+ } else {
+ qdevice_log(LOG_ERR, "POLL_ERR (%u) on main socket", pfd->out_flags);
+
+ instance->schedule_disconnect = 1;
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_SERVER_CLOSED_CONNECTION;
+ }
+}
+
+static void
+qdevice_net_poll_read_heuristics_log(struct qdevice_net_instance *instance)
+{
+ int res;
+
+ res = qdevice_heuristics_log_read_from_pipe(&instance->qdevice_instance_ptr->heuristics_instance);
+ if (res == -1) {
+ instance->schedule_disconnect = 1;
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_HEURISTICS_WORKER_CLOSED;
+ }
+}
+
+static void
+qdevice_net_poll_read_heuristics_cmd(struct qdevice_net_instance *instance)
+{
+ int res;
+
+ res = qdevice_heuristics_cmd_read_from_pipe(&instance->qdevice_instance_ptr->heuristics_instance);
+ if (res == -1) {
+ instance->schedule_disconnect = 1;
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_HEURISTICS_WORKER_CLOSED;
+ }
+}
+
+static void
+qdevice_net_poll_write_heuristics_cmd(struct qdevice_net_instance *instance)
+{
+ int res;
+
+ res = qdevice_heuristics_cmd_write(&instance->qdevice_instance_ptr->heuristics_instance);
+ if (res == -1) {
+ instance->schedule_disconnect = 1;
+ instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_HEURISTICS_CANT_SEND_RECEIVE_MSG;
+ }
+}
+
+static void
+qdevice_net_poll_read_ipc_socket(struct qdevice_net_instance *instance)
+{
+ struct unix_socket_client *client;
+ PRFileDesc *prfd;
+ struct qdevice_ipc_user_data *user_data;
+
+ if (qdevice_ipc_accept(instance->qdevice_instance_ptr, &client) != 0) {
+ return ;
+ }
+
+ prfd = PR_CreateSocketPollFd(client->socket);
+ if (prfd == NULL) {
+ qdevice_log_nss(LOG_CRIT, "Can't create NSPR poll fd for IPC client. "
+ "Disconnecting client");
+ qdevice_ipc_client_disconnect(instance->qdevice_instance_ptr, client);
+
+ return ;
+ }
+
+ user_data = (struct qdevice_ipc_user_data *)client->user_data;
+ user_data->model_data = (void *)prfd;
+}
+
+static PRPollDesc *
+qdevice_net_pr_poll_array_create(struct qdevice_net_instance *instance)
+{
+ struct pr_poll_array *poll_array;
+ PRPollDesc *poll_desc;
+ struct qdevice_net_poll_array_user_data *user_data;
+ struct unix_socket_client *ipc_client;
+ const struct unix_socket_client_list *ipc_client_list;
+ struct qdevice_ipc_user_data *qdevice_ipc_user_data;
+
+ poll_array = &instance->poll_array;
+ ipc_client_list = &instance->qdevice_instance_ptr->local_ipc.clients;
+
+ if (qdevice_ipc_is_closed(instance->qdevice_instance_ptr)) {
+ qdevice_log(LOG_DEBUG, "Local socket is closed");
+ instance->schedule_disconnect = 1;
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_LOCAL_SOCKET_CLOSED;
+
+ return (NULL);
+ }
+
+ pr_poll_array_clean(poll_array);
+
+ if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+ return (NULL);
+ }
+ poll_desc->fd = instance->votequorum_poll_fd;
+ poll_desc->in_flags = PR_POLL_READ;
+ user_data->type = QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_VOTEQUORUM;
+
+ if (!instance->qdevice_instance_ptr->sync_in_progress) {
+ if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+ return (NULL);
+ }
+ poll_desc->fd = instance->cmap_poll_fd;
+ poll_desc->in_flags = PR_POLL_READ;
+ user_data->type = QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_CMAP;
+ }
+
+ if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+ return (NULL);
+ }
+ poll_desc->fd = instance->ipc_socket_poll_fd;
+ poll_desc->in_flags = PR_POLL_READ;
+ user_data->type = QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET;
+
+ if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+ return (NULL);
+ }
+
+ poll_desc->fd = instance->heuristics_pipe_log_recv_poll_fd;
+ poll_desc->in_flags = PR_POLL_READ;
+ user_data->type = QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_LOG_RECV;
+
+ if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+ return (NULL);
+ }
+
+ poll_desc->fd = instance->heuristics_pipe_cmd_recv_poll_fd;
+ poll_desc->in_flags = PR_POLL_READ;
+ user_data->type = QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_CMD_RECV;
+
+ if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+ return (NULL);
+ }
+
+ if (!send_buffer_list_empty(
+ &instance->qdevice_instance_ptr->heuristics_instance.cmd_out_buffer_list)) {
+ poll_desc->fd = instance->heuristics_pipe_cmd_send_poll_fd;
+ poll_desc->in_flags = PR_POLL_WRITE;
+ user_data->type = QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_CMD_SEND;
+
+ if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+ return (NULL);
+ }
+ }
+
+ if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT ||
+ !instance->non_blocking_client.destroyed) {
+ if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+ return (NULL);
+ }
+
+ user_data->type = QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_SOCKET;
+
+ if (instance->state == QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT) {
+ poll_desc->fd = instance->non_blocking_client.socket;
+ poll_desc->in_flags = PR_POLL_WRITE | PR_POLL_EXCEPT;
+ } else {
+ poll_desc->fd = instance->socket;
+ poll_desc->in_flags = PR_POLL_READ;
+
+ if (!send_buffer_list_empty(&instance->send_buffer_list)) {
+ poll_desc->in_flags |= PR_POLL_WRITE;
+ }
+ }
+ }
+
+ TAILQ_FOREACH(ipc_client, ipc_client_list, entries) {
+ if (!ipc_client->reading_line && !ipc_client->writing_buffer) {
+ continue;
+ }
+
+ if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+ return (NULL);
+ }
+
+ qdevice_ipc_user_data = (struct qdevice_ipc_user_data *)ipc_client->user_data;
+ poll_desc->fd = (PRFileDesc *)qdevice_ipc_user_data->model_data;
+ if (ipc_client->reading_line) {
+ poll_desc->in_flags |= PR_POLL_READ;
+ }
+
+ if (ipc_client->writing_buffer) {
+ poll_desc->in_flags |= PR_POLL_WRITE;
+ }
+
+ user_data->type = QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT;
+ user_data->ipc_client = ipc_client;
+ }
+
+ pr_poll_array_gc(poll_array);
+
+ return (poll_array->array);
+}
+
+int
+qdevice_net_poll(struct qdevice_net_instance *instance)
+{
+ PRPollDesc *pfds;
+ PRFileDesc *prfd;
+ PRInt32 poll_res;
+ ssize_t i;
+ struct qdevice_net_poll_array_user_data *user_data;
+ struct unix_socket_client *ipc_client;
+ struct qdevice_ipc_user_data *qdevice_ipc_user_data;
+ int case_processed;
+
+ pfds = qdevice_net_pr_poll_array_create(instance);
+ if (pfds == NULL) {
+ return (-1);
+ }
+
+ instance->schedule_disconnect = 0;
+
+ if ((poll_res = PR_Poll(pfds, pr_poll_array_size(&instance->poll_array),
+ timer_list_time_to_expire(&instance->main_timer_list))) > 0) {
+ for (i = 0; i < pr_poll_array_size(&instance->poll_array); i++) {
+ user_data = pr_poll_array_get_user_data(&instance->poll_array, i);
+
+ ipc_client = user_data->ipc_client;
+
+ if (pfds[i].out_flags & PR_POLL_READ) {
+ case_processed = 0;
+
+ switch (user_data->type) {
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_SOCKET:
+ case_processed = 1;
+ qdevice_net_poll_read_socket(instance);
+ break;
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_VOTEQUORUM:
+ case_processed = 1;
+ qdevice_net_poll_read_votequorum(instance);
+ break;
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_CMAP:
+ case_processed = 1;
+ qdevice_net_poll_read_cmap(instance);
+ break;
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET:
+ case_processed = 1;
+ qdevice_net_poll_read_ipc_socket(instance);
+ break;
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT:
+ case_processed = 1;
+ qdevice_ipc_io_read(instance->qdevice_instance_ptr, ipc_client);
+ break;
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_CMD_SEND:
+ /*
+ * Read on heuristics cmd send fd shouldn't happen
+ */
+ break;
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_CMD_RECV:
+ case_processed = 1;
+ qdevice_net_poll_read_heuristics_cmd(instance);
+ break;
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_LOG_RECV:
+ case_processed = 1;
+ qdevice_net_poll_read_heuristics_log(instance);
+ break;
+ /*
+ * Default is not defined intentionally. Compiler shows warning when
+ * new poll_array_user_data_type is added
+ */
+ }
+
+ if (!case_processed) {
+ qdevice_log(LOG_CRIT, "Unhandled read on poll descriptor %u", i);
+ exit(1);
+ }
+ }
+
+ if (!instance->schedule_disconnect && pfds[i].out_flags & PR_POLL_WRITE) {
+ case_processed = 0;
+
+ switch (user_data->type) {
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_SOCKET:
+ case_processed = 1;
+ qdevice_net_poll_write_socket(instance, &pfds[i]);
+ break;
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT:
+ case_processed = 1;
+ qdevice_ipc_io_write(instance->qdevice_instance_ptr, ipc_client);
+ break;
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_CMD_SEND:
+ case_processed = 1;
+ qdevice_net_poll_write_heuristics_cmd(instance);
+ break;
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_VOTEQUORUM:
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_CMAP:
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET:
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_CMD_RECV:
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_LOG_RECV:
+ /*
+ * Write on votequorum, cmap, ipc socket and
+ * heuristics log shouldn't happen.
+ */
+ break;
+ /*
+ * Default is not defined intentionally. Compiler shows warning when
+ * new poll_array_user_data_type is added
+ */
+ }
+
+ if (!case_processed) {
+ qdevice_log(LOG_CRIT, "Unhandled write on poll descriptor %u", i);
+ exit(1);
+ }
+ }
+
+ if (!instance->schedule_disconnect &&
+ (pfds[i].out_flags & (PR_POLL_ERR|PR_POLL_NVAL|PR_POLL_HUP|PR_POLL_EXCEPT)) &&
+ !(pfds[i].out_flags & (PR_POLL_READ|PR_POLL_WRITE))) {
+ case_processed = 0;
+
+ switch (user_data->type) {
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_SOCKET:
+ case_processed = 1;
+ qdevice_net_poll_err_socket(instance, &pfds[i]);
+ break;
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET:
+ case_processed = 1;
+ if (pfds[i].out_flags != PR_POLL_NVAL) {
+ qdevice_log(LOG_CRIT, "POLLERR (%u) on local socket",
+ pfds[i].out_flags);
+ exit(1);
+ } else {
+ qdevice_log(LOG_DEBUG, "Local socket is closed");
+ instance->schedule_disconnect = 1;
+ instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_LOCAL_SOCKET_CLOSED;
+ }
+ break;
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT:
+ case_processed = 1;
+ qdevice_log(LOG_DEBUG, "POLL_ERR (%u) on ipc client socket. "
+ "Disconnecting.", pfds[i].out_flags);
+ ipc_client->schedule_disconnect = 1;
+ break;
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_VOTEQUORUM:
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_CMAP:
+ case_processed = 1;
+ qdevice_log(LOG_DEBUG, "POLL_ERR (%u) on corosync socket. "
+ "Disconnecting.", pfds[i].out_flags);
+
+ instance->schedule_disconnect = 1;
+ instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_COROSYNC_CONNECTION_CLOSED;
+ break;
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_LOG_RECV:
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_CMD_RECV:
+ case QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_HEURISTICS_CMD_SEND:
+ case_processed = 1;
+
+ /*
+ * Closed pipe doesn't mean return of PR_POLL_READ. To display
+ * better log message, we call read log as if POLL_READ would
+ * be set.
+ */
+ qdevice_net_poll_read_heuristics_log(instance);
+
+ qdevice_log(LOG_DEBUG, "POLL_ERR (%u) on heuristics pipe. "
+ "Disconnecting.", pfds[i].out_flags);
+
+ instance->schedule_disconnect = 1;
+ instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_HEURISTICS_WORKER_CLOSED;
+ break;
+ /*
+ * Default is not defined intentionally. Compiler shows warning when
+ * new poll_array_user_data_type is added
+ */
+ }
+
+ if (!case_processed) {
+ qdevice_log(LOG_CRIT, "Unhandled error on poll descriptor %u", i);
+ exit(1);
+ }
+ }
+
+ if (user_data->type == QDEVICE_NET_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT &&
+ ipc_client->schedule_disconnect) {
+ qdevice_ipc_user_data = (struct qdevice_ipc_user_data *)ipc_client->user_data;
+ prfd = (PRFileDesc *)qdevice_ipc_user_data->model_data;
+
+ if (PR_DestroySocketPollFd(prfd) != PR_SUCCESS) {
+ qdevice_log_nss(LOG_WARNING, "Unable to destroy client IPC poll socket fd");
+ }
+
+ qdevice_ipc_client_disconnect(instance->qdevice_instance_ptr, ipc_client);
+ }
+ }
+ }
+
+ if (!instance->schedule_disconnect) {
+ timer_list_expire(&instance->main_timer_list);
+ }
+
+ if (instance->schedule_disconnect) {
+ /*
+ * Schedule disconnect can be set by this function, by some timer_list callback
+ * or cmap/votequorum callbacks
+ */
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QDEVICE_NET_POLL_H_
+#define _QDEVICE_NET_POLL_H_
+
+
+#include "qdevice-net-instance.h"
+
+extern int qdevice_net_poll(struct qdevice_net_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_POLL_H_ */
--- /dev/null
+/*
+ * 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 "qdevice-log.h"
+#include "qdevice-log-debug.h"
+#include "qdevice-net-send.h"
+#include "qdevice-net-votequorum.h"
+#include "msg.h"
+#include "utils.h"
+
+int
+qdevice_net_send_echo_request(struct qdevice_net_instance *instance)
+{
+ struct send_buffer_list_entry *send_buffer;
+
+ send_buffer = send_buffer_list_get_new(&instance->send_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_CRIT, "Can't allocate send list buffer for reply msg.");
+
+ return (-1);
+ }
+
+ instance->echo_request_expected_msg_seq_num++;
+
+ if (msg_create_echo_request(&send_buffer->buffer, 1,
+ instance->echo_request_expected_msg_seq_num) == 0) {
+ qdevice_log(LOG_ERR, "Can't allocate send buffer for echo request msg");
+
+ send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer);
+
+ return (-1);
+ }
+
+ send_buffer_list_put(&instance->send_buffer_list, send_buffer);
+
+ return (0);
+}
+
+int
+qdevice_net_send_preinit(struct qdevice_net_instance *instance)
+{
+ struct send_buffer_list_entry *send_buffer;
+
+ send_buffer = send_buffer_list_get_new(&instance->send_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_ERR, "Can't allocate send list buffer for preinit msg");
+
+ return (-1);
+ }
+
+ if (msg_create_preinit(&send_buffer->buffer, instance->cluster_name, 1,
+ instance->last_msg_seq_num) == 0) {
+ qdevice_log(LOG_ERR, "Can't allocate buffer");
+
+ send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer);
+ return (-1);
+ }
+
+ send_buffer_list_put(&instance->send_buffer_list, send_buffer);
+
+ instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_PREINIT_REPLY;
+
+ return (0);
+}
+
+int
+qdevice_net_send_init(struct qdevice_net_instance *instance)
+{
+ enum msg_type *supported_msgs;
+ size_t no_supported_msgs;
+ enum tlv_opt_type *supported_opts;
+ size_t no_supported_opts;
+ struct send_buffer_list_entry *send_buffer;
+ struct tlv_ring_id tlv_rid;
+
+ tlv_get_supported_options(&supported_opts, &no_supported_opts);
+ msg_get_supported_messages(&supported_msgs, &no_supported_msgs);
+ instance->last_msg_seq_num++;
+
+ send_buffer = send_buffer_list_get_new(&instance->send_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_ERR, "Can't allocate send list buffer for init msg");
+
+ return (-1);
+ }
+
+ qdevice_net_votequorum_ring_id_to_tlv(&tlv_rid,
+ &instance->qdevice_instance_ptr->vq_node_list_ring_id);
+
+ if (msg_create_init(&send_buffer->buffer, 1, instance->last_msg_seq_num,
+ instance->decision_algorithm,
+ supported_msgs, no_supported_msgs, supported_opts, no_supported_opts,
+ instance->qdevice_instance_ptr->node_id, instance->heartbeat_interval,
+ &instance->tie_breaker, &tlv_rid) == 0) {
+ qdevice_log(LOG_ERR, "Can't allocate send buffer for init msg");
+
+ send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer);
+ return (-1);
+ }
+
+ memcpy(&instance->last_sent_ring_id, &tlv_rid, sizeof(instance->last_sent_ring_id));
+
+ send_buffer_list_put(&instance->send_buffer_list, send_buffer);
+
+ instance->state = QDEVICE_NET_INSTANCE_STATE_WAITING_INIT_REPLY;
+
+ return (0);
+}
+
+int
+qdevice_net_send_ask_for_vote(struct qdevice_net_instance *instance)
+{
+ struct send_buffer_list_entry *send_buffer;
+
+ send_buffer = send_buffer_list_get_new(&instance->send_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_ERR, "Can't allocate send list buffer for ask for vote msg");
+
+ return (-1);
+ }
+
+ instance->last_msg_seq_num++;
+
+ qdevice_log(LOG_DEBUG, "Sending ask for vote seq = "UTILS_PRI_MSG_SEQ,
+ instance->last_msg_seq_num);
+
+ if (msg_create_ask_for_vote(&send_buffer->buffer, instance->last_msg_seq_num) == 0) {
+ qdevice_log(LOG_ERR, "Can't allocate send buffer for ask for vote msg");
+
+ send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer);
+ return (-1);
+ }
+
+ send_buffer_list_put(&instance->send_buffer_list, send_buffer);
+
+ return (0);
+}
+
+int
+qdevice_net_send_config_node_list(struct qdevice_net_instance *instance,
+ const struct node_list *nlist, int config_version_set, uint64_t config_version,
+ int initial)
+{
+ struct send_buffer_list_entry *send_buffer;
+
+ send_buffer = send_buffer_list_get_new(&instance->send_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_ERR, "Can't allocate send list buffer for config "
+ "node list msg");
+
+ return (-1);
+ }
+
+ instance->last_msg_seq_num++;
+
+ qdevice_log(LOG_DEBUG, "Sending config node list seq = "UTILS_PRI_MSG_SEQ,
+ instance->last_msg_seq_num);
+ qdevice_log_debug_dump_node_list(nlist);
+
+ if (msg_create_node_list(&send_buffer->buffer, instance->last_msg_seq_num,
+ (initial ? TLV_NODE_LIST_TYPE_INITIAL_CONFIG : TLV_NODE_LIST_TYPE_CHANGED_CONFIG),
+ 0, NULL, config_version_set, config_version, 0, TLV_QUORATE_INQUORATE,
+ 0, TLV_HEURISTICS_UNDEFINED, nlist) == 0) {
+ qdevice_log(LOG_ERR, "Can't allocate send buffer for config list msg");
+
+ send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer);
+ return (-1);
+ }
+
+ send_buffer_list_put(&instance->send_buffer_list, send_buffer);
+
+ return (0);
+}
+
+int
+qdevice_net_send_heuristics_change(struct qdevice_net_instance *instance,
+ enum tlv_heuristics heuristics)
+{
+ struct send_buffer_list_entry *send_buffer;
+
+ send_buffer = send_buffer_list_get_new(&instance->send_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_ERR, "Can't allocate send list buffer for heuristics change msg");
+
+ return (-1);
+ }
+
+ instance->last_msg_seq_num++;
+
+ qdevice_log(LOG_DEBUG, "Sending heuristics change seq = "UTILS_PRI_MSG_SEQ
+ ", heuristics = %s",
+ instance->last_msg_seq_num, tlv_heuristics_to_str(heuristics));
+
+ if (msg_create_heuristics_change(&send_buffer->buffer, instance->last_msg_seq_num,
+ heuristics) == 0) {
+ qdevice_log(LOG_ERR, "Can't allocate send buffer for heuristics change msg");
+
+ send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer);
+ return (-1);
+ }
+
+ send_buffer_list_put(&instance->send_buffer_list, send_buffer);
+
+ return (0);
+}
+
+int
+qdevice_net_send_membership_node_list(struct qdevice_net_instance *instance,
+ const struct tlv_ring_id *ring_id,
+ uint32_t node_list_entries, uint32_t node_list[], enum tlv_heuristics heuristics)
+{
+ struct node_list nlist;
+ struct send_buffer_list_entry *send_buffer;
+ uint32_t i;
+
+ node_list_init(&nlist);
+
+ for (i = 0; i < node_list_entries; i++) {
+ if (node_list_add(&nlist, node_list[i], 0, TLV_NODE_STATE_NOT_SET) == NULL) {
+ qdevice_log(LOG_ERR, "Can't allocate membership node list.");
+
+ node_list_free(&nlist);
+
+ return (-1);
+ }
+ }
+
+ send_buffer = send_buffer_list_get_new(&instance->send_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_ERR, "Can't allocate send list buffer for membership "
+ "node list msg");
+
+ node_list_free(&nlist);
+
+ return (-1);
+ }
+
+ instance->last_msg_seq_num++;
+
+ qdevice_log(LOG_DEBUG, "Sending membership node list seq = "UTILS_PRI_MSG_SEQ", "
+ "ringid = ("UTILS_PRI_RING_ID"), heuristics = %s.", instance->last_msg_seq_num,
+ ring_id->node_id, ring_id->seq, tlv_heuristics_to_str(heuristics));
+ qdevice_log_debug_dump_node_list(&nlist);
+
+ if (msg_create_node_list(&send_buffer->buffer, instance->last_msg_seq_num,
+ TLV_NODE_LIST_TYPE_MEMBERSHIP,
+ 1, ring_id, 0, 0, 0, 0, 1, heuristics, &nlist) == 0) {
+ qdevice_log(LOG_ERR, "Can't allocate send buffer for membership list msg");
+
+ node_list_free(&nlist);
+
+ send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer);
+ return (-1);
+ }
+
+ memcpy(&instance->last_sent_ring_id, ring_id, sizeof(instance->last_sent_ring_id));
+
+ node_list_free(&nlist);
+
+ send_buffer_list_put(&instance->send_buffer_list, send_buffer);
+
+ return (0);
+}
+
+int
+qdevice_net_send_quorum_node_list(struct qdevice_net_instance *instance,
+ enum tlv_quorate quorate,
+ uint32_t node_list_entries, votequorum_node_t node_list[])
+{
+ struct node_list nlist;
+ struct send_buffer_list_entry *send_buffer;
+ uint32_t i;
+
+ node_list_init(&nlist);
+
+ for (i = 0; i < node_list_entries; i++) {
+ if (node_list[i].nodeid == 0) {
+ continue;
+ }
+
+ if (node_list_add(&nlist, node_list[i].nodeid, 0,
+ qdevice_net_votequorum_node_state_to_tlv(node_list[i].state)) == NULL) {
+ qdevice_log(LOG_ERR, "Can't allocate quorum node list.");
+
+ node_list_free(&nlist);
+
+ return (-1);
+ }
+ }
+
+ send_buffer = send_buffer_list_get_new(&instance->send_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_ERR, "Can't allocate send list buffer for quorum "
+ "node list msg");
+
+ node_list_free(&nlist);
+
+ return (-1);
+ }
+
+ instance->last_msg_seq_num++;
+
+ qdevice_log(LOG_DEBUG, "Sending quorum node list seq = "UTILS_PRI_MSG_SEQ", quorate = %u",
+ instance->last_msg_seq_num, quorate);
+ qdevice_log_debug_dump_node_list(&nlist);
+
+ if (msg_create_node_list(&send_buffer->buffer, instance->last_msg_seq_num,
+ TLV_NODE_LIST_TYPE_QUORUM,
+ 0, NULL, 0, 0, 1, quorate, 0, TLV_HEURISTICS_UNDEFINED, &nlist) == 0) {
+ qdevice_log(LOG_ERR, "Can't allocate send buffer for quorum list msg");
+
+ node_list_free(&nlist);
+
+ send_buffer_list_discard_new(&instance->send_buffer_list, send_buffer);
+ return (-1);
+ }
+
+ node_list_free(&nlist);
+
+ send_buffer_list_put(&instance->send_buffer_list, send_buffer);
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QDEVICE_NET_SEND_H_
+#define _QDEVICE_NET_SEND_H_
+
+#include <sys/types.h>
+
+#include "qdevice-net-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_net_send_echo_request(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_send_preinit(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_send_init(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_send_ask_for_vote(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_send_config_node_list(struct qdevice_net_instance *instance,
+ const struct node_list *nlist, int config_version_set, uint64_t config_version,
+ int initial);
+
+extern int qdevice_net_send_heuristics_change(struct qdevice_net_instance *instance,
+ enum tlv_heuristics heuristics);
+
+extern int qdevice_net_send_membership_node_list(
+ struct qdevice_net_instance *instance, const struct tlv_ring_id *ring_id,
+ uint32_t node_list_entries, uint32_t node_list[], enum tlv_heuristics heuristics);
+
+extern int qdevice_net_send_quorum_node_list(
+ struct qdevice_net_instance *instance, enum tlv_quorate quorate,
+ uint32_t node_list_entries, votequorum_node_t node_list[]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_SEND_H_ */
--- /dev/null
+/*
+ * 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 "msg.h"
+#include "msgio.h"
+#include "qnet-config.h"
+#include "qdevice-log.h"
+#include "qdevice-net-msg-received.h"
+#include "qdevice-net-nss.h"
+#include "qdevice-net-send.h"
+#include "qdevice-net-socket.h"
+
+/*
+ * -1 means end of connection (EOF) or some other unhandled error. 0 = success
+ */
+int
+qdevice_net_socket_read(struct qdevice_net_instance *instance)
+{
+ int res;
+ int ret_val;
+ int orig_skipping_msg;
+
+ orig_skipping_msg = instance->skipping_msg;
+
+ res = msgio_read(instance->socket, &instance->receive_buffer,
+ &instance->msg_already_received_bytes, &instance->skipping_msg);
+
+ if (!orig_skipping_msg && instance->skipping_msg) {
+ qdevice_log(LOG_DEBUG, "msgio_read set skipping_msg");
+ }
+
+ ret_val = 0;
+
+ switch (res) {
+ case 0:
+ /*
+ * Partial read
+ */
+ break;
+ case -1:
+ qdevice_log(LOG_DEBUG, "Server closed connection");
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_SERVER_CLOSED_CONNECTION;
+ ret_val = -1;
+ break;
+ case -2:
+ qdevice_log(LOG_ERR, "Unhandled error when reading from server. "
+ "Disconnecting from server");
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE;
+ ret_val = -1;
+ break;
+ case -3:
+ qdevice_log(LOG_ERR, "Can't store message header from server. "
+ "Disconnecting from server");
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE;
+ ret_val = -1;
+ break;
+ case -4:
+ qdevice_log(LOG_ERR, "Can't store message from server. "
+ "Disconnecting from server");
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE;
+ ret_val = -1;
+ break;
+ case -5:
+ qdevice_log(LOG_WARNING, "Server sent unsupported msg type %u. "
+ "Disconnecting from server", msg_get_type(&instance->receive_buffer));
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_UNSUPPORTED_MSG;
+ ret_val = -1;
+ break;
+ case -6:
+ qdevice_log(LOG_WARNING,
+ "Server wants to send too long message %u bytes. Disconnecting from server",
+ msg_get_len(&instance->receive_buffer));
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_READ_MESSAGE;
+ ret_val = -1;
+ break;
+ case 1:
+ /*
+ * Full message received / skipped
+ */
+ if (!instance->skipping_msg) {
+ if (qdevice_net_msg_received(instance) == -1) {
+ ret_val = -1;
+ }
+ } else {
+ qdevice_log(LOG_CRIT, "net_socket_read in skipping msg state");
+ exit(1);
+ }
+
+ instance->skipping_msg = 0;
+ instance->msg_already_received_bytes = 0;
+ dynar_clean(&instance->receive_buffer);
+ break;
+ default:
+ qdevice_log(LOG_CRIT, "qdevice_net_socket_read unhandled error %d", res);
+ exit(1);
+ break;
+ }
+
+ return (ret_val);
+}
+
+static int
+qdevice_net_socket_write_finished(struct qdevice_net_instance *instance)
+{
+ PRFileDesc *new_pr_fd;
+
+ if (instance->state == QDEVICE_NET_INSTANCE_STATE_WAITING_STARTTLS_BEING_SENT) {
+ /*
+ * StartTLS sent to server. Begin with TLS handshake
+ */
+ if ((new_pr_fd = nss_sock_start_ssl_as_client(instance->socket,
+ instance->advanced_settings->net_nss_qnetd_cn,
+ qdevice_net_nss_bad_cert_hook,
+ qdevice_net_nss_get_client_auth_data,
+ instance, 0, NULL)) == NULL) {
+ qdevice_log_nss(LOG_ERR, "Can't start TLS");
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_START_TLS;
+ return (-1);
+ }
+
+ /*
+ * And send init msg
+ */
+ if (qdevice_net_send_init(instance) != 0) {
+ instance->disconnect_reason =
+ QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+
+ return (-1);
+ }
+
+ instance->socket = new_pr_fd;
+ instance->using_tls = 1;
+ }
+
+ return (0);
+}
+
+int
+qdevice_net_socket_write(struct qdevice_net_instance *instance)
+{
+ int res;
+ struct send_buffer_list_entry *send_buffer;
+ enum msg_type sent_msg_type;
+
+ send_buffer = send_buffer_list_get_active(&instance->send_buffer_list);
+ if (send_buffer == NULL) {
+ qdevice_log(LOG_CRIT, "send_buffer_list_get_active returned NULL");
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SEND_MESSAGE;
+
+ return (-1);
+ }
+
+ res = msgio_write(instance->socket, &send_buffer->buffer,
+ &send_buffer->msg_already_sent_bytes);
+
+ if (res == 1) {
+ sent_msg_type = msg_get_type(&send_buffer->buffer);
+
+ send_buffer_list_delete(&instance->send_buffer_list, send_buffer);
+
+ if (sent_msg_type != MSG_TYPE_ECHO_REQUEST) {
+ if (qdevice_net_socket_write_finished(instance) == -1) {
+ return (-1);
+ }
+ }
+ }
+
+ if (res == -1) {
+ qdevice_log_nss(LOG_CRIT, "PR_Send returned 0");
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_SERVER_CLOSED_CONNECTION;
+ return (-1);
+ }
+
+ if (res == -2) {
+ qdevice_log_nss(LOG_ERR, "Unhandled error when sending message to server");
+ instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_SEND_MESSAGE;
+
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QDEVICE_NET_SOCKET_H_
+#define _QDEVICE_NET_SOCKET_H_
+
+#include "qdevice-net-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qdevice_net_socket_read(struct qdevice_net_instance *instance);
+
+extern int qdevice_net_socket_write(struct qdevice_net_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_SOCKET_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 "qdevice-log.h"
+#include "qdevice-net-votequorum.h"
+
+enum tlv_node_state
+qdevice_net_votequorum_node_state_to_tlv(uint32_t votequorum_node_state)
+{
+ enum tlv_node_state res;
+
+ switch (votequorum_node_state) {
+ case VOTEQUORUM_NODESTATE_MEMBER: res = TLV_NODE_STATE_MEMBER; break;
+ case VOTEQUORUM_NODESTATE_DEAD: res = TLV_NODE_STATE_DEAD; break;
+ case VOTEQUORUM_NODESTATE_LEAVING: res = TLV_NODE_STATE_LEAVING; break;
+ default:
+ qdevice_log(LOG_ERR, "qdevice_net_votequorum_node_state_to_tlv: Unhandled votequorum "
+ "node state %"PRIu32, votequorum_node_state);
+ exit(1);
+ break;
+ }
+
+ return (res);
+}
+
+void
+qdevice_net_votequorum_ring_id_to_tlv(struct tlv_ring_id *tlv_rid,
+ const votequorum_ring_id_t *votequorum_rid)
+{
+
+ tlv_rid->node_id = votequorum_rid->nodeid;
+ tlv_rid->seq = votequorum_rid->seq;
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QDEVICE_NET_VOTEQUORUM_H_
+#define _QDEVICE_NET_VOTEQUORUM_H_
+
+#include <corosync/votequorum.h>
+
+#include "qdevice-net-instance.h"
+#include "tlv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern enum tlv_node_state qdevice_net_votequorum_node_state_to_tlv(
+ uint32_t votequorum_node_state);
+
+extern void qdevice_net_votequorum_ring_id_to_tlv(struct tlv_ring_id *tlv_rid,
+ const votequorum_ring_id_t *votequorum_rid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_NET_VOTEQUORUM_H_ */
--- /dev/null
+/*
+ * 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 <poll.h>
+
+#include "qdevice-config.h"
+#include "qdevice-log.h"
+#include "qdevice-votequorum.h"
+#include "qdevice-model.h"
+#include "utils.h"
+
+static void
+qdevice_votequorum_quorum_notify_callback(votequorum_handle_t votequorum_handle,
+ uint64_t context, uint32_t quorate,
+ uint32_t node_list_entries, votequorum_node_t node_list[])
+{
+ struct qdevice_instance *instance;
+ uint32_t u32;
+
+ if (votequorum_context_get(votequorum_handle, (void **)&instance) != CS_OK) {
+ qdevice_log(LOG_CRIT, "Fatal error. Can't get votequorum context");
+ exit(1);
+ }
+
+ instance->sync_in_progress = 0;
+
+ qdevice_log(LOG_DEBUG, "Votequorum quorum notify callback:");
+ qdevice_log(LOG_DEBUG, " Quorate = %u", quorate);
+
+ qdevice_log(LOG_DEBUG, " Node list (size = %"PRIu32"):", node_list_entries);
+ for (u32 = 0; u32 < node_list_entries; u32++) {
+ qdevice_log(LOG_DEBUG, " %"PRIu32" nodeid = "UTILS_PRI_NODE_ID", state = %"PRIu32,
+ u32, node_list[u32].nodeid, node_list[u32].state);
+ }
+
+ if (qdevice_model_votequorum_quorum_notify(instance, quorate, node_list_entries,
+ node_list) != 0) {
+ qdevice_log(LOG_DEBUG, "qdevice_model_votequorum_quorum_notify returned error -> exit");
+ exit(2);
+ }
+
+ instance->vq_quorum_quorate = quorate;
+ instance->vq_quorum_node_list_entries = node_list_entries;
+
+ free(instance->vq_quorum_node_list);
+ instance->vq_quorum_node_list = malloc(sizeof(*node_list) * node_list_entries);
+ if (instance->vq_quorum_node_list == NULL) {
+ qdevice_log(LOG_CRIT, "Can't alloc votequorum node list memory");
+ exit(1);
+ }
+ memcpy(instance->vq_quorum_node_list, node_list, sizeof(*node_list) * node_list_entries);
+}
+
+static int
+qdevice_votequorum_heuristics_exec_result_callback(
+ void *heuristics_instance_ptr,
+ uint32_t seq_number, enum qdevice_heuristics_exec_result exec_result)
+{
+ struct qdevice_heuristics_instance *heuristics_instance;
+ struct qdevice_instance *instance;
+
+ heuristics_instance = (struct qdevice_heuristics_instance *)heuristics_instance_ptr;
+ instance = heuristics_instance->qdevice_instance_ptr;
+
+ if (qdevice_heuristics_result_notifier_list_set_active(
+ &instance->heuristics_instance.exec_result_notifier_list,
+ qdevice_votequorum_heuristics_exec_result_callback, 0) != 0) {
+ qdevice_log(LOG_CRIT, "Can't deactivate votequrorum heuristics exec callback notifier");
+ exit(2);
+ }
+
+ qdevice_log(LOG_DEBUG, "Votequorum heuristics exec result callback:");
+ qdevice_log(LOG_DEBUG, " seq_number = %"PRIu32", exec_result = %s",
+ seq_number, qdevice_heuristics_exec_result_to_str(exec_result));
+
+ if (qdevice_model_votequorum_node_list_heuristics_notify(instance, instance->vq_node_list_ring_id,
+ instance->vq_node_list_entries, instance->vq_node_list, exec_result) != 0) {
+ qdevice_log(LOG_DEBUG, "qdevice_votequorum_node_list_heuristics_notify_callback returned error -> exit");
+ exit(2);
+ }
+
+ instance->vq_node_list_initial_heuristics_finished = 1;
+ instance->vq_node_list_heuristics_result = exec_result;
+
+ return (0);
+}
+
+static void
+qdevice_votequorum_node_list_notify_callback(votequorum_handle_t votequorum_handle,
+ uint64_t context, votequorum_ring_id_t votequorum_ring_id,
+ uint32_t node_list_entries, uint32_t node_list[])
+{
+ struct qdevice_instance *instance;
+ uint32_t u32;
+
+ if (votequorum_context_get(votequorum_handle, (void **)&instance) != CS_OK) {
+ qdevice_log(LOG_CRIT, "Fatal error. Can't get votequorum context");
+ exit(1);
+ }
+
+ instance->sync_in_progress = 1;
+ memcpy(&instance->vq_poll_ring_id, &votequorum_ring_id, sizeof(votequorum_ring_id));
+
+ qdevice_log(LOG_DEBUG, "Votequorum nodelist notify callback:");
+ qdevice_log(LOG_DEBUG, " Ring_id = ("UTILS_PRI_RING_ID")",
+ votequorum_ring_id.nodeid, votequorum_ring_id.seq);
+
+ qdevice_log(LOG_DEBUG, " Node list (size = %"PRIu32"):", node_list_entries);
+ for (u32 = 0; u32 < node_list_entries; u32++) {
+ qdevice_log(LOG_DEBUG, " %"PRIu32" nodeid = "UTILS_PRI_NODE_ID,
+ u32, node_list[u32]);
+ }
+
+ if (qdevice_model_votequorum_node_list_notify(instance, votequorum_ring_id, node_list_entries,
+ node_list) != 0) {
+ qdevice_log(LOG_DEBUG, "qdevice_votequorum_node_list_notify_callback returned error -> exit");
+ exit(2);
+ }
+
+ if (qdevice_heuristics_result_notifier_list_set_active(
+ &instance->heuristics_instance.exec_result_notifier_list,
+ qdevice_votequorum_heuristics_exec_result_callback, 1) != 0) {
+ qdevice_log(LOG_CRIT, "Can't activate votequrorum heuristics exec callback notifier");
+ exit(2);
+ }
+
+ if (qdevice_heuristics_exec(&instance->heuristics_instance, instance->sync_in_progress) != 0) {
+ qdevice_log(LOG_CRIT, "Can't start heuristics -> exit");
+ exit(2);
+ }
+
+ instance->vq_node_list_initial_ring_id_set = 1;
+ memcpy(&instance->vq_node_list_ring_id, &votequorum_ring_id, sizeof(votequorum_ring_id));
+ instance->vq_node_list_entries = node_list_entries;
+ free(instance->vq_node_list);
+ instance->vq_node_list = malloc(sizeof(*node_list) * node_list_entries);
+ if (instance->vq_node_list == NULL) {
+ qdevice_log(LOG_CRIT, "Can't alloc votequorum node list memory");
+ exit(1);
+ }
+ memcpy(instance->vq_node_list, node_list, sizeof(*node_list) * node_list_entries);
+}
+
+static void
+qdevice_votequorum_expected_votes_notify_callback(votequorum_handle_t votequorum_handle,
+ uint64_t context, uint32_t expected_votes)
+{
+ struct qdevice_instance *instance;
+
+ if (votequorum_context_get(votequorum_handle, (void **)&instance) != CS_OK) {
+ qdevice_log(LOG_CRIT, "Fatal error. Can't get votequorum context");
+ exit(1);
+ }
+
+ qdevice_log(LOG_DEBUG, "Votequorum expected_votes notify callback:");
+ qdevice_log(LOG_DEBUG, " Expected_votes: "UTILS_PRI_EXPECTED_VOTES, expected_votes);
+
+ if (qdevice_model_votequorum_expected_votes_notify(instance, expected_votes) != 0) {
+ qdevice_log(LOG_DEBUG, "qdevice_votequorum_expected_votes_notify_callback returned error -> exit");
+ exit(2);
+ }
+
+ instance->vq_expected_votes = expected_votes;
+}
+
+void
+qdevice_votequorum_init(struct qdevice_instance *instance)
+{
+ votequorum_callbacks_t votequorum_callbacks;
+ votequorum_handle_t votequorum_handle;
+ cs_error_t res;
+ int no_retries;
+ struct votequorum_info vq_info;
+
+ memset(&votequorum_callbacks, 0, sizeof(votequorum_callbacks));
+
+ votequorum_callbacks.votequorum_quorum_notify_fn =
+ qdevice_votequorum_quorum_notify_callback;
+
+ votequorum_callbacks.votequorum_nodelist_notify_fn =
+ qdevice_votequorum_node_list_notify_callback;
+
+ votequorum_callbacks.votequorum_expectedvotes_notify_fn =
+ qdevice_votequorum_expected_votes_notify_callback;
+
+ no_retries = 0;
+
+ while ((res = votequorum_initialize(&votequorum_handle,
+ &votequorum_callbacks)) == CS_ERR_TRY_AGAIN &&
+ no_retries++ < instance->advanced_settings->max_cs_try_again) {
+ (void)poll(NULL, 0, 1000);
+ }
+
+ if (res != CS_OK) {
+ qdevice_log(LOG_CRIT, "Failed to initialize the votequorum API. Error %s", cs_strerror(res));
+ exit(1);
+ }
+
+ if ((res = votequorum_qdevice_register(votequorum_handle,
+ instance->advanced_settings->votequorum_device_name)) != CS_OK) {
+ qdevice_log(LOG_CRIT, "Can't register votequorum device. Error %s", cs_strerror(res));
+ exit(1);
+ }
+
+ if ((res = votequorum_context_set(votequorum_handle, (void *)instance)) != CS_OK) {
+ qdevice_log(LOG_CRIT, "Can't set votequorum context. Error %s", cs_strerror(res));
+ exit(1);
+ }
+
+ if ((res = votequorum_getinfo(votequorum_handle, VOTEQUORUM_QDEVICE_NODEID,
+ &vq_info)) != CS_OK) {
+ qdevice_log(LOG_CRIT, "Can't get votequorum information. Error %s", cs_strerror(res));
+ exit(1);
+ }
+ instance->vq_expected_votes = vq_info.node_expected_votes;
+
+ instance->votequorum_handle = votequorum_handle;
+
+ votequorum_fd_get(votequorum_handle, &instance->votequorum_poll_fd);
+
+ if ((res = votequorum_trackstart(instance->votequorum_handle, 0,
+ CS_TRACK_CHANGES)) != CS_OK) {
+ qdevice_log(LOG_CRIT, "Can't start tracking votequorum changes. Error %s",
+ cs_strerror(res));
+ exit(1);
+ }
+
+ if (qdevice_heuristics_result_notifier_list_add(&instance->heuristics_instance.exec_result_notifier_list,
+ qdevice_votequorum_heuristics_exec_result_callback) == NULL) {
+ qdevice_log(LOG_CRIT, "Can't add votequrorum heuristics exec callback into notifier");
+ exit(1);
+ }
+}
+
+void
+qdevice_votequorum_destroy(struct qdevice_instance *instance)
+{
+ cs_error_t res;
+
+ free(instance->vq_quorum_node_list); instance->vq_quorum_node_list = NULL;
+ free(instance->vq_node_list); instance->vq_node_list = NULL;
+
+ res = votequorum_trackstop(instance->votequorum_handle);
+ if (res != CS_OK) {
+ qdevice_log(LOG_WARNING, "Can't start tracking votequorum changes. Error %s",
+ cs_strerror(res));
+ }
+
+ res = votequorum_qdevice_unregister(instance->votequorum_handle,
+ instance->advanced_settings->votequorum_device_name);
+
+ if (res != CS_OK) {
+ qdevice_log(LOG_WARNING, "Unable to unregister votequorum device. Error %s", cs_strerror(res));
+ }
+
+ res = votequorum_finalize(instance->votequorum_handle);
+ if (res != CS_OK) {
+ qdevice_log(LOG_WARNING, "Unable to finalize votequorum. Error %s", cs_strerror(res));
+ }
+}
+
+int
+qdevice_votequorum_wait_for_ring_id(struct qdevice_instance *instance)
+{
+ int no_retries;
+
+ no_retries = 0;
+
+ while (qdevice_votequorum_dispatch(instance) != -1 &&
+ no_retries++ < instance->advanced_settings->max_cs_try_again &&
+ !instance->vq_node_list_initial_ring_id_set) {
+ (void)poll(NULL, 0, 1000);
+ }
+
+ if (!instance->vq_node_list_initial_ring_id_set) {
+ qdevice_log(LOG_CRIT, "Can't get initial votequorum membership information.");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qdevice_votequorum_dispatch(struct qdevice_instance *instance)
+{
+ cs_error_t res;
+
+ res = votequorum_dispatch(instance->votequorum_handle, CS_DISPATCH_ALL);
+
+ if (res != CS_OK && res != CS_ERR_TRY_AGAIN) {
+ qdevice_log(LOG_ERR, "Can't dispatch votequorum messages");
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qdevice_votequorum_poll(struct qdevice_instance *instance, int cast_vote)
+{
+ cs_error_t res;
+
+ instance->vq_last_poll = time(NULL);
+ instance->vq_last_poll_cast_vote = cast_vote;
+
+ res = votequorum_qdevice_poll(instance->votequorum_handle,
+ instance->advanced_settings->votequorum_device_name, cast_vote,
+ instance->vq_poll_ring_id);
+
+ if (res != CS_OK && res != CS_ERR_TRY_AGAIN) {
+ if (res == CS_ERR_MESSAGE_ERROR) {
+ qdevice_log(LOG_INFO, "qdevice_votequorum_poll called with old ring id");
+ } else {
+ qdevice_log(LOG_CRIT, "Can't call votequorum_qdevice_poll. Error %s",
+ cs_strerror(res));
+
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+int
+qdevice_votequorum_master_wins(struct qdevice_instance *instance, int allow)
+{
+ cs_error_t res;
+ int final_allow;
+
+ final_allow = allow;
+
+ if (instance->advanced_settings->master_wins ==
+ QDEVICE_ADVANCED_SETTINGS_MASTER_WINS_FORCE_OFF && allow) {
+ qdevice_log(LOG_WARNING, "Allow of master wins is requested, but user forcibly "
+ "disallowed it. Keeping master wins disallowed.");
+
+ final_allow = 0;
+ }
+
+ if (instance->advanced_settings->master_wins ==
+ QDEVICE_ADVANCED_SETTINGS_MASTER_WINS_FORCE_ON && !allow) {
+ qdevice_log(LOG_WARNING, "Disallow of master wins is requested, but user forcibly "
+ "allowed it. Keeping master wins allowed.");
+
+ final_allow = 1;
+ }
+
+ res = votequorum_qdevice_master_wins(instance->votequorum_handle,
+ instance->advanced_settings->votequorum_device_name, final_allow);
+
+ if (res != CS_OK) {
+ qdevice_log(LOG_CRIT, "Can't set master wins. Error %s", cs_strerror(res));
+
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QDEVICE_VOTEQUORUM_H_
+#define _QDEVICE_VOTEQUORUM_H_
+
+#include <corosync/votequorum.h>
+
+#include "qdevice-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void qdevice_votequorum_init(struct qdevice_instance *instance);
+
+extern void qdevice_votequorum_destroy(struct qdevice_instance *instance);
+
+extern int qdevice_votequorum_dispatch(struct qdevice_instance *instance);
+
+extern int qdevice_votequorum_poll(struct qdevice_instance *instance,
+ int cast_vote);
+
+extern int qdevice_votequorum_wait_for_ring_id(
+ struct qdevice_instance *instance);
+
+extern int qdevice_votequorum_master_wins(struct qdevice_instance *instance,
+ int allow);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QDEVICE_VOTEQUORUM_H_ */
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNET_CONFIG_H_
+#define _QNET_CONFIG_H_
+
+#include <config.h>
+
+#include "tlv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * There are "hardcoded" defaults for both qnetd and qdevice-net. It's not so good
+ * idea to change them as long as you are not 100% sure what you are doing. Also
+ * most of them can be changed in CLI via advanced_settings (-S).
+ */
+
+#define QNETD_PROGRAM_NAME "corosync-qnetd"
+#define QNETD_DEFAULT_HOST_PORT 5403
+#define QNETD_DEFAULT_LISTEN_BACKLOG 10
+#define QNETD_MIN_LISTEN_BACKLOG 1
+#define QNETD_DEFAULT_MAX_CLIENT_SEND_BUFFERS 32
+#define QNETD_MIN_CLIENT_SEND_BUFFERS 2
+#define QNETD_DEFAULT_MAX_CLIENT_SEND_SIZE (1 << 15)
+#define QNETD_DEFAULT_MAX_CLIENT_RECEIVE_SIZE (1 << 15)
+#define QNETD_MIN_CLIENT_RECEIVE_SEND_SIZE 16
+#define QNETD_DEFAULT_MAX_CLIENTS 0
+
+#define QNETD_DEFAULT_NSS_DB_DIR COROSYSCONFDIR "/qnetd/nssdb"
+#define QNETD_DEFAULT_CERT_NICKNAME "QNetd Cert"
+
+#define QNETD_DEFAULT_TLS_SUPPORTED TLV_TLS_SUPPORTED
+#define QNETD_DEFAULT_TLS_CLIENT_CERT_REQUIRED 1
+
+#define QNETD_DEFAULT_HEARTBEAT_INTERVAL_MIN (1*1000)
+#define QNETD_DEFAULT_HEARTBEAT_INTERVAL_MAX (2*60*1000)
+#define QNETD_MIN_HEARTBEAT_INTERVAL 1
+
+#define QNETD_DEFAULT_DPD_ENABLED 1
+#define QNETD_DEFAULT_DPD_INTERVAL (10*1000)
+#define QNETD_MIN_DPD_INTERVAL 1
+
+#define QNETD_DEFAULT_LOCK_FILE LOCALSTATEDIR"/run/corosync-qnetd/corosync-qnetd.pid"
+#define QNETD_DEFAULT_LOCAL_SOCKET_FILE LOCALSTATEDIR"/run/corosync-qnetd/corosync-qnetd.sock"
+#define QNETD_DEFAULT_LOCAL_SOCKET_BACKLOG 10
+#define QNETD_MIN_LOCAL_SOCKET_BACKLOG 1
+
+#define QNETD_DEFAULT_IPC_MAX_CLIENTS 10
+#define QNETD_MIN_IPC_MAX_CLIENTS 0
+#define QNETD_DEFAULT_IPC_MAX_RECEIVE_SIZE (4*1024)
+#define QNETD_DEFAULT_IPC_MAX_SEND_SIZE (10*1024*1024)
+#define QNETD_MIN_IPC_RECEIVE_SEND_SIZE 1024
+
+#define QNETD_TOOL_PROGRAM_NAME "corosync-qnetd-tool"
+
+#define QDEVICE_NET_DEFAULT_NSS_DB_DIR COROSYSCONFDIR "/qdevice/net/nssdb"
+
+#define QDEVICE_NET_DEFAULT_INITIAL_MSG_RECEIVE_SIZE (1 << 15)
+#define QDEVICE_NET_DEFAULT_INITIAL_MSG_SEND_SIZE (1 << 15)
+#define QDEVICE_NET_DEFAULT_MIN_MSG_SEND_SIZE QDEVICE_NET_DEFAULT_INITIAL_MSG_SEND_SIZE
+#define QDEVICE_NET_DEFAULT_MAX_MSG_RECEIVE_SIZE (1 << 24)
+#define QDEVICE_NET_DEFAULT_MAX_SEND_BUFFERS 10
+#define QDEVICE_NET_MIN_MAX_SEND_BUFFERS 2
+#define QDEVICE_NET_MIN_MSG_RECEIVE_SEND_SIZE 16
+
+#define QDEVICE_NET_DEFAULT_NSS_QNETD_CN "Qnetd Server"
+
+#define QDEVICE_NET_DEFAULT_NSS_CLIENT_CERT_NICKNAME "Cluster Cert"
+
+
+#define QDEVICE_NET_DEFAULT_ALGORITHM TLV_DECISION_ALGORITHM_TYPE_FFSPLIT
+
+#define QDEVICE_NET_DEFAULT_TLS_SUPPORTED TLV_TLS_SUPPORTED
+
+#define QDEVICE_NET_DEFAULT_TIE_BREAKER_MODE TLV_TIE_BREAKER_MODE_LOWEST
+
+#define QDEVICE_NET_DEFAULT_HEARTBEAT_INTERVAL_MIN QNETD_DEFAULT_HEARTBEAT_INTERVAL_MIN
+#define QDEVICE_NET_DEFAULT_HEARTBEAT_INTERVAL_MAX QNETD_DEFAULT_HEARTBEAT_INTERVAL_MAX
+#define QDEVICE_NET_MIN_HEARTBEAT_INTERVAL 1
+
+#define QDEVICE_NET_DEFAULT_MIN_CONNECT_TIMEOUT (1*1000)
+#define QDEVICE_NET_DEFAULT_MAX_CONNECT_TIMEOUT (2*60*1000)
+#define QDEVICE_NET_MIN_CONNECT_TIMEOUT 1
+
+#ifdef DEBUG
+#define QDEVICE_NET_DEFAULT_TEST_ALGORITHM_ENABLED 1
+#else
+#define QDEVICE_NET_DEFAULT_TEST_ALGORITHM_ENABLED 0
+#endif
+
+/*
+ * Decision algorithms supported by qnetd
+ */
+#define QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE 4
+
+extern enum tlv_decision_algorithm_type
+ qnetd_static_supported_decision_algorithms[QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE];
+
+#define QDEVICE_NET_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNET_CONFIG_H_ */
--- /dev/null
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "dynar.h"
+#include "dynar-getopt-lex.h"
+#include "dynar-str.h"
+#include "qnet-config.h"
+#include "qnetd-advanced-settings.h"
+#include "utils.h"
+
+int
+qnetd_advanced_settings_init(struct qnetd_advanced_settings *settings)
+{
+
+ memset(settings, 0, sizeof(*settings));
+ settings->listen_backlog = QNETD_DEFAULT_LISTEN_BACKLOG;
+ settings->max_client_send_buffers = QNETD_DEFAULT_MAX_CLIENT_SEND_BUFFERS;
+ settings->max_client_send_size = QNETD_DEFAULT_MAX_CLIENT_SEND_SIZE;
+ settings->max_client_receive_size = QNETD_DEFAULT_MAX_CLIENT_RECEIVE_SIZE;
+ if ((settings->nss_db_dir = strdup(QNETD_DEFAULT_NSS_DB_DIR)) == NULL) {
+ return (-1);
+ }
+ if ((settings->cert_nickname = strdup(QNETD_DEFAULT_CERT_NICKNAME)) == NULL) {
+ return (-1);
+ }
+ settings->heartbeat_interval_min = QNETD_DEFAULT_HEARTBEAT_INTERVAL_MIN;
+ settings->heartbeat_interval_max = QNETD_DEFAULT_HEARTBEAT_INTERVAL_MAX;
+ settings->dpd_enabled = QNETD_DEFAULT_DPD_ENABLED;
+ settings->dpd_interval = QNETD_DEFAULT_DPD_INTERVAL;
+ if ((settings->lock_file = strdup(QNETD_DEFAULT_LOCK_FILE)) == NULL) {
+ return (-1);
+ }
+ if ((settings->local_socket_file = strdup(QNETD_DEFAULT_LOCAL_SOCKET_FILE)) == NULL) {
+ return (-1);
+ }
+ settings->local_socket_backlog = QNETD_DEFAULT_LOCAL_SOCKET_BACKLOG;
+ settings->ipc_max_clients = QNETD_DEFAULT_IPC_MAX_CLIENTS;
+ settings->ipc_max_receive_size = QNETD_DEFAULT_IPC_MAX_RECEIVE_SIZE;
+ settings->ipc_max_send_size = QNETD_DEFAULT_IPC_MAX_SEND_SIZE;
+
+ return (0);
+}
+
+void
+qnetd_advanced_settings_destroy(struct qnetd_advanced_settings *settings)
+{
+
+ free(settings->nss_db_dir);
+ free(settings->cert_nickname);
+ free(settings->lock_file);
+ free(settings->local_socket_file);
+}
+
+/*
+ * 0 - No error
+ * -1 - Unknown option
+ * -2 - Incorrect value
+ */
+int
+qnetd_advanced_settings_set(struct qnetd_advanced_settings *settings,
+ const char *option, const char *value)
+{
+ long long int tmpll;
+ char *ep;
+
+ if (strcasecmp(option, "listen_backlog") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QNETD_MIN_LISTEN_BACKLOG || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->listen_backlog = (int)tmpll;
+ } else if (strcasecmp(option, "max_client_send_buffers") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QNETD_MIN_CLIENT_SEND_BUFFERS || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->max_client_send_buffers = (size_t)tmpll;
+ } else if (strcasecmp(option, "max_client_send_size") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QNETD_MIN_CLIENT_RECEIVE_SEND_SIZE || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->max_client_send_size = (size_t)tmpll;
+ } else if (strcasecmp(option, "max_client_receive_size") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QNETD_MIN_CLIENT_RECEIVE_SEND_SIZE || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->max_client_receive_size = (size_t)tmpll;
+ } else if (strcasecmp(option, "nss_db_dir") == 0) {
+ free(settings->nss_db_dir);
+
+ if ((settings->nss_db_dir = strdup(value)) == NULL) {
+ return (-1);
+ }
+ } else if (strcasecmp(option, "cert_nickname") == 0) {
+ free(settings->cert_nickname);
+
+ if ((settings->cert_nickname = strdup(value)) == NULL) {
+ return (-1);
+ }
+ } else if (strcasecmp(option, "heartbeat_interval_min") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QNETD_MIN_HEARTBEAT_INTERVAL || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->heartbeat_interval_min = (uint32_t)tmpll;
+ } else if (strcasecmp(option, "heartbeat_interval_max") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QNETD_MIN_HEARTBEAT_INTERVAL || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->heartbeat_interval_max = (uint32_t)tmpll;
+ } else if (strcasecmp(option, "dpd_enabled") == 0) {
+ if ((tmpll = utils_parse_bool_str(value)) == -1) {
+ return (-2);
+ }
+
+ settings->dpd_enabled = (uint8_t)tmpll;
+ } else if (strcasecmp(option, "dpd_interval") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QNETD_MIN_DPD_INTERVAL || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->dpd_interval = (uint32_t)tmpll;
+ } else if (strcasecmp(option, "lock_file") == 0) {
+ free(settings->lock_file);
+
+ if ((settings->lock_file = strdup(value)) == NULL) {
+ return (-1);
+ }
+ } else if (strcasecmp(option, "local_socket_file") == 0) {
+ free(settings->local_socket_file);
+
+ if ((settings->local_socket_file = strdup(value)) == NULL) {
+ return (-1);
+ }
+ } else if (strcasecmp(option, "local_socket_backlog") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QNETD_MIN_LOCAL_SOCKET_BACKLOG || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->local_socket_backlog = (int)tmpll;
+ } else if (strcasecmp(option, "ipc_max_clients") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QNETD_MIN_IPC_MAX_CLIENTS || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->ipc_max_clients = (size_t)tmpll;
+ } else if (strcasecmp(option, "ipc_max_receive_size") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QNETD_MIN_IPC_RECEIVE_SEND_SIZE || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->ipc_max_receive_size = (size_t)tmpll;
+ } else if (strcasecmp(option, "ipc_max_send_size") == 0) {
+ tmpll = strtoll(value, &ep, 10);
+ if (tmpll < QNETD_MIN_IPC_RECEIVE_SEND_SIZE || errno != 0 || *ep != '\0') {
+ return (-2);
+ }
+
+ settings->ipc_max_send_size = (size_t)tmpll;
+ } else {
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNETD_ADVANCED_SETTINGS_H_
+#define _QNETD_ADVANCED_SETTINGS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qnetd_advanced_settings {
+ int listen_backlog;
+ size_t max_client_send_buffers;
+ size_t max_client_send_size;
+ size_t max_client_receive_size;
+ char *nss_db_dir;
+ char *cert_nickname;
+ uint32_t heartbeat_interval_min;
+ uint32_t heartbeat_interval_max;
+ uint8_t dpd_enabled;
+ uint32_t dpd_interval;
+ char *lock_file;
+ char *local_socket_file;
+ int local_socket_backlog;
+ size_t ipc_max_clients;
+ size_t ipc_max_send_size;
+ size_t ipc_max_receive_size;
+};
+
+extern int qnetd_advanced_settings_init(struct qnetd_advanced_settings *settings);
+
+extern int qnetd_advanced_settings_set(struct qnetd_advanced_settings *settings,
+ const char *option, const char *value);
+
+extern void qnetd_advanced_settings_destroy(struct qnetd_advanced_settings *settings);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_ADVANCED_SETTINGS_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Christine Caulfield (ccaulfie@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.
+ */
+
+
+/*
+ * This is a simple 'last man standing' algorithm for 2 node clusters
+ *
+ * If the node is the only one left in the cluster that can see the
+ * qdevice server then we return a vote.
+ *
+ * If more than one node can see the qdevice server but the nodes can't
+ * see each other then we return a vote to the nominated tie_breaker node
+ *
+ * If there are more than two nodes, then we don't return a vote.
+ * this is not our job.
+ */
+
+#include <sys/types.h>
+
+#include <string.h>
+#include <limits.h>
+
+#include "qnetd-algo-2nodelms.h"
+#include "qnetd-log.h"
+#include "qnetd-cluster-list.h"
+#include "qnetd-algo-utils.h"
+#include "utils.h"
+
+struct qnetd_algo_2nodelms_info {
+ int num_config_nodes;
+ enum tlv_vote last_result;
+};
+
+enum tlv_reply_error_code
+qnetd_algo_2nodelms_client_init(struct qnetd_client *client)
+{
+ struct qnetd_algo_2nodelms_info *info;
+
+ info = malloc(sizeof(struct qnetd_algo_2nodelms_info));
+ if (!info) {
+ return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
+ }
+ client->algorithm_data = info;
+ info->last_result = 0;
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+/*
+ * Called after client sent configuration node list
+ * All client fields are already set. Nodes is actual node list, initial is used
+ * to distinquish between initial node list and changed 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.
+ *
+ * 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 sent back to client)
+ */
+enum tlv_reply_error_code
+qnetd_algo_2nodelms_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)
+{
+ struct node_list_entry *node_info;
+ struct qnetd_algo_2nodelms_info *info = client->algorithm_data;
+ int node_count = 0;
+
+ /* Check this is a 2 node cluster */
+ TAILQ_FOREACH(node_info, nodes, entries) {
+ node_count++;
+ }
+ info->num_config_nodes = node_count;
+ qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s config_list has %d nodes", client->cluster_name, node_count);
+
+ if (node_count != 2) {
+ qnetd_log(LOG_INFO, "algo-2nodelms: cluster %s does not have 2 configured nodes, it has %d", client->cluster_name, node_count);
+
+ *result_vote = TLV_VOTE_NACK;
+ return (TLV_REPLY_ERROR_CODE_UNSUPPORTED_DECISION_ALGORITHM);
+ }
+
+ *result_vote = TLV_VOTE_NO_CHANGE;
+
+ 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 sent back to client)
+ */
+
+enum tlv_reply_error_code
+qnetd_algo_2nodelms_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_heuristics heuristics,
+ enum tlv_vote *result_vote)
+{
+ struct node_list_entry *node_info;
+ struct qnetd_client *other_client;
+ struct qnetd_algo_2nodelms_info *info = client->algorithm_data;
+ int node_count = 0;
+ uint32_t low_node_id = UINT32_MAX;
+ uint32_t high_node_id = 0;
+ enum tlv_heuristics other_node_heuristics;
+
+ /* If we're a newcomer and there is another active partition, then we must NACK
+ * to avoid quorum moving to us from already active nodes.
+ */
+ if (info->last_result == 0) {
+ TAILQ_FOREACH(other_client, &client->cluster->client_list, cluster_entries) {
+ struct qnetd_algo_2nodelms_info *other_info = other_client->algorithm_data;
+ if (!tlv_ring_id_eq(ring_id, &other_client->last_ring_id) &&
+ other_info->last_result == TLV_VOTE_ACK) {
+
+ /* Don't save NACK, we need to know subsequently if we haven't been voting */
+ *result_vote = TLV_VOTE_NACK;
+ qnetd_log(LOG_DEBUG, "algo-2nodelms: we are a new partition and another active partition exists. NACK");
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+ }
+ }
+ }
+
+ /* If both nodes are present, then we're OK. return a vote */
+ TAILQ_FOREACH(node_info, nodes, entries) {
+ node_count++;
+ }
+
+ qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s (client %p nodeid "UTILS_PRI_NODE_ID") membership list has %d member nodes (ring ID "UTILS_PRI_RING_ID")", client->cluster_name, client, client->node_id, node_count, ring_id->node_id, ring_id->seq);
+
+ if (node_count == 2) {
+ qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s running normally. Both nodes active", client->cluster_name);
+ *result_vote = info->last_result = TLV_VOTE_ACK;
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+ }
+
+ /* Now look for other clients connected from this cluster that can't see us any more */
+ node_count = 0;
+ other_node_heuristics = TLV_HEURISTICS_UNDEFINED;
+ TAILQ_FOREACH(other_client, &client->cluster->client_list, cluster_entries) {
+ node_count++;
+
+ qnetd_log(LOG_DEBUG, "algo-2nodelms: seen nodeid "UTILS_PRI_NODE_ID" on client %p (ring ID "UTILS_PRI_RING_ID")", other_client->node_id, other_client, other_client->last_ring_id.node_id, other_client->last_ring_id.seq);
+ if (other_client->node_id < low_node_id) {
+ low_node_id = other_client->node_id;
+ }
+ if (other_client->node_id > high_node_id) {
+ high_node_id = other_client->node_id;
+ }
+ if (other_client != client) {
+ other_node_heuristics = other_client->last_heuristics;
+ }
+ }
+ qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s %d nodes running independently", client->cluster_name, node_count);
+
+ /* Only 1 node alive .. allow it to continue */
+ if (node_count == 1) {
+ qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s running on 'last-man'", client->cluster_name);
+ *result_vote = info->last_result = TLV_VOTE_ACK;
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+ }
+
+ /*
+ * Both nodes are alive.
+ * Check their heuristics.
+ */
+ if (tlv_heuristics_cmp(heuristics, other_node_heuristics) > 0) {
+ *result_vote = info->last_result = TLV_VOTE_ACK;
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+ } else if (tlv_heuristics_cmp(heuristics, other_node_heuristics) < 0) {
+ *result_vote = info->last_result = TLV_VOTE_NACK;
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+ }
+
+ /* Heuristics are equal -> Only give a vote to the nominated tie-breaker node */
+ switch (client->tie_breaker.mode) {
+
+ case TLV_TIE_BREAKER_MODE_LOWEST:
+ if (client->node_id == low_node_id) {
+ qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s running on low node-id %d", client->cluster_name, low_node_id);
+ *result_vote = info->last_result = TLV_VOTE_ACK;
+ }
+ else {
+ qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s node-id %d denied vote because low nodeid %d is active", client->cluster_name, client->node_id, low_node_id);
+ *result_vote = info->last_result = TLV_VOTE_NACK;
+ }
+ break;
+ case TLV_TIE_BREAKER_MODE_HIGHEST:
+ if (client->node_id == high_node_id) {
+ qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s running on high node-id %d", client->cluster_name, high_node_id);
+ *result_vote = info->last_result = TLV_VOTE_ACK;
+ }
+ else {
+ qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s node-id %d denied vote because high nodeid %d is active", client->cluster_name, client->node_id, high_node_id);
+ *result_vote = info->last_result = TLV_VOTE_NACK;
+ }
+ break;
+ case TLV_TIE_BREAKER_MODE_NODE_ID:
+ if (client->node_id == client->tie_breaker.node_id) {
+ qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s running on nominated tie-breaker node %d", client->cluster_name, client->tie_breaker.node_id);
+ *result_vote = info->last_result = TLV_VOTE_ACK;
+ }
+ else {
+ qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s node-id %d denied vote because nominated tie-breaker nodeid %d is active", client->cluster_name, client->node_id, client->tie_breaker.node_id);
+ *result_vote = info->last_result = TLV_VOTE_NACK;
+ }
+ break;
+ default:
+ qnetd_log(LOG_DEBUG, "algo-2nodelms: cluster %s node-id %d denied vote because tie-breaker option is invalid: %d", client->cluster_name, client->node_id, client->tie_breaker.mode);
+ *result_vote = info->last_result = TLV_VOTE_NACK;
+ }
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+enum tlv_reply_error_code
+qnetd_algo_2nodelms_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)
+{
+
+ *result_vote = TLV_VOTE_NO_CHANGE;
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+/*
+ * Called after client disconnect. Client structure is still existing (and it's part
+ * of a client->cluster), but it is destroyed (and removed from cluster) right after
+ * this callback finishes. Callback is used mainly for destroing client->algorithm_data.
+ */
+void
+qnetd_algo_2nodelms_client_disconnect(struct qnetd_client *client, int server_going_down)
+{
+ qnetd_log(LOG_INFO, "algo-2nodelms: Client %p (cluster %s, node_id "UTILS_PRI_NODE_ID") "
+ "disconnect", client, client->cluster_name, client->node_id);
+
+ qnetd_log(LOG_INFO, "algo-2nodelms: server going down %u", server_going_down);
+
+ free(client->algorithm_data);
+}
+
+/*
+ * Called after client sent ask for vote message. This is usually happening after server
+ * replied TLV_VOTE_ASK_LATER.
+ */
+enum tlv_reply_error_code
+qnetd_algo_2nodelms_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_seq_num,
+ enum tlv_vote *result_vote)
+{
+ struct qnetd_algo_2nodelms_info *info = client->algorithm_data;
+
+ qnetd_log(LOG_INFO, "algo-2nodelms: Client %p (cluster %s, node_id "UTILS_PRI_NODE_ID") "
+ "asked for a vote", client, client->cluster_name, client->node_id);
+
+ if (info->last_result == 0) {
+ *result_vote = TLV_VOTE_ASK_LATER;
+ }
+ else {
+ *result_vote = info->last_result;
+ }
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+enum tlv_reply_error_code
+qnetd_algo_2nodelms_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num)
+{
+
+ qnetd_log(LOG_INFO, "algo-2nodelms: Client %p (cluster %s, node_id "UTILS_PRI_NODE_ID") "
+ "replied back to vote info message", client, client->cluster_name, client->node_id);
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+enum tlv_reply_error_code
+qnetd_algo_2nodelms_heuristics_change_received(struct qnetd_client *client, uint32_t msg_seq_num,
+ enum tlv_heuristics heuristics, enum tlv_vote *result_vote)
+{
+
+ qnetd_log(LOG_INFO, "algo-2nodelms: heuristics change is not supported.");
+
+ *result_vote = TLV_VOTE_NO_CHANGE;
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+enum tlv_reply_error_code
+qnetd_algo_2nodelms_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_2nodelms = {
+ .init = qnetd_algo_2nodelms_client_init,
+ .config_node_list_received = qnetd_algo_2nodelms_config_node_list_received,
+ .membership_node_list_received = qnetd_algo_2nodelms_membership_node_list_received,
+ .quorum_node_list_received = qnetd_algo_2nodelms_quorum_node_list_received,
+ .client_disconnect = qnetd_algo_2nodelms_client_disconnect,
+ .ask_for_vote_received = qnetd_algo_2nodelms_ask_for_vote_received,
+ .vote_info_reply_received = qnetd_algo_2nodelms_vote_info_reply_received,
+ .heuristics_change_received = qnetd_algo_2nodelms_heuristics_change_received,
+ .timer_callback = qnetd_algo_2nodelms_timer_callback,
+};
+
+enum tlv_reply_error_code qnetd_algo_2nodelms_register()
+{
+ return qnetd_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_2NODELMS, &qnetd_algo_2nodelms);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QNETD_ALGO_2NODELMS_H_
+#define _QNETD_ALGO_2NODELMS_H_
+
+#include "qnetd-algorithm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern enum tlv_reply_error_code qnetd_algo_2nodelms_client_init(struct qnetd_client *client);
+
+extern enum tlv_reply_error_code qnetd_algo_2nodelms_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);
+
+extern enum tlv_reply_error_code qnetd_algo_2nodelms_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_heuristics heuristics, enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algo_2nodelms_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);
+
+extern void qnetd_algo_2nodelms_client_disconnect(
+ struct qnetd_client *client, int server_going_down);
+
+extern enum tlv_reply_error_code qnetd_algo_2nodelms_ask_for_vote_received(
+ struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algo_2nodelms_vote_info_reply_received(
+ struct qnetd_client *client, uint32_t msg_seq_num);
+
+extern enum tlv_reply_error_code qnetd_algo_2nodelms_heuristics_change_received(
+ struct qnetd_client *client, uint32_t msg_seq_num,
+ enum tlv_heuristics heuristics, enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algo_2nodelms_timer_callback(
+ struct qnetd_client *client, int *reschedule_timer,
+ int *send_vote, enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algo_2nodelms_register(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_ALGO_2NODELMS_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <sys/types.h>
+
+#include <string.h>
+
+#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_preferred_partition(const struct qnetd_client *client,
+ const struct node_list *config_node_list, const struct node_list *membership_node_list)
+{
+ uint32_t preferred_node_id;
+ struct node_list_entry *node_entry;
+ int case_processed;
+
+ preferred_node_id = 0;
+ case_processed = 0;
+
+ switch (client->tie_breaker.mode) {
+ case TLV_TIE_BREAKER_MODE_LOWEST:
+ node_entry = TAILQ_FIRST(config_node_list);
+
+ preferred_node_id = node_entry->node_id;
+
+ TAILQ_FOREACH(node_entry, config_node_list, entries) {
+ if (node_entry->node_id < preferred_node_id) {
+ preferred_node_id = node_entry->node_id;
+ }
+ }
+ case_processed = 1;
+ break;
+ case TLV_TIE_BREAKER_MODE_HIGHEST:
+ node_entry = TAILQ_FIRST(config_node_list);
+
+ preferred_node_id = node_entry->node_id;
+
+ TAILQ_FOREACH(node_entry, config_node_list, entries) {
+ if (node_entry->node_id > preferred_node_id) {
+ preferred_node_id = node_entry->node_id;
+ }
+ }
+ case_processed = 1;
+ break;
+ case TLV_TIE_BREAKER_MODE_NODE_ID:
+ preferred_node_id = client->tie_breaker.node_id;
+ case_processed = 1;
+ break;
+ }
+
+ if (!case_processed) {
+ qnetd_log(LOG_CRIT, "qnetd_algo_ffsplit_is_preferred_partition unprocessed "
+ "tie_breaker.mode");
+ exit(1);
+ }
+
+ return (node_list_find_node_id(membership_node_list, preferred_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 void
+qnetd_algo_ffsplit_get_active_clients_in_partition_stats(const struct qnetd_client *client,
+ const struct node_list *client_membership_node_list, enum tlv_heuristics client_heuristics,
+ size_t *no_clients, size_t *no_heuristics_pass, size_t *no_heuristics_fail)
+{
+ const struct node_list_entry *iter_node;
+ const struct qnetd_client *iter_client;
+ enum tlv_heuristics iter_heuristics;
+
+ *no_clients = 0;
+ *no_heuristics_pass = 0;
+ *no_heuristics_fail = 0;
+
+ if (client == NULL || client_membership_node_list == NULL) {
+ return ;
+ }
+
+ TAILQ_FOREACH(iter_node, client_membership_node_list, entries) {
+ iter_client = qnetd_cluster_find_client_by_node_id(client->cluster,
+ iter_node->node_id);
+ if (iter_client != NULL) {
+ (*no_clients)++;
+
+ if (iter_client == client) {
+ iter_heuristics = client_heuristics;
+ } else {
+ iter_heuristics = iter_client->last_heuristics;
+ }
+
+ if (iter_heuristics == TLV_HEURISTICS_PASS) {
+ (*no_heuristics_pass)++;
+ } else if (iter_heuristics == TLV_HEURISTICS_FAIL) {
+ (*no_heuristics_fail)++;
+ }
+ }
+ }
+}
+
+/*
+ * 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,
+ enum tlv_heuristics heuristics_1,
+ const struct qnetd_client *client2,
+ const struct node_list *config_node_list2, const struct node_list *membership_node_list2,
+ enum tlv_heuristics heuristics_2)
+{
+ size_t part1_active_clients, part2_active_clients;
+ size_t part1_no_heuristics_pass, part2_no_heuristics_pass;
+ size_t part1_no_heuristics_fail, part2_no_heuristics_fail;
+ size_t part1_score, part2_score;
+
+ int res;
+
+ res = -1;
+
+ 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) {
+ res = 1; goto exit_res;
+ } else {
+ res = 0; goto exit_res;
+ }
+ } else {
+ if (node_list_size(membership_node_list1) > node_list_size(config_node_list1) / 2) {
+ res = 1; goto exit_res;
+ } else if (node_list_size(membership_node_list1) < node_list_size(config_node_list1) / 2) {
+ res = 0; goto exit_res;
+ }
+
+ /*
+ * 50:50 split
+ */
+
+ /*
+ * Check how many active clients are in partitions and heuristics results
+ */
+ qnetd_algo_ffsplit_get_active_clients_in_partition_stats(client1,
+ membership_node_list1, heuristics_1, &part1_active_clients,
+ &part1_no_heuristics_pass, &part1_no_heuristics_fail);
+ qnetd_algo_ffsplit_get_active_clients_in_partition_stats(client2,
+ membership_node_list2, heuristics_2, &part2_active_clients,
+ &part2_no_heuristics_pass, &part2_no_heuristics_fail);
+
+ /*
+ * Partition can contain clients with one of 4 states:
+ * 1. Not-connected to qnetd (D)
+ * 2. Disabled heuristics (U)
+ * 3. Enabled heuristics with pass result (P)
+ * 4. Enabled heuristics with fail result (F)
+ *
+ * The question is, what partition should get vote is kind of hard with
+ * so much states. Following simple "score" seems to be good enough, but may
+ * be suboptimal in some cases. As and example let's say there are
+ * 2 partitions with 4 nodes each. Partition 1 looks like PDDD and partition 2 looks
+ * like FUUU. Partition 1 score is 1 + (1 - 0), partition 2 score is 4 + (0 - 1).
+ * Partition 2 wins eventho there is one processor with failed heuristics.
+ */
+ part1_score = part1_active_clients + (part1_no_heuristics_pass - part1_no_heuristics_fail);
+ part2_score = part2_active_clients + (part2_no_heuristics_pass - part2_no_heuristics_fail);
+
+ if (part1_score > part2_score) {
+ res = 1; goto exit_res;
+ } else if (part1_score < part2_score) {
+ res = 0; goto exit_res;
+ }
+
+ if (part1_active_clients > part2_active_clients) {
+ res = 1; goto exit_res;
+ } else if (part1_active_clients < part2_active_clients) {
+ res = 0; goto exit_res;
+ }
+
+ /*
+ * Number of active clients in both partitions equals. Use tie-breaker.
+ */
+
+ if (qnetd_algo_ffsplit_is_preferred_partition(client1, config_node_list1,
+ membership_node_list1)) {
+ res = 1; goto exit_res;
+ } else {
+ res = 0; goto exit_res;
+ }
+ }
+
+exit_res:
+ if (res == -1) {
+ qnetd_log(LOG_CRIT, "qnetd_algo_ffsplit_partition_cmp unhandled case");
+ exit(1);
+ /* NOTREACHED */
+ }
+
+ return (res);
+}
+
+/*
+ * 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,
+ enum tlv_heuristics client_heuristics)
+{
+ 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;
+ enum tlv_heuristics iter_heuristics, best_heuristics;
+
+ best_client = NULL;
+ best_config_node_list = best_membership_node_list = NULL;
+ best_heuristics = TLV_HEURISTICS_UNDEFINED;
+
+ /*
+ * 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;
+ iter_heuristics = client_heuristics;
+ } else {
+ iter_config_node_list = &iter_client->configuration_node_list;
+ iter_membership_node_list = &iter_client->last_membership_node_list;
+ iter_heuristics = iter_client->last_heuristics;
+ }
+
+ if (qnetd_algo_ffsplit_partition_cmp(iter_client, iter_config_node_list,
+ iter_membership_node_list, iter_heuristics, best_client, best_config_node_list,
+ best_membership_node_list, best_heuristics) > 0) {
+ best_client = iter_client;
+ best_config_node_list = iter_config_node_list;
+ best_membership_node_list = iter_membership_node_list;
+ best_heuristics = iter_heuristics;
+ }
+ }
+
+ 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 sent (nodes in QNETD_ALGO_FFSPLIT_CLIENT_STATE_SENDING_ACK state),
+ * otherwise only NACK votes are sent (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, enum tlv_heuristics client_heuristics)
+{
+ 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, client_heuristics);
+ 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, client->last_heuristics);
+ }
+
+ 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_heuristics heuristics, 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, heuristics);
+ }
+
+ 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,
+ client->last_heuristics);
+
+ 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_heuristics_change_received(struct qnetd_client *client, uint32_t msg_seq_num,
+ enum tlv_heuristics heuristics, enum tlv_vote *result_vote)
+{
+
+ if (node_list_size(&client->configuration_node_list) == 0 ||
+ node_list_size(&client->last_membership_node_list) == 0) {
+ /*
+ * Config or membership 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, &client->last_ring_id,
+ &client->configuration_node_list, &client->last_membership_node_list,
+ heuristics);
+ }
+
+ 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,
+ .heuristics_change_received = qnetd_algo_ffsplit_heuristics_change_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));
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QNETD_ALGO_FFSPLIT_H_
+#define _QNETD_ALGO_FFSPLIT_H_
+
+#include "qnetd-algorithm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern enum tlv_reply_error_code qnetd_algo_ffsplit_client_init(struct qnetd_client *client);
+
+extern 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);
+
+extern 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_heuristics heuristics, enum tlv_vote *result_vote);
+
+extern 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);
+
+extern void qnetd_algo_ffsplit_client_disconnect(
+ struct qnetd_client *client, int server_going_down);
+
+extern 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);
+
+extern enum tlv_reply_error_code qnetd_algo_ffsplit_vote_info_reply_received(
+ struct qnetd_client *client, uint32_t msg_seq_num);
+
+extern enum tlv_reply_error_code qnetd_algo_ffsplit_heuristics_change_received(
+ struct qnetd_client *client, uint32_t msg_seq_num,
+ enum tlv_heuristics heuristics, enum tlv_vote *result_vote);
+
+extern 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);
+
+extern enum tlv_reply_error_code qnetd_algo_ffsplit_register(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_ALGO_FFSPLIT_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Christine Caulfield (ccaulfie@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.
+ */
+
+
+/*
+ * This is a 'last man standing' algorithm for 2+ node clusters
+ *
+ * If the node is the only one left in the cluster that can see the
+ * qdevice server then we return a vote.
+ *
+ * If more than one node can see the qdevice server but some nodes can't
+ * see each other then we divide the cluster up into 'partitions' based on
+ * their ring_id and return a vote to nodes in the partition that contains
+ * a nominated nodeid. (lowest, highest, etc)
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <string.h>
+#include <limits.h>
+
+#include "qnetd-algo-lms.h"
+#include "qnetd-log.h"
+#include "qnetd-cluster-list.h"
+#include "qnetd-algo-utils.h"
+#include "qnetd-client-algo-timer.h"
+#include "utils.h"
+
+struct qnetd_algo_lms_info {
+ int num_config_nodes;
+ enum tlv_vote last_result;
+ partitions_list_t partition_list;
+};
+
+static enum tlv_reply_error_code do_lms_algorithm(struct qnetd_client *client, const struct tlv_ring_id *cur_ring_id, enum tlv_vote *result_vote)
+{
+ struct qnetd_client *other_client;
+ struct qnetd_algo_lms_info *info = client->algorithm_data;
+ struct qnetd_algo_partition *cur_partition;
+ struct qnetd_algo_partition *largest_partition;
+ struct qnetd_algo_partition *best_score_partition;
+ const struct tlv_ring_id *ring_id = cur_ring_id;
+ int num_partitions;
+ int joint_leader;
+
+ /* We are running the algorithm, don't do it again unless we say so */
+ qnetd_client_algo_timer_abort(client);
+
+ if (qnetd_algo_all_ring_ids_match(client, ring_id) == -1) {
+ qnetd_log(LOG_DEBUG, "algo-lms: nodeid %d: ring ID (" UTILS_PRI_RING_ID ") not unique in this membership, waiting",
+ client->node_id, ring_id->node_id, ring_id->seq);
+
+ qnetd_client_algo_timer_schedule(client);
+ *result_vote = info->last_result = TLV_VOTE_WAIT_FOR_REPLY;
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+ }
+
+ /* Create and count the number of separate partitions */
+ if ( (num_partitions = qnetd_algo_create_partitions(client, &info->partition_list, ring_id)) == -1) {
+ qnetd_log(LOG_DEBUG, "algo-lms: Error creating partition list");
+ return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
+ }
+
+ /* This can happen if we are first on the block */
+ if (num_partitions == 0) {
+ qnetd_log(LOG_DEBUG, "algo-lms: No partitions found");
+
+ qnetd_client_algo_timer_schedule(client);
+ *result_vote = info->last_result = TLV_VOTE_WAIT_FOR_REPLY;
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+ }
+
+ qnetd_algo_dump_partitions(&info->partition_list);
+
+ /* Only 1 partition - let votequorum sort it out */
+ if (num_partitions == 1) {
+ qnetd_log(LOG_DEBUG, "algo-lms: Only 1 partition. This is votequorum's problem, not ours");
+ qnetd_algo_free_partitions(&info->partition_list);
+ *result_vote = info->last_result = TLV_VOTE_ACK;
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+ }
+
+
+ /* If we're a newcomer and there is another active partition, then we must NACK
+ * to avoid quorum moving to us from already active nodes.
+ */
+ if (info->last_result == 0) {
+ TAILQ_FOREACH(other_client, &client->cluster->client_list, cluster_entries) {
+ struct qnetd_algo_lms_info *other_info = other_client->algorithm_data;
+ if (!tlv_ring_id_eq(ring_id, &other_client->last_ring_id) &&
+ other_info->last_result == TLV_VOTE_ACK) {
+ qnetd_algo_free_partitions(&info->partition_list);
+
+ /* Don't save NACK, we need to know subsequently if we haven't been voting */
+ *result_vote = TLV_VOTE_NACK;
+ qnetd_log(LOG_DEBUG, "algo-lms: we are a new partition and another active partition exists. NACK");
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+ }
+ }
+ }
+
+ /*
+ * Find the partition with highest score
+ */
+ best_score_partition = NULL;
+ TAILQ_FOREACH(cur_partition, &info->partition_list, entries) {
+ if (!best_score_partition ||
+ best_score_partition->score < cur_partition->score) {
+ best_score_partition = cur_partition;
+ }
+ }
+ qnetd_log(LOG_DEBUG, "algo-lms: best score partition is (" UTILS_PRI_RING_ID ") with score %d",
+ best_score_partition->ring_id.node_id, best_score_partition->ring_id.seq, best_score_partition->score);
+
+ /* Now check if it's really the highest score, and not just the joint-highest */
+ joint_leader = 0;
+ TAILQ_FOREACH(cur_partition, &info->partition_list, entries) {
+ if (best_score_partition != cur_partition &&
+ best_score_partition->score == cur_partition->score) {
+ joint_leader = 1;
+ }
+ }
+
+ if (!joint_leader) {
+ /* Partition with highest score is unique, allow us to run if we're in that partition. */
+ if (tlv_ring_id_eq(&best_score_partition->ring_id, ring_id)) {
+ qnetd_log(LOG_DEBUG, "algo-lms: We are in the best score partition. ACK");
+ *result_vote = info->last_result = TLV_VOTE_ACK;
+ }
+ else {
+ qnetd_log(LOG_DEBUG, "algo-lms: We are NOT in the best score partition. NACK");
+ *result_vote = info->last_result = TLV_VOTE_NACK;
+ }
+
+ qnetd_algo_free_partitions(&info->partition_list);
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+ }
+
+ /*
+ * There are multiple partitions with same score. Find the largest partition
+ */
+ largest_partition = NULL;
+ TAILQ_FOREACH(cur_partition, &info->partition_list, entries) {
+ if (!largest_partition ||
+ largest_partition->num_nodes < cur_partition->num_nodes) {
+ largest_partition = cur_partition;
+ }
+ }
+
+ qnetd_log(LOG_DEBUG, "algo-lms: largest partition is (" UTILS_PRI_RING_ID ") with %d nodes",
+ largest_partition->ring_id.node_id, largest_partition->ring_id.seq, largest_partition->num_nodes);
+
+ /* Now check if it's really the largest, and not just the joint-largest */
+ joint_leader = 0;
+ TAILQ_FOREACH(cur_partition, &info->partition_list, entries) {
+ if (largest_partition != cur_partition &&
+ largest_partition->num_nodes == cur_partition->num_nodes) {
+ joint_leader = 1;
+ }
+ }
+
+ if (!joint_leader) {
+ /* Largest partition is unique, allow us to run if we're in that partition. */
+ if (tlv_ring_id_eq(&largest_partition->ring_id, ring_id)) {
+ qnetd_log(LOG_DEBUG, "algo-lms: We are in the largest partition. ACK");
+ *result_vote = info->last_result = TLV_VOTE_ACK;
+ }
+ else {
+ qnetd_log(LOG_DEBUG, "algo-lms: We are NOT in the largest partition. NACK");
+ *result_vote = info->last_result = TLV_VOTE_NACK;
+ }
+ }
+ else {
+ uint32_t tb_node_id;
+ struct tlv_ring_id tb_node_ring_id = {0LL, 0};
+
+ /* Look for the tie-breaker node */
+ if (client->tie_breaker.mode == TLV_TIE_BREAKER_MODE_LOWEST) {
+ tb_node_id = INT_MAX;
+ }
+ else if (client->tie_breaker.mode == TLV_TIE_BREAKER_MODE_HIGHEST) {
+ tb_node_id = 0;
+ }
+ else if (client->tie_breaker.mode == TLV_TIE_BREAKER_MODE_NODE_ID) {
+ tb_node_id = client->tie_breaker.node_id;
+ }
+ else {
+ qnetd_log(LOG_DEBUG, "algo-lms: denied vote because tie-breaker option is invalid: %d",
+ client->tie_breaker.mode);
+ tb_node_id = -1;
+ }
+
+ /* Find the tie_breaker node */
+ TAILQ_FOREACH(other_client, &client->cluster->client_list, cluster_entries) {
+ switch (client->tie_breaker.mode) {
+
+ case TLV_TIE_BREAKER_MODE_LOWEST:
+ if (other_client->node_id < tb_node_id) {
+ tb_node_id = other_client->node_id;
+ memcpy(&tb_node_ring_id, &other_client->last_ring_id, sizeof(struct tlv_ring_id));
+ qnetd_log(LOG_DEBUG, "algo-lms: Looking for low node ID. found %d (" UTILS_PRI_RING_ID ")",
+ tb_node_id, tb_node_ring_id.node_id, tb_node_ring_id.seq);
+ }
+ break;
+
+ case TLV_TIE_BREAKER_MODE_HIGHEST:
+ if (other_client->node_id > tb_node_id) {
+ tb_node_id = other_client->node_id;
+ memcpy(&tb_node_ring_id, &other_client->last_ring_id, sizeof(struct tlv_ring_id));
+ qnetd_log(LOG_DEBUG, "algo-lms: Looking for high node ID. found %d (" UTILS_PRI_RING_ID ")",
+ tb_node_id, tb_node_ring_id.node_id, tb_node_ring_id.seq);
+ }
+ break;
+ case TLV_TIE_BREAKER_MODE_NODE_ID:
+ if (client->tie_breaker.node_id == client->node_id) {
+ memcpy(&tb_node_ring_id, &other_client->last_ring_id, sizeof(struct tlv_ring_id));
+ qnetd_log(LOG_DEBUG, "algo-lms: Looking for nominated node ID. found %d (" UTILS_PRI_RING_ID ")",
+ tb_node_id, tb_node_ring_id.node_id, tb_node_ring_id.seq);
+
+ }
+ break;
+ default:
+ qnetd_log(LOG_DEBUG, "algo-lms: denied vote because tie-breaker option is invalid: %d",
+ client->tie_breaker.mode);
+ memset(&tb_node_ring_id, 0, sizeof(struct tlv_ring_id));
+ }
+ }
+
+ if (client->node_id == tb_node_id || tlv_ring_id_eq(&tb_node_ring_id, ring_id)) {
+ qnetd_log(LOG_DEBUG, "algo-lms: We are in the same partition (" UTILS_PRI_RING_ID ") as tie-breaker node id %d. ACK",
+ tb_node_ring_id.node_id, tb_node_ring_id.seq, tb_node_id);
+ *result_vote = info->last_result = TLV_VOTE_ACK;
+ }
+ else {
+ qnetd_log(LOG_DEBUG, "algo-lms: We are NOT in the same partition (" UTILS_PRI_RING_ID ") as tie-breaker node id %d. NACK",
+ tb_node_ring_id.node_id, tb_node_ring_id.seq, tb_node_id);
+ *result_vote = info->last_result = TLV_VOTE_NACK;
+ }
+ }
+
+ qnetd_algo_free_partitions(&info->partition_list);
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+enum tlv_reply_error_code
+qnetd_algo_lms_client_init(struct qnetd_client *client)
+{
+ struct qnetd_algo_lms_info *info;
+
+ info = malloc(sizeof(struct qnetd_algo_lms_info));
+ if (!info) {
+ return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
+ }
+
+ memset(info, 0, sizeof(*info));
+ client->algorithm_data = info;
+ info->last_result = 0; /* status unknown, or NEW */
+ TAILQ_INIT(&info->partition_list);
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+/*
+ * We got the config node list. Simply count the number of available nodes
+ * and wait for the quorum list.
+ */
+enum tlv_reply_error_code
+qnetd_algo_lms_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)
+{
+ struct node_list_entry *node_info;
+ struct qnetd_algo_lms_info *info = client->algorithm_data;
+ int node_count = 0;
+
+ TAILQ_FOREACH(node_info, nodes, entries) {
+ node_count++;
+ }
+ info->num_config_nodes = node_count;
+ qnetd_log(LOG_DEBUG, "algo-lms: cluster %s config_list has %d nodes", client->cluster_name, node_count);
+
+ *result_vote = TLV_VOTE_NO_CHANGE;
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+/*
+ * membership node list. This is where we get to work.
+ */
+
+enum tlv_reply_error_code
+qnetd_algo_lms_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_heuristics heuristics, enum tlv_vote *result_vote)
+{
+ qnetd_log(LOG_DEBUG, " ");
+ qnetd_log(LOG_DEBUG, "algo-lms: membership list from node %d partition (" UTILS_PRI_RING_ID ")", client->node_id, ring_id->node_id, ring_id->seq);
+
+ return do_lms_algorithm(client, ring_id, result_vote);
+}
+
+/*
+ * The quorum node list is received after corosync has decided which nodes are in the cluster.
+ * We run our algorithm again to be sure that things still match. By this time we will (or should)
+ * all know the current ring_id (not guaranteed when the membership list is received). So this
+ * might be the most reliable return.
+ */
+enum tlv_reply_error_code
+qnetd_algo_lms_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)
+{
+ qnetd_log(LOG_DEBUG, " ");
+ qnetd_log(LOG_DEBUG, "algo-lms: quorum node list from node %d partition (" UTILS_PRI_RING_ID ")", client->node_id, client->last_ring_id.node_id, client->last_ring_id.seq);
+ return do_lms_algorithm(client, &client->last_ring_id, result_vote);
+}
+
+/*
+ * Called after client disconnect. Client structure is still existing (and it's part
+ * of a client->cluster), but it is destroyed (and removed from cluster) right after
+ * this callback finishes. Callback is used mainly for destroing client->algorithm_data.
+ */
+void
+qnetd_algo_lms_client_disconnect(struct qnetd_client *client, int server_going_down)
+{
+ qnetd_log(LOG_DEBUG, "algo-lms: Client %p (cluster %s, node_id "UTILS_PRI_NODE_ID") "
+ "disconnect", client, client->cluster_name, client->node_id);
+
+ qnetd_log(LOG_INFO, "algo-lms: server going down %u", server_going_down);
+
+ free(client->algorithm_data);
+}
+
+/*
+ * Called after client sent ask for vote message. This is usually happening after server
+ * replied TLV_VOTE_WAIT_FOR_REPLY.
+ */
+enum tlv_reply_error_code
+qnetd_algo_lms_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_seq_num,
+ enum tlv_vote *result_vote)
+{
+ qnetd_log(LOG_DEBUG, " ");
+ qnetd_log(LOG_DEBUG, "algo-lms: Client %p (cluster %s, node_id "UTILS_PRI_NODE_ID") "
+ "asked for a vote", client, client->cluster_name, client->node_id);
+
+ return do_lms_algorithm(client, &client->last_ring_id, result_vote);
+}
+
+enum tlv_reply_error_code
+qnetd_algo_lms_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num)
+{
+ qnetd_log(LOG_DEBUG, "algo-lms: Client %p (cluster %s, node_id "UTILS_PRI_NODE_ID") "
+ "replied back to vote info message", client, client->cluster_name, client->node_id);
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+enum tlv_reply_error_code
+qnetd_algo_lms_heuristics_change_received(struct qnetd_client *client, uint32_t msg_seq_num,
+ enum tlv_heuristics heuristics, enum tlv_vote *result_vote)
+{
+
+ qnetd_log(LOG_INFO, "algo-lms: heuristics change is not supported.");
+
+ *result_vote = TLV_VOTE_NO_CHANGE;
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+enum tlv_reply_error_code
+qnetd_algo_lms_timer_callback(struct qnetd_client *client, int *reschedule_timer,
+ int *send_vote, enum tlv_vote *result_vote)
+{
+ enum tlv_reply_error_code ret;
+
+ qnetd_log(LOG_DEBUG, "algo-lms: Client %p (cluster %s, node_id "UTILS_PRI_NODE_ID") "
+ "Timer callback", client, client->cluster_name, client->node_id);
+
+ ret = do_lms_algorithm(client, &client->last_ring_id, result_vote);
+
+ if (ret == TLV_REPLY_ERROR_CODE_NO_ERROR &&
+ (*result_vote == TLV_VOTE_ACK || *result_vote == TLV_VOTE_NACK)) {
+ *send_vote = 1;
+ }
+
+ if (ret == TLV_REPLY_ERROR_CODE_NO_ERROR &&
+ *result_vote == TLV_VOTE_WAIT_FOR_REPLY) {
+ /*
+ * Reschedule was called in the do_lms_algorithm but algo_timer is
+ * not stack based so there can only be one. So if do_lms aborted
+ * the active timer, and scheduled it again the timer would be aborted
+ * if reschedule_timer was not set.
+ */
+ *reschedule_timer = 1;
+ }
+
+ return ret;
+}
+
+static struct qnetd_algorithm qnetd_algo_lms = {
+ .init = qnetd_algo_lms_client_init,
+ .config_node_list_received = qnetd_algo_lms_config_node_list_received,
+ .membership_node_list_received = qnetd_algo_lms_membership_node_list_received,
+ .quorum_node_list_received = qnetd_algo_lms_quorum_node_list_received,
+ .client_disconnect = qnetd_algo_lms_client_disconnect,
+ .ask_for_vote_received = qnetd_algo_lms_ask_for_vote_received,
+ .vote_info_reply_received = qnetd_algo_lms_vote_info_reply_received,
+ .heuristics_change_received = qnetd_algo_lms_heuristics_change_received,
+ .timer_callback = qnetd_algo_lms_timer_callback,
+};
+
+enum tlv_reply_error_code qnetd_algo_lms_register()
+{
+ return qnetd_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_LMS, &qnetd_algo_lms);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QNETD_ALGO_LMS_H_
+#define _QNETD_ALGO_LMS_H_
+
+#include "qnetd-algorithm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern enum tlv_reply_error_code qnetd_algo_lms_client_init(struct qnetd_client *client);
+
+extern enum tlv_reply_error_code qnetd_algo_lms_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);
+
+extern enum tlv_reply_error_code qnetd_algo_lms_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_heuristics heuristics, enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algo_lms_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);
+
+extern void qnetd_algo_lms_client_disconnect(
+ struct qnetd_client *client, int server_going_down);
+
+extern enum tlv_reply_error_code qnetd_algo_lms_ask_for_vote_received(
+ struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algo_lms_vote_info_reply_received(
+ struct qnetd_client *client, uint32_t msg_seq_num);
+
+extern enum tlv_reply_error_code qnetd_algo_lms_heuristics_change_received(
+ struct qnetd_client *client, uint32_t msg_seq_num,
+ enum tlv_heuristics heuristics, enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algo_lms_timer_callback(
+ struct qnetd_client *client, int *reschedule_timer,
+ int *send_vote, enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algo_lms_register(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_ALGO_LMS_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <sys/types.h>
+
+#include <string.h>
+
+#include "qnetd-algo-test.h"
+#include "qnetd-log.h"
+#include "qnetd-cluster-list.h"
+#include "qnetd-client-send.h"
+#include "qnetd-log-debug.h"
+#include "qnetd-client-algo-timer.h"
+#include "utils.h"
+
+/*
+ * Called right after client sent init message. This happens after initial accept of client,
+ * tls handshake and sending basic information about cluster/client.
+ * Known information:
+ * - client->cluster_name (client->cluster_name_len)
+ * - client->node_id (client->node_id_set = 1)
+ * - client->decision_algorithm
+ * - client->cluster
+ * - client->last_ring_id
+ *
+ * Callback is designed mainly for allocating client->algorithm_data. It's also already
+ * part of the cluster, so can access (alloc) client->cluster->algorithm_data.
+ *
+ * client is initialized qnetd_client structure.
+ *
+ * Return TLV_REPLY_ERROR_CODE_NO_ERROR on success, different TLV_REPLY_ERROR_CODE_*
+ * on failure (error is sent back to client)
+ */
+enum tlv_reply_error_code
+qnetd_algo_test_client_init(struct qnetd_client *client)
+{
+ int *algo_data;
+
+ qnetd_log(LOG_WARNING, "algo-test: Client %s (cluster = '%s', node_id = "
+ UTILS_PRI_NODE_ID") initiated test algorithm. It's not recommended to use test "
+ "algorithm because it can create multiple quorate partitions!", client->addr_str,
+ client->cluster_name, client->node_id);
+
+ qnetd_log(LOG_INFO, "algo-test: client_init");
+
+ client->algorithm_data = malloc(sizeof(int));
+ if (client->algorithm_data == NULL) {
+ return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
+ }
+
+ algo_data = client->algorithm_data;
+ *algo_data = 42;
+
+ if (qnetd_cluster_size(client->cluster) == 1) {
+ /*
+ * First client in the cluster
+ */
+ qnetd_log(LOG_INFO, "algo-test: Initializing cluster->algorithm data");
+
+ client->cluster->algorithm_data = malloc(sizeof(int));
+ if (client->cluster->algorithm_data == NULL) {
+ return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
+ }
+
+ algo_data = client->cluster->algorithm_data;
+ *algo_data = 42;
+ }
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+
+/*
+ * Called after client sent configuration node list
+ * All client fields are already set. Nodes is actual node list, initial is used
+ * for distrinquish between initial node list and changed 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.
+ *
+ * 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_test_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)
+{
+
+ qnetd_log(LOG_INFO, "algo-test: node_list_received");
+
+ *result_vote = TLV_VOTE_NO_CHANGE;
+
+ 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.
+ * ring_id is copied from client votequorum callback.
+ * heuristics is result of client heuristics (or TLV_HEURISTICS_UNDEFINED if heuristics
+ * are disabled or not supported by client)
+ *
+ * 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 sent back to client)
+ */
+
+enum tlv_reply_error_code
+qnetd_algo_test_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_heuristics heuristics, enum tlv_vote *result_vote)
+{
+
+ qnetd_log(LOG_INFO, "algo-test: membership_node_list_received");
+
+ *result_vote = TLV_VOTE_ACK;
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+/*
+ * Called after client sent quorum node list.
+ * All client fields are already set. Nodes is actual node list.
+ * msg_seq_num is 32-bit number set by client.
+ * quorate is copied from client votequorum callback.
+ * Function is just informative. If client vote is required to change, it's possible
+ * to use qnetd_client_send_vote_info.
+ *
+ * Return TLV_REPLY_ERROR_CODE_NO_ERROR on success, different TLV_REPLY_ERROR_CODE_*
+ * on failure (error is sent back to client)
+ */
+enum tlv_reply_error_code
+qnetd_algo_test_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)
+{
+
+ qnetd_log(LOG_INFO, "algo-test: quorum_node_list_received");
+
+ *result_vote = TLV_VOTE_NO_CHANGE;
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+/*
+ * Called after client disconnect. Client structure is still existing (and it's part
+ * of a client->cluster), but it is destroyed (and removed from cluster) right after
+ * this callback finishes. Callback is used mainly for destroing client->algorithm_data.
+ */
+void
+qnetd_algo_test_client_disconnect(struct qnetd_client *client, int server_going_down)
+{
+
+ qnetd_log(LOG_INFO, "algo-test: client_disconnect");
+
+ free(client->algorithm_data);
+
+ if (qnetd_cluster_size(client->cluster) == 1) {
+ /*
+ * Last client in the cluster
+ */
+ qnetd_log(LOG_INFO, "algo-test: Finalizing cluster->algorithm data");
+
+ free(client->cluster->algorithm_data);
+ }
+}
+
+/*
+ * Called after client sent ask for vote message. This is usually happening after server
+ * replied TLV_VOTE_ASK_LATER.
+ */
+enum tlv_reply_error_code
+qnetd_algo_test_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_seq_num,
+ enum tlv_vote *result_vote)
+{
+
+ qnetd_log(LOG_INFO, "algo-test: ask_for_vote_received");
+
+ *result_vote = TLV_VOTE_ACK;
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+enum tlv_reply_error_code
+qnetd_algo_test_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num)
+{
+
+ qnetd_log(LOG_INFO, "algo-test: vote_info_reply_received");
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+/*
+ * Called after client sent heuristics change message.
+ * heuristics is result of client regular heuristics (cannot be TLV_HEURISTICS_UNDEFINED)
+ * Variables client->last_regular_heuristics and client->last_heuristics are updated after
+ * the call.
+ */
+enum tlv_reply_error_code
+qnetd_algo_test_heuristics_change_received(struct qnetd_client *client, uint32_t msg_seq_num,
+ enum tlv_heuristics heuristics, enum tlv_vote *result_vote)
+{
+
+ qnetd_log(LOG_INFO, "algo-test: heuristics_change_received");
+
+ *result_vote = TLV_VOTE_NO_CHANGE;
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+/*
+ * Called as a result of qnetd_client_algo_timer_schedule function call after timeout expires.
+ *
+ * If send_vote is set by callback to non zero value, result_vote must also be set and such vote is
+ * sent to client. Result_vote is ignored if send_vote = 0 (default).
+ *
+ * If reschedule timer (default value = 0) is set to non zero value, callback is called again later
+ * with same timeout as originaly created.
+ *
+ * Return TLV_REPLY_ERROR_CODE_NO_ERROR on success, different TLV_REPLY_ERROR_CODE_*
+ * on failure (error is sent back to client)
+ */
+enum tlv_reply_error_code
+qnetd_algo_test_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_test = {
+ .init = qnetd_algo_test_client_init,
+ .config_node_list_received = qnetd_algo_test_config_node_list_received,
+ .membership_node_list_received = qnetd_algo_test_membership_node_list_received,
+ .quorum_node_list_received = qnetd_algo_test_quorum_node_list_received,
+ .client_disconnect = qnetd_algo_test_client_disconnect,
+ .ask_for_vote_received = qnetd_algo_test_ask_for_vote_received,
+ .vote_info_reply_received = qnetd_algo_test_vote_info_reply_received,
+ .heuristics_change_received = qnetd_algo_test_heuristics_change_received,
+ .timer_callback = qnetd_algo_test_timer_callback,
+};
+
+enum tlv_reply_error_code qnetd_algo_test_register()
+{
+
+ return (qnetd_algorithm_register(TLV_DECISION_ALGORITHM_TYPE_TEST, &qnetd_algo_test));
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QNETD_ALGO_TEST_H_
+#define _QNETD_ALGO_TEST_H_
+
+#include "qnetd-algorithm.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern enum tlv_reply_error_code qnetd_algo_test_client_init(struct qnetd_client *client);
+
+extern enum tlv_reply_error_code qnetd_algo_test_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);
+
+extern enum tlv_reply_error_code qnetd_algo_test_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_heuristics heuristics,
+ enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algo_test_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);
+
+extern void qnetd_algo_test_client_disconnect(
+ struct qnetd_client *client, int server_going_down);
+
+extern enum tlv_reply_error_code qnetd_algo_test_ask_for_vote_received(
+ struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algo_test_vote_info_reply_received(
+ struct qnetd_client *client, uint32_t msg_seq_num);
+
+extern enum tlv_reply_error_code qnetd_algo_test_heuristics_change_received(
+ struct qnetd_client *client, uint32_t msg_seq_num,
+ enum tlv_heuristics heuristics, enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algo_test_timer_callback(
+ struct qnetd_client *client, int *reschedule_timer,
+ int *send_vote, enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algo_test_register(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_ALGO_TEST_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Christine Caulfield (ccaulfie@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 <sys/types.h>
+#include <string.h>
+
+#include "qnetd-log.h"
+#include "qnetd-cluster-list.h"
+#include "qnetd-algo-utils.h"
+#include "utils.h"
+
+/*
+ * Returns -1 if any node that is supposedly in the same cluster partition
+ * as us has a different ring_id.
+ * If this happens it simply means that qnetd does not yet have the full current view
+ * of the cluster and should wait until all of the ring_ids in this membership list match up
+ */
+int
+qnetd_algo_all_ring_ids_match(struct qnetd_client *client, const struct tlv_ring_id *ring_id)
+{
+ struct node_list_entry *node_info;
+ struct qnetd_client *other_client;
+
+ TAILQ_FOREACH(other_client, &client->cluster->client_list, cluster_entries) {
+ int in_our_partition = 0;
+
+ if (other_client == client) {
+ continue; /* We've seen our membership list */
+ }
+ qnetd_log(LOG_DEBUG, "algo-util: all_ring_ids_match: seen nodeid %d (client %p) ring_id (" UTILS_PRI_RING_ID ")", other_client->node_id, other_client, other_client->last_ring_id.node_id, other_client->last_ring_id.seq);
+
+ /* Look down our node list and see if this client is known to us */
+ TAILQ_FOREACH(node_info, &client->last_membership_node_list, entries) {
+ if (node_info->node_id == other_client->node_id) {
+ in_our_partition = 1;
+ }
+ }
+
+ if (in_our_partition == 0) {
+ /*
+ * Also try to look from the other side to see if we are
+ * not in the other node's membership list.
+ * Because if so it may mean the membership lists are not equal
+ */
+ TAILQ_FOREACH(node_info, &other_client->last_membership_node_list, entries) {
+ if (node_info->node_id == client->node_id) {
+ in_our_partition = 1;
+ }
+ }
+ }
+
+ /*
+ * If the other nodes on our side of a partition have a different ring ID then
+ * we need to wait until they have all caught up before making a decision
+ */
+ if (in_our_partition && !tlv_ring_id_eq(ring_id, &other_client->last_ring_id)) {
+ qnetd_log(LOG_DEBUG, "algo-util: nodeid %d in our partition has different ring_id (" UTILS_PRI_RING_ID ") to us (" UTILS_PRI_RING_ID ")", other_client->node_id, other_client->last_ring_id.node_id, other_client->last_ring_id.seq, ring_id->node_id, ring_id->seq);
+ return (-1); /* ring IDs don't match */
+ }
+ }
+
+ return (0);
+}
+
+struct qnetd_algo_partition *
+qnetd_algo_find_partition(partitions_list_t *partitions_list, const struct tlv_ring_id *ring_id)
+{
+ struct qnetd_algo_partition *cur_partition;
+
+ TAILQ_FOREACH(cur_partition, partitions_list, entries) {
+ if (tlv_ring_id_eq(&cur_partition->ring_id, ring_id)) {
+ return (cur_partition);
+ }
+ }
+
+ return (NULL);
+}
+
+int
+qnetd_algo_create_partitions(struct qnetd_client *client, partitions_list_t *partitions_list, const struct tlv_ring_id *ring_id)
+{
+ struct qnetd_client *other_client;
+ int num_partitions = 0;
+
+ TAILQ_FOREACH(other_client, &client->cluster->client_list, cluster_entries) {
+ struct qnetd_algo_partition *partition;
+
+ if (other_client->last_ring_id.seq == 0){
+ continue; /* not initialised yet */
+ }
+ partition = qnetd_algo_find_partition(partitions_list, &other_client->last_ring_id);
+ if (!partition) {
+ partition = malloc(sizeof(struct qnetd_algo_partition));
+ if (!partition) {
+ return (-1);
+ }
+ partition->num_nodes = 0;
+ partition->score = 0;
+ memcpy(&partition->ring_id, &other_client->last_ring_id, sizeof(*ring_id));
+ num_partitions++;
+ TAILQ_INSERT_TAIL(partitions_list, partition, entries);
+ }
+ partition->num_nodes++;
+
+ /*
+ * Score is computer similar way as in the ffsplit algorithm
+ */
+ partition->score++;
+ if (other_client->last_heuristics == TLV_HEURISTICS_PASS) {
+ partition->score++;
+ } else if (other_client->last_heuristics == TLV_HEURISTICS_FAIL) {
+ partition->score--;
+ }
+
+ }
+
+ return (num_partitions);
+}
+
+
+void
+qnetd_algo_free_partitions(partitions_list_t *partitions_list)
+{
+ struct qnetd_algo_partition *cur_partition;
+ struct qnetd_algo_partition *partition_next;
+
+ cur_partition = TAILQ_FIRST(partitions_list);
+
+ while (cur_partition != NULL) {
+ partition_next = TAILQ_NEXT(cur_partition, entries);
+
+ free(cur_partition);
+
+ cur_partition = partition_next;
+ }
+
+ TAILQ_INIT(partitions_list);
+}
+
+void
+qnetd_algo_dump_partitions(partitions_list_t *partitions_list)
+{
+ struct qnetd_algo_partition *partition;
+
+ TAILQ_FOREACH(partition, partitions_list, entries) {
+ qnetd_log(LOG_DEBUG, "algo-util: partition (" UTILS_PRI_RING_ID ") (%p) has %d nodes",
+ partition->ring_id.node_id, partition->ring_id.seq, partition, partition->num_nodes);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Christine Caulfield (ccaulfie@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.
+ */
+
+#ifndef _QNETD_ALGO_UTILS_H_
+#define _QNETD_ALGO_UTILS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qnetd_algo_partition {
+ struct tlv_ring_id ring_id;
+ int num_nodes;
+ int score;
+ TAILQ_ENTRY(qnetd_algo_partition) entries;
+};
+
+typedef TAILQ_HEAD(, qnetd_algo_partition) partitions_list_t;
+
+extern int qnetd_algo_all_ring_ids_match(struct qnetd_client *client,
+ const struct tlv_ring_id *ring_id);
+
+extern struct qnetd_algo_partition *qnetd_algo_find_partition(partitions_list_t *partitions,
+ const struct tlv_ring_id *ring_id);
+
+extern int qnetd_algo_create_partitions(struct qnetd_client *client,
+ partitions_list_t *partitions, const struct tlv_ring_id *ring_id);
+
+extern void qnetd_algo_free_partitions(partitions_list_t *partitions);
+
+extern void qnetd_algo_dump_partitions(partitions_list_t *partitions);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_ALGO_UTILS_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <sys/types.h>
+
+#include "qnet-config.h"
+#include "qnetd-algorithm.h"
+#include "qnetd-algo-test.h"
+#include "qnetd-algo-ffsplit.h"
+#include "qnetd-algo-2nodelms.h"
+#include "qnetd-algo-lms.h"
+#include "qnetd-log.h"
+
+static struct qnetd_algorithm *qnetd_algorithm_array[QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE];
+
+enum tlv_reply_error_code
+qnetd_algorithm_client_init(struct qnetd_client *client)
+{
+ if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qnetd_algorithm_array[client->decision_algorithm] == NULL) {
+ qnetd_log(LOG_CRIT, "qnetd_algorithm_client_init unhandled decision algorithm");
+
+ return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
+ }
+
+ return (qnetd_algorithm_array[client->decision_algorithm]->init(client));
+}
+
+enum tlv_reply_error_code
+qnetd_algorithm_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 (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qnetd_algorithm_array[client->decision_algorithm] == NULL) {
+ qnetd_log(LOG_CRIT, "qnetd_algorithm_config_node_list_received unhandled "
+ "decision algorithm");
+ return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
+ }
+
+ return (qnetd_algorithm_array[client->decision_algorithm]->config_node_list_received(
+ client, msg_seq_num,
+ config_version_set, config_version, nodes, initial, result_vote));
+}
+
+enum tlv_reply_error_code
+qnetd_algorithm_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_heuristics heuristics, enum tlv_vote *result_vote)
+{
+
+ if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qnetd_algorithm_array[client->decision_algorithm] == NULL) {
+ qnetd_log(LOG_CRIT, "qnetd_algorithm_membership_node_list_received unhandled "
+ "decision algorithm");
+ return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
+ }
+
+ return (qnetd_algorithm_array[client->decision_algorithm]->membership_node_list_received(
+ client, msg_seq_num,
+ ring_id, nodes, heuristics, result_vote));
+}
+
+enum tlv_reply_error_code
+qnetd_algorithm_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)
+{
+
+ if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qnetd_algorithm_array[client->decision_algorithm] == NULL) {
+ qnetd_log(LOG_CRIT, "algorithm_quorum_node_list_received unhandled "
+ "decision algorithm");
+ return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
+ }
+
+ return (qnetd_algorithm_array[client->decision_algorithm]->quorum_node_list_received(
+ client, msg_seq_num, quorate, nodes, result_vote));
+}
+
+void
+qnetd_algorithm_client_disconnect(struct qnetd_client *client, int server_going_down)
+{
+
+ if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qnetd_algorithm_array[client->decision_algorithm] == NULL) {
+ qnetd_log(LOG_CRIT, "qnetd_algorithm_client_disconnect unhandled decision "
+ "algorithm");
+ return;
+ }
+
+ qnetd_algorithm_array[client->decision_algorithm]->client_disconnect(client, server_going_down);
+}
+
+enum tlv_reply_error_code
+qnetd_algorithm_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_seq_num,
+ enum tlv_vote *result_vote)
+{
+
+ if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qnetd_algorithm_array[client->decision_algorithm] == NULL) {
+ qnetd_log(LOG_CRIT, "qnetd_algorithm_ask_for_vote_received unhandled "
+ "decision algorithm");
+ return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
+ }
+
+ return (qnetd_algorithm_array[client->decision_algorithm]->ask_for_vote_received(
+ client, msg_seq_num, result_vote));
+}
+
+enum tlv_reply_error_code
+qnetd_algorithm_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num)
+{
+
+ if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qnetd_algorithm_array[client->decision_algorithm] == NULL) {
+ qnetd_log(LOG_CRIT, "qnetd_algorithm_vote_info_reply_received unhandled decision algorithm");
+ return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
+ }
+
+ return (qnetd_algorithm_array[client->decision_algorithm]->vote_info_reply_received(
+ client, msg_seq_num));
+
+}
+
+enum tlv_reply_error_code
+qnetd_algorithm_heuristics_change_received(struct qnetd_client *client, uint32_t msg_seq_num,
+ enum tlv_heuristics heuristics, enum tlv_vote *result_vote)
+{
+
+ if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qnetd_algorithm_array[client->decision_algorithm] == NULL) {
+ qnetd_log(LOG_CRIT, "qnetd_algorithm_ask_for_vote_received unhandled "
+ "decision algorithm");
+ return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
+ }
+
+ return (qnetd_algorithm_array[client->decision_algorithm]->heuristics_change_received(
+ client, msg_seq_num, heuristics, result_vote));
+}
+
+enum tlv_reply_error_code
+qnetd_algorithm_timer_callback(struct qnetd_client *client, int *reschedule_timer,
+ int *send_vote, enum tlv_vote *result_vote)
+{
+
+ if (client->decision_algorithm >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE ||
+ qnetd_algorithm_array[client->decision_algorithm] == NULL) {
+ qnetd_log(LOG_CRIT, "qnetd_algorithm_timer_callback unhandled decision algorithm");
+ return (TLV_REPLY_ERROR_CODE_INTERNAL_ERROR);
+ }
+
+ return (qnetd_algorithm_array[client->decision_algorithm]->timer_callback(
+ client, reschedule_timer, send_vote, result_vote));
+}
+
+int
+qnetd_algorithm_register(enum tlv_decision_algorithm_type algorithm_number,
+ struct qnetd_algorithm *algorithm)
+{
+
+ if (algorithm_number >= QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE) {
+ qnetd_log(LOG_CRIT, "Failed to register unsupported decision algorithm %u",
+ algorithm_number);
+ return (-1);
+ }
+
+ if (qnetd_algorithm_array[algorithm_number] != NULL) {
+ qnetd_log(LOG_CRIT, "Failed to register decision algorithm %u, "
+ "it's already registered.", algorithm_number);
+ return (-1);
+ }
+
+ qnetd_algorithm_array[algorithm_number] = algorithm;
+
+ return (0);
+}
+
+int
+qnetd_algorithm_register_all(void)
+{
+
+ if (qnetd_algo_test_register() != 0) {
+ qnetd_log(LOG_CRIT, "Failed to register decision algorithm 'test'");
+ return (-1);
+ }
+ if (qnetd_algo_ffsplit_register() != 0) {
+ qnetd_log(LOG_CRIT, "Failed to register decision algorithm 'ffsplit'");
+ return (-1);
+ }
+ if (qnetd_algo_2nodelms_register() != 0) {
+ qnetd_log(LOG_CRIT, "Failed to register decision algorithm '2nodelms'");
+ return (-1);
+ }
+ if (qnetd_algo_lms_register() != 0) {
+ qnetd_log(LOG_CRIT, "Failed to register decision algorithm 'lms'");
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QNETD_ALGORITHM_H_
+#define _QNETD_ALGORITHM_H_
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include "tlv.h"
+#include "qnetd-client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern enum tlv_reply_error_code qnetd_algorithm_client_init(struct qnetd_client *client);
+
+extern enum tlv_reply_error_code qnetd_algorithm_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);
+
+extern enum tlv_reply_error_code qnetd_algorithm_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_heuristics heuristics, enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algorithm_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);
+
+extern void qnetd_algorithm_client_disconnect(
+ struct qnetd_client *client, int server_going_down);
+
+extern enum tlv_reply_error_code qnetd_algorithm_ask_for_vote_received(
+ struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algorithm_vote_info_reply_received(
+ struct qnetd_client *client, uint32_t msg_seq_num);
+
+extern enum tlv_reply_error_code qnetd_algorithm_heuristics_change_received(
+ struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_heuristics heuristics,
+ enum tlv_vote *result_vote);
+
+extern enum tlv_reply_error_code qnetd_algorithm_timer_callback(
+ struct qnetd_client *client, int *reschedule_timer, int *send_vote, enum tlv_vote *result_vote);
+
+struct qnetd_algorithm {
+ enum tlv_reply_error_code (*init)(struct qnetd_client *client);
+
+ void (*client_disconnect)(struct qnetd_client *client, int server_going_down);
+
+ enum tlv_reply_error_code (*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_heuristics, enum tlv_vote *result_vote);
+
+ enum tlv_reply_error_code (*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);
+
+ enum tlv_reply_error_code (*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);
+
+ enum tlv_reply_error_code (*ask_for_vote_received)(
+ struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_vote *result_vote);
+
+ enum tlv_reply_error_code (*vote_info_reply_received)(struct qnetd_client *client,
+ uint32_t msg_seq_num);
+
+ enum tlv_reply_error_code (*heuristics_change_received)(
+ struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_heuristics heuristics,
+ enum tlv_vote *result_vote);
+
+ enum tlv_reply_error_code (*timer_callback)(struct qnetd_client *client,
+ int *reschedule_timer, int *send_vote, enum tlv_vote *result_vote);
+};
+
+extern int qnetd_algorithm_register(
+ enum tlv_decision_algorithm_type algorithm_number, struct qnetd_algorithm *algorithm);
+
+extern int qnetd_algorithm_register_all(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_ALGORITHM_H_ */
--- /dev/null
+/*
+ * 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 "qnetd-log.h"
+#include "qnetd-client-algo-timer.h"
+#include "qnetd-client-send.h"
+#include "qnetd-algorithm.h"
+#include "timer-list.h"
+
+static int
+qnetd_client_algo_timer_callback(void *data1, void *data2)
+{
+ struct qnetd_client *client;
+ enum tlv_vote result_vote;
+ int send_vote;
+ int reschedule_timer;
+ enum tlv_reply_error_code reply_error_code;
+
+ client = (struct qnetd_client *)data1;
+
+ result_vote = TLV_VOTE_WAIT_FOR_REPLY;
+ send_vote = 0;
+ reschedule_timer = 0;
+
+ reply_error_code = qnetd_algorithm_timer_callback(client, &reschedule_timer,
+ &send_vote, &result_vote);
+
+ if (reply_error_code != TLV_REPLY_ERROR_CODE_NO_ERROR) {
+ qnetd_log(LOG_ERR, "Algorithm for client %s returned error code. "
+ "Sending error reply.", client->addr_str);
+
+ if (qnetd_client_send_err(client, 0, 0, reply_error_code) != 0) {
+ client->schedule_disconnect = 1;
+ return (0);
+ }
+
+ return (0);
+ } else {
+ qnetd_log(LOG_DEBUG, "Algorithm for client %s decided to %s timer and %s vote "
+ "with value %s", client->addr_str,
+ (reschedule_timer ? "reschedule" : "not reschedule"),
+ (send_vote ? "send" : "not send"),
+ tlv_vote_to_str(result_vote));
+ }
+
+ if (send_vote) {
+ client->algo_timer_vote_info_msq_seq_number++;
+
+ if (qnetd_client_send_vote_info(client,
+ client->algo_timer_vote_info_msq_seq_number, &client->last_ring_id,
+ result_vote) != 0) {
+ client->schedule_disconnect = 1;
+ return (0);
+ }
+ }
+
+ if (reschedule_timer) {
+ /*
+ * Timer list makes sure to schedule callback again
+ */
+ return (-1);
+ }
+
+ client->algo_timer = NULL;
+ return (0);
+}
+
+int
+qnetd_client_algo_timer_is_scheduled(struct qnetd_client *client)
+{
+
+ return (client->algo_timer != NULL);
+}
+
+int
+qnetd_client_algo_timer_schedule_timeout(struct qnetd_client *client, uint32_t timeout)
+{
+
+ if (qnetd_client_algo_timer_is_scheduled(client)) {
+ if (qnetd_client_algo_timer_abort(client) != 0) {
+ qnetd_log(LOG_ERR, "Can't abort algo timer");
+
+ return (-1);
+ }
+ }
+
+ client->algo_timer = timer_list_add(client->main_timer_list, timeout,
+ qnetd_client_algo_timer_callback, (void *)client, NULL);
+ if (client->algo_timer == NULL) {
+ qnetd_log(LOG_ERR, "Can't schedule algo timer");
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qnetd_client_algo_timer_schedule(struct qnetd_client *client)
+{
+
+ return (qnetd_client_algo_timer_schedule_timeout(client, client->heartbeat_interval / 4));
+}
+
+int
+qnetd_client_algo_timer_abort(struct qnetd_client *client)
+{
+
+ if (qnetd_client_algo_timer_is_scheduled(client)) {
+ timer_list_delete(client->main_timer_list, client->algo_timer);
+ client->algo_timer = NULL;
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNETD_CLIENT_ALGO_TIMER_H_
+#define _QNETD_CLIENT_ALGO_TIMER_H_
+
+#include "qnetd-client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qnetd_client_algo_timer_is_scheduled(struct qnetd_client *client);
+
+extern int qnetd_client_algo_timer_schedule_timeout(struct qnetd_client *client,
+ uint32_t timeout);
+
+extern int qnetd_client_algo_timer_schedule(struct qnetd_client *client);
+
+extern int qnetd_client_algo_timer_abort(struct qnetd_client *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_CLIENT_ALGO_TIMER_H_ */
--- /dev/null
+/*
+ * 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 <sys/types.h>
+#include <arpa/inet.h>
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qnetd-client-list.h"
+
+void
+qnetd_client_list_init(struct qnetd_client_list *client_list)
+{
+
+ TAILQ_INIT(client_list);
+}
+
+struct qnetd_client *
+qnetd_client_list_add(struct qnetd_client_list *client_list, PRFileDesc *sock, PRNetAddr *addr,
+ char *addr_str,
+ size_t max_receive_size, size_t max_send_buffers, size_t max_send_size,
+ struct timer_list *main_timer_list)
+{
+ struct qnetd_client *client;
+
+ client = (struct qnetd_client *)malloc(sizeof(*client));
+ if (client == NULL) {
+ return (NULL);
+ }
+
+ qnetd_client_init(client, sock, addr, addr_str, max_receive_size, max_send_buffers,
+ max_send_size, main_timer_list);
+
+ TAILQ_INSERT_TAIL(client_list, client, entries);
+
+ return (client);
+}
+
+void
+qnetd_client_list_free(struct qnetd_client_list *client_list)
+{
+ struct qnetd_client *client;
+ struct qnetd_client *client_next;
+
+ client = TAILQ_FIRST(client_list);
+
+ while (client != NULL) {
+ client_next = TAILQ_NEXT(client, entries);
+
+ qnetd_client_destroy(client);
+ free(client);
+
+ client = client_next;
+ }
+
+ TAILQ_INIT(client_list);
+}
+
+void
+qnetd_client_list_del(struct qnetd_client_list *client_list, struct qnetd_client *client)
+{
+
+ TAILQ_REMOVE(client_list, client, entries);
+
+ qnetd_client_destroy(client);
+ free(client);
+}
+
+size_t
+qnetd_client_list_no_clients(struct qnetd_client_list *client_list)
+{
+ size_t res;
+ struct qnetd_client *client;
+
+ res = 0;
+
+ TAILQ_FOREACH(client, client_list, entries) {
+ res++;
+ }
+
+ return (res);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNETD_CLIENT_LIST_H_
+#define _QNETD_CLIENT_LIST_H_
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include "qnetd-client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+TAILQ_HEAD(qnetd_client_list, qnetd_client);
+
+extern void qnetd_client_list_init(struct qnetd_client_list *client_list);
+
+extern struct qnetd_client *qnetd_client_list_add(struct qnetd_client_list *client_list,
+ PRFileDesc *sock, PRNetAddr *addr, char *addr_str, size_t max_receive_size,
+ size_t max_send_buffers, size_t max_send_size, struct timer_list *main_timer_list);
+
+extern void qnetd_client_list_free(struct qnetd_client_list *client_list);
+
+extern void qnetd_client_list_del(struct qnetd_client_list *client_list,
+ struct qnetd_client *client);
+
+extern size_t qnetd_client_list_no_clients(
+ struct qnetd_client_list *client_list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_CLIENT_LIST_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <sys/types.h>
+
+#include "qnetd-algorithm.h"
+#include "qnetd-instance.h"
+#include "qnetd-log.h"
+#include "qnetd-log-debug.h"
+#include "qnetd-client-send.h"
+#include "msg.h"
+#include "nss-sock.h"
+
+#include "qnetd-client-msg-received.h"
+
+/*
+ * 0 - Success
+ * -1 - Disconnect client
+ * -2 - Error reply sent, but no need to disconnect client
+ */
+static int
+qnetd_client_msg_received_check_tls(struct qnetd_instance *instance, struct qnetd_client *client,
+ const struct msg_decoded *msg)
+{
+ int check_certificate;
+ int tls_required;
+ CERTCertificate *peer_cert;
+ int case_processed;
+
+ check_certificate = 0;
+ tls_required = 0;
+
+ case_processed = 0;
+
+ switch (instance->tls_supported) {
+ case TLV_TLS_UNSUPPORTED:
+ case_processed = 1;
+ tls_required = 0;
+ check_certificate = 0;
+ break;
+ case TLV_TLS_SUPPORTED:
+ case_processed = 1;
+ tls_required = 0;
+
+ if (client->tls_started && instance->tls_client_cert_required &&
+ !client->tls_peer_certificate_verified) {
+ check_certificate = 1;
+ }
+ break;
+ case TLV_TLS_REQUIRED:
+ case_processed = 1;
+ tls_required = 1;
+
+ if (instance->tls_client_cert_required && !client->tls_peer_certificate_verified) {
+ check_certificate = 1;
+ }
+ break;
+ /*
+ * Default is not defined intentionally. Compiler shows warning when new
+ * tls supported is added
+ */
+ }
+
+ if (!case_processed) {
+ qnetd_log(LOG_ERR, "Unhandled instance tls supported %u", instance->tls_supported);
+ exit(1);
+ }
+
+ if (tls_required && !client->tls_started) {
+ qnetd_log(LOG_ERR, "TLS is required but doesn't started yet. "
+ "Sending back error message");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_TLS_REQUIRED) != 0) {
+ return (-1);
+ }
+
+ return (-2);
+ }
+
+ if (check_certificate) {
+ peer_cert = SSL_PeerCertificate(client->socket);
+
+ if (peer_cert == NULL) {
+ qnetd_log(LOG_ERR, "Client doesn't sent valid certificate. "
+ "Disconnecting client");
+
+ return (-1);
+ }
+
+ if (CERT_VerifyCertName(peer_cert, client->cluster_name) != SECSuccess) {
+ qnetd_log(LOG_ERR, "Client doesn't sent certificate with valid CN. "
+ "Disconnecting client");
+
+ CERT_DestroyCertificate(peer_cert);
+
+ return (-1);
+ }
+
+ CERT_DestroyCertificate(peer_cert);
+
+ client->tls_peer_certificate_verified = 1;
+ }
+
+ return (0);
+}
+
+static int
+qnetd_client_msg_received_preinit(struct qnetd_instance *instance, struct qnetd_client *client,
+ const struct msg_decoded *msg)
+{
+ struct send_buffer_list_entry *send_buffer;
+
+ if (msg->cluster_name == NULL) {
+ qnetd_log(LOG_ERR, "Received preinit message without cluster name. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ client->cluster_name = malloc(msg->cluster_name_len + 1);
+ if (client->cluster_name == NULL) {
+ qnetd_log(LOG_ERR, "Can't allocate cluster name. Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_INTERNAL_ERROR) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+ memset(client->cluster_name, 0, msg->cluster_name_len + 1);
+ memcpy(client->cluster_name, msg->cluster_name, msg->cluster_name_len);
+
+ client->cluster_name_len = msg->cluster_name_len;
+ client->preinit_received = 1;
+
+ send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
+ if (send_buffer == NULL) {
+ qnetd_log(LOG_ERR, "Can't alloc preinit reply msg from list. "
+ "Disconnecting client connection.");
+
+ return (-1);
+ }
+
+ if (msg_create_preinit_reply(&send_buffer->buffer, msg->seq_number_set, msg->seq_number,
+ instance->tls_supported, instance->tls_client_cert_required) == 0) {
+ qnetd_log(LOG_ERR, "Can't alloc preinit reply msg. "
+ "Disconnecting client connection.");
+
+ send_buffer_list_discard_new(&client->send_buffer_list, send_buffer);
+
+ return (-1);
+ };
+
+ send_buffer_list_put(&client->send_buffer_list, send_buffer);
+
+ return (0);
+}
+
+static int
+qnetd_client_msg_received_unexpected_msg(struct qnetd_client *client,
+ const struct msg_decoded *msg, const char *msg_str)
+{
+
+ qnetd_log(LOG_ERR, "Received %s message. Sending back error message", msg_str);
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_UNEXPECTED_MESSAGE) != 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+qnetd_client_msg_received_preinit_reply(struct qnetd_instance *instance,
+ struct qnetd_client *client, const struct msg_decoded *msg)
+{
+
+ return (qnetd_client_msg_received_unexpected_msg(client, msg, "preinit reply"));
+}
+
+static int
+qnetd_client_msg_received_starttls(struct qnetd_instance *instance, struct qnetd_client *client,
+ const struct msg_decoded *msg)
+{
+ PRFileDesc *new_pr_fd;
+
+ if (!client->preinit_received) {
+ qnetd_log(LOG_ERR, "Received starttls before preinit message. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_PREINIT_REQUIRED) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ if ((new_pr_fd = nss_sock_start_ssl_as_server(client->socket, instance->server.cert,
+ instance->server.private_key, instance->tls_client_cert_required, 0, NULL)) == NULL) {
+ qnetd_log_nss(LOG_ERR, "Can't start TLS. Disconnecting client.");
+
+ return (-1);
+ }
+
+ client->tls_started = 1;
+ client->tls_peer_certificate_verified = 0;
+ client->socket = new_pr_fd;
+
+ return (0);
+}
+
+static int
+qnetd_client_msg_received_server_error(struct qnetd_instance *instance, struct qnetd_client *client,
+ const struct msg_decoded *msg)
+{
+
+ return (qnetd_client_msg_received_unexpected_msg(client, msg, "server error"));
+}
+
+/*
+ * Checks if new client send information are valid. It means:
+ * - in cluster is no duplicate node with same nodeid
+ * - it has same tie_breaker as other nodes in cluster
+ * - it has same algorithm as other nodes in cluster
+ */
+static enum tlv_reply_error_code
+qnetd_client_msg_received_init_check_new_client(struct qnetd_instance *instance,
+ struct qnetd_client *new_client)
+{
+ struct qnetd_cluster *cluster;
+ struct qnetd_client *client;
+
+ cluster = qnetd_cluster_list_find_by_name(&instance->clusters, new_client->cluster_name,
+ new_client->cluster_name_len);
+
+ if (cluster == NULL) {
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+ }
+
+ TAILQ_FOREACH(client, &cluster->client_list, cluster_entries) {
+ if (!tlv_tie_breaker_eq(&new_client->tie_breaker, &client->tie_breaker)) {
+ qnetd_log(LOG_ERR, "Received init message contains tie-breaker which "
+ "differs from rest of cluster. Sending error reply");
+
+ return (TLV_REPLY_ERROR_CODE_TIE_BREAKER_DIFFERS_FROM_OTHER_NODES);
+ }
+
+ if (new_client->decision_algorithm != client->decision_algorithm) {
+ qnetd_log(LOG_ERR, "Received init message contains algorithm which "
+ "differs from rest of cluster. Sending error reply");
+
+ return (TLV_REPLY_ERROR_CODE_ALGORITHM_DIFFERS_FROM_OTHER_NODES);
+ }
+
+ if (new_client->node_id == client->node_id) {
+ qnetd_log(LOG_ERR, "Received init message contains node id which is "
+ "duplicate of other node in cluster. Sending error reply");
+
+ return (TLV_REPLY_ERROR_CODE_DUPLICATE_NODE_ID);
+ }
+ }
+
+ return (TLV_REPLY_ERROR_CODE_NO_ERROR);
+}
+
+static int
+qnetd_client_msg_received_init(struct qnetd_instance *instance, struct qnetd_client *client,
+ const struct msg_decoded *msg)
+{
+ int res;
+ size_t zi;
+ enum msg_type *supported_msgs;
+ size_t no_supported_msgs;
+ enum tlv_opt_type *supported_opts;
+ size_t no_supported_opts;
+ struct send_buffer_list_entry *send_buffer;
+ enum tlv_reply_error_code reply_error_code;
+ struct qnetd_cluster *cluster;
+
+ supported_msgs = NULL;
+ supported_opts = NULL;
+ no_supported_msgs = 0;
+ no_supported_opts = 0;
+
+ reply_error_code = TLV_REPLY_ERROR_CODE_NO_ERROR;
+
+ if ((res = qnetd_client_msg_received_check_tls(instance, client, msg)) != 0) {
+ return (res == -1 ? -1 : 0);
+ }
+
+ if (!client->preinit_received) {
+ qnetd_log(LOG_ERR, "Received init before preinit message. Sending error reply.");
+
+ reply_error_code = TLV_REPLY_ERROR_CODE_PREINIT_REQUIRED;
+ }
+
+ if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR && !msg->node_id_set) {
+ qnetd_log(LOG_ERR, "Received init message without node id set. "
+ "Sending error reply.");
+
+ reply_error_code = TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION;
+ } else {
+ client->node_id_set = 1;
+ client->node_id = msg->node_id;
+ }
+
+ if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR && !msg->ring_id_set) {
+ qnetd_log(LOG_ERR, "Received init message without ring id set. "
+ "Sending error reply.");
+
+ reply_error_code = TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION;
+ } else {
+ memcpy(&client->last_ring_id, &msg->ring_id, sizeof(struct tlv_ring_id));
+ }
+
+ if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR && !msg->heartbeat_interval_set) {
+ qnetd_log(LOG_ERR, "Received init message without heartbeat interval set. "
+ "Sending error reply.");
+
+ reply_error_code = TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION;
+ } else {
+ if (msg->heartbeat_interval < instance->advanced_settings->heartbeat_interval_min ||
+ msg->heartbeat_interval > instance->advanced_settings->heartbeat_interval_max) {
+ qnetd_log(LOG_ERR, "Client requested invalid heartbeat interval %u. "
+ "Sending error reply.", msg->heartbeat_interval);
+
+ reply_error_code = TLV_REPLY_ERROR_CODE_INVALID_HEARTBEAT_INTERVAL;
+ } else {
+ client->heartbeat_interval = msg->heartbeat_interval;
+ }
+ }
+
+ if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR && !msg->tie_breaker_set) {
+ qnetd_log(LOG_ERR, "Received init message without tie-breaker set. "
+ "Sending error reply.");
+
+ reply_error_code = TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION;
+ } else {
+ memcpy(&client->tie_breaker, &msg->tie_breaker, sizeof(msg->tie_breaker));
+ }
+
+ if (msg->supported_messages != NULL) {
+ /*
+ * Client sent supported messages. For now this is ignored but in the future
+ * this may be used to ensure backward compatibility.
+ */
+/*
+ for (i = 0; i < msg->no_supported_messages; i++) {
+ qnetd_log(LOG_DEBUG, "Client supports %u message",
+ (int)msg->supported_messages[i]);
+ }
+*/
+
+ /*
+ * Sent back supported messages
+ */
+ msg_get_supported_messages(&supported_msgs, &no_supported_msgs);
+ }
+
+ if (msg->supported_options != NULL) {
+ /*
+ * Client sent supported options. For now this is ignored but in the future
+ * this may be used to ensure backward compatibility.
+ */
+/*
+ for (i = 0; i < msg->no_supported_options; i++) {
+ qnetd_log(LOG_DEBUG, "Client supports %u option",
+ (int)msg->supported_messages[i]);
+ }
+*/
+
+ /*
+ * Send back supported options
+ */
+ tlv_get_supported_options(&supported_opts, &no_supported_opts);
+ }
+
+ if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR && !msg->decision_algorithm_set) {
+ qnetd_log(LOG_ERR, "Received init message without decision algorithm. "
+ "Sending error reply.");
+
+ reply_error_code = TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION;
+ } else {
+ /*
+ * Check if decision algorithm requested by client is supported
+ */
+ res = 0;
+
+ for (zi = 0; zi < QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE && !res; zi++) {
+ if (qnetd_static_supported_decision_algorithms[zi] ==
+ msg->decision_algorithm) {
+ res = 1;
+ }
+ }
+
+ if (!res) {
+ qnetd_log(LOG_ERR, "Client requested unsupported decision algorithm %u. "
+ "Sending error reply.", msg->decision_algorithm);
+
+ reply_error_code = TLV_REPLY_ERROR_CODE_UNSUPPORTED_DECISION_ALGORITHM;
+ }
+
+ client->decision_algorithm = msg->decision_algorithm;
+ }
+
+ if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR) {
+ reply_error_code = qnetd_client_msg_received_init_check_new_client(instance,
+ client);
+ }
+
+ if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR) {
+ cluster = qnetd_cluster_list_add_client(&instance->clusters, client);
+ if (cluster == NULL) {
+ qnetd_log(LOG_ERR, "Can't add client to cluster list. "
+ "Sending error reply.");
+
+ reply_error_code = TLV_REPLY_ERROR_CODE_INTERNAL_ERROR;
+ } else {
+ client->cluster = cluster;
+ client->cluster_list = &instance->clusters;
+ }
+ }
+
+ if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR) {
+ qnetd_log_debug_new_client_connected(client);
+
+ reply_error_code = qnetd_algorithm_client_init(client);
+ }
+
+ if (reply_error_code == TLV_REPLY_ERROR_CODE_NO_ERROR) {
+ /*
+ * Correct init received
+ */
+ client->init_received = 1;
+ } else {
+ qnetd_log(LOG_ERR, "Algorithm returned error code. Sending error reply.");
+ }
+
+ send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
+ if (send_buffer == NULL) {
+ qnetd_log(LOG_ERR, "Can't alloc init reply msg from list. "
+ "Disconnecting client connection.");
+
+ return (-1);
+ }
+
+ if (msg_create_init_reply(&send_buffer->buffer, msg->seq_number_set, msg->seq_number,
+ reply_error_code,
+ supported_msgs, no_supported_msgs, supported_opts, no_supported_opts,
+ instance->advanced_settings->max_client_receive_size,
+ instance->advanced_settings->max_client_send_size,
+ qnetd_static_supported_decision_algorithms,
+ QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE) == 0) {
+ qnetd_log(LOG_ERR, "Can't alloc init reply msg. Disconnecting client connection.");
+
+ send_buffer_list_discard_new(&client->send_buffer_list, send_buffer);
+
+ return (-1);
+ }
+
+ send_buffer_list_put(&client->send_buffer_list, send_buffer);
+
+ return (0);
+}
+
+static int
+qnetd_client_msg_received_init_reply(struct qnetd_instance *instance, struct qnetd_client *client,
+ const struct msg_decoded *msg)
+{
+
+ return (qnetd_client_msg_received_unexpected_msg(client, msg, "init reply"));
+}
+
+static int
+qnetd_client_msg_received_set_option_reply(struct qnetd_instance *instance,
+ struct qnetd_client *client, const struct msg_decoded *msg)
+{
+
+ return (qnetd_client_msg_received_unexpected_msg(client, msg, "set option reply"));
+}
+
+static int
+qnetd_client_msg_received_set_option(struct qnetd_instance *instance, struct qnetd_client *client,
+ const struct msg_decoded *msg)
+{
+ int res;
+ struct send_buffer_list_entry *send_buffer;
+
+ if ((res = qnetd_client_msg_received_check_tls(instance, client, msg)) != 0) {
+ return (res == -1 ? -1 : 0);
+ }
+
+ if (!client->init_received) {
+ qnetd_log(LOG_ERR, "Received set option message before init message. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_INIT_REQUIRED) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ if (msg->heartbeat_interval_set) {
+ /*
+ * Check if heartbeat interval is valid
+ */
+ if (msg->heartbeat_interval < instance->advanced_settings->heartbeat_interval_min ||
+ msg->heartbeat_interval > instance->advanced_settings->heartbeat_interval_max) {
+ qnetd_log(LOG_ERR, "Client requested invalid heartbeat interval %u. "
+ "Sending error reply.", msg->heartbeat_interval);
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_INVALID_HEARTBEAT_INTERVAL) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ client->heartbeat_interval = msg->heartbeat_interval;
+ }
+
+ send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
+ if (send_buffer == NULL) {
+ qnetd_log(LOG_ERR, "Can't alloc set option reply msg from list. "
+ "Disconnecting client connection.");
+
+ return (-1);
+ }
+
+ if (msg_create_set_option_reply(&send_buffer->buffer, msg->seq_number_set, msg->seq_number,
+ client->heartbeat_interval) == 0) {
+ qnetd_log(LOG_ERR, "Can't alloc set option reply msg. "
+ "Disconnecting client connection.");
+
+ send_buffer_list_discard_new(&client->send_buffer_list, send_buffer);
+
+ return (-1);
+ }
+
+ send_buffer_list_put(&client->send_buffer_list, send_buffer);
+
+ return (0);
+}
+
+static int
+qnetd_client_msg_received_echo_reply(struct qnetd_instance *instance, struct qnetd_client *client,
+ const struct msg_decoded *msg)
+{
+
+ return (qnetd_client_msg_received_unexpected_msg(client, msg, "echo reply"));
+}
+
+static int
+qnetd_client_msg_received_echo_request(struct qnetd_instance *instance, struct qnetd_client *client,
+ const struct msg_decoded *msg, const struct dynar *msg_orig)
+{
+ int res;
+ struct send_buffer_list_entry *send_buffer;
+
+ if ((res = qnetd_client_msg_received_check_tls(instance, client, msg)) != 0) {
+ return (res == -1 ? -1 : 0);
+ }
+
+ if (!client->init_received) {
+ qnetd_log(LOG_ERR, "Received echo request before init message. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_INIT_REQUIRED) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
+ if (send_buffer == NULL) {
+ qnetd_log(LOG_ERR, "Can't alloc echo reply msg from list. "
+ "Disconnecting client connection.");
+
+ return (-1);
+ }
+
+ if (msg_create_echo_reply(&send_buffer->buffer, msg_orig) == 0) {
+ qnetd_log(LOG_ERR, "Can't alloc echo reply msg. Disconnecting client connection.");
+
+ send_buffer_list_discard_new(&client->send_buffer_list, send_buffer);
+
+ return (-1);
+ }
+
+ send_buffer_list_put(&client->send_buffer_list, send_buffer);
+
+ return (0);
+}
+
+static int
+qnetd_client_msg_received_node_list(struct qnetd_instance *instance, struct qnetd_client *client,
+ const struct msg_decoded *msg)
+{
+ int res;
+ struct send_buffer_list_entry *send_buffer;
+ enum tlv_reply_error_code reply_error_code;
+ enum tlv_vote result_vote;
+ int case_processed;
+
+ reply_error_code = TLV_REPLY_ERROR_CODE_NO_ERROR;
+
+ if ((res = qnetd_client_msg_received_check_tls(instance, client, msg)) != 0) {
+ return (res == -1 ? -1 : 0);
+ }
+
+ if (!client->init_received) {
+ qnetd_log(LOG_ERR, "Received node list message before init message. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_INIT_REQUIRED) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ if (!msg->node_list_type_set) {
+ qnetd_log(LOG_ERR, "Received node list message without node list type set. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ if (!msg->seq_number_set) {
+ qnetd_log(LOG_ERR, "Received node list message without seq number set. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ result_vote = TLV_VOTE_NO_CHANGE;
+
+ case_processed = 0;
+ switch (msg->node_list_type) {
+ case TLV_NODE_LIST_TYPE_INITIAL_CONFIG:
+ case TLV_NODE_LIST_TYPE_CHANGED_CONFIG:
+ case_processed = 1;
+ qnetd_log_debug_config_node_list_received(client, msg->seq_number,
+ msg->config_version_set, msg->config_version, &msg->nodes,
+ (msg->node_list_type == TLV_NODE_LIST_TYPE_INITIAL_CONFIG));
+
+ reply_error_code = qnetd_algorithm_config_node_list_received(client,
+ msg->seq_number, msg->config_version_set, msg->config_version,
+ &msg->nodes,
+ (msg->node_list_type == TLV_NODE_LIST_TYPE_INITIAL_CONFIG),
+ &result_vote);
+ break;
+ case TLV_NODE_LIST_TYPE_MEMBERSHIP:
+ case_processed = 1;
+ if (!msg->ring_id_set) {
+ qnetd_log(LOG_ERR, "Received node list message without ring id number set. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ qnetd_log_debug_membership_node_list_received(client, msg->seq_number, &msg->ring_id,
+ msg->heuristics, &msg->nodes);
+
+ reply_error_code = qnetd_algorithm_membership_node_list_received(client,
+ msg->seq_number, &msg->ring_id, &msg->nodes, msg->heuristics, &result_vote);
+ break;
+ case TLV_NODE_LIST_TYPE_QUORUM:
+ case_processed = 1;
+ if (!msg->quorate_set) {
+ qnetd_log(LOG_ERR, "Received quorum list message without quorate set. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ qnetd_log_debug_quorum_node_list_received(client, msg->seq_number,msg->quorate,
+ &msg->nodes);
+
+ reply_error_code = qnetd_algorithm_quorum_node_list_received(client,
+ msg->seq_number,msg->quorate, &msg->nodes, &result_vote);
+ break;
+ /*
+ * Default is not defined intentionally. Compiler shows warning when new
+ * node list type is added
+ */
+ }
+
+ if (!case_processed) {
+ qnetd_log(LOG_ERR, "qnetd_client_msg_received_node_list fatal error. "
+ "Unhandled node_list_type");
+ exit(1);
+ }
+
+ if (reply_error_code != TLV_REPLY_ERROR_CODE_NO_ERROR) {
+ qnetd_log(LOG_ERR, "Algorithm returned error code. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ reply_error_code) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ } else {
+ qnetd_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote));
+ }
+
+ /*
+ * Store node list for future use
+ */
+ case_processed = 0;
+ switch (msg->node_list_type) {
+ case TLV_NODE_LIST_TYPE_INITIAL_CONFIG:
+ case TLV_NODE_LIST_TYPE_CHANGED_CONFIG:
+ case_processed = 1;
+ node_list_free(&client->configuration_node_list);
+ if (node_list_clone(&client->configuration_node_list, &msg->nodes) == -1) {
+ qnetd_log(LOG_ERR, "Can't alloc config node list clone. "
+ "Disconnecting client connection.");
+
+ return (-1);
+ }
+ client->config_version_set = msg->config_version_set;
+ client->config_version = msg->config_version;
+
+ break;
+ case TLV_NODE_LIST_TYPE_MEMBERSHIP:
+ case_processed = 1;
+ node_list_free(&client->last_membership_node_list);
+ if (node_list_clone(&client->last_membership_node_list, &msg->nodes) == -1) {
+ qnetd_log(LOG_ERR, "Can't alloc membership node list clone. "
+ "Disconnecting client connection.");
+
+ return (-1);
+ }
+ memcpy(&client->last_ring_id, &msg->ring_id, sizeof(struct tlv_ring_id));
+ client->last_membership_heuristics = msg->heuristics;
+ client->last_heuristics = msg->heuristics;
+ break;
+ case TLV_NODE_LIST_TYPE_QUORUM:
+ case_processed = 1;
+ node_list_free(&client->last_quorum_node_list);
+ if (node_list_clone(&client->last_quorum_node_list, &msg->nodes) == -1) {
+ qnetd_log(LOG_ERR, "Can't alloc quorum node list clone. "
+ "Disconnecting client connection.");
+
+ return (-1);
+ }
+ break;
+ /*
+ * Default is not defined intentionally. Compiler shows warning when new
+ * node list type is added
+ */
+ }
+
+ if (!case_processed) {
+ qnetd_log(LOG_ERR, "qnetd_client_msg_received_node_list fatal error. "
+ "Unhandled node_list_type");
+ exit(1);
+ }
+
+ /*
+ * Store result vote
+ */
+ client->last_sent_vote = result_vote;
+ if (result_vote == TLV_VOTE_ACK || result_vote == TLV_VOTE_NACK) {
+ client->last_sent_ack_nack_vote = result_vote;
+ }
+
+ send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
+ if (send_buffer == NULL) {
+ qnetd_log(LOG_ERR, "Can't alloc node list reply msg from list. "
+ "Disconnecting client connection.");
+
+ return (-1);
+ }
+
+ if (msg_create_node_list_reply(&send_buffer->buffer, msg->seq_number, msg->node_list_type,
+ &client->last_ring_id, result_vote) == 0) {
+ qnetd_log(LOG_ERR, "Can't alloc node list reply msg. "
+ "Disconnecting client connection.");
+
+ send_buffer_list_discard_new(&client->send_buffer_list, send_buffer);
+
+ return (-1);
+ }
+
+ send_buffer_list_put(&client->send_buffer_list, send_buffer);
+
+ return (0);
+}
+
+static int
+qnetd_client_msg_received_node_list_reply(struct qnetd_instance *instance,
+ struct qnetd_client *client, const struct msg_decoded *msg)
+{
+
+ return (qnetd_client_msg_received_unexpected_msg(client, msg, "node list reply"));
+}
+
+static int
+qnetd_client_msg_received_ask_for_vote(struct qnetd_instance *instance, struct qnetd_client *client,
+ const struct msg_decoded *msg)
+{
+ int res;
+ struct send_buffer_list_entry *send_buffer;
+ enum tlv_reply_error_code reply_error_code;
+ enum tlv_vote result_vote;
+
+ reply_error_code = TLV_REPLY_ERROR_CODE_NO_ERROR;
+
+ if ((res = qnetd_client_msg_received_check_tls(instance, client, msg)) != 0) {
+ return (res == -1 ? -1 : 0);
+ }
+
+ if (!client->init_received) {
+ qnetd_log(LOG_ERR, "Received ask for vote message before init message. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_INIT_REQUIRED) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ if (!msg->seq_number_set) {
+ qnetd_log(LOG_ERR, "Received ask for vote message without seq number set. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ qnetd_log_debug_ask_for_vote_received(client, msg->seq_number);
+
+ reply_error_code = qnetd_algorithm_ask_for_vote_received(client, msg->seq_number,
+ &result_vote);
+
+ if (reply_error_code != TLV_REPLY_ERROR_CODE_NO_ERROR) {
+ qnetd_log(LOG_ERR, "Algorithm returned error code. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ reply_error_code) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ } else {
+ qnetd_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote));
+ }
+
+ /*
+ * Store result vote
+ */
+ client->last_sent_vote = result_vote;
+ if (result_vote == TLV_VOTE_ACK || result_vote == TLV_VOTE_NACK) {
+ client->last_sent_ack_nack_vote = result_vote;
+ }
+
+ send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
+ if (send_buffer == NULL) {
+ qnetd_log(LOG_ERR, "Can't alloc ask for vote reply msg from list. "
+ "Disconnecting client connection.");
+
+ return (-1);
+ }
+
+ if (msg_create_ask_for_vote_reply(&send_buffer->buffer, msg->seq_number,
+ &client->last_ring_id, result_vote) == 0) {
+ qnetd_log(LOG_ERR, "Can't alloc ask for vote reply msg. "
+ "Disconnecting client connection.");
+
+ send_buffer_list_discard_new(&client->send_buffer_list, send_buffer);
+
+ return (-1);
+ }
+
+ send_buffer_list_put(&client->send_buffer_list, send_buffer);
+
+ return (0);
+}
+
+static int
+qnetd_client_msg_received_ask_for_vote_reply(struct qnetd_instance *instance,
+ struct qnetd_client *client, const struct msg_decoded *msg)
+{
+
+ return (qnetd_client_msg_received_unexpected_msg(client, msg, "ask for vote reply"));
+}
+
+static int
+qnetd_client_msg_received_vote_info(struct qnetd_instance *instance, struct qnetd_client *client,
+ const struct msg_decoded *msg)
+{
+
+ return (qnetd_client_msg_received_unexpected_msg(client, msg, "vote info"));
+}
+
+static int
+qnetd_client_msg_received_vote_info_reply(struct qnetd_instance *instance,
+ struct qnetd_client *client, const struct msg_decoded *msg)
+{
+ int res;
+ enum tlv_reply_error_code reply_error_code;
+
+ reply_error_code = TLV_REPLY_ERROR_CODE_NO_ERROR;
+
+ if ((res = qnetd_client_msg_received_check_tls(instance, client, msg)) != 0) {
+ return (res == -1 ? -1 : 0);
+ }
+
+ if (!client->init_received) {
+ qnetd_log(LOG_ERR, "Received vote info reply before init message. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_INIT_REQUIRED) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ if (!msg->seq_number_set) {
+ qnetd_log(LOG_ERR, "Received vote info reply message without seq number set. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ qnetd_log_debug_vote_info_reply_received(client, msg->seq_number);
+
+ reply_error_code = qnetd_algorithm_vote_info_reply_received(client, msg->seq_number);
+
+ if (reply_error_code != TLV_REPLY_ERROR_CODE_NO_ERROR) {
+ qnetd_log(LOG_ERR, "Algorithm returned error code. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ reply_error_code) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ return (0);
+}
+
+static int
+qnetd_client_msg_received_heuristics_change(struct qnetd_instance *instance, struct qnetd_client *client,
+ const struct msg_decoded *msg)
+{
+ int res;
+ struct send_buffer_list_entry *send_buffer;
+ enum tlv_reply_error_code reply_error_code;
+ enum tlv_vote result_vote;
+
+ reply_error_code = TLV_REPLY_ERROR_CODE_NO_ERROR;
+
+ if ((res = qnetd_client_msg_received_check_tls(instance, client, msg)) != 0) {
+ return (res == -1 ? -1 : 0);
+ }
+
+ if (!client->init_received) {
+ qnetd_log(LOG_ERR, "Received heuristics change message before init message. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_INIT_REQUIRED) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ if (!msg->seq_number_set || msg->heuristics == TLV_HEURISTICS_UNDEFINED) {
+ qnetd_log(LOG_ERR, "Received heuristics change message without seq number set or "
+ "with undefined heuristics. Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ qnetd_log_debug_heuristics_change_received(client, msg->seq_number, msg->heuristics);
+
+ reply_error_code = qnetd_algorithm_heuristics_change_received(client, msg->seq_number,
+ msg->heuristics, &result_vote);
+
+ if (reply_error_code != TLV_REPLY_ERROR_CODE_NO_ERROR) {
+ qnetd_log(LOG_ERR, "Algorithm returned error code. "
+ "Sending error reply.");
+
+ if (qnetd_client_send_err(client, msg->seq_number_set, msg->seq_number,
+ reply_error_code) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ } else {
+ qnetd_log(LOG_DEBUG, "Algorithm result vote is %s", tlv_vote_to_str(result_vote));
+ }
+
+ /*
+ * Store result vote and heuristics result
+ */
+ client->last_sent_vote = result_vote;
+ if (result_vote == TLV_VOTE_ACK || result_vote == TLV_VOTE_NACK) {
+ client->last_sent_ack_nack_vote = result_vote;
+ }
+ client->last_regular_heuristics = msg->heuristics;
+ client->last_heuristics = msg->heuristics;
+
+ send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
+ if (send_buffer == NULL) {
+ qnetd_log(LOG_ERR, "Can't alloc heuristics change reply msg from list. "
+ "Disconnecting client connection.");
+
+ return (-1);
+ }
+
+ if (msg_create_heuristics_change_reply(&send_buffer->buffer, msg->seq_number,
+ &client->last_ring_id, msg->heuristics, result_vote) == 0) {
+ qnetd_log(LOG_ERR, "Can't alloc heuristics change reply msg. "
+ "Disconnecting client connection.");
+
+ send_buffer_list_discard_new(&client->send_buffer_list, send_buffer);
+
+ return (-1);
+ }
+
+ send_buffer_list_put(&client->send_buffer_list, send_buffer);
+
+ return (0);
+}
+
+static int
+qnetd_client_msg_received_heuristics_change_reply(struct qnetd_instance *instance, struct qnetd_client *client,
+ const struct msg_decoded *msg)
+{
+
+ return (qnetd_client_msg_received_unexpected_msg(client, msg, "heuristics change reply"));
+}
+
+int
+qnetd_client_msg_received(struct qnetd_instance *instance, struct qnetd_client *client)
+{
+ struct msg_decoded msg;
+ int res;
+ int ret_val;
+ int msg_processed;
+
+ client->dpd_msg_received_since_last_check = 1;
+
+ msg_decoded_init(&msg);
+
+ res = msg_decode(&client->receive_buffer, &msg);
+ if (res != 0) {
+ /*
+ * Error occurred. Send server error.
+ */
+ qnetd_log_msg_decode_error(res);
+ qnetd_log(LOG_INFO, "Sending back error message");
+
+ if (qnetd_client_send_err(client, msg.seq_number_set, msg.seq_number,
+ TLV_REPLY_ERROR_CODE_ERROR_DECODING_MSG) != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ ret_val = 0;
+
+ msg_processed = 0;
+ switch (msg.type) {
+ case MSG_TYPE_PREINIT:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_preinit(instance, client, &msg);
+ break;
+ case MSG_TYPE_PREINIT_REPLY:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_preinit_reply(instance, client, &msg);
+ break;
+ case MSG_TYPE_STARTTLS:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_starttls(instance, client, &msg);
+ break;
+ case MSG_TYPE_INIT:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_init(instance, client, &msg);
+ break;
+ case MSG_TYPE_INIT_REPLY:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_init_reply(instance, client, &msg);
+ break;
+ case MSG_TYPE_SERVER_ERROR:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_server_error(instance, client, &msg);
+ break;
+ case MSG_TYPE_SET_OPTION:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_set_option(instance, client, &msg);
+ break;
+ case MSG_TYPE_SET_OPTION_REPLY:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_set_option_reply(instance, client, &msg);
+ break;
+ case MSG_TYPE_ECHO_REQUEST:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_echo_request(instance, client, &msg,
+ &client->receive_buffer);
+ break;
+ case MSG_TYPE_ECHO_REPLY:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_echo_reply(instance, client, &msg);
+ break;
+ case MSG_TYPE_NODE_LIST:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_node_list(instance, client, &msg);
+ break;
+ case MSG_TYPE_NODE_LIST_REPLY:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_node_list_reply(instance, client, &msg);
+ break;
+ case MSG_TYPE_ASK_FOR_VOTE:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_ask_for_vote(instance, client, &msg);
+ break;
+ case MSG_TYPE_ASK_FOR_VOTE_REPLY:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_ask_for_vote_reply(instance, client, &msg);
+ break;
+ case MSG_TYPE_VOTE_INFO:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_vote_info(instance, client, &msg);
+ break;
+ case MSG_TYPE_VOTE_INFO_REPLY:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_vote_info_reply(instance, client, &msg);
+ break;
+ case MSG_TYPE_HEURISTICS_CHANGE:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_heuristics_change(instance, client, &msg);
+ break;
+ case MSG_TYPE_HEURISTICS_CHANGE_REPLY:
+ msg_processed = 1;
+ ret_val = qnetd_client_msg_received_heuristics_change_reply(instance, client,
+ &msg);
+ break;
+ /*
+ * Default is not defined intentionally. Compiler shows warning when new
+ * msg type is added.
+ */
+ }
+
+ if (!msg_processed) {
+ qnetd_log(LOG_ERR, "Unsupported message %u received from client. "
+ "Sending back error message", msg.type);
+
+ if (qnetd_client_send_err(client, msg.seq_number_set, msg.seq_number,
+ TLV_REPLY_ERROR_CODE_UNSUPPORTED_MESSAGE) != 0) {
+ ret_val = -1;
+ }
+ }
+
+ msg_decoded_destroy(&msg);
+
+ return (ret_val);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNETD_CLIENT_MSG_RECEIVED_H_
+#define _QNETD_CLIENT_MSG_RECEIVED_H_
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qnetd_client_msg_received(struct qnetd_instance *instance,
+ struct qnetd_client *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_CLIENT_MSG_RECEIVED_H_ */
--- /dev/null
+/*
+ * 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 <sys/types.h>
+
+#include "msgio.h"
+#include "msg.h"
+#include "nss-sock.h"
+#include "qnetd-log.h"
+#include "qnetd-client-net.h"
+#include "qnetd-client-send.h"
+#include "qnetd-client-msg-received.h"
+
+#define CLIENT_ADDR_STR_LEN_COLON_PORT (1 + 5 + 1)
+#define CLIENT_ADDR_STR_LEN (INET6_ADDRSTRLEN + CLIENT_ADDR_STR_LEN_COLON_PORT)
+
+static int
+qnetd_client_net_write_finished(struct qnetd_instance *instance, struct qnetd_client *client)
+{
+
+ /*
+ * Callback is currently unused
+ */
+
+ return (0);
+}
+
+int
+qnetd_client_net_write(struct qnetd_instance *instance, struct qnetd_client *client)
+{
+ int res;
+ struct send_buffer_list_entry *send_buffer;
+
+ send_buffer = send_buffer_list_get_active(&client->send_buffer_list);
+ if (send_buffer == NULL) {
+ qnetd_log_nss(LOG_CRIT, "send_buffer_list_get_active returned NULL");
+
+ return (-1);
+ }
+
+ res = msgio_write(client->socket, &send_buffer->buffer,
+ &send_buffer->msg_already_sent_bytes);
+
+ if (res == 1) {
+ send_buffer_list_delete(&client->send_buffer_list, send_buffer);
+
+ if (qnetd_client_net_write_finished(instance, client) == -1) {
+ return (-1);
+ }
+ }
+
+ if (res == -1) {
+ qnetd_log_nss(LOG_CRIT, "PR_Send returned 0");
+
+ return (-1);
+ }
+
+ if (res == -2) {
+ qnetd_log_nss(LOG_ERR, "Unhandled error when sending message to client");
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * -1 means end of connection (EOF) or some other unhandled error. 0 = success
+ */
+int
+qnetd_client_net_read(struct qnetd_instance *instance, struct qnetd_client *client)
+{
+ int res;
+ int ret_val;
+ int orig_skipping_msg;
+
+ orig_skipping_msg = client->skipping_msg;
+
+ res = msgio_read(client->socket, &client->receive_buffer,
+ &client->msg_already_received_bytes, &client->skipping_msg);
+
+ if (!orig_skipping_msg && client->skipping_msg) {
+ qnetd_log(LOG_DEBUG, "msgio_read set skipping_msg");
+ }
+
+ ret_val = 0;
+
+ switch (res) {
+ case 0:
+ /*
+ * Partial read
+ */
+ break;
+ case -1:
+ qnetd_log(LOG_DEBUG, "Client closed connection");
+ ret_val = -1;
+ break;
+ case -2:
+ qnetd_log_nss(LOG_ERR, "Unhandled error when reading from client. "
+ "Disconnecting client");
+ ret_val = -1;
+ break;
+ case -3:
+ qnetd_log(LOG_ERR, "Can't store message header from client. Disconnecting client");
+ ret_val = -1;
+ break;
+ case -4:
+ qnetd_log(LOG_ERR, "Can't store message from client. Skipping message");
+ client->skipping_msg_reason = TLV_REPLY_ERROR_CODE_ERROR_DECODING_MSG;
+ break;
+ case -5:
+ qnetd_log(LOG_WARNING, "Client sent unsupported msg type %u. Skipping message",
+ msg_get_type(&client->receive_buffer));
+ client->skipping_msg_reason = TLV_REPLY_ERROR_CODE_UNSUPPORTED_MESSAGE;
+ break;
+ case -6:
+ qnetd_log(LOG_WARNING,
+ "Client wants to send too long message %u bytes. Skipping message",
+ msg_get_len(&client->receive_buffer));
+ client->skipping_msg_reason = TLV_REPLY_ERROR_CODE_MESSAGE_TOO_LONG;
+ break;
+ case 1:
+ /*
+ * Full message received / skipped
+ */
+ if (!client->skipping_msg) {
+ if (qnetd_client_msg_received(instance, client) == -1) {
+ ret_val = -1;
+ }
+ } else {
+ if (qnetd_client_send_err(client, 0, 0, client->skipping_msg_reason) != 0) {
+ ret_val = -1;
+ }
+ }
+
+ client->skipping_msg = 0;
+ client->skipping_msg_reason = TLV_REPLY_ERROR_CODE_NO_ERROR;
+ client->msg_already_received_bytes = 0;
+ dynar_clean(&client->receive_buffer);
+ break;
+ default:
+ qnetd_log(LOG_ERR, "Unhandled msgio_read error %d\n", res);
+ exit(1);
+ break;
+ }
+
+ return (ret_val);
+}
+
+int
+qnetd_client_net_accept(struct qnetd_instance *instance)
+{
+ PRNetAddr client_addr;
+ PRFileDesc *client_socket;
+ struct qnetd_client *client;
+ char *client_addr_str;
+ int res_err;
+
+ client_addr_str = NULL;
+
+ res_err = -1;
+
+ if ((client_socket = PR_Accept(instance->server.socket, &client_addr,
+ PR_INTERVAL_NO_TIMEOUT)) == NULL) {
+ qnetd_log_nss(LOG_ERR, "Can't accept connection");
+ return (-1);
+ }
+
+ if (nss_sock_set_non_blocking(client_socket) != 0) {
+ qnetd_log_nss(LOG_ERR, "Can't set client socket to non blocking mode");
+ goto exit_close;
+ }
+
+ if (instance->max_clients != 0 &&
+ qnetd_client_list_no_clients(&instance->clients) >= instance->max_clients) {
+ qnetd_log(LOG_ERR, "Maximum clients reached. Not accepting connection");
+ goto exit_close;
+ }
+
+ client_addr_str = malloc(CLIENT_ADDR_STR_LEN);
+ if (client_addr_str == NULL) {
+ qnetd_log(LOG_ERR, "Can't alloc client addr str memory. Not accepting connection");
+ goto exit_close;
+ }
+
+ if (PR_NetAddrToString(&client_addr, client_addr_str, CLIENT_ADDR_STR_LEN) != PR_SUCCESS) {
+ qnetd_log_nss(LOG_ERR, "Can't convert client address to string. Not accepting connection");
+ goto exit_close;
+ }
+
+ if (snprintf(client_addr_str + strlen(client_addr_str),
+ CLIENT_ADDR_STR_LEN_COLON_PORT, ":%"PRIu16,
+ ntohs(client_addr.ipv6.port)) >= CLIENT_ADDR_STR_LEN_COLON_PORT) {
+ qnetd_log(LOG_ERR, "Can't store port to client addr str. Not accepting connection");
+ goto exit_close;
+ }
+
+ client = qnetd_client_list_add(&instance->clients, client_socket, &client_addr,
+ client_addr_str,
+ instance->advanced_settings->max_client_receive_size,
+ instance->advanced_settings->max_client_send_buffers,
+ instance->advanced_settings->max_client_send_size, &instance->main_timer_list);
+ if (client == NULL) {
+ qnetd_log(LOG_ERR, "Can't add client to list");
+ res_err = -2;
+ goto exit_close;
+ }
+
+ return (0);
+
+exit_close:
+ free(client_addr_str);
+ PR_Close(client_socket);
+
+ return (res_err);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNETD_CLIENT_NET_H_
+#define _QNETD_CLIENT_NET_H_
+
+#include <sys/types.h>
+
+#include "qnetd-client.h"
+#include "qnetd-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qnetd_client_net_write(struct qnetd_instance *instance,
+ struct qnetd_client *client);
+
+extern int qnetd_client_net_read(struct qnetd_instance *instance,
+ struct qnetd_client *client);
+
+extern int qnetd_client_net_accept(struct qnetd_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_CLIENT_NET_H_ */
--- /dev/null
+/*
+ * 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 <sys/types.h>
+
+#include <string.h>
+
+#include "qnetd-client-send.h"
+#include "qnetd-log.h"
+#include "qnetd-log-debug.h"
+#include "msg.h"
+
+int
+qnetd_client_send_err(struct qnetd_client *client, int add_msg_seq_number, uint32_t msg_seq_number,
+ enum tlv_reply_error_code reply)
+{
+ struct send_buffer_list_entry *send_buffer;
+
+ send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
+ if (send_buffer == NULL) {
+ qnetd_log(LOG_ERR, "Can't alloc server error msg from list. "
+ "Disconnecting client connection.");
+
+ return (-1);
+ }
+
+ if (msg_create_server_error(&send_buffer->buffer, add_msg_seq_number,
+ msg_seq_number, reply) == 0) {
+ qnetd_log(LOG_ERR, "Can't alloc server error msg. "
+ "Disconnecting client connection.");
+
+ send_buffer_list_discard_new(&client->send_buffer_list, send_buffer);
+ return (-1);
+ };
+
+ send_buffer_list_put(&client->send_buffer_list, send_buffer);
+
+ return (0);
+}
+
+int
+qnetd_client_send_vote_info(struct qnetd_client *client, uint32_t msg_seq_number,
+ const struct tlv_ring_id *ring_id, enum tlv_vote vote)
+{
+ struct send_buffer_list_entry *send_buffer;
+
+ /*
+ * Store result vote
+ */
+ client->last_sent_vote = vote;
+ if (vote == TLV_VOTE_ACK || vote == TLV_VOTE_NACK) {
+ client->last_sent_ack_nack_vote = vote;
+ }
+
+ qnetd_log_debug_send_vote_info(client, msg_seq_number, vote);
+
+ send_buffer = send_buffer_list_get_new(&client->send_buffer_list);
+ if (send_buffer == NULL) {
+ qnetd_log(LOG_ERR, "Can't alloc vote info msg from list. "
+ "Disconnecting client connection.");
+
+ return (-1);
+ }
+
+ if (msg_create_vote_info(&send_buffer->buffer, msg_seq_number, ring_id, vote) == 0) {
+ qnetd_log(LOG_ERR, "Can't alloc vote info msg. "
+ "Disconnecting client connection.");
+
+ send_buffer_list_discard_new(&client->send_buffer_list, send_buffer);
+ return (-1);
+ };
+
+ send_buffer_list_put(&client->send_buffer_list, send_buffer);
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNETD_CLIENT_SEND_H_
+#define _QNETD_CLIENT_SEND_H_
+
+#include <sys/types.h>
+
+#include "qnetd-client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qnetd_client_send_err(struct qnetd_client *client,
+ int add_msg_seq_number, uint32_t msg_seq_number, enum tlv_reply_error_code reply);
+
+extern int qnetd_client_send_vote_info(struct qnetd_client *client,
+ uint32_t msg_seq_number, const struct tlv_ring_id *ring_id, enum tlv_vote vote);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_CLIENT_SEND_H_ */
--- /dev/null
+/*
+ * 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 <sys/types.h>
+
+#include <string.h>
+
+#include "qnet-config.h"
+#include "qnetd-client.h"
+
+void
+qnetd_client_init(struct qnetd_client *client, PRFileDesc *sock, PRNetAddr *addr,
+ char *addr_str,
+ size_t max_receive_size, size_t max_send_buffers, size_t max_send_size,
+ struct timer_list *main_timer_list)
+{
+
+ memset(client, 0, sizeof(*client));
+ client->socket = sock;
+ client->addr_str = addr_str;
+ memcpy(&client->addr, addr, sizeof(*addr));
+ dynar_init(&client->receive_buffer, max_receive_size);
+ send_buffer_list_init(&client->send_buffer_list, max_send_buffers, max_send_size);
+ node_list_init(&client->configuration_node_list);
+ node_list_init(&client->last_membership_node_list);
+ node_list_init(&client->last_quorum_node_list);
+ client->main_timer_list = main_timer_list;
+}
+
+void
+qnetd_client_destroy(struct qnetd_client *client)
+{
+
+ free(client->cluster_name);
+ free(client->addr_str);
+ node_list_free(&client->last_quorum_node_list);
+ node_list_free(&client->last_membership_node_list);
+ node_list_free(&client->configuration_node_list);
+ send_buffer_list_free(&client->send_buffer_list);
+ dynar_destroy(&client->receive_buffer);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QNETD_CLIENT_H_
+#define _QNETD_CLIENT_H_
+
+#include <sys/types.h>
+
+#include <sys/queue.h>
+#include <inttypes.h>
+
+#include <nspr.h>
+#include "dynar.h"
+#include "tlv.h"
+#include "send-buffer-list.h"
+#include "node-list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qnetd_client {
+ PRFileDesc *socket;
+ PRNetAddr addr;
+ char *addr_str;
+ struct dynar receive_buffer;
+ struct send_buffer_list send_buffer_list;
+ size_t msg_already_received_bytes;
+ int skipping_msg; /* When incorrect message was received skip it */
+ int tls_started; /* Set after TLS started */
+ int tls_peer_certificate_verified; /* Certificate is verified only once */
+ int preinit_received;
+ int init_received;
+ char *cluster_name;
+ size_t cluster_name_len;
+ uint8_t node_id_set;
+ uint32_t node_id;
+ enum tlv_decision_algorithm_type decision_algorithm;
+ struct tlv_tie_breaker tie_breaker;
+ uint32_t heartbeat_interval;
+ enum tlv_reply_error_code skipping_msg_reason;
+ void *algorithm_data;
+ struct node_list configuration_node_list;
+ uint8_t config_version_set;
+ uint64_t config_version;
+ struct node_list last_membership_node_list;
+ struct node_list last_quorum_node_list;
+ struct tlv_ring_id last_ring_id;
+ struct qnetd_cluster *cluster;
+ struct qnetd_cluster_list *cluster_list;
+ struct timer_list *main_timer_list;
+ struct timer_list_entry *algo_timer;
+ uint32_t algo_timer_vote_info_msq_seq_number;
+ int schedule_disconnect;
+ uint32_t dpd_time_since_last_check;
+ uint32_t dpd_msg_received_since_last_check;
+ enum tlv_vote last_sent_vote;
+ enum tlv_vote last_sent_ack_nack_vote;
+ enum tlv_heuristics last_membership_heuristics; /* Passed in membership node list */
+ enum tlv_heuristics last_regular_heuristics; /* Passed in heuristics change callback */
+ enum tlv_heuristics last_heuristics; /* Latest heuristics both membership and regular */
+ TAILQ_ENTRY(qnetd_client) entries;
+ TAILQ_ENTRY(qnetd_client) cluster_entries;
+};
+
+extern void qnetd_client_init(struct qnetd_client *client, PRFileDesc *sock,
+ PRNetAddr *addr, char *addr_str, size_t max_receive_size, size_t max_send_buffers,
+ size_t max_send_size, struct timer_list *main_timer_list);
+
+extern void qnetd_client_destroy(struct qnetd_client *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_CLIENT_H_ */
--- /dev/null
+/*
+ * 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 <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qnetd-cluster-list.h"
+
+void
+qnetd_cluster_list_init(struct qnetd_cluster_list *list)
+{
+
+ TAILQ_INIT(list);
+}
+
+struct qnetd_cluster *
+qnetd_cluster_list_find_by_name(struct qnetd_cluster_list *list,
+ const char *cluster_name, size_t cluster_name_len)
+{
+ struct qnetd_cluster *cluster;
+
+ TAILQ_FOREACH(cluster, list, entries) {
+ if (cluster->cluster_name_len == cluster_name_len &&
+ memcmp(cluster->cluster_name, cluster_name, cluster_name_len) == 0) {
+ return (cluster);
+ }
+ }
+
+ return (NULL);
+}
+
+struct qnetd_cluster *
+qnetd_cluster_list_add_client(struct qnetd_cluster_list *list, struct qnetd_client *client)
+{
+ struct qnetd_cluster *cluster;
+
+ cluster = qnetd_cluster_list_find_by_name(list, client->cluster_name,
+ client->cluster_name_len);
+ if (cluster == NULL) {
+ cluster = (struct qnetd_cluster *)malloc(sizeof(*cluster));
+ if (cluster == NULL) {
+ return (NULL);
+ }
+
+ if (qnetd_cluster_init(cluster, client->cluster_name,
+ client->cluster_name_len) != 0) {
+ free(cluster);
+
+ return (NULL);
+ }
+
+ TAILQ_INSERT_TAIL(list, cluster, entries);
+ }
+
+ TAILQ_INSERT_TAIL(&cluster->client_list, client, cluster_entries);
+
+ return (cluster);
+}
+
+void
+qnetd_cluster_list_del_client(struct qnetd_cluster_list *list, struct qnetd_cluster *cluster,
+ struct qnetd_client *client)
+{
+
+ TAILQ_REMOVE(&cluster->client_list, client, cluster_entries);
+
+ if (TAILQ_EMPTY(&cluster->client_list)) {
+ TAILQ_REMOVE(list, cluster, entries);
+
+ qnetd_cluster_destroy(cluster);
+ free(cluster);
+ }
+}
+
+void
+qnetd_cluster_list_free(struct qnetd_cluster_list *list)
+{
+ struct qnetd_cluster *cluster;
+ struct qnetd_cluster *cluster_next;
+
+ cluster = TAILQ_FIRST(list);
+ while (cluster != NULL) {
+ cluster_next = TAILQ_NEXT(cluster, entries);
+
+ qnetd_cluster_destroy(cluster);
+ free(cluster);
+
+ cluster = cluster_next;
+ }
+
+ TAILQ_INIT(list);
+}
+
+size_t
+qnetd_cluster_list_size(const struct qnetd_cluster_list *list)
+{
+ size_t res;
+ struct qnetd_cluster *cluster;
+
+ res = 0;
+
+ TAILQ_FOREACH(cluster, list, entries) {
+ res++;
+ }
+
+ return (res);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNETD_CLUSTER_LIST_H_
+#define _QNETD_CLUSTER_LIST_H_
+
+#include <sys/types.h>
+
+#include <sys/queue.h>
+#include <inttypes.h>
+
+#include "qnetd-client-list.h"
+#include "qnetd-cluster.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+TAILQ_HEAD(qnetd_cluster_list, qnetd_cluster);
+
+extern void qnetd_cluster_list_init(struct qnetd_cluster_list *list);
+
+extern struct qnetd_cluster *qnetd_cluster_list_find_by_name(
+ struct qnetd_cluster_list *list, const char *cluster_name, size_t cluster_name_len);
+
+extern struct qnetd_cluster *qnetd_cluster_list_add_client(
+ struct qnetd_cluster_list *list, struct qnetd_client *client);
+
+extern void qnetd_cluster_list_del_client(
+ struct qnetd_cluster_list *list, struct qnetd_cluster *cluster, struct qnetd_client *client);
+
+extern void qnetd_cluster_list_free(struct qnetd_cluster_list *list);
+
+extern size_t qnetd_cluster_list_size(
+ const struct qnetd_cluster_list *list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_CLUSTER_LIST_H_ */
--- /dev/null
+/*
+ * 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 <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "qnetd-cluster.h"
+
+int
+qnetd_cluster_init(struct qnetd_cluster *cluster, const char *cluster_name, size_t cluster_name_len)
+{
+
+ memset(cluster, 0, sizeof(*cluster));
+
+ cluster->cluster_name = malloc(cluster_name_len + 1);
+ if (cluster->cluster_name == NULL) {
+ return (-1);
+ }
+ memset(cluster->cluster_name, 0, cluster_name_len + 1);
+ memcpy(cluster->cluster_name, cluster_name, cluster_name_len);
+
+ cluster->cluster_name_len = cluster_name_len;
+ TAILQ_INIT(&cluster->client_list);
+
+ return (0);
+}
+
+void
+qnetd_cluster_destroy(struct qnetd_cluster *cluster)
+{
+
+ free(cluster->cluster_name);
+ cluster->cluster_name = NULL;
+}
+
+size_t
+qnetd_cluster_size(const struct qnetd_cluster *cluster)
+{
+ size_t res;
+ struct qnetd_client *client;
+
+ res = 0;
+
+ TAILQ_FOREACH(client, &cluster->client_list, cluster_entries) {
+ res++;
+ }
+
+ return (res);
+}
+
+struct qnetd_client *
+qnetd_cluster_find_client_by_node_id(const struct qnetd_cluster *cluster, uint32_t node_id)
+{
+ struct qnetd_client *client;
+
+ TAILQ_FOREACH(client, &cluster->client_list, cluster_entries) {
+ if (client->node_id == node_id) {
+ return (client);
+ }
+ }
+
+ return (NULL);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNETD_CLUSTER_H_
+#define _QNETD_CLUSTER_H_
+
+#include <sys/types.h>
+
+#include <sys/queue.h>
+#include <inttypes.h>
+
+#include "tlv.h"
+#include "qnetd-client-list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qnetd_cluster {
+ char *cluster_name;
+ size_t cluster_name_len;
+ void *algorithm_data;
+ struct qnetd_client_list client_list;
+ TAILQ_ENTRY(qnetd_cluster) entries;
+};
+
+extern int qnetd_cluster_init(struct qnetd_cluster *cluster,
+ const char *cluster_name, size_t cluster_name_len);
+
+extern void qnetd_cluster_destroy(struct qnetd_cluster *cluster);
+
+extern size_t qnetd_cluster_size(const struct qnetd_cluster *cluster);
+
+extern struct qnetd_client *qnetd_cluster_find_client_by_node_id(
+ const struct qnetd_cluster *cluster, uint32_t node_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_CLUSTER_H_ */
--- /dev/null
+/*
+ * 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 "qnetd-dpd-timer.h"
+#include "qnetd-log.h"
+
+static int
+qnetd_dpd_timer_cb(void *data1, void *data2)
+{
+ struct qnetd_instance *instance;
+ struct qnetd_client *client;
+
+ instance = (struct qnetd_instance *)data1;
+
+ TAILQ_FOREACH(client, &instance->clients, entries) {
+ if (!client->init_received) {
+ continue;
+ }
+
+ client->dpd_time_since_last_check += instance->advanced_settings->dpd_interval;
+
+ if (client->dpd_time_since_last_check > (client->heartbeat_interval * 2)) {
+ if (!client->dpd_msg_received_since_last_check) {
+ qnetd_log(LOG_WARNING, "Client %s doesn't sent any message during "
+ "%"PRIu32"ms. Disconnecting",
+ client->addr_str, client->dpd_time_since_last_check);
+
+ client->schedule_disconnect = 1;
+ } else {
+ client->dpd_time_since_last_check = 0;
+ client->dpd_msg_received_since_last_check = 0;
+ }
+ }
+ }
+
+ return (-1);
+}
+
+int
+qnetd_dpd_timer_init(struct qnetd_instance *instance)
+{
+
+ if (!instance->advanced_settings->dpd_enabled) {
+ return (0);
+ }
+
+ instance->dpd_timer = timer_list_add(&instance->main_timer_list,
+ instance->advanced_settings->dpd_interval,
+ qnetd_dpd_timer_cb, (void *)instance, NULL);
+ if (instance->dpd_timer == NULL) {
+ qnetd_log(LOG_ERR, "Can't initialize dpd timer");
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+qnetd_dpd_timer_destroy(struct qnetd_instance *instance)
+{
+
+ if (instance->dpd_timer != NULL) {
+ timer_list_delete(&instance->main_timer_list, instance->dpd_timer);
+ instance->dpd_timer = NULL;
+ }
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNETD_DPD_TIMER_H_
+#define _QNETD_DPD_TIMER_H_
+
+#include "qnetd-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qnetd_dpd_timer_init(struct qnetd_instance *instance);
+
+extern void qnetd_dpd_timer_destroy(struct qnetd_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_DPD_TIMER_H_ */
--- /dev/null
+/*
+ * 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 <sys/types.h>
+
+#include <pk11func.h>
+#include "qnetd-instance.h"
+#include "qnetd-client.h"
+#include "qnetd-algorithm.h"
+#include "qnetd-log-debug.h"
+#include "qnetd-dpd-timer.h"
+#include "qnetd-poll-array-user-data.h"
+#include "qnetd-client-algo-timer.h"
+
+int
+qnetd_instance_init(struct qnetd_instance *instance,
+ enum tlv_tls_supported tls_supported, int tls_client_cert_required, size_t max_clients,
+ const struct qnetd_advanced_settings *advanced_settings)
+{
+
+ memset(instance, 0, sizeof(*instance));
+
+ instance->advanced_settings = advanced_settings;
+
+ pr_poll_array_init(&instance->poll_array, sizeof(struct qnetd_poll_array_user_data));
+ qnetd_client_list_init(&instance->clients);
+ qnetd_cluster_list_init(&instance->clusters);
+
+ instance->tls_supported = tls_supported;
+ instance->tls_client_cert_required = tls_client_cert_required;
+
+ instance->max_clients = max_clients;
+
+ timer_list_init(&instance->main_timer_list);
+
+ if (qnetd_dpd_timer_init(instance) != 0) {
+ return (0);
+ }
+
+ return (0);
+}
+
+int
+qnetd_instance_destroy(struct qnetd_instance *instance)
+{
+ struct qnetd_client *client;
+ struct qnetd_client *client_next;
+
+ qnetd_dpd_timer_destroy(instance);
+
+ client = TAILQ_FIRST(&instance->clients);
+ while (client != NULL) {
+ client_next = TAILQ_NEXT(client, entries);
+
+ qnetd_instance_client_disconnect(instance, client, 1);
+
+ client = client_next;
+ }
+
+ pr_poll_array_destroy(&instance->poll_array);
+ qnetd_cluster_list_free(&instance->clusters);
+ qnetd_client_list_free(&instance->clients);
+ timer_list_free(&instance->main_timer_list);
+
+ return (0);
+}
+
+void
+qnetd_instance_client_disconnect(struct qnetd_instance *instance, struct qnetd_client *client,
+ int server_going_down)
+{
+
+ qnetd_log_debug_client_disconnect(client, server_going_down);
+
+ if (client->init_received) {
+ qnetd_algorithm_client_disconnect(client, server_going_down);
+ }
+
+ PR_Close(client->socket);
+ if (client->cluster != NULL) {
+ qnetd_cluster_list_del_client(&instance->clusters, client->cluster, client);
+ }
+ qnetd_client_algo_timer_abort(client);
+ qnetd_client_list_del(&instance->clients, client);
+}
+
+int
+qnetd_instance_init_certs(struct qnetd_instance *instance)
+{
+
+ instance->server.cert = PK11_FindCertFromNickname(
+ instance->advanced_settings->cert_nickname, NULL);
+ if (instance->server.cert == NULL) {
+ return (-1);
+ }
+
+ instance->server.private_key = PK11_FindKeyByAnyCert(instance->server.cert, NULL);
+ if (instance->server.private_key == NULL) {
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNETD_INSTANCE_H_
+#define _QNETD_INSTANCE_H_
+
+#include <sys/types.h>
+
+#include <certt.h>
+#include <keyhi.h>
+#include <sys/queue.h>
+
+#include "qnetd-client-list.h"
+#include "qnetd-cluster-list.h"
+#include "pr-poll-array.h"
+#include "qnet-config.h"
+#include "timer-list.h"
+#include "unix-socket-ipc.h"
+#include "qnetd-advanced-settings.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qnetd_instance {
+ struct {
+ PRFileDesc *socket;
+ CERTCertificate *cert;
+ SECKEYPrivateKey *private_key;
+ } server;
+ size_t max_clients;
+ struct qnetd_client_list clients;
+ struct qnetd_cluster_list clusters;
+ struct pr_poll_array poll_array;
+ enum tlv_tls_supported tls_supported;
+ int tls_client_cert_required;
+ const char *host_addr;
+ uint16_t host_port;
+ struct timer_list main_timer_list;
+ struct timer_list_entry *dpd_timer; /* Dead peer detection timer */
+ struct unix_socket_ipc local_ipc;
+ PRFileDesc *ipc_socket_poll_fd;
+ const struct qnetd_advanced_settings *advanced_settings;
+};
+
+extern int qnetd_instance_init(struct qnetd_instance *instance,
+ enum tlv_tls_supported tls_supported, int tls_client_cert_required, size_t max_clients,
+ const struct qnetd_advanced_settings *advanced_settings);
+
+extern int qnetd_instance_destroy(struct qnetd_instance *instance);
+
+extern void qnetd_instance_client_disconnect(struct qnetd_instance *instance,
+ struct qnetd_client *client, int server_going_down);
+
+extern int qnetd_instance_init_certs(struct qnetd_instance *instance);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_INSTANCE_H_ */
--- /dev/null
+/*
+ * 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 "dynar-str.h"
+#include "qnetd-ipc-cmd.h"
+#include "qnetd-log.h"
+#include "utils.h"
+
+int
+qnetd_ipc_cmd_status(struct qnetd_instance *instance, struct dynar *outbuf, int verbose)
+{
+
+ if (dynar_str_catf(outbuf, "QNetd address:\t\t\t%s:%"PRIu16"\n",
+ (instance->host_addr != NULL ? instance->host_addr : "*"), instance->host_port) == -1) {
+ return (-1);
+ }
+
+ if (dynar_str_catf(outbuf, "TLS:\t\t\t\t%s%s\n",
+ tlv_tls_supported_to_str(instance->tls_supported),
+ ((instance->tls_supported != TLV_TLS_UNSUPPORTED && instance->tls_client_cert_required) ?
+ " (client certificate required)" : "")) == -1) {
+ return (-1);
+ }
+
+ if (dynar_str_catf(outbuf, "Connected clients:\t\t%zu\n",
+ qnetd_client_list_no_clients(&instance->clients)) == -1) {
+ return (-1);
+ }
+
+ if (dynar_str_catf(outbuf, "Connected clusters:\t\t%zu\n",
+ qnetd_cluster_list_size(&instance->clusters)) == -1) {
+ return (-1);
+ }
+
+ if (!verbose) {
+ return (0);
+ }
+
+ if (instance->max_clients != 0) {
+ if (dynar_str_catf(outbuf, "Maximum allowed clients:\t%zu\n",
+ instance->max_clients) == -1) {
+ return (-1);
+ }
+ }
+
+ if (dynar_str_catf(outbuf, "Maximum send/receive size:\t%zu/%zu bytes\n",
+ instance->advanced_settings->max_client_send_size,
+ instance->advanced_settings->max_client_receive_size) == -1) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+qnetd_ipc_cmd_add_tie_breaker(const struct qnetd_client *client,
+ struct dynar *outbuf)
+{
+
+ if (dynar_str_catf(outbuf, " Tie-breaker:\t") == -1) {
+ return (0);
+ }
+
+ switch (client->tie_breaker.mode) {
+ case TLV_TIE_BREAKER_MODE_LOWEST:
+ if (dynar_str_catf(outbuf, "Node with lowest node ID") == -1) {
+ return (0);
+ }
+ break;
+ case TLV_TIE_BREAKER_MODE_HIGHEST:
+ if (dynar_str_catf(outbuf, "Node with highest node ID") == -1) {
+ return (0);
+ }
+ break;
+ case TLV_TIE_BREAKER_MODE_NODE_ID:
+ if (dynar_str_catf(outbuf, "Node with node ID "UTILS_PRI_NODE_ID,
+ client->tie_breaker.node_id) == -1) {
+ return (0);
+ }
+ break;
+ }
+
+ return (dynar_str_catf(outbuf, "\n") != -1);
+}
+
+static int
+qnetd_ipc_cmd_list_add_node_list(struct dynar *outbuf, int verbose, const struct node_list *nlist)
+{
+ struct node_list_entry *node_info;
+ int i;
+
+ i = 0;
+
+ TAILQ_FOREACH(node_info, nlist, entries) {
+ if (i != 0) {
+ if (dynar_str_catf(outbuf, ", ") == -1) {
+ return (-1);
+ }
+ }
+
+ if (dynar_str_catf(outbuf, UTILS_PRI_NODE_ID, node_info->node_id) == -1) {
+ return (-1);
+ }
+
+ if (node_info->data_center_id != 0) {
+ if (dynar_str_catf(outbuf, " (" UTILS_PRI_DATACENTER_ID ")",
+ node_info->data_center_id) == -1) {
+ return (-1);
+ }
+
+ }
+
+ i++;
+ }
+
+ return (0);
+}
+
+static int
+qnetd_ipc_cmd_list_add_client_info(const struct qnetd_client *client, struct dynar *outbuf,
+ int verbose, size_t client_no)
+{
+
+ if (dynar_str_catf(outbuf, " Node ID "UTILS_PRI_NODE_ID":\n",
+ client->node_id) == -1) {
+ return (-1);
+ }
+
+ if (dynar_str_catf(outbuf, " Client address:\t\t%s\n",
+ client->addr_str) == -1) {
+ return (-1);
+ }
+
+ if (verbose) {
+ if (dynar_str_catf(outbuf, " HB interval:\t\t%"PRIu32"ms\n",
+ client->heartbeat_interval) == -1) {
+ return (-1);
+ }
+ }
+
+ if (client->config_version_set) {
+ if (dynar_str_catf(outbuf, " Configuration version:\t"
+ UTILS_PRI_CONFIG_VERSION"\n", client->config_version) == -1) {
+ return (-1);
+ }
+ }
+
+ if (!node_list_is_empty(&client->configuration_node_list)) {
+ if ((dynar_str_catf(outbuf, " Configured node list:\t") == -1) ||
+ (qnetd_ipc_cmd_list_add_node_list(outbuf, verbose,
+ &client->configuration_node_list) == -1) ||
+ (dynar_str_catf(outbuf, "\n") == -1)) {
+ return (-1);
+ }
+ }
+
+ if (verbose) {
+ if (dynar_str_catf(outbuf, " Ring ID:\t\t"UTILS_PRI_RING_ID"\n",
+ client->last_ring_id.node_id, client->last_ring_id.seq) == -1) {
+ return (-1);
+ }
+ }
+
+ if (!node_list_is_empty(&client->last_membership_node_list)) {
+ if ((dynar_str_catf(outbuf, " Membership node list:\t") == -1) ||
+ (qnetd_ipc_cmd_list_add_node_list(outbuf, verbose,
+ &client->last_membership_node_list) == -1) ||
+ (dynar_str_catf(outbuf, "\n") == -1)) {
+ return (-1);
+ }
+ }
+
+ if (client->last_heuristics != TLV_HEURISTICS_UNDEFINED || verbose) {
+ if (dynar_str_catf(outbuf, " Heuristics:\t\t%s",
+ tlv_heuristics_to_str(client->last_heuristics)) == -1) {
+ return (-1);
+ }
+
+ if (verbose) {
+ if (dynar_str_catf(outbuf, " (membership: %s, regular: %s)",
+ tlv_heuristics_to_str(client->last_membership_heuristics),
+ tlv_heuristics_to_str(client->last_regular_heuristics)) == -1) {
+ return (-1);
+ }
+ }
+
+ if (dynar_str_catf(outbuf, "\n") == -1) {
+ return (-1);
+ }
+ }
+
+ if (verbose) {
+ if (dynar_str_catf(outbuf, " TLS active:\t\t%s",
+ (client->tls_started ? "Yes" : "No")) == -1) {
+ return (-1);
+ }
+
+ if (client->tls_started && client->tls_peer_certificate_verified) {
+ if (dynar_str_catf(outbuf, " (client certificate verified)") == -1) {
+ return (-1);
+ }
+ }
+
+ if (dynar_str_catf(outbuf, "\n") == -1) {
+ return (-1);
+ }
+ }
+
+ if (client->last_sent_vote != TLV_VOTE_UNDEFINED) {
+ if (dynar_str_catf(outbuf, " Vote:\t\t\t%s",
+ tlv_vote_to_str(client->last_sent_vote)) == -1) {
+ return (-1);
+ }
+
+ if (client->last_sent_ack_nack_vote != TLV_VOTE_UNDEFINED) {
+ if (dynar_str_catf(outbuf, " (%s)",
+ tlv_vote_to_str(client->last_sent_ack_nack_vote)) == -1) {
+ return (-1);
+ }
+ }
+
+ if (dynar_str_catf(outbuf, "\n") == -1) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+int
+qnetd_ipc_cmd_list(struct qnetd_instance *instance, struct dynar *outbuf, int verbose,
+ const char *cluster_name)
+{
+ struct qnetd_cluster *cluster;
+ struct qnetd_client *client;
+ size_t cluster_no, client_no;
+
+ cluster_no = 0;
+ TAILQ_FOREACH(cluster, &instance->clusters, entries) {
+ if (cluster_name != NULL && strcmp(cluster_name, "") != 0 &&
+ strcmp(cluster_name, cluster->cluster_name) != 0) {
+ continue;
+ }
+
+ if (dynar_str_catf(outbuf, "Cluster \"%s\":\n", cluster->cluster_name) == -1) {
+ return (-1);
+ }
+
+ client_no = 0;
+
+ TAILQ_FOREACH(client, &cluster->client_list, cluster_entries) {
+ if (client_no == 0) {
+ if (dynar_str_catf(outbuf, " Algorithm:\t\t%s\n",
+ tlv_decision_algorithm_type_to_str(
+ client->decision_algorithm)) == -1) {
+ return (-1);
+ }
+
+ if (!qnetd_ipc_cmd_add_tie_breaker(client, outbuf)) {
+ return (-1);
+ }
+ }
+
+ if (qnetd_ipc_cmd_list_add_client_info(client, outbuf, verbose,
+ client_no) == -1) {
+ return (-1);
+ }
+
+ client_no++;
+ }
+
+ cluster_no++;
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNETD_IPC_CMD_H_
+#define _QNETD_IPC_CMD_H_
+
+#include "dynar.h"
+#include "qnetd-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int qnetd_ipc_cmd_status(struct qnetd_instance *instance,
+ struct dynar *outbuf, int verbose);
+
+extern int qnetd_ipc_cmd_list(struct qnetd_instance *instance,
+ struct dynar *outbuf, int verbose, const char *cluster_name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_IPC_CMD_H_ */
--- /dev/null
+/*
+ * 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 "qnet-config.h"
+#include "qnetd-ipc.h"
+#include "qnetd-ipc-cmd.h"
+#include "qnetd-log.h"
+#include "unix-socket-ipc.h"
+#include "dynar-simple-lex.h"
+#include "dynar-str.h"
+
+/*
+ * Needed for creating nspr handle from unix fd
+ */
+#include <private/pprio.h>
+
+int
+qnetd_ipc_init(struct qnetd_instance *instance)
+{
+
+ if (unix_socket_ipc_init(&instance->local_ipc,
+ instance->advanced_settings->local_socket_file,
+ instance->advanced_settings->local_socket_backlog,
+ instance->advanced_settings->ipc_max_clients,
+ instance->advanced_settings->ipc_max_receive_size,
+ instance->advanced_settings->ipc_max_send_size) != 0) {
+ qnetd_log_err(LOG_ERR, "Can't create unix socket");
+
+ return (-1);
+ }
+
+ if ((instance->ipc_socket_poll_fd = PR_CreateSocketPollFd(instance->local_ipc.socket)) == NULL) {
+ qnetd_log_nss(LOG_CRIT, "Can't create NSPR IPC socket poll fd");
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+qnetd_ipc_close(struct qnetd_instance *instance)
+{
+ int res;
+
+ res = unix_socket_ipc_close(&instance->local_ipc);
+ if (res != 0) {
+ qnetd_log_err(LOG_WARNING, "Can't close local IPC");
+ }
+
+ return (res);
+}
+
+int
+qnetd_ipc_is_closed(struct qnetd_instance *instance)
+{
+
+ return (unix_socket_ipc_is_closed(&instance->local_ipc));
+}
+
+int
+qnetd_ipc_destroy(struct qnetd_instance *instance)
+{
+ int res;
+ struct unix_socket_client *client;
+ const struct unix_socket_client_list *ipc_client_list;
+
+ ipc_client_list = &instance->local_ipc.clients;
+
+ TAILQ_FOREACH(client, ipc_client_list, entries) {
+ free(client->user_data);
+ }
+
+ if (PR_DestroySocketPollFd(instance->ipc_socket_poll_fd) != PR_SUCCESS) {
+ qnetd_log_nss(LOG_WARNING, "Unable to destroy IPC poll socket fd");
+ }
+
+ res = unix_socket_ipc_destroy(&instance->local_ipc);
+ if (res != 0) {
+ qnetd_log_err(LOG_WARNING, "Can't destroy local IPC");
+ }
+
+ return (res);
+}
+
+int
+qnetd_ipc_accept(struct qnetd_instance *instance, struct unix_socket_client **res_client)
+{
+ int res;
+ int accept_res;
+ PRFileDesc *prfd;
+
+ accept_res = unix_socket_ipc_accept(&instance->local_ipc, res_client);
+
+ switch (accept_res) {
+ case -1:
+ qnetd_log_err(LOG_ERR, "Can't accept local IPC connection");
+ res = -1;
+ goto return_res;
+ break;
+ case -2:
+ qnetd_log(LOG_ERR, "Maximum IPC clients reached. Not accepting connection");
+ res = -1;
+ goto return_res;
+ break;
+ case -3:
+ qnetd_log(LOG_ERR, "Can't add client to list");
+ res = -1;
+ goto return_res;
+ break;
+ default:
+ unix_socket_client_read_line(*res_client, 1);
+ res = 0;
+ break;
+ }
+
+ (*res_client)->user_data = malloc(sizeof(struct qnetd_ipc_user_data));
+ if ((*res_client)->user_data == NULL) {
+ qnetd_log(LOG_ERR, "Can't alloc IPC client user data");
+ res = -1;
+ qnetd_ipc_client_disconnect(instance, *res_client);
+
+ goto return_res;
+ }
+ memset((*res_client)->user_data, 0, sizeof(struct qnetd_ipc_user_data));
+
+ prfd = PR_CreateSocketPollFd((*res_client)->socket);
+ if (prfd == NULL) {
+ qnetd_log_nss(LOG_CRIT, "Can't create NSPR poll fd for IPC client. Disconnecting client");
+ qnetd_ipc_client_disconnect(instance, *res_client);
+ res = -1;
+
+ goto return_res;
+ }
+
+ ((struct qnetd_ipc_user_data *)(*res_client)->user_data)->nspr_poll_fd = prfd;
+
+return_res:
+ return (res);
+}
+
+void
+qnetd_ipc_client_disconnect(struct qnetd_instance *instance, struct unix_socket_client *client)
+{
+
+ if (PR_DestroySocketPollFd(
+ ((struct qnetd_ipc_user_data *)(client)->user_data)->nspr_poll_fd) != PR_SUCCESS) {
+ qnetd_log_nss(LOG_WARNING, "Unable to destroy client IPC poll socket fd");
+ }
+
+ free(client->user_data);
+ unix_socket_ipc_client_disconnect(&instance->local_ipc, client);
+}
+
+int
+qnetd_ipc_send_error(struct qnetd_instance *instance, struct unix_socket_client *client,
+ const char *error_fmt, ...)
+{
+ va_list ap;
+ int res;
+
+ va_start(ap, error_fmt);
+ res = ((dynar_str_cpy(&client->send_buffer, "Error\n") == 0) &&
+ (dynar_str_vcatf(&client->send_buffer, error_fmt, ap) > 0) &&
+ (dynar_str_cat(&client->send_buffer, "\n") == 0));
+
+ va_end(ap);
+
+ if (res) {
+ unix_socket_client_write_buffer(client, 1);
+ } else {
+ qnetd_log(LOG_ERR, "Can't send ipc error to client (buffer too small)");
+ }
+
+ return (res ? 0 : -1);
+}
+
+int
+qnetd_ipc_send_buffer(struct qnetd_instance *instance, struct unix_socket_client *client)
+{
+
+ if (dynar_str_prepend(&client->send_buffer, "OK\n") != 0) {
+ qnetd_log(LOG_ERR, "Can't send ipc message to client (buffer too small)");
+
+ if (qnetd_ipc_send_error(instance, client, "Internal IPC buffer too small") != 0) {
+ return (-1);
+ }
+
+ return (0);
+ }
+
+ unix_socket_client_write_buffer(client, 1);
+
+ return (0);
+}
+
+static void
+qnetd_ipc_parse_line(struct qnetd_instance *instance, struct unix_socket_client *client)
+{
+ struct dynar_simple_lex lex;
+ struct dynar *token;
+ char *str;
+ struct qnetd_ipc_user_data *ipc_user_data;
+ int verbose;
+ char *cluster_name;
+
+ ipc_user_data = (struct qnetd_ipc_user_data *)client->user_data;
+
+ dynar_simple_lex_init(&lex, &client->receive_buffer, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
+ token = dynar_simple_lex_token_next(&lex);
+
+ verbose = 0;
+ cluster_name = NULL;
+
+ if (token == NULL) {
+ goto exit_err_low_mem;
+ }
+
+ str = dynar_data(token);
+ if (strcasecmp(str, "") == 0) {
+ qnetd_log(LOG_DEBUG, "IPC client doesn't send command");
+ if (qnetd_ipc_send_error(instance, client, "No command specified") != 0) {
+ client->schedule_disconnect = 1;
+ }
+ } else if (strcasecmp(str, "shutdown") == 0) {
+ qnetd_log(LOG_DEBUG, "IPC client requested shutdown");
+
+ ipc_user_data->shutdown_requested = 1;
+
+ if (qnetd_ipc_send_buffer(instance, client) != 0) {
+ client->schedule_disconnect = 1;
+ }
+ } else if (strcasecmp(str, "status") == 0) {
+ token = dynar_simple_lex_token_next(&lex);
+ if (token == NULL) {
+ goto exit_err_low_mem;
+ }
+
+ str = dynar_data(token);
+
+ if (token != NULL && strcmp(str, "") != 0) {
+ if (strcasecmp(str, "verbose") == 0) {
+ verbose = 1;
+ }
+ }
+
+ if (qnetd_ipc_cmd_status(instance, &client->send_buffer, verbose) != 0) {
+ if (qnetd_ipc_send_error(instance, client, "Can't get QNetd status") != 0) {
+ client->schedule_disconnect = 1;
+ }
+ } else {
+ if (qnetd_ipc_send_buffer(instance, client) != 0) {
+ client->schedule_disconnect = 1;
+ }
+ }
+ } else if (strcasecmp(str, "list") == 0) {
+ while (((token = dynar_simple_lex_token_next(&lex)) != NULL) &&
+ (str = dynar_data(token), strcmp(str, "") != 0)) {
+ if (strcasecmp(str, "verbose") == 0) {
+ verbose = 1;
+ } else if (strcasecmp(str, "cluster") == 0) {
+ token = dynar_simple_lex_token_next(&lex);
+ if (token == NULL) {
+ goto exit_err_low_mem;
+ }
+
+ free(cluster_name); cluster_name = NULL;
+ if ((cluster_name = strdup(dynar_data(token))) == NULL) {
+ goto exit_err_low_mem;
+ }
+ } else {
+ break;
+ }
+ }
+
+ if (qnetd_ipc_cmd_list(instance, &client->send_buffer, verbose, cluster_name) != 0) {
+ if (qnetd_ipc_send_error(instance, client, "Can't get QNetd cluster list") != 0) {
+ client->schedule_disconnect = 1;
+ }
+ } else {
+ if (qnetd_ipc_send_buffer(instance, client) != 0) {
+ client->schedule_disconnect = 1;
+ }
+ }
+
+ free(cluster_name); cluster_name = NULL;
+ } else {
+ qnetd_log(LOG_DEBUG, "IPC client sent unknown command");
+ if (qnetd_ipc_send_error(instance, client, "Unknown command '%s'", str) != 0) {
+ client->schedule_disconnect = 1;
+ }
+ }
+
+ dynar_simple_lex_destroy(&lex);
+
+ return ;
+
+exit_err_low_mem:
+ free(cluster_name); cluster_name = NULL;
+
+ qnetd_log(LOG_ERR, "Can't alloc memory for simple lex");
+
+ if (qnetd_ipc_send_error(instance, client, "Command too long") != 0) {
+ client->schedule_disconnect = 1;
+ }
+}
+
+void
+qnetd_ipc_io_read(struct qnetd_instance *instance, struct unix_socket_client *client)
+{
+ int res;
+
+ res = unix_socket_client_io_read(client);
+
+ switch (res) {
+ case 0:
+ /*
+ * Partial read
+ */
+ break;
+ case -1:
+ qnetd_log(LOG_DEBUG, "IPC client closed connection");
+ client->schedule_disconnect = 1;
+ break;
+ case -2:
+ qnetd_log(LOG_ERR, "Can't store message from IPC client. Disconnecting client.");
+ client->schedule_disconnect = 1;
+ break;
+ case -3:
+ qnetd_log_err(LOG_ERR, "Can't receive message from IPC client. Disconnecting client.");
+ client->schedule_disconnect = 1;
+ break;
+ case 1:
+ /*
+ * Full message received
+ */
+ unix_socket_client_read_line(client, 0);
+
+ qnetd_ipc_parse_line(instance, client);
+ break;
+ }
+}
+
+void
+qnetd_ipc_io_write(struct qnetd_instance *instance, struct unix_socket_client *client)
+{
+ int res;
+ struct qnetd_ipc_user_data *ipc_user_data;
+
+ ipc_user_data = (struct qnetd_ipc_user_data *)client->user_data;
+
+ res = unix_socket_client_io_write(client);
+
+ switch (res) {
+ case 0:
+ /*
+ * Partial send
+ */
+ break;
+ case -1:
+ qnetd_log(LOG_DEBUG, "IPC client closed connection");
+ client->schedule_disconnect = 1;
+ break;
+ case -2:
+ qnetd_log_err(LOG_ERR, "Can't send message to IPC client. Disconnecting client");
+ client->schedule_disconnect = 1;
+ break;
+ case 1:
+ /*
+ * Full message sent
+ */
+ unix_socket_client_write_buffer(client, 0);
+ client->schedule_disconnect = 1;
+
+ if (ipc_user_data->shutdown_requested) {
+ qnetd_ipc_close(instance);
+ }
+
+ break;
+ }
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNETD_IPC_H_
+#define _QNETD_IPC_H_
+
+#include "qnetd-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qnetd_ipc_user_data {
+ int shutdown_requested;
+ PRFileDesc *nspr_poll_fd;
+};
+
+extern int qnetd_ipc_init(struct qnetd_instance *instance);
+
+extern int qnetd_ipc_close(struct qnetd_instance *instance);
+
+extern int qnetd_ipc_is_closed(struct qnetd_instance *instance);
+
+extern int qnetd_ipc_destroy(struct qnetd_instance *instance);
+
+extern int qnetd_ipc_accept(struct qnetd_instance *instance,
+ struct unix_socket_client **res_client);
+
+extern void qnetd_ipc_client_disconnect(struct qnetd_instance *instance,
+ struct unix_socket_client *client);
+
+extern void qnetd_ipc_io_read(struct qnetd_instance *instance,
+ struct unix_socket_client *client);
+
+extern void qnetd_ipc_io_write(struct qnetd_instance *instance,
+ struct unix_socket_client *client);
+
+extern int qnetd_ipc_send_error(struct qnetd_instance *instance,
+ struct unix_socket_client *client, const char *error_fmt, ...)
+ __attribute__((__format__(__printf__, 3, 4)));
+
+extern int qnetd_ipc_send_buffer(struct qnetd_instance *instance,
+ struct unix_socket_client *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_IPC_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 "qnetd-log.h"
+#include "qnetd-log-debug.h"
+#include "utils.h"
+
+void
+qnetd_log_debug_dump_cluster(struct qnetd_cluster *cluster)
+{
+ struct qnetd_client *client;
+
+ qnetd_log(LOG_DEBUG, " cluster dump:");
+ TAILQ_FOREACH(client, &cluster->client_list, cluster_entries) {
+ qnetd_log(LOG_DEBUG, " client = %s, node_id = "UTILS_PRI_NODE_ID,
+ client->addr_str, client->node_id);
+ }
+}
+
+void
+qnetd_log_debug_new_client_connected(struct qnetd_client *client)
+{
+
+ qnetd_log(LOG_DEBUG, "New client connected");
+ qnetd_log(LOG_DEBUG, " cluster name = %s", client->cluster_name);
+ qnetd_log(LOG_DEBUG, " tls started = %u", client->tls_started);
+ qnetd_log(LOG_DEBUG, " tls peer certificate verified = %u",
+ client->tls_peer_certificate_verified);
+ qnetd_log(LOG_DEBUG, " node_id = "UTILS_PRI_NODE_ID, client->node_id);
+ qnetd_log(LOG_DEBUG, " pointer = %p", client);
+ qnetd_log(LOG_DEBUG, " addr_str = %s", client->addr_str);
+ qnetd_log(LOG_DEBUG, " ring id = (" UTILS_PRI_RING_ID ")", client->last_ring_id.node_id,
+ client->last_ring_id.seq);
+
+ qnetd_log_debug_dump_cluster(client->cluster);
+}
+
+void
+qnetd_log_debug_dump_node_list(struct qnetd_client *client, const struct node_list *nodes)
+{
+ struct node_list_entry *node_info;
+
+ qnetd_log(LOG_DEBUG, " node list:");
+ TAILQ_FOREACH(node_info, nodes, entries) {
+ qnetd_log(LOG_DEBUG, " node_id = "UTILS_PRI_NODE_ID", "
+ "data_center_id = "UTILS_PRI_DATACENTER_ID", "
+ "node_state = %s", node_info->node_id, node_info->data_center_id,
+ tlv_node_state_to_str(node_info->node_state));
+ }
+}
+
+void
+qnetd_log_debug_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)
+{
+
+ qnetd_log(LOG_DEBUG, "Client %s (cluster %s, node_id "UTILS_PRI_NODE_ID") "
+ "sent %s node list.", client->addr_str, client->cluster_name, client->node_id,
+ (initial ? "initial" : "changed"));
+
+ qnetd_log(LOG_DEBUG, " msg seq num = "UTILS_PRI_MSG_SEQ, msg_seq_num);
+
+ if (config_version_set) {
+ qnetd_log(LOG_DEBUG, " config version = " UTILS_PRI_CONFIG_VERSION, config_version);
+ }
+
+ qnetd_log_debug_dump_node_list(client, nodes);
+}
+
+void
+qnetd_log_debug_membership_node_list_received(struct qnetd_client *client,
+ uint32_t msg_seq_num, const struct tlv_ring_id *ring_id,
+ enum tlv_heuristics heuristics, const struct node_list *nodes)
+{
+ qnetd_log(LOG_DEBUG, "Client %s (cluster %s, node_id "UTILS_PRI_NODE_ID") "
+ "sent membership node list.", client->addr_str, client->cluster_name, client->node_id);
+
+ qnetd_log(LOG_DEBUG, " msg seq num = "UTILS_PRI_MSG_SEQ, msg_seq_num);
+
+ qnetd_log(LOG_DEBUG, " ring id = (" UTILS_PRI_RING_ID ")", ring_id->node_id, ring_id->seq);
+
+ qnetd_log(LOG_DEBUG, " heuristics = %s ", tlv_heuristics_to_str(heuristics));
+
+ qnetd_log_debug_dump_node_list(client, nodes);
+}
+
+void
+qnetd_log_debug_quorum_node_list_received(struct qnetd_client *client,
+ uint32_t msg_seq_num, enum tlv_quorate quorate, const struct node_list *nodes)
+{
+
+ qnetd_log(LOG_DEBUG, "Client %s (cluster %s, node_id "UTILS_PRI_NODE_ID") "
+ "sent quorum node list.", client->addr_str, client->cluster_name, client->node_id);
+
+ qnetd_log(LOG_DEBUG, " msg seq num = "UTILS_PRI_MSG_SEQ, msg_seq_num);
+ qnetd_log(LOG_DEBUG, " quorate = %u", quorate);
+
+ qnetd_log_debug_dump_node_list(client, nodes);
+}
+
+void
+qnetd_log_debug_client_disconnect(struct qnetd_client *client, int server_going_down)
+{
+
+ qnetd_log(LOG_DEBUG, "Client %s (init_received %u, cluster %s, node_id "
+ UTILS_PRI_NODE_ID") disconnect%s", client->addr_str, client->init_received,
+ client->cluster_name, client->node_id,
+ (server_going_down ? " (server is going down)" : ""));
+}
+
+void
+qnetd_log_debug_ask_for_vote_received(struct qnetd_client *client, uint32_t msg_seq_num)
+{
+
+ qnetd_log(LOG_DEBUG, "Client %s (cluster %s, node_id "UTILS_PRI_NODE_ID") "
+ "asked for a vote", client->addr_str, client->cluster_name, client->node_id);
+ qnetd_log(LOG_DEBUG, " msg seq num = "UTILS_PRI_MSG_SEQ, msg_seq_num);
+}
+
+void
+qnetd_log_debug_vote_info_reply_received(struct qnetd_client *client, uint32_t msg_seq_num)
+{
+
+ qnetd_log(LOG_DEBUG, "Client %s (cluster %s, node_id "UTILS_PRI_NODE_ID") "
+ "replied back to vote info message", client->addr_str, client->cluster_name,
+ client->node_id);
+ qnetd_log(LOG_DEBUG, " msg seq num = "UTILS_PRI_MSG_SEQ, msg_seq_num);
+}
+
+void
+qnetd_log_debug_send_vote_info(struct qnetd_client *client, uint32_t msg_seq_num, enum tlv_vote vote)
+{
+
+ qnetd_log(LOG_DEBUG, "Sending vote info to client %s (cluster %s, node_id "UTILS_PRI_NODE_ID") ",
+ client->addr_str, client->cluster_name, client->node_id);
+ qnetd_log(LOG_DEBUG, " msg seq num = "UTILS_PRI_MSG_SEQ, msg_seq_num);
+ qnetd_log(LOG_DEBUG, " vote = %s", tlv_vote_to_str(vote));
+}
+
+void
+qnetd_log_debug_heuristics_change_received(struct qnetd_client *client, uint32_t msg_seq_num,
+ enum tlv_heuristics heuristics)
+{
+
+ qnetd_log(LOG_DEBUG, "Client %s (cluster %s, node_id "UTILS_PRI_NODE_ID") "
+ "sent heuristics change", client->addr_str, client->cluster_name, client->node_id);
+ qnetd_log(LOG_DEBUG, " msg seq num = "UTILS_PRI_MSG_SEQ, msg_seq_num);
+ qnetd_log(LOG_DEBUG, " heuristics = %s", tlv_heuristics_to_str(heuristics));
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _QNETD_LOG_DEBUG_H_
+#define _QNETD_LOG_DEBUG_H_
+
+#include "qnetd-client.h"
+#include "qnetd-cluster-list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void qnetd_log_debug_dump_cluster(struct qnetd_cluster *cluster);
+
+extern void qnetd_log_debug_new_client_connected(struct qnetd_client *client);
+
+extern void qnetd_log_debug_dump_node_list(struct qnetd_client *client,
+ const struct node_list *nodes);
+
+extern void qnetd_log_debug_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);
+
+extern void qnetd_log_debug_membership_node_list_received(struct qnetd_client *client,
+ uint32_t msg_seq_num, const struct tlv_ring_id *ring_id,
+ enum tlv_heuristics heuristics, const struct node_list *nodes);
+
+extern void qnetd_log_debug_quorum_node_list_received(struct qnetd_client *client,
+ uint32_t msg_seq_num, enum tlv_quorate quorate, const struct node_list *nodes);
+
+extern void qnetd_log_debug_client_disconnect(struct qnetd_client *client,
+ int server_going_down);
+
+extern void qnetd_log_debug_ask_for_vote_received(struct qnetd_client *client,
+ uint32_t msg_seq_num);
+
+extern void qnetd_log_debug_vote_info_reply_received(struct qnetd_client *client,
+ uint32_t msg_seq_num);
+
+extern void qnetd_log_debug_send_vote_info(struct qnetd_client *client,
+ uint32_t msg_seq_num, enum tlv_vote vote);
+
+extern void qnetd_log_debug_heuristics_change_received(struct qnetd_client *client,
+ uint32_t msg_seq_num, enum tlv_heuristics heuristics);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_LOG_DEBUG_H_ */
--- /dev/null
+/*
+ * 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 <syslog.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "qnet-config.h"
+#include "qnetd-log.h"
+
+static int qnetd_log_config_target = 0;
+static int qnetd_log_config_debug = 0;
+static int qnetd_log_config_priority_bump = 0;
+
+static const char qnetd_log_month_str[][4] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+struct qnetd_log_syslog_prio_to_str_item {
+ int priority;
+ const char *priority_str;
+};
+
+static struct qnetd_log_syslog_prio_to_str_item qnetd_syslog_prio_to_str_array[] = {
+ {LOG_EMERG, "emerg"},
+ {LOG_ALERT, "alert"},
+ {LOG_CRIT, "crit"},
+ {LOG_ERR, "error"},
+ {LOG_WARNING, "warning"},
+ {LOG_NOTICE, "notice"},
+ {LOG_INFO, "info"},
+ {LOG_DEBUG, "debug"},
+ {-1, NULL}};
+
+void
+qnetd_log_init(int target)
+{
+
+ qnetd_log_config_target = target;
+
+ if (qnetd_log_config_target & QNETD_LOG_TARGET_SYSLOG) {
+ openlog(QNETD_PROGRAM_NAME, LOG_PID, LOG_DAEMON);
+ }
+}
+
+static const char *
+qnetd_log_syslog_prio_to_str(int priority)
+{
+
+ if (priority >= LOG_EMERG && priority <= LOG_DEBUG) {
+ return (qnetd_syslog_prio_to_str_array[priority].priority_str);
+ } else {
+ return ("none");
+ }
+}
+
+void
+qnetd_log_vprintf(int priority, const char *format, va_list ap)
+{
+ time_t current_time;
+ struct tm tm_res;
+ int final_priority;
+ va_list ap_copy;
+
+ if (priority != LOG_DEBUG || (qnetd_log_config_debug)) {
+ if (qnetd_log_config_target & QNETD_LOG_TARGET_STDERR) {
+ current_time = time(NULL);
+ localtime_r(¤t_time, &tm_res);
+
+ fprintf(stderr, "%s %02d %02d:%02d:%02d ",
+ qnetd_log_month_str[tm_res.tm_mon], tm_res.tm_mday, tm_res.tm_hour,
+ tm_res.tm_min, tm_res.tm_sec);
+
+ fprintf(stderr, "%-7s ", qnetd_log_syslog_prio_to_str(priority));
+
+ va_copy(ap_copy, ap);
+ vfprintf(stderr, format, ap_copy);
+ va_end(ap_copy);
+ fprintf(stderr, "\n");
+ }
+
+ if (qnetd_log_config_target & QNETD_LOG_TARGET_SYSLOG) {
+ final_priority = priority;
+ if (qnetd_log_config_priority_bump && priority > LOG_INFO) {
+ final_priority = LOG_INFO;
+ }
+
+ va_copy(ap_copy, ap);
+ vsyslog(final_priority, format, ap);
+ va_end(ap_copy);
+ }
+ }
+}
+
+void
+qnetd_log_printf(int priority, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ qnetd_log_vprintf(priority, format, ap);
+
+ va_end(ap);
+}
+
+void
+qnetd_log_close(void)
+{
+
+ if (qnetd_log_config_target & QNETD_LOG_TARGET_SYSLOG) {
+ closelog();
+ }
+}
+
+void
+qnetd_log_set_debug(int enabled)
+{
+
+ qnetd_log_config_debug = enabled;
+}
+
+void
+qnetd_log_set_priority_bump(int enabled)
+{
+
+ qnetd_log_config_priority_bump = enabled;
+}
+
+void
+qnetd_log_msg_decode_error(int ret)
+{
+
+ switch (ret) {
+ case -1:
+ qnetd_log(LOG_WARNING, "Received message with option with invalid length");
+ break;
+ case -2:
+ qnetd_log(LOG_CRIT, "Can't allocate memory");
+ break;
+ case -3:
+ qnetd_log(LOG_WARNING, "Received inconsistent msg (tlv len > msg size)");
+ break;
+ case -4:
+ qnetd_log(LOG_WARNING, "Received message with option with invalid value");
+ break;
+ default:
+ qnetd_log(LOG_ERR, "Unknown error occurred when decoding message");
+ break;
+ }
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNETD_LOG_H_
+#define _QNETD_LOG_H_
+
+#include <syslog.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define QNETD_LOG_TARGET_STDERR 1
+#define QNETD_LOG_TARGET_SYSLOG 2
+
+#define qnetd_log(...) qnetd_log_printf(__VA_ARGS__)
+#define qnetd_log_nss(priority, str) qnetd_log_printf(priority, "%s (%d): %s", \
+ str, PR_GetError(), PR_ErrorToString(PR_GetError(), PR_LANGUAGE_I_DEFAULT));
+
+#define qnetd_log_err(priority, str) qnetd_log_printf(priority, "%s (%d): %s", \
+ str, errno, strerror(errno))
+
+extern void qnetd_log_init(int target);
+
+extern void qnetd_log_printf(int priority, const char *format, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+
+extern void qnetd_log_vprintf(int priority, const char *format, va_list ap)
+ __attribute__((__format__(__printf__, 2, 0)));
+
+extern void qnetd_log_close(void);
+
+extern void qnetd_log_set_debug(int enabled);
+
+extern void qnetd_log_set_priority_bump(int enabled);
+
+extern void qnetd_log_msg_decode_error(int ret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_LOG_H_ */
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _QNETD_POLL_ARRAY_USER_DATA_H_
+#define _QNETD_POLL_ARRAY_USER_DATA_H_
+
+#include "qnetd-client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum qnetd_poll_array_user_data_type {
+ QNETD_POLL_ARRAY_USER_DATA_TYPE_SOCKET,
+ QNETD_POLL_ARRAY_USER_DATA_TYPE_CLIENT,
+ QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET,
+ QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT,
+};
+
+struct qnetd_poll_array_user_data {
+ enum qnetd_poll_array_user_data_type type;
+ struct qnetd_client *client;
+ struct unix_socket_client *ipc_client;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_POLL_ARRAY_USER_DATA_H_ */
--- /dev/null
+/*
+ * 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 <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "send-buffer-list.h"
+
+void
+send_buffer_list_init(struct send_buffer_list *sblist, size_t max_list_entries,
+ size_t max_buffer_size)
+{
+
+ memset(sblist, 0, sizeof(*sblist));
+
+ sblist->max_list_entries = max_list_entries;
+ sblist->allocated_list_entries = 0;
+ sblist->max_buffer_size = max_buffer_size;
+ TAILQ_INIT(&sblist->list);
+ TAILQ_INIT(&sblist->free_list);
+}
+
+struct send_buffer_list_entry *
+send_buffer_list_get_new(struct send_buffer_list *sblist)
+{
+ struct send_buffer_list_entry *entry;
+
+ if (!TAILQ_EMPTY(&sblist->free_list)) {
+ /*
+ * Use free list entry
+ */
+ entry = TAILQ_FIRST(&sblist->free_list);
+ TAILQ_REMOVE(&sblist->free_list, entry, entries);
+
+ dynar_clean(&entry->buffer);
+ dynar_set_max_size(&entry->buffer, sblist->max_buffer_size);
+ } else {
+ if (sblist->allocated_list_entries + 1 > sblist->max_list_entries) {
+ return (NULL);
+ }
+
+ sblist->allocated_list_entries++;
+
+ /*
+ * Alloc new entry
+ */
+ entry = malloc(sizeof(*entry));
+ if (entry == NULL) {
+ return (NULL);
+ }
+
+ dynar_init(&entry->buffer, sblist->max_buffer_size);
+ }
+
+ entry->msg_already_sent_bytes = 0;
+
+ return (entry);
+}
+
+void
+send_buffer_list_put(struct send_buffer_list *sblist, struct send_buffer_list_entry *sblist_entry)
+{
+
+ TAILQ_INSERT_TAIL(&sblist->list, sblist_entry, entries);
+}
+
+void
+send_buffer_list_discard_new(struct send_buffer_list *sblist, struct send_buffer_list_entry *sblist_entry)
+{
+
+ TAILQ_INSERT_HEAD(&sblist->free_list, sblist_entry, entries);
+}
+
+struct send_buffer_list_entry *
+send_buffer_list_get_active(const struct send_buffer_list *sblist)
+{
+ struct send_buffer_list_entry *entry;
+
+ entry = TAILQ_FIRST(&sblist->list);
+
+ return (entry);
+}
+
+void
+send_buffer_list_delete(struct send_buffer_list *sblist,
+ struct send_buffer_list_entry *sblist_entry)
+{
+
+ /*
+ * Move item to free list
+ */
+ TAILQ_REMOVE(&sblist->list, sblist_entry, entries);
+ TAILQ_INSERT_HEAD(&sblist->free_list, sblist_entry, entries);
+}
+
+int
+send_buffer_list_empty(const struct send_buffer_list *sblist)
+{
+
+ return (TAILQ_EMPTY(&sblist->list));
+}
+
+void
+send_buffer_list_free(struct send_buffer_list *sblist)
+{
+ struct send_buffer_list_entry *entry;
+ struct send_buffer_list_entry *entry_next;
+
+ entry = TAILQ_FIRST(&sblist->list);
+
+ while (entry != NULL) {
+ entry_next = TAILQ_NEXT(entry, entries);
+
+ dynar_destroy(&entry->buffer);
+ free(entry);
+
+ entry = entry_next;
+ }
+
+ entry = TAILQ_FIRST(&sblist->free_list);
+
+ while (entry != NULL) {
+ entry_next = TAILQ_NEXT(entry, entries);
+
+ dynar_destroy(&entry->buffer);
+ free(entry);
+
+ entry = entry_next;
+ }
+
+ sblist->allocated_list_entries = 0;
+ TAILQ_INIT(&sblist->list);
+ TAILQ_INIT(&sblist->free_list);
+}
+
+void
+send_buffer_list_set_max_buffer_size(struct send_buffer_list *sblist, size_t max_buffer_size)
+{
+
+ sblist->max_buffer_size = max_buffer_size;
+}
+
+void
+send_buffer_list_set_max_list_entries(struct send_buffer_list *sblist, size_t max_list_entries)
+{
+
+ sblist->max_list_entries = max_list_entries;
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _SEND_BUFFER_LIST_H_
+#define _SEND_BUFFER_LIST_H_
+
+#include <sys/queue.h>
+
+#include "dynar.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct send_buffer_list_entry {
+ struct dynar buffer;
+ size_t msg_already_sent_bytes;
+
+ TAILQ_ENTRY(send_buffer_list_entry) entries;
+};
+
+struct send_buffer_list {
+ size_t max_list_entries;
+ size_t allocated_list_entries;
+
+ size_t max_buffer_size;
+
+ TAILQ_HEAD(, send_buffer_list_entry) list;
+ TAILQ_HEAD(, send_buffer_list_entry) free_list;
+};
+
+extern void send_buffer_list_init(struct send_buffer_list *sblist,
+ size_t max_list_entries, size_t max_buffer_size);
+
+extern struct send_buffer_list_entry *send_buffer_list_get_new(struct send_buffer_list *sblist);
+
+extern void send_buffer_list_put(struct send_buffer_list *sblist,
+ struct send_buffer_list_entry *sblist_entry);
+
+extern void send_buffer_list_discard_new(
+ struct send_buffer_list *sblist, struct send_buffer_list_entry *sblist_entry);
+
+extern struct send_buffer_list_entry *send_buffer_list_get_active(
+ const struct send_buffer_list *sblist);
+
+extern void send_buffer_list_delete(struct send_buffer_list *sblist,
+ struct send_buffer_list_entry *sblist_entry);
+
+extern int send_buffer_list_empty(
+ const struct send_buffer_list *sblist);
+
+extern void send_buffer_list_free(struct send_buffer_list *sblist);
+
+extern void send_buffer_list_set_max_buffer_size(
+ struct send_buffer_list *sblist, size_t max_buffer_size);
+
+extern void send_buffer_list_set_max_list_entries(
+ struct send_buffer_list *sblist, size_t max_list_entries);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SEND_BUFFER_LIST_H_ */
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "dynar.h"
+#include "dynar-str.h"
+#include "dynar-getopt-lex.h"
+
+int
+main(void)
+{
+ struct dynar input_str;
+ struct dynar_getopt_lex lex;
+
+ dynar_init(&input_str, 128);
+
+ assert(dynar_str_catf(&input_str, "option=value") != -1);
+ dynar_getopt_lex_init(&lex, &input_str);
+ assert(dynar_getopt_lex_token_next(&lex) == 0);
+ assert(strcmp(dynar_data(&lex.option), "option") == 0);
+ assert(strcmp(dynar_data(&lex.value), "value") == 0);
+ assert(dynar_getopt_lex_token_next(&lex) == 0);
+ assert(strcmp(dynar_data(&lex.option), "") == 0);
+ assert(strcmp(dynar_data(&lex.value), "") == 0);
+ dynar_getopt_lex_destroy(&lex);
+
+ assert(dynar_str_cpy(&input_str, "") == 0);
+ assert(dynar_str_catf(&input_str, "option1=value1,option2=value2") != -1);
+ dynar_getopt_lex_init(&lex, &input_str);
+ assert(dynar_getopt_lex_token_next(&lex) == 0);
+ assert(strcmp(dynar_data(&lex.option), "option1") == 0);
+ assert(strcmp(dynar_data(&lex.value), "value1") == 0);
+ assert(dynar_getopt_lex_token_next(&lex) == 0);
+ assert(strcmp(dynar_data(&lex.option), "option2") == 0);
+ assert(strcmp(dynar_data(&lex.value), "value2") == 0);
+ assert(dynar_getopt_lex_token_next(&lex) == 0);
+ assert(strcmp(dynar_data(&lex.option), "") == 0);
+ assert(strcmp(dynar_data(&lex.value), "") == 0);
+ dynar_getopt_lex_destroy(&lex);
+
+ assert(dynar_str_cpy(&input_str, "") == 0);
+ assert(dynar_str_catf(&input_str, "option1=value1,option2=value2,option3=value3") != -1);
+ dynar_getopt_lex_init(&lex, &input_str);
+ assert(dynar_getopt_lex_token_next(&lex) == 0);
+ assert(strcmp(dynar_data(&lex.option), "option1") == 0);
+ assert(strcmp(dynar_data(&lex.value), "value1") == 0);
+ assert(dynar_getopt_lex_token_next(&lex) == 0);
+ assert(strcmp(dynar_data(&lex.option), "option2") == 0);
+ assert(strcmp(dynar_data(&lex.value), "value2") == 0);
+ assert(dynar_getopt_lex_token_next(&lex) == 0);
+ assert(strcmp(dynar_data(&lex.option), "option3") == 0);
+ assert(strcmp(dynar_data(&lex.value), "value3") == 0);
+ assert(dynar_getopt_lex_token_next(&lex) == 0);
+ assert(strcmp(dynar_data(&lex.option), "") == 0);
+ assert(strcmp(dynar_data(&lex.value), "") == 0);
+ dynar_getopt_lex_destroy(&lex);
+
+ assert(dynar_str_cpy(&input_str, "") == 0);
+ assert(dynar_str_catf(&input_str, "option1,option2=value2") != -1);
+ dynar_getopt_lex_init(&lex, &input_str);
+ assert(dynar_getopt_lex_token_next(&lex) == 0);
+ assert(strcmp(dynar_data(&lex.option), "option1") == 0);
+ assert(strcmp(dynar_data(&lex.value), "") == 0);
+ assert(dynar_getopt_lex_token_next(&lex) == 0);
+ assert(strcmp(dynar_data(&lex.option), "option2") == 0);
+ assert(strcmp(dynar_data(&lex.value), "value2") == 0);
+ assert(dynar_getopt_lex_token_next(&lex) == 0);
+ assert(strcmp(dynar_data(&lex.option), "") == 0);
+ assert(strcmp(dynar_data(&lex.value), "") == 0);
+ dynar_getopt_lex_destroy(&lex);
+
+ dynar_destroy(&input_str);
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "dynar.h"
+#include "dynar-str.h"
+#include "dynar-simple-lex.h"
+
+int
+main(void)
+{
+ struct dynar input_str;
+ struct dynar_simple_lex lex;
+ struct dynar *output_str_ptr;
+ struct dynar output_str;
+ const char *cstr;
+
+ dynar_init(&input_str, 128);
+
+ assert(dynar_str_catf(&input_str, "token1 token2") != -1);
+ dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_PLAIN);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "token1") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "token2") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ dynar_simple_lex_destroy(&lex);
+
+ assert(dynar_str_cpy(&input_str, "") == 0);
+ assert(dynar_str_catf(&input_str, " token1 token2 ") != -1);
+ dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_PLAIN);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "token1") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "token2") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ dynar_simple_lex_destroy(&lex);
+
+ assert(dynar_str_cpy(&input_str, "") == 0);
+ assert(dynar_str_catf(&input_str, " token1 token2 \ntoken3") != -1);
+ dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_PLAIN);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "token1") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "token2") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ dynar_simple_lex_destroy(&lex);
+
+ assert(dynar_str_cpy(&input_str, "") == 0);
+ assert(dynar_str_catf(&input_str, "\\ab\\cd e\\fg\\ h i\\") != -1);
+ dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_PLAIN);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "\\ab\\cd") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "e\\fg\\") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "h") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "i\\") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ dynar_simple_lex_destroy(&lex);
+
+ assert(dynar_str_cpy(&input_str, "") == 0);
+ assert(dynar_str_catf(&input_str, " a b\rc") != -1);
+ dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_PLAIN);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "a") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "b") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ dynar_simple_lex_destroy(&lex);
+
+ assert(dynar_str_cpy(&input_str, "") == 0);
+ assert(dynar_str_catf(&input_str, "\\ab\\\\cd e\\fg\\ h i\\") != -1);
+ dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_BACKSLASH);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "ab\\cd") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "efg h") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "i") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ dynar_simple_lex_destroy(&lex);
+
+ assert(dynar_str_cpy(&input_str, "") == 0);
+ assert(dynar_str_catf(&input_str, "ab\\\\cd e\\fg\\ h ij\\\na") != -1);
+ dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_BACKSLASH);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "ab\\cd") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "efg h") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "ij") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ dynar_simple_lex_destroy(&lex);
+
+ assert(dynar_str_cpy(&input_str, "") == 0);
+ assert(dynar_str_catf(&input_str, " a b\\\rc") != -1);
+ dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_BACKSLASH);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "a") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "b") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ dynar_simple_lex_destroy(&lex);
+
+ assert(dynar_str_cpy(&input_str, "") == 0);
+ assert(dynar_str_catf(&input_str, "abc def \"ghi\" jkl \"m n o\"") != -1);
+ dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "abc") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "def") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "ghi") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "jkl") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "m n o") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ dynar_simple_lex_destroy(&lex);
+
+ assert(dynar_str_cpy(&input_str, "") == 0);
+ assert(dynar_str_catf(&input_str, "a\\bc \"d\\e \\\"f\\\\ \\\" \"g hij") != -1);
+ dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "abc") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "d\\e \"f\\ \" g") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "hij") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ dynar_simple_lex_destroy(&lex);
+
+ assert(dynar_str_cpy(&input_str, "") == 0);
+ assert(dynar_str_catf(&input_str, "abc \"d e \r\n") != -1);
+ dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "abc") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "d e ") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ dynar_simple_lex_destroy(&lex);
+
+ assert(dynar_str_cpy(&input_str, "") == 0);
+ assert(dynar_str_catf(&input_str, "abc \"d e \\\"\\\r\n") != -1);
+ dynar_simple_lex_init(&lex, &input_str, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "abc") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "d e \"") == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ dynar_simple_lex_destroy(&lex);
+
+ dynar_init(&output_str, 128);
+ assert(dynar_str_quote_cpy(&output_str, "abcd") == 0);
+ assert(dynar_size(&output_str) == 6);
+ assert(memcmp(dynar_data(&output_str), "\"abcd\"", dynar_size(&output_str)) == 0);
+ assert(dynar_str_cat(&output_str, " ") == 0);
+ assert(dynar_str_quote_cat(&output_str, "abcd") == 0);
+ assert(dynar_size(&output_str) == 13);
+ assert(memcmp(dynar_data(&output_str), "\"abcd\" \"abcd\"", dynar_size(&output_str)) == 0);
+
+ assert(dynar_str_quote_cpy(&output_str, "ab\\cd") == 0);
+ assert(dynar_size(&output_str) == 8);
+ assert(memcmp(dynar_data(&output_str), "\"ab\\\\cd\"", dynar_size(&output_str)) == 0);
+
+ assert(dynar_str_quote_cpy(&output_str, "ab\\\\cd") == 0);
+ assert(dynar_size(&output_str) == 10);
+ assert(memcmp(dynar_data(&output_str), "\"ab\\\\\\\\cd\"", dynar_size(&output_str)) == 0);
+
+ assert(dynar_str_quote_cpy(&output_str, "ab cd \\\"e") == 0);
+ assert(dynar_size(&output_str) == 13);
+ assert(memcmp(dynar_data(&output_str), "\"ab cd \\\\\\\"e\"", dynar_size(&output_str)) == 0);
+
+ cstr = "ab cd \\ ef\\g h\"i";
+ assert(dynar_str_quote_cpy(&output_str, cstr) == 0);
+ dynar_simple_lex_init(&lex, &output_str, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strlen(cstr) == dynar_size(output_str_ptr) - 1);
+ assert(memcmp(cstr, dynar_data(output_str_ptr), strlen(cstr)) == 0);
+ assert((output_str_ptr = dynar_simple_lex_token_next(&lex)) != NULL);
+ assert(strcmp(dynar_data(output_str_ptr), "") == 0);
+ dynar_simple_lex_destroy(&lex);
+
+ dynar_destroy(&input_str);
+ dynar_destroy(&output_str);
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "dynar.h"
+#include "dynar-str.h"
+
+int
+main(void)
+{
+ struct dynar str;
+
+ dynar_init(&str, 3);
+ assert(dynar_cat(&str, "a", 1) == 0);
+ assert(dynar_size(&str) == 1);
+ assert(dynar_cat(&str, "b", 1) == 0);
+ assert(dynar_size(&str) == 2);
+ assert(dynar_cat(&str, "c", 1) == 0);
+ assert(dynar_size(&str) == 3);
+ assert(dynar_cat(&str, "d", 1) != 0);
+ assert(memcmp(dynar_data(&str), "abc", 3) == 0);
+
+ dynar_clean(&str);
+ assert(dynar_size(&str) == 0);
+ dynar_set_max_size(&str, 4);
+ assert(dynar_cat(&str, "a", 1) == 0);
+ assert(dynar_size(&str) == 1);
+ assert(memcmp(dynar_data(&str), "a", 1) == 0);
+ assert(dynar_cat(&str, "b", 1) == 0);
+ assert(dynar_size(&str) == 2);
+ assert(dynar_cat(&str, "c", 1) == 0);
+ assert(dynar_size(&str) == 3);
+ assert(dynar_cat(&str, "d", 1) == 0);
+ assert(dynar_size(&str) == 4);
+ assert(memcmp(dynar_data(&str), "abcd", 4) == 0);
+
+ assert(dynar_str_cpy(&str, "e") == 0);
+ assert(dynar_size(&str) == 1);
+ assert(memcmp(dynar_data(&str), "e", 1) == 0);
+
+ assert(dynar_str_cpy(&str, "fgh") == 0);
+ assert(dynar_size(&str) == 3);
+ assert(memcmp(dynar_data(&str), "fgh", 3) == 0);
+
+ assert(dynar_str_cpy(&str, "fghi") == 0);
+ assert(dynar_size(&str) == 4);
+ assert(memcmp(dynar_data(&str), "fghi", 4) == 0);
+
+ assert(dynar_str_cpy(&str, "fghij") != 0);
+ assert(dynar_size(&str) == 4);
+ assert(dynar_str_cat(&str, "a") != 0);
+
+ assert(dynar_str_cpy(&str, "") == 0);
+ assert(dynar_size(&str) == 0);
+
+ assert(dynar_str_cat(&str, "a") == 0);
+ assert(dynar_size(&str) == 1);
+ assert(memcmp(dynar_data(&str), "a", 1) == 0);
+
+ assert(dynar_str_cat(&str, "b") == 0);
+ assert(dynar_size(&str) == 2);
+ assert(memcmp(dynar_data(&str), "ab", 2) == 0);
+
+ assert(dynar_str_cat(&str, "cd") == 0);
+ assert(dynar_size(&str) == 4);
+ assert(memcmp(dynar_data(&str), "abcb", 1) == 0);
+
+ assert(dynar_str_cpy(&str, "") == 0);
+ assert(dynar_str_catf(&str, "%s", "a") == 1);
+ assert(memcmp(dynar_data(&str), "a", 1) == 0);
+ assert(dynar_str_catf(&str, "%s", "ab") == 2);
+ assert(memcmp(dynar_data(&str), "aab", 3) == 0);
+ assert(dynar_str_cpy(&str, "") == 0);
+ assert(dynar_str_catf(&str, "%s", "abc") == 3);
+ assert(dynar_str_cpy(&str, "") == 0);
+ assert(dynar_str_catf(&str, "%s", "abcd") == -1);
+ assert(dynar_str_cpy(&str, "a") == 0);
+ assert(dynar_str_catf(&str, "%s", "") == 0);
+ assert(memcmp(dynar_data(&str), "a", 1) == 0);
+
+ dynar_destroy(&str);
+ dynar_init(&str, 5);
+ assert(dynar_str_catf(&str, "%s", "abcd") == 4);
+ dynar_destroy(&str);
+ dynar_init(&str, 5);
+ assert(dynar_str_catf(&str, "%s", "") == 0);
+ assert(dynar_str_catf(&str, "%s", "abc") == 3);
+ assert(dynar_str_catf(&str, "%s", "d") == 1);
+ assert(memcmp(dynar_data(&str), "abcd", 4) == 0);
+ dynar_destroy(&str);
+
+ dynar_init(&str, 10);
+ assert(dynar_str_cat(&str, "abcd") == 0);
+ assert(memcmp(dynar_data(&str), "abcd", 4) == 0);
+ assert(dynar_str_prepend(&str, "e") == 0);
+ assert(dynar_size(&str) == 5);
+ assert(memcmp(dynar_data(&str), "eabcd", 5) == 0);
+ assert(dynar_str_prepend(&str, "fgh") == 0);
+ assert(dynar_size(&str) == 8);
+ assert(memcmp(dynar_data(&str), "fgheabcd", 8) == 0);
+ assert(dynar_str_prepend(&str, "ijk") != 0);
+ assert(dynar_size(&str) == 8);
+ assert(dynar_str_prepend(&str, "ij") == 0);
+ assert(dynar_size(&str) == 10);
+ assert(memcmp(dynar_data(&str), "ijfgheabcd", 10) == 0);
+ dynar_destroy(&str);
+
+ dynar_init(&str, 10);
+ assert(dynar_str_cat(&str, "abcd") == 0);
+ assert(memcmp(dynar_data(&str), "abcd", 4) == 0);
+ assert(dynar_str_prepend(&str, "ef") == 0);
+ assert(dynar_size(&str) == 6);
+ assert(memcmp(dynar_data(&str), "efabcd", 6) == 0);
+ assert(dynar_str_cat(&str, "ij") == 0);
+ assert(dynar_size(&str) == 8);
+ assert(memcmp(dynar_data(&str), "efabcdij", 8) == 0);
+ assert(dynar_str_prepend(&str, "k") == 0);
+ assert(dynar_size(&str) == 9);
+ assert(memcmp(dynar_data(&str), "kefabcdij", 9) == 0);
+ assert(dynar_str_cat(&str, "l") == 0);
+ assert(dynar_size(&str) == 10);
+ assert(memcmp(dynar_data(&str), "kefabcdijl", 10) == 0);
+ dynar_destroy(&str);
+
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <poll.h>
+#include <signal.h>
+
+#include "process-list.h"
+
+static int no_executed;
+static int no_finished;
+
+static void
+signal_handlers_register(void)
+{
+ struct sigaction act;
+
+ act.sa_handler = SIG_DFL;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+
+ sigaction(SIGCHLD, &act, NULL);
+
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+
+ sigaction(SIGPIPE, &act, NULL);
+}
+
+static void
+plist_notify(enum process_list_notify_reason reason, const struct process_list_entry *entry,
+ void *user_data)
+{
+
+ assert(user_data == (void *)0x42);
+
+ switch (reason) {
+ case PROCESS_LIST_NOTIFY_REASON_EXECUTED:
+ no_executed++;
+ break;
+ case PROCESS_LIST_NOTIFY_REASON_FINISHED:
+ no_finished++;
+ break;
+ }
+}
+
+int
+main(void)
+{
+ struct process_list plist;
+ struct process_list_entry *plist_entry;
+ int i;
+ int timeout;
+ int no_repeats;
+
+ signal_handlers_register();
+
+ process_list_init(&plist, 10, 1, plist_notify, (void *)0x42);
+ plist_entry = process_list_add(&plist, "test name", "command");
+ assert(plist_entry != NULL);
+ assert(strcmp(plist_entry->name, "test name") == 0);
+ assert(plist_entry->state == PROCESS_LIST_ENTRY_STATE_INITIALIZED);
+ assert(plist_entry->exec_argc == 1);
+ assert(plist_entry->exec_argv[0] != NULL && strcmp(plist_entry->exec_argv[0], "command") == 0);
+ assert(plist_entry->exec_argv[1] == NULL);
+
+ plist_entry = process_list_add(&plist, "test name", "/bin/ping -c \"host wit\\\"h space\" notaspace");
+ assert(plist_entry != NULL);
+ assert(strcmp(plist_entry->name, "test name") == 0);
+ assert(plist_entry->state == PROCESS_LIST_ENTRY_STATE_INITIALIZED);
+ assert(plist_entry->exec_argc == 4);
+ assert(plist_entry->exec_argv[0] != NULL && strcmp(plist_entry->exec_argv[0], "/bin/ping") == 0);
+ assert(plist_entry->exec_argv[1] != NULL && strcmp(plist_entry->exec_argv[1], "-c") == 0);
+ assert(plist_entry->exec_argv[2] != NULL && strcmp(plist_entry->exec_argv[2], "host wit\"h space") == 0);
+ assert(plist_entry->exec_argv[3] != NULL && strcmp(plist_entry->exec_argv[3], "notaspace") == 0);
+ assert(plist_entry->exec_argv[4] == NULL);
+
+ process_list_free(&plist);
+
+ /*
+ * Test no process
+ */
+ no_executed = 0;
+ no_finished = 0;
+ assert(process_list_exec_initialized(&plist) == 0);
+ assert(no_executed == 0);
+ assert(process_list_get_no_running(&plist) == 0);
+
+ /*
+ * Wait to exit
+ */
+ no_repeats = 10;
+ timeout = 1000 / no_repeats;
+ for (i = 0; i < no_repeats; i++) {
+ assert(process_list_waitpid(&plist) == 0);
+ if (process_list_get_no_running(&plist) > 0) {
+ poll(NULL, 0, timeout);
+ }
+ }
+
+ assert(process_list_waitpid(&plist) == 0);
+ assert(process_list_get_no_running(&plist) == 0);
+ assert(no_finished == 0);
+ assert(process_list_get_summary_result(&plist) == 0);
+ assert(process_list_get_summary_result_short(&plist) == 0);
+
+ process_list_free(&plist);
+
+ /*
+ * Test two processes. /bin/true and /bin/false. Accumulated result should be fail
+ */
+ plist_entry = process_list_add(&plist, "true", "/bin/true");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "false", "/bin/false");
+ assert(plist_entry != NULL);
+
+ no_executed = 0;
+ no_finished = 0;
+ assert(process_list_exec_initialized(&plist) == 0);
+ assert(no_executed == 2);
+ assert(process_list_get_no_running(&plist) == 2);
+
+ /*
+ * Wait to exit
+ */
+ no_repeats = 10;
+ timeout = 1000 / no_repeats;
+ for (i = 0; i < no_repeats; i++) {
+ assert(process_list_waitpid(&plist) == 0);
+ if (process_list_get_no_running(&plist) > 0) {
+ poll(NULL, 0, timeout);
+ }
+ }
+
+ assert(process_list_waitpid(&plist) == 0);
+ assert(process_list_get_no_running(&plist) == 0);
+ assert(no_finished == 2);
+ assert(process_list_get_summary_result(&plist) == 1);
+ assert(process_list_get_summary_result_short(&plist) == 1);
+
+ process_list_free(&plist);
+
+ /*
+ * Test two processes. /bin/true and one non-existing. Accumulated result should be fail
+ */
+ plist_entry = process_list_add(&plist, "true", "/bin/true");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "false", "/nonexistingdir/nonexistingfile");
+ assert(plist_entry != NULL);
+
+ no_executed = 0;
+ no_finished = 0;
+ assert(process_list_exec_initialized(&plist) == 0);
+ assert(no_executed == 2);
+ assert(process_list_get_no_running(&plist) == 2);
+
+ /*
+ * Wait to exit
+ */
+ no_repeats = 10;
+ timeout = 1000 / no_repeats;
+ for (i = 0; i < no_repeats; i++) {
+ assert(process_list_waitpid(&plist) == 0);
+ if (process_list_get_no_running(&plist) > 0) {
+ poll(NULL, 0, timeout);
+ }
+ }
+
+ assert(process_list_waitpid(&plist) == 0);
+ assert(process_list_get_no_running(&plist) == 0);
+ assert(no_finished == 2);
+ assert(process_list_get_summary_result(&plist) == 1);
+ assert(process_list_get_summary_result_short(&plist) == 1);
+
+ process_list_free(&plist);
+
+ /*
+ * Test three processes /bin/true. Accumulated result should be success.
+ */
+ plist_entry = process_list_add(&plist, "true", "/bin/true");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "true2", "/bin/true");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "true3", "/bin/true");
+ assert(plist_entry != NULL);
+
+ no_executed = 0;
+ no_finished = 0;
+ assert(process_list_exec_initialized(&plist) == 0);
+ assert(no_executed == 3);
+ assert(process_list_get_no_running(&plist) == 3);
+
+ /*
+ * Wait to exit
+ */
+ no_repeats = 10;
+ timeout = 1000 / no_repeats;
+ for (i = 0; i < no_repeats; i++) {
+ assert(process_list_waitpid(&plist) == 0);
+ if (process_list_get_no_running(&plist) > 0) {
+ poll(NULL, 0, timeout);
+ }
+ }
+
+ assert(process_list_waitpid(&plist) == 0);
+ assert(process_list_get_no_running(&plist) == 0);
+ assert(no_finished == 3);
+ assert(process_list_get_summary_result(&plist) == 0);
+ assert(process_list_get_summary_result_short(&plist) == 0);
+
+ process_list_free(&plist);
+
+ /*
+ * Test two processes. /bin/true and cat. Waiting for maximum of 2 sec
+ */
+ plist_entry = process_list_add(&plist, "true", "/bin/true");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "cat", "/bin/cat /dev/zero");
+ assert(plist_entry != NULL);
+
+ no_executed = 0;
+ no_finished = 0;
+ assert(process_list_exec_initialized(&plist) == 0);
+ assert(no_executed == 2);
+ assert(process_list_get_no_running(&plist) == 2);
+
+ poll(NULL, 0, 500);
+ assert(process_list_waitpid(&plist) == 0);
+ poll(NULL, 0, 500);
+ assert(process_list_waitpid(&plist) == 0);
+
+ assert(process_list_get_no_running(&plist) == 1);
+ assert(no_finished == 1);
+ assert(process_list_get_summary_result(&plist) == -1);
+ assert(process_list_get_summary_result_short(&plist) == -1);
+
+ process_list_move_active_entries_to_kill_list(&plist);
+ assert(process_list_process_kill_list(&plist) == 0);
+ poll(NULL, 0, 500);
+ assert(process_list_process_kill_list(&plist) == 0);
+ poll(NULL, 0, 500);
+ assert(process_list_waitpid(&plist) == 0);
+
+ assert(process_list_get_kill_list_items(&plist) == 0);
+
+ assert(process_list_process_kill_list(&plist) == 0);
+
+ process_list_free(&plist);
+
+ /*
+ * Test two bash proceses. One ignores INT and second ignores INT and TERM. Waiting for maximum of 2 sec
+ */
+ plist_entry = process_list_add(&plist, "ignoresig1", "bash -c \"trap 'echo trap' SIGINT;while true;do sleep 1;done\"");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "ignoresig2", "bash -c \"trap 'echo trap' SIGINT SIGTERM;while true;do sleep 1;done\"");
+ assert(plist_entry != NULL);
+
+ no_executed = 0;
+ no_finished = 0;
+ assert(process_list_exec_initialized(&plist) == 0);
+ assert(no_executed == 2);
+ assert(process_list_get_no_running(&plist) == 2);
+
+ poll(NULL, 0, 500);
+ assert(process_list_waitpid(&plist) == 0);
+
+ assert(process_list_get_no_running(&plist) == 2);
+ assert(no_finished == 0);
+ assert(process_list_get_summary_result(&plist) == -1);
+ assert(process_list_get_summary_result_short(&plist) == -1);
+
+ process_list_move_active_entries_to_kill_list(&plist);
+ assert(process_list_process_kill_list(&plist) == 0);
+ poll(NULL, 0, 500);
+ assert(process_list_waitpid(&plist) == 0);
+ assert(process_list_get_kill_list_items(&plist) == 1);
+
+ assert(process_list_process_kill_list(&plist) == 0);
+ poll(NULL, 0, 500);
+ assert(process_list_waitpid(&plist) == 0);
+ assert(process_list_get_kill_list_items(&plist) == 0);
+
+ process_list_free(&plist);
+
+ /*
+ * Test 3 processes. Test if entries are properly deallocated
+ */
+ process_list_init(&plist, 3, 1, plist_notify, (void *)0x42);
+ plist_entry = process_list_add(&plist, "true", "/bin/true");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "true2", "/bin/true");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "true3", "/bin/true");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "true4", "/bin/true");
+ assert(plist_entry == NULL);
+
+ no_executed = 0;
+ no_finished = 0;
+ assert(process_list_exec_initialized(&plist) == 0);
+ assert(no_executed == 3);
+ assert(process_list_get_no_running(&plist) == 3);
+
+ /*
+ * Wait to exit
+ */
+ no_repeats = 10;
+ timeout = 1000 / no_repeats;
+ for (i = 0; i < no_repeats; i++) {
+ assert(process_list_waitpid(&plist) == 0);
+ if (process_list_get_no_running(&plist) > 0) {
+ poll(NULL, 0, timeout);
+ }
+ }
+
+ assert(process_list_waitpid(&plist) == 0);
+ assert(process_list_get_no_running(&plist) == 0);
+ assert(no_finished == 3);
+ assert(process_list_get_summary_result(&plist) == 0);
+ assert(process_list_get_summary_result_short(&plist) == 0);
+
+ process_list_move_active_entries_to_kill_list(&plist);
+
+ plist_entry = process_list_add(&plist, "true", "/bin/true");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "ignoresig1", "bash -c \"trap 'echo trap' SIGINT;while true;do sleep 1;done\"");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "ignoresig2", "bash -c \"trap 'echo trap' SIGINT SIGTERM;while true;do sleep 1;done\"");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "true4", "/bin/true");
+ assert(plist_entry == NULL);
+
+ no_executed = 0;
+ no_finished = 0;
+ assert(process_list_exec_initialized(&plist) == 0);
+ assert(no_executed == 3);
+ assert(process_list_get_no_running(&plist) == 3);
+
+ poll(NULL, 0, 500);
+ assert(process_list_waitpid(&plist) == 0);
+
+ assert(process_list_get_no_running(&plist) == 2);
+ assert(no_finished == 1);
+ assert(process_list_get_summary_result(&plist) == -1);
+ assert(process_list_get_summary_result_short(&plist) == -1);
+
+ plist_entry = process_list_add(&plist, "true4", "/bin/true");
+ assert(plist_entry == NULL);
+
+ process_list_move_active_entries_to_kill_list(&plist);
+
+ plist_entry = process_list_add(&plist, "true4", "/bin/true");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "true5", "/bin/true");
+ assert(plist_entry == NULL);
+
+ assert(process_list_process_kill_list(&plist) == 0);
+ poll(NULL, 0, 500);
+ assert(process_list_waitpid(&plist) == 0);
+ assert(process_list_get_kill_list_items(&plist) == 1);
+
+ assert(process_list_process_kill_list(&plist) == 0);
+ poll(NULL, 0, 500);
+ assert(process_list_waitpid(&plist) == 0);
+ assert(process_list_get_kill_list_items(&plist) == 0);
+
+ process_list_move_active_entries_to_kill_list(&plist);
+ assert(process_list_get_summary_result(&plist) == 0);
+ assert(process_list_get_summary_result_short(&plist) == 0);
+
+ plist_entry = process_list_add(&plist, "true", "/bin/true");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "true2", "/bin/true");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "true3", "/bin/true");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "true4", "/bin/true");
+ assert(plist_entry == NULL);
+
+ process_list_free(&plist);
+
+ /*
+ * Test 3 processes and difference between summary and short-circuit summary
+ */
+ process_list_init(&plist, 3, 1, plist_notify, (void *)0x42);
+ plist_entry = process_list_add(&plist, "true", "/bin/true");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "false", "/bin/false");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "loop", "bash -c \"while true;do sleep 1;done\"");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "true4", "/bin/true");
+ assert(plist_entry == NULL);
+
+ no_executed = 0;
+ no_finished = 0;
+ assert(process_list_exec_initialized(&plist) == 0);
+ assert(no_executed == 3);
+ assert(process_list_get_no_running(&plist) == 3);
+
+ /*
+ * Wait to exit
+ */
+ no_repeats = 10;
+ timeout = 1000 / no_repeats;
+ for (i = 0; i < no_repeats; i++) {
+ assert(process_list_waitpid(&plist) == 0);
+ if (process_list_get_no_running(&plist) > 0) {
+ poll(NULL, 0, timeout);
+ }
+ }
+
+ assert(process_list_waitpid(&plist) == 0);
+ assert(process_list_get_no_running(&plist) == 1);
+ assert(no_finished == 2);
+ assert(process_list_get_summary_result(&plist) == -1);
+ assert(process_list_get_summary_result_short(&plist) == 1);
+
+ process_list_move_active_entries_to_kill_list(&plist);
+ assert(process_list_process_kill_list(&plist) == 0);
+ poll(NULL, 0, 500);
+ assert(process_list_waitpid(&plist) == 0);
+ assert(process_list_get_kill_list_items(&plist) == 0);
+
+ process_list_free(&plist);
+
+ /*
+ * Test process_list_killall by running two bash proceses.
+ * One ignores INT and second ignores INT and TERM. Waiting for maximum of 2 sec
+ */
+ plist_entry = process_list_add(&plist, "ignoresig1", "bash -c \"trap 'echo trap' SIGINT;while true;do sleep 1;done\"");
+ assert(plist_entry != NULL);
+
+ plist_entry = process_list_add(&plist, "ignoresig2", "bash -c \"trap 'echo trap' SIGINT SIGTERM;while true;do sleep 1;done\"");
+ assert(plist_entry != NULL);
+
+ no_executed = 0;
+ no_finished = 0;
+ assert(process_list_exec_initialized(&plist) == 0);
+ assert(no_executed == 2);
+ assert(process_list_get_no_running(&plist) == 2);
+
+ poll(NULL, 0, 500);
+ assert(process_list_waitpid(&plist) == 0);
+
+ assert(process_list_get_no_running(&plist) == 2);
+ assert(no_finished == 0);
+ assert(process_list_get_summary_result(&plist) == -1);
+ assert(process_list_get_summary_result_short(&plist) == -1);
+
+ assert(process_list_killall(&plist, 2000) == 0);
+ assert(process_list_get_kill_list_items(&plist) == 0);
+
+ process_list_free(&plist);
+
+ /*
+ * Empty killall exits with sucess result
+ */
+ assert(process_list_killall(&plist, 2000) == 0);
+
+ process_list_free(&plist);
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "qnetd-cluster-list.h"
+#include "qnetd-client.h"
+#include "qnetd-client-list.h"
+
+static struct qnetd_client_list clients;
+static struct qnetd_cluster_list clusters;
+
+static void
+add_client(const char *cluster_name, size_t cluster_name_len,
+ struct qnetd_client **client, struct qnetd_cluster **cluster)
+{
+ PRNetAddr addr;
+ struct qnetd_client *tmp_client;
+ struct qnetd_cluster *tmp_cluster;
+ char *client_addr_str;
+
+ memset(&addr, 0, sizeof(addr));
+
+ client_addr_str = strdup("addrstr");
+ assert(client_addr_str != NULL);
+
+ tmp_client = qnetd_client_list_add(&clients, NULL, &addr, client_addr_str, 1000, 2, 1000, NULL);
+ assert(tmp_client != NULL);
+ tmp_client->cluster_name = malloc(cluster_name_len + 1);
+ assert(tmp_client->cluster_name != NULL);
+ memcpy(tmp_client->cluster_name, cluster_name, cluster_name_len);
+ tmp_client->cluster_name_len = cluster_name_len;
+
+ tmp_cluster = qnetd_cluster_list_add_client(&clusters, tmp_client);
+ assert(cluster != NULL);
+ tmp_client->cluster = tmp_cluster;
+
+ *client = tmp_client;
+ *cluster = tmp_cluster;
+}
+
+static int
+no_clients_in_cluster(struct qnetd_cluster *cluster)
+{
+ int i;
+ struct qnetd_client *client;
+
+ i = 0;
+
+ TAILQ_FOREACH(client, &cluster->client_list, cluster_entries) {
+ i++;
+ }
+
+ return (i);
+}
+
+static int
+no_clusters(void)
+{
+ int i;
+ struct qnetd_cluster *cluster;
+
+ i = 0;
+
+ TAILQ_FOREACH(cluster, &clusters, entries) {
+ i++;
+ }
+
+ return (i);
+}
+
+static int
+is_client_in_cluster(struct qnetd_cluster *cluster, const struct qnetd_client *client)
+{
+ struct qnetd_client *tmp_client;
+
+ TAILQ_FOREACH(tmp_client, &cluster->client_list, cluster_entries) {
+ if (tmp_client == client) {
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+static void
+del_client(struct qnetd_client *client)
+{
+
+ qnetd_cluster_list_del_client(&clusters, client->cluster, client);
+ qnetd_client_list_del(&clients, client);
+}
+
+int
+main(void)
+{
+ struct qnetd_client *client[4];
+ struct qnetd_cluster *cluster[4];
+ const char *cl_name;
+
+ qnetd_client_list_init(&clients);
+ qnetd_cluster_list_init(&clusters);
+
+ assert(no_clusters() == 0);
+
+ cl_name = "test_cluster";
+ add_client(cl_name, strlen(cl_name), &client[0], &cluster[0]);
+ assert(no_clusters() == 1);
+ add_client(cl_name, strlen(cl_name), &client[1], &cluster[1]);
+ assert(no_clusters() == 1);
+
+ cl_name = "cluster2";
+ add_client(cl_name, strlen(cl_name), &client[2], &cluster[2]);
+ assert(no_clusters() == 2);
+ add_client(cl_name, strlen(cl_name), &client[3], &cluster[3]);
+ assert(no_clusters() == 2);
+
+ assert(cluster[0] == cluster[1]);
+ assert(cluster[2] == cluster[3]);
+ assert(cluster[0] != cluster[2]);
+
+ assert(no_clients_in_cluster(cluster[0]) == 2);
+ assert(no_clients_in_cluster(cluster[2]) == 2);
+
+ assert(is_client_in_cluster(cluster[0], client[0]));
+ assert(is_client_in_cluster(client[0]->cluster, client[0]));
+ assert(is_client_in_cluster(cluster[0], client[1]));
+ assert(!is_client_in_cluster(cluster[0], client[2]));
+ assert(!is_client_in_cluster(cluster[0], client[3]));
+
+ assert(!is_client_in_cluster(cluster[2], client[0]));
+ assert(!is_client_in_cluster(cluster[2], client[1]));
+ assert(is_client_in_cluster(cluster[2], client[2]));
+ assert(is_client_in_cluster(cluster[2], client[3]));
+ assert(is_client_in_cluster(client[2]->cluster, client[2]));
+
+ del_client(client[0]);
+ assert(no_clusters() == 2);
+ assert(no_clients_in_cluster(cluster[0]) == 1);
+ assert(no_clients_in_cluster(cluster[2]) == 2);
+
+ assert(!is_client_in_cluster(cluster[0], client[0]));
+ assert(is_client_in_cluster(cluster[0], client[1]));
+ assert(!is_client_in_cluster(cluster[0], client[2]));
+ assert(!is_client_in_cluster(cluster[0], client[3]));
+
+ add_client(cl_name, strlen(cl_name), &client[0], &cluster[0]);
+ assert(no_clients_in_cluster(cluster[1]) == 1);
+ assert(no_clients_in_cluster(cluster[2]) == 3);
+
+ assert(!is_client_in_cluster(cluster[1], client[0]));
+ assert(is_client_in_cluster(cluster[1], client[1]));
+ assert(!is_client_in_cluster(cluster[1], client[2]));
+ assert(!is_client_in_cluster(cluster[1], client[3]));
+
+ assert(is_client_in_cluster(cluster[2], client[0]));
+ assert(!is_client_in_cluster(cluster[2], client[1]));
+ assert(is_client_in_cluster(cluster[2], client[2]));
+ assert(is_client_in_cluster(cluster[2], client[3]));
+
+ del_client(client[1]);
+ assert(no_clusters() == 1);
+
+ del_client(client[2]);
+ assert(no_clusters() == 1);
+
+ del_client(client[3]);
+ assert(no_clusters() == 1);
+
+ del_client(client[0]);
+ assert(no_clusters() == 0);
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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 <string.h>
+
+#include "timer-list.h"
+
+void
+timer_list_init(struct timer_list *tlist)
+{
+
+ memset(tlist, 0, sizeof(*tlist));
+
+ TAILQ_INIT(&tlist->list);
+ TAILQ_INIT(&tlist->free_list);
+}
+
+static PRIntervalTime
+timer_list_entry_time_to_expire(const struct timer_list_entry *entry, PRIntervalTime current_time)
+{
+ PRIntervalTime diff, half_interval;
+
+ diff = entry->expire_time - current_time;
+ half_interval = ~0;
+ half_interval /= 2;
+
+ if (diff > half_interval) {
+ return (0);
+ }
+
+ return (diff);
+}
+
+static int
+timer_list_entry_cmp(const struct timer_list_entry *entry1,
+ const struct timer_list_entry *entry2, PRIntervalTime current_time)
+{
+ PRIntervalTime diff1, diff2;
+ int res;
+
+ diff1 = timer_list_entry_time_to_expire(entry1, current_time);
+ diff2 = timer_list_entry_time_to_expire(entry2, current_time);
+
+ res = 0;
+
+ if (diff1 < diff2) res = -1;
+ if (diff1 > diff2) res = 1;
+
+ return (res);
+}
+
+static void
+timer_list_insert_into_list(struct timer_list *tlist, struct timer_list_entry *new_entry)
+{
+ struct timer_list_entry *entry;
+
+ /*
+ * This can overflow and it's not a problem
+ */
+ new_entry->expire_time = new_entry->epoch + PR_MillisecondsToInterval(new_entry->interval);
+
+ entry = TAILQ_FIRST(&tlist->list);
+ while (entry != NULL) {
+ if (timer_list_entry_cmp(entry, new_entry, new_entry->epoch) > 0) {
+ /*
+ * Insert new entry right before current entry
+ */
+ TAILQ_INSERT_BEFORE(entry, new_entry, entries);
+
+ break;
+ }
+
+ entry = TAILQ_NEXT(entry, entries);
+ }
+
+ if (entry == NULL) {
+ TAILQ_INSERT_TAIL(&tlist->list, new_entry, entries);
+ }
+}
+
+struct timer_list_entry *
+timer_list_add(struct timer_list *tlist, PRUint32 interval, timer_list_cb_fn func, void *data1,
+ void *data2)
+{
+ struct timer_list_entry *new_entry;
+
+ if (interval < 1 || interval > TIMER_LIST_MAX_INTERVAL) {
+ return (NULL);
+ }
+
+ if (!TAILQ_EMPTY(&tlist->free_list)) {
+ /*
+ * Use free list entry
+ */
+ new_entry = TAILQ_FIRST(&tlist->free_list);
+ TAILQ_REMOVE(&tlist->free_list, new_entry, entries);
+ } else {
+ /*
+ * Alloc new entry
+ */
+ new_entry = malloc(sizeof(*new_entry));
+ if (new_entry == NULL) {
+ return (NULL);
+ }
+ }
+
+ memset(new_entry, 0, sizeof(*new_entry));
+ new_entry->epoch = PR_IntervalNow();
+ new_entry->interval = interval;
+ new_entry->func = func;
+ new_entry->user_data1 = data1;
+ new_entry->user_data2 = data2;
+ new_entry->is_active = 1;
+
+ timer_list_insert_into_list(tlist, new_entry);
+
+ return (new_entry);
+}
+
+void
+timer_list_reschedule(struct timer_list *tlist, struct timer_list_entry *entry)
+{
+
+ if (entry->is_active) {
+ entry->epoch = PR_IntervalNow();
+ TAILQ_REMOVE(&tlist->list, entry, entries);
+ timer_list_insert_into_list(tlist, entry);
+ }
+}
+
+void
+timer_list_expire(struct timer_list *tlist)
+{
+ PRIntervalTime now;
+ struct timer_list_entry *entry;
+ int res;
+
+ now = PR_IntervalNow();
+
+ while ((entry = TAILQ_FIRST(&tlist->list)) != NULL &&
+ timer_list_entry_time_to_expire(entry, now) == 0) {
+ /*
+ * Expired
+ */
+ res = entry->func(entry->user_data1, entry->user_data2);
+ if (res == 0) {
+ /*
+ * Move item to free list
+ */
+ timer_list_delete(tlist, entry);
+ } else if (entry->is_active) {
+ /*
+ * Schedule again
+ */
+ entry->epoch = now;
+ TAILQ_REMOVE(&tlist->list, entry, entries);
+ timer_list_insert_into_list(tlist, entry);
+ }
+ }
+}
+
+PRIntervalTime
+timer_list_time_to_expire(struct timer_list *tlist)
+{
+ struct timer_list_entry *entry;
+
+ entry = TAILQ_FIRST(&tlist->list);
+ if (entry == NULL) {
+ return (PR_INTERVAL_NO_TIMEOUT);
+ }
+
+ return (timer_list_entry_time_to_expire(entry, PR_IntervalNow()));
+}
+
+uint32_t
+timer_list_time_to_expire_ms(struct timer_list *tlist)
+{
+ struct timer_list_entry *entry;
+ uint32_t u32;
+
+ entry = TAILQ_FIRST(&tlist->list);
+ if (entry == NULL) {
+ u32 = ~((uint32_t)0);
+ return (u32);
+ }
+
+ return (PR_IntervalToMilliseconds(timer_list_entry_time_to_expire(entry, PR_IntervalNow())));
+}
+
+void
+timer_list_delete(struct timer_list *tlist, struct timer_list_entry *entry)
+{
+
+ if (entry->is_active) {
+ /*
+ * Move item to free list
+ */
+ TAILQ_REMOVE(&tlist->list, entry, entries);
+ TAILQ_INSERT_HEAD(&tlist->free_list, entry, entries);
+ entry->is_active = 0;
+ }
+}
+
+void
+timer_list_free(struct timer_list *tlist)
+{
+ struct timer_list_entry *entry;
+ struct timer_list_entry *entry_next;
+
+
+ entry = TAILQ_FIRST(&tlist->list);
+
+ while (entry != NULL) {
+ entry_next = TAILQ_NEXT(entry, entries);
+
+ free(entry);
+
+ entry = entry_next;
+ }
+
+ entry = TAILQ_FIRST(&tlist->free_list);
+
+ while (entry != NULL) {
+ entry_next = TAILQ_NEXT(entry, entries);
+
+ free(entry);
+
+ entry = entry_next;
+ }
+
+ timer_list_init(tlist);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _TIMER_LIST_H_
+#define _TIMER_LIST_H_
+
+#include <sys/queue.h>
+
+#include <nspr.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * PR Interval is 32-bit integer which overflows. Maximum useable interval is around
+ * 6 hours (less). So define max interval as 5 hours
+ */
+#define TIMER_LIST_MAX_INTERVAL 18000000
+
+typedef int (*timer_list_cb_fn)(void *data1, void *data2);
+
+struct timer_list_entry {
+ /* Time when timer was planned */
+ PRIntervalTime epoch;
+ /* Number of miliseconds to expire */
+ PRUint32 interval;
+ /* Time when timer expires (epoch + interval) */
+ PRIntervalTime expire_time;
+ timer_list_cb_fn func;
+ void *user_data1;
+ void *user_data2;
+ int is_active;
+ TAILQ_ENTRY(timer_list_entry) entries;
+};
+
+struct timer_list {
+ TAILQ_HEAD(, timer_list_entry) list;
+ TAILQ_HEAD(, timer_list_entry) free_list;
+};
+
+extern void timer_list_init(struct timer_list *tlist);
+
+extern struct timer_list_entry *timer_list_add(struct timer_list *tlist,
+ PRUint32 interval, timer_list_cb_fn func, void *data1, void *data2);
+
+extern void timer_list_reschedule(struct timer_list *tlist,
+ struct timer_list_entry *entry);
+
+extern void timer_list_delete(struct timer_list *tlist,
+ struct timer_list_entry *entry);
+
+extern void timer_list_expire(struct timer_list *tlist);
+
+extern PRIntervalTime timer_list_time_to_expire(struct timer_list *tlist);
+
+extern uint32_t timer_list_time_to_expire_ms(struct timer_list *tlist);
+
+extern void timer_list_free(struct timer_list *tlist);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TIMER_LIST_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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 <sys/types.h>
+#include <arpa/inet.h>
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * 64-bit variant of ntoh is not exactly standard...
+ */
+#if defined(__linux__)
+#include <endian.h>
+#elif defined(__FreeBSD__) || defined(__NetBSD__)
+#include <sys/endian.h>
+#elif defined(__OpenBSD__)
+#define be64toh(x) betoh64(x)
+#endif
+
+#include "tlv.h"
+
+#define TLV_TYPE_LENGTH 2
+#define TLV_LENGTH_LENGTH 2
+
+#define TLV_STATIC_SUPPORTED_OPTIONS_SIZE 23
+
+enum tlv_opt_type tlv_static_supported_options[TLV_STATIC_SUPPORTED_OPTIONS_SIZE] = {
+ TLV_OPT_MSG_SEQ_NUMBER,
+ TLV_OPT_CLUSTER_NAME,
+ TLV_OPT_TLS_SUPPORTED,
+ TLV_OPT_TLS_CLIENT_CERT_REQUIRED,
+ TLV_OPT_SUPPORTED_MESSAGES,
+ TLV_OPT_SUPPORTED_OPTIONS,
+ TLV_OPT_REPLY_ERROR_CODE,
+ TLV_OPT_SERVER_MAXIMUM_REQUEST_SIZE,
+ TLV_OPT_SERVER_MAXIMUM_REPLY_SIZE,
+ TLV_OPT_NODE_ID,
+ TLV_OPT_SUPPORTED_DECISION_ALGORITHMS,
+ TLV_OPT_DECISION_ALGORITHM,
+ TLV_OPT_HEARTBEAT_INTERVAL,
+ TLV_OPT_RING_ID,
+ TLV_OPT_CONFIG_VERSION,
+ TLV_OPT_DATA_CENTER_ID,
+ TLV_OPT_NODE_STATE,
+ TLV_OPT_NODE_INFO,
+ TLV_OPT_NODE_LIST_TYPE,
+ TLV_OPT_VOTE,
+ TLV_OPT_QUORATE,
+ TLV_OPT_TIE_BREAKER,
+ TLV_OPT_HEURISTICS,
+};
+
+int
+tlv_add(struct dynar *msg, enum tlv_opt_type opt_type, uint16_t opt_len, const void *value)
+{
+ uint16_t nlen;
+ uint16_t nopt_type;
+
+ if (dynar_size(msg) + sizeof(nopt_type) + sizeof(nlen) + opt_len > dynar_max_size(msg)) {
+ return (-1);
+ }
+
+ nopt_type = htons((uint16_t)opt_type);
+ nlen = htons(opt_len);
+
+ dynar_cat(msg, &nopt_type, sizeof(nopt_type));
+ dynar_cat(msg, &nlen, sizeof(nlen));
+ dynar_cat(msg, value, opt_len);
+
+ return (0);
+}
+
+int
+tlv_add_u32(struct dynar *msg, enum tlv_opt_type opt_type, uint32_t u32)
+{
+ uint32_t nu32;
+
+ nu32 = htonl(u32);
+
+ return (tlv_add(msg, opt_type, sizeof(nu32), &nu32));
+}
+
+int
+tlv_add_u8(struct dynar *msg, enum tlv_opt_type opt_type, uint8_t u8)
+{
+
+ return (tlv_add(msg, opt_type, sizeof(u8), &u8));
+}
+
+int
+tlv_add_u16(struct dynar *msg, enum tlv_opt_type opt_type, uint16_t u16)
+{
+ uint16_t nu16;
+
+ nu16 = htons(u16);
+
+ return (tlv_add(msg, opt_type, sizeof(nu16), &nu16));
+}
+
+int
+tlv_add_u64(struct dynar *msg, enum tlv_opt_type opt_type, uint64_t u64)
+{
+ uint64_t nu64;
+
+ nu64 = htobe64(u64);
+
+ return (tlv_add(msg, opt_type, sizeof(nu64), &nu64));
+}
+
+int
+tlv_add_string(struct dynar *msg, enum tlv_opt_type opt_type, const char *str)
+{
+
+ return (tlv_add(msg, opt_type, strlen(str), str));
+}
+
+int
+tlv_add_msg_seq_number(struct dynar *msg, uint32_t msg_seq_number)
+{
+
+ return (tlv_add_u32(msg, TLV_OPT_MSG_SEQ_NUMBER, msg_seq_number));
+}
+
+int
+tlv_add_cluster_name(struct dynar *msg, const char *cluster_name)
+{
+
+ return (tlv_add_string(msg, TLV_OPT_CLUSTER_NAME, cluster_name));
+}
+
+int
+tlv_add_tls_supported(struct dynar *msg, enum tlv_tls_supported tls_supported)
+{
+
+ return (tlv_add_u8(msg, TLV_OPT_TLS_SUPPORTED, tls_supported));
+}
+
+int
+tlv_add_tls_client_cert_required(struct dynar *msg, int tls_client_cert_required)
+{
+
+ return (tlv_add_u8(msg, TLV_OPT_TLS_CLIENT_CERT_REQUIRED, tls_client_cert_required));
+}
+
+int
+tlv_add_u16_array(struct dynar *msg, enum tlv_opt_type opt_type, const uint16_t *array,
+ size_t array_size)
+{
+ size_t i;
+ uint16_t *nu16a;
+ uint16_t opt_len;
+ int res;
+
+ nu16a = malloc(sizeof(uint16_t) * array_size);
+ if (nu16a == NULL) {
+ return (-1);
+ }
+
+ for (i = 0; i < array_size; i++) {
+ nu16a[i] = htons(array[i]);
+ }
+
+ opt_len = sizeof(uint16_t) * array_size;
+
+ res = tlv_add(msg, opt_type, opt_len, nu16a);
+
+ free(nu16a);
+
+ return (res);
+}
+
+int
+tlv_add_supported_options(struct dynar *msg, const enum tlv_opt_type *supported_options,
+ size_t no_supported_options)
+{
+ uint16_t *u16a;
+ size_t i;
+ int res;
+
+ u16a = malloc(sizeof(*u16a) * no_supported_options);
+ if (u16a == NULL) {
+ return (-1);
+ }
+
+ for (i = 0; i < no_supported_options; i++) {
+ u16a[i] = (uint16_t)supported_options[i];
+ }
+
+ res = (tlv_add_u16_array(msg, TLV_OPT_SUPPORTED_OPTIONS, u16a, no_supported_options));
+
+ free(u16a);
+
+ return (res);
+}
+
+int
+tlv_add_supported_decision_algorithms(struct dynar *msg,
+ const enum tlv_decision_algorithm_type *supported_algorithms, size_t no_supported_algorithms)
+{
+ uint16_t *u16a;
+ size_t i;
+ int res;
+
+ u16a = malloc(sizeof(*u16a) * no_supported_algorithms);
+ if (u16a == NULL) {
+ return (-1);
+ }
+
+ for (i = 0; i < no_supported_algorithms; i++) {
+ u16a[i] = (uint16_t)supported_algorithms[i];
+ }
+
+ res = (tlv_add_u16_array(msg, TLV_OPT_SUPPORTED_DECISION_ALGORITHMS, u16a,
+ no_supported_algorithms));
+
+ free(u16a);
+
+ return (res);
+}
+
+int
+tlv_add_reply_error_code(struct dynar *msg, enum tlv_reply_error_code error_code)
+{
+
+ return (tlv_add_u16(msg, TLV_OPT_REPLY_ERROR_CODE, (uint16_t)error_code));
+}
+
+int
+tlv_add_server_maximum_request_size(struct dynar *msg, size_t server_maximum_request_size)
+{
+
+ return (tlv_add_u32(msg, TLV_OPT_SERVER_MAXIMUM_REQUEST_SIZE, server_maximum_request_size));
+}
+
+int
+tlv_add_server_maximum_reply_size(struct dynar *msg, size_t server_maximum_reply_size)
+{
+
+ return (tlv_add_u32(msg, TLV_OPT_SERVER_MAXIMUM_REPLY_SIZE, server_maximum_reply_size));
+}
+
+int
+tlv_add_node_id(struct dynar *msg, uint32_t node_id)
+{
+
+ return (tlv_add_u32(msg, TLV_OPT_NODE_ID, node_id));
+}
+
+int
+tlv_add_decision_algorithm(struct dynar *msg, enum tlv_decision_algorithm_type decision_algorithm)
+{
+
+ return (tlv_add_u16(msg, TLV_OPT_DECISION_ALGORITHM, (uint16_t)decision_algorithm));
+}
+
+int
+tlv_add_heartbeat_interval(struct dynar *msg, uint32_t heartbeat_interval)
+{
+
+ return (tlv_add_u32(msg, TLV_OPT_HEARTBEAT_INTERVAL, heartbeat_interval));
+}
+
+int
+tlv_add_ring_id(struct dynar *msg, const struct tlv_ring_id *ring_id)
+{
+ uint64_t nu64;
+ uint32_t nu32;
+ char tmp_buf[12];
+
+ nu32 = htonl(ring_id->node_id);
+ nu64 = htobe64(ring_id->seq);
+
+ memcpy(tmp_buf, &nu32, sizeof(nu32));
+ memcpy(tmp_buf + sizeof(nu32), &nu64, sizeof(nu64));
+
+ return (tlv_add(msg, TLV_OPT_RING_ID, sizeof(tmp_buf), tmp_buf));
+}
+
+int
+tlv_add_tie_breaker(struct dynar *msg, const struct tlv_tie_breaker *tie_breaker)
+{
+ uint32_t nu32;
+ uint8_t u8;
+ char tmp_buf[5];
+
+ u8 = tie_breaker->mode;
+ nu32 = (tie_breaker->mode == TLV_TIE_BREAKER_MODE_NODE_ID ?
+ htonl(tie_breaker->node_id) : 0);
+
+ memcpy(tmp_buf, &u8, sizeof(u8));
+ memcpy(tmp_buf + sizeof(u8), &nu32, sizeof(nu32));
+
+ return (tlv_add(msg, TLV_OPT_TIE_BREAKER, sizeof(tmp_buf), tmp_buf));
+}
+
+int
+tlv_add_config_version(struct dynar *msg, uint64_t config_version)
+{
+
+ return (tlv_add_u64(msg, TLV_OPT_CONFIG_VERSION, config_version));
+}
+
+int
+tlv_add_data_center_id(struct dynar *msg, uint32_t data_center_id)
+{
+
+ return (tlv_add_u32(msg, TLV_OPT_DATA_CENTER_ID, data_center_id));
+}
+
+int
+tlv_add_node_state(struct dynar *msg, enum tlv_node_state node_state)
+{
+
+ return (tlv_add_u8(msg, TLV_OPT_NODE_STATE, node_state));
+}
+
+int
+tlv_add_node_info(struct dynar *msg, const struct tlv_node_info *node_info)
+{
+ struct dynar opt_value;
+ int res;
+
+ res = 0;
+ /*
+ * Create sub message,
+ */
+ dynar_init(&opt_value, 1024);
+ if ((res = tlv_add_node_id(&opt_value, node_info->node_id)) != 0) {
+ goto exit_dynar_destroy;
+ }
+
+ if (node_info->data_center_id != 0) {
+ if ((res = tlv_add_data_center_id(&opt_value, node_info->data_center_id)) != 0) {
+ goto exit_dynar_destroy;
+ }
+ }
+
+ if (node_info->node_state != TLV_NODE_STATE_NOT_SET) {
+ if ((res = tlv_add_node_state(&opt_value, node_info->node_state)) != 0) {
+ goto exit_dynar_destroy;
+ }
+ }
+
+ res = tlv_add(msg, TLV_OPT_NODE_INFO, dynar_size(&opt_value), dynar_data(&opt_value));
+ if (res != 0) {
+ goto exit_dynar_destroy;
+ }
+
+
+exit_dynar_destroy:
+ dynar_destroy(&opt_value);
+
+ return (res);
+}
+
+int
+tlv_add_node_list_type(struct dynar *msg, enum tlv_node_list_type node_list_type)
+{
+
+ return (tlv_add_u8(msg, TLV_OPT_NODE_LIST_TYPE, node_list_type));
+}
+
+int
+tlv_add_vote(struct dynar *msg, enum tlv_vote vote)
+{
+
+ return (tlv_add_u8(msg, TLV_OPT_VOTE, vote));
+}
+
+int
+tlv_add_quorate(struct dynar *msg, enum tlv_quorate quorate)
+{
+
+ return (tlv_add_u8(msg, TLV_OPT_QUORATE, quorate));
+}
+
+int
+tlv_add_heuristics(struct dynar *msg, enum tlv_heuristics heuristics)
+{
+
+ if (heuristics == TLV_HEURISTICS_UNDEFINED) {
+ return (-1);
+ }
+
+ return (tlv_add_u8(msg, TLV_OPT_HEURISTICS, heuristics));
+}
+
+void
+tlv_iter_init_str(const char *msg, size_t msg_len, size_t msg_header_len,
+ struct tlv_iterator *tlv_iter)
+{
+
+ tlv_iter->msg = msg;
+ tlv_iter->msg_len = msg_len;
+ tlv_iter->current_pos = 0;
+ tlv_iter->msg_header_len = msg_header_len;
+ tlv_iter->iter_next_called = 0;
+}
+
+void
+tlv_iter_init(const struct dynar *msg, size_t msg_header_len, struct tlv_iterator *tlv_iter)
+{
+
+ tlv_iter_init_str(dynar_data(msg), dynar_size(msg), msg_header_len, tlv_iter);
+}
+
+enum tlv_opt_type
+tlv_iter_get_type(const struct tlv_iterator *tlv_iter)
+{
+ uint16_t ntype;
+ uint16_t type;
+
+ memcpy(&ntype, tlv_iter->msg + tlv_iter->current_pos, sizeof(ntype));
+ type = ntohs(ntype);
+
+ return (type);
+}
+
+uint16_t
+tlv_iter_get_len(const struct tlv_iterator *tlv_iter)
+{
+ uint16_t nlen;
+ uint16_t len;
+
+ memcpy(&nlen, tlv_iter->msg + tlv_iter->current_pos + TLV_TYPE_LENGTH, sizeof(nlen));
+ len = ntohs(nlen);
+
+ return (len);
+}
+
+const char *
+tlv_iter_get_data(const struct tlv_iterator *tlv_iter)
+{
+
+ return (tlv_iter->msg + tlv_iter->current_pos + TLV_TYPE_LENGTH + TLV_LENGTH_LENGTH);
+}
+
+int
+tlv_iter_next(struct tlv_iterator *tlv_iter)
+{
+ uint16_t len;
+
+ if (tlv_iter->iter_next_called == 0) {
+ tlv_iter->iter_next_called = 1;
+ tlv_iter->current_pos = tlv_iter->msg_header_len;
+
+ goto check_tlv_validity;
+ }
+
+ len = tlv_iter_get_len(tlv_iter);
+
+ if (tlv_iter->current_pos + TLV_TYPE_LENGTH + TLV_LENGTH_LENGTH + len >=
+ tlv_iter->msg_len) {
+ return (0);
+ }
+
+ tlv_iter->current_pos += TLV_TYPE_LENGTH + TLV_LENGTH_LENGTH + len;
+
+check_tlv_validity:
+ /*
+ * Check if tlv is valid = is not larger than whole message
+ */
+ len = tlv_iter_get_len(tlv_iter);
+
+ if (tlv_iter->current_pos + TLV_TYPE_LENGTH + TLV_LENGTH_LENGTH + len > tlv_iter->msg_len) {
+ return (-1);
+ }
+
+ return (1);
+}
+
+int
+tlv_iter_decode_u32(struct tlv_iterator *tlv_iter, uint32_t *res)
+{
+ const char *opt_data;
+ uint16_t opt_len;
+ uint32_t nu32;
+
+ opt_len = tlv_iter_get_len(tlv_iter);
+ opt_data = tlv_iter_get_data(tlv_iter);
+
+ if (opt_len != sizeof(nu32)) {
+ return (-1);
+ }
+
+ memcpy(&nu32, opt_data, sizeof(nu32));
+ *res = ntohl(nu32);
+
+ return (0);
+}
+
+int
+tlv_iter_decode_u8(struct tlv_iterator *tlv_iter, uint8_t *res)
+{
+ const char *opt_data;
+ uint16_t opt_len;
+
+ opt_len = tlv_iter_get_len(tlv_iter);
+ opt_data = tlv_iter_get_data(tlv_iter);
+
+ if (opt_len != sizeof(*res)) {
+ return (-1);
+ }
+
+ memcpy(res, opt_data, sizeof(*res));
+
+ return (0);
+}
+
+int
+tlv_iter_decode_client_cert_required(struct tlv_iterator *tlv_iter, uint8_t *client_cert_required)
+{
+
+ return (tlv_iter_decode_u8(tlv_iter, client_cert_required));
+}
+
+int
+tlv_iter_decode_str(struct tlv_iterator *tlv_iter, char **str, size_t *str_len)
+{
+ const char *opt_data;
+ uint16_t opt_len;
+ char *tmp_str;
+
+ opt_len = tlv_iter_get_len(tlv_iter);
+ opt_data = tlv_iter_get_data(tlv_iter);
+
+ tmp_str = malloc(opt_len + 1);
+ if (tmp_str == NULL) {
+ return (-1);
+ }
+
+ memcpy(tmp_str, opt_data, opt_len);
+ tmp_str[opt_len] = '\0';
+
+ *str = tmp_str;
+ *str_len = opt_len;
+
+ return (0);
+}
+
+int
+tlv_iter_decode_u16_array(struct tlv_iterator *tlv_iter, uint16_t **u16a, size_t *no_items)
+{
+ uint16_t opt_len;
+ uint16_t *u16a_res;
+ size_t i;
+
+ opt_len = tlv_iter_get_len(tlv_iter);
+
+ if (opt_len % sizeof(uint16_t) != 0) {
+ return (-1);
+ }
+
+ *no_items = opt_len / sizeof(uint16_t);
+
+ u16a_res = malloc(sizeof(uint16_t) * *no_items);
+ if (u16a_res == NULL) {
+ return (-2);
+ }
+
+ memcpy(u16a_res, tlv_iter_get_data(tlv_iter), opt_len);
+
+ for (i = 0; i < *no_items; i++) {
+ u16a_res[i] = ntohs(u16a_res[i]);
+ }
+
+ *u16a = u16a_res;
+
+ return (0);
+}
+
+int
+tlv_iter_decode_supported_options(struct tlv_iterator *tlv_iter,
+ enum tlv_opt_type **supported_options, size_t *no_supported_options)
+{
+ uint16_t *u16a;
+ enum tlv_opt_type *tlv_opt_array;
+ size_t i;
+ int res;
+
+ res = tlv_iter_decode_u16_array(tlv_iter, &u16a, no_supported_options);
+ if (res != 0) {
+ return (res);
+ }
+
+ tlv_opt_array = malloc(sizeof(enum tlv_opt_type) * *no_supported_options);
+ if (tlv_opt_array == NULL) {
+ free(u16a);
+ return (-2);
+ }
+
+ for (i = 0; i < *no_supported_options; i++) {
+ tlv_opt_array[i] = (enum tlv_opt_type)u16a[i];
+ }
+
+ free(u16a);
+
+ *supported_options = tlv_opt_array;
+
+ return (0);
+}
+
+int
+tlv_iter_decode_supported_decision_algorithms(struct tlv_iterator *tlv_iter,
+ enum tlv_decision_algorithm_type **supported_decision_algorithms,
+ size_t *no_supported_decision_algorithms)
+{
+ uint16_t *u16a;
+ enum tlv_decision_algorithm_type *tlv_decision_algorithm_type_array;
+ size_t i;
+ int res;
+
+ res = tlv_iter_decode_u16_array(tlv_iter, &u16a, no_supported_decision_algorithms);
+ if (res != 0) {
+ return (res);
+ }
+
+ tlv_decision_algorithm_type_array = malloc(
+ sizeof(enum tlv_decision_algorithm_type) * *no_supported_decision_algorithms);
+
+ if (tlv_decision_algorithm_type_array == NULL) {
+ free(u16a);
+ return (-2);
+ }
+
+ for (i = 0; i < *no_supported_decision_algorithms; i++) {
+ tlv_decision_algorithm_type_array[i] = (enum tlv_decision_algorithm_type)u16a[i];
+ }
+
+ free(u16a);
+
+ *supported_decision_algorithms = tlv_decision_algorithm_type_array;
+
+ return (0);
+}
+
+int
+tlv_iter_decode_u16(struct tlv_iterator *tlv_iter, uint16_t *u16)
+{
+ const char *opt_data;
+ uint16_t opt_len;
+ uint16_t nu16;
+
+ opt_len = tlv_iter_get_len(tlv_iter);
+ opt_data = tlv_iter_get_data(tlv_iter);
+
+ if (opt_len != sizeof(nu16)) {
+ return (-1);
+ }
+
+ memcpy(&nu16, opt_data, sizeof(nu16));
+ *u16 = ntohs(nu16);
+
+ return (0);
+}
+
+int
+tlv_iter_decode_u64(struct tlv_iterator *tlv_iter, uint64_t *u64)
+{
+ const char *opt_data;
+ uint64_t opt_len;
+ uint64_t nu64;
+
+ opt_len = tlv_iter_get_len(tlv_iter);
+ opt_data = tlv_iter_get_data(tlv_iter);
+
+ if (opt_len != sizeof(nu64)) {
+ return (-1);
+ }
+
+ memcpy(&nu64, opt_data, sizeof(nu64));
+ *u64 = be64toh(nu64);
+
+ return (0);
+}
+
+int
+tlv_iter_decode_reply_error_code(struct tlv_iterator *tlv_iter,
+ enum tlv_reply_error_code *reply_error_code)
+{
+
+ return (tlv_iter_decode_u16(tlv_iter, (uint16_t *)reply_error_code));
+}
+
+int
+tlv_iter_decode_tls_supported(struct tlv_iterator *tlv_iter, enum tlv_tls_supported *tls_supported)
+{
+ uint8_t u8;
+ enum tlv_tls_supported tmp_tls_supported;
+
+ if (tlv_iter_decode_u8(tlv_iter, &u8) != 0) {
+ return (-1);
+ }
+
+ tmp_tls_supported = u8;
+
+ if (tmp_tls_supported != TLV_TLS_UNSUPPORTED &&
+ tmp_tls_supported != TLV_TLS_SUPPORTED &&
+ tmp_tls_supported != TLV_TLS_REQUIRED) {
+ return (-4);
+ }
+
+ *tls_supported = tmp_tls_supported;
+
+ return (0);
+}
+
+int
+tlv_iter_decode_decision_algorithm(struct tlv_iterator *tlv_iter,
+ enum tlv_decision_algorithm_type *decision_algorithm)
+{
+ uint16_t u16;
+
+ if (tlv_iter_decode_u16(tlv_iter, &u16) != 0) {
+ return (-1);
+ }
+
+ *decision_algorithm = (enum tlv_decision_algorithm_type)u16;
+
+ return (0);
+}
+
+int
+tlv_iter_decode_ring_id(struct tlv_iterator *tlv_iter, struct tlv_ring_id *ring_id)
+{
+ const char *opt_data;
+ uint16_t opt_len;
+ uint32_t nu32;
+ uint64_t nu64;
+ char tmp_buf[12];
+
+ opt_len = tlv_iter_get_len(tlv_iter);
+ opt_data = tlv_iter_get_data(tlv_iter);
+
+ if (opt_len != sizeof(tmp_buf)) {
+ return (-1);
+ }
+
+ memcpy(&nu32, opt_data, sizeof(nu32));
+ memcpy(&nu64, opt_data + sizeof(nu32), sizeof(nu64));
+
+ ring_id->node_id = ntohl(nu32);
+ ring_id->seq = be64toh(nu64);
+
+ return (0);
+}
+
+int
+tlv_iter_decode_tie_breaker(struct tlv_iterator *tlv_iter, struct tlv_tie_breaker *tie_breaker)
+{
+ const char *opt_data;
+ uint16_t opt_len;
+ uint32_t nu32;
+ uint8_t u8;
+ enum tlv_tie_breaker_mode tie_breaker_mode;
+ char tmp_buf[5];
+
+ opt_len = tlv_iter_get_len(tlv_iter);
+ opt_data = tlv_iter_get_data(tlv_iter);
+
+ if (opt_len != sizeof(tmp_buf)) {
+ return (-1);
+ }
+
+ memcpy(&u8, opt_data, sizeof(u8));
+ tie_breaker_mode = u8;
+
+ if (tie_breaker_mode != TLV_TIE_BREAKER_MODE_LOWEST &&
+ tie_breaker_mode != TLV_TIE_BREAKER_MODE_HIGHEST &&
+ tie_breaker_mode != TLV_TIE_BREAKER_MODE_NODE_ID) {
+ return (-4);
+ }
+
+ memcpy(&nu32, opt_data + sizeof(u8), sizeof(nu32));
+
+ tie_breaker->mode = tie_breaker_mode;
+ tie_breaker->node_id = (tie_breaker->mode == TLV_TIE_BREAKER_MODE_NODE_ID ?
+ ntohl(nu32) : 0);
+
+ return (0);
+}
+
+int
+tlv_iter_decode_node_state(struct tlv_iterator *tlv_iter, enum tlv_node_state *node_state)
+{
+ uint8_t u8;
+ enum tlv_node_state tmp_node_state;
+
+ if (tlv_iter_decode_u8(tlv_iter, &u8) != 0) {
+ return (-1);
+ }
+
+ tmp_node_state = u8;
+
+ if (tmp_node_state != TLV_NODE_STATE_MEMBER &&
+ tmp_node_state != TLV_NODE_STATE_DEAD &&
+ tmp_node_state != TLV_NODE_STATE_LEAVING) {
+ return (-4);
+ }
+
+ *node_state = tmp_node_state;
+
+ return (0);
+}
+
+int
+tlv_iter_decode_node_info(struct tlv_iterator *tlv_iter, struct tlv_node_info *node_info)
+{
+ struct tlv_iterator data_tlv_iter;
+ int iter_res;
+ int res;
+ enum tlv_opt_type opt_type;
+ struct tlv_node_info tmp_node_info;
+
+ memset(&tmp_node_info, 0, sizeof(tmp_node_info));
+
+ tlv_iter_init_str(tlv_iter_get_data(tlv_iter), tlv_iter_get_len(tlv_iter), 0,
+ &data_tlv_iter);
+
+ while ((iter_res = tlv_iter_next(&data_tlv_iter)) > 0) {
+ opt_type = tlv_iter_get_type(&data_tlv_iter);
+
+ switch (opt_type) {
+ case TLV_OPT_NODE_ID:
+ if ((res = tlv_iter_decode_u32(&data_tlv_iter,
+ &tmp_node_info.node_id)) != 0) {
+ return (res);
+ }
+ break;
+ case TLV_OPT_DATA_CENTER_ID:
+ if ((res = tlv_iter_decode_u32(&data_tlv_iter,
+ &tmp_node_info.data_center_id)) != 0) {
+ return (res);
+ }
+ break;
+ case TLV_OPT_NODE_STATE:
+ if ((res = tlv_iter_decode_node_state(&data_tlv_iter,
+ &tmp_node_info.node_state)) != 0) {
+ return (res);
+ }
+ break;
+ default:
+ /*
+ * Other options are not processed
+ */
+ break;
+ }
+ }
+
+ if (iter_res != 0) {
+ return (-3);
+ }
+
+ if (tmp_node_info.node_id == 0) {
+ return (-4);
+ }
+
+ memcpy(node_info, &tmp_node_info, sizeof(tmp_node_info));
+
+ return (0);
+}
+
+int
+tlv_iter_decode_node_list_type(struct tlv_iterator *tlv_iter,
+ enum tlv_node_list_type *node_list_type)
+{
+ uint8_t u8;
+ enum tlv_node_list_type tmp_node_list_type;
+
+ if (tlv_iter_decode_u8(tlv_iter, &u8) != 0) {
+ return (-1);
+ }
+
+ tmp_node_list_type = u8;
+
+ if (tmp_node_list_type != TLV_NODE_LIST_TYPE_INITIAL_CONFIG &&
+ tmp_node_list_type != TLV_NODE_LIST_TYPE_CHANGED_CONFIG &&
+ tmp_node_list_type != TLV_NODE_LIST_TYPE_MEMBERSHIP &&
+ tmp_node_list_type != TLV_NODE_LIST_TYPE_QUORUM) {
+ return (-4);
+ }
+
+ *node_list_type = tmp_node_list_type;
+
+ return (0);
+}
+
+int
+tlv_iter_decode_vote(struct tlv_iterator *tlv_iter, enum tlv_vote *vote)
+{
+ uint8_t u8;
+ enum tlv_vote tmp_vote;
+
+ if (tlv_iter_decode_u8(tlv_iter, &u8) != 0) {
+ return (-1);
+ }
+
+ tmp_vote = u8;
+
+ if (tmp_vote != TLV_VOTE_ACK &&
+ tmp_vote != TLV_VOTE_NACK &&
+ tmp_vote != TLV_VOTE_ASK_LATER &&
+ tmp_vote != TLV_VOTE_WAIT_FOR_REPLY &&
+ tmp_vote != TLV_VOTE_NO_CHANGE) {
+ return (-4);
+ }
+
+ *vote = tmp_vote;
+
+ return (0);
+}
+
+int
+tlv_iter_decode_quorate(struct tlv_iterator *tlv_iter, enum tlv_quorate *quorate)
+{
+ uint8_t u8;
+ enum tlv_quorate tmp_quorate;
+
+ if (tlv_iter_decode_u8(tlv_iter, &u8) != 0) {
+ return (-1);
+ }
+
+ tmp_quorate = u8;
+
+ if (tmp_quorate != TLV_QUORATE_QUORATE &&
+ tmp_quorate != TLV_QUORATE_INQUORATE) {
+ return (-4);
+ }
+
+ *quorate = tmp_quorate;
+
+ return (0);
+}
+
+int
+tlv_iter_decode_heuristics(struct tlv_iterator *tlv_iter, enum tlv_heuristics *heuristics)
+{
+ uint8_t u8;
+ enum tlv_heuristics tmp_heuristics;
+
+ if (tlv_iter_decode_u8(tlv_iter, &u8) != 0) {
+ return (-1);
+ }
+
+ tmp_heuristics = u8;
+
+ if (tmp_heuristics != TLV_HEURISTICS_PASS &&
+ tmp_heuristics != TLV_HEURISTICS_FAIL) {
+ return (-4);
+ }
+
+ *heuristics = tmp_heuristics;
+
+ return (0);
+}
+
+void
+tlv_get_supported_options(enum tlv_opt_type **supported_options, size_t *no_supported_options)
+{
+
+ *supported_options = tlv_static_supported_options;
+ *no_supported_options = TLV_STATIC_SUPPORTED_OPTIONS_SIZE;
+}
+
+int
+tlv_ring_id_eq(const struct tlv_ring_id *rid1, const struct tlv_ring_id *rid2)
+{
+
+ return (rid1->node_id == rid2->node_id && rid1->seq == rid2->seq);
+}
+
+int
+tlv_tie_breaker_eq(const struct tlv_tie_breaker *tb1, const struct tlv_tie_breaker *tb2)
+{
+
+ if (tb1->mode == tb2->mode && tb1->mode == TLV_TIE_BREAKER_MODE_NODE_ID) {
+ return (tb1->node_id == tb2->node_id);
+ }
+
+ return (tb1->mode == tb2->mode);
+}
+
+const char *
+tlv_vote_to_str(enum tlv_vote vote)
+{
+
+ switch (vote) {
+ case TLV_VOTE_UNDEFINED: break;
+ case TLV_VOTE_ACK: return ("ACK"); break;
+ case TLV_VOTE_NACK: return ("NACK"); break;
+ case TLV_VOTE_ASK_LATER: return ("Ask later"); break;
+ case TLV_VOTE_WAIT_FOR_REPLY: return ("Wait for reply"); break;
+ case TLV_VOTE_NO_CHANGE: return ("No change"); break;
+ }
+
+ return ("Unknown vote value");
+}
+
+const char *
+tlv_node_state_to_str(enum tlv_node_state state)
+{
+
+ switch (state) {
+ case TLV_NODE_STATE_NOT_SET: return ("not set"); break;
+ case TLV_NODE_STATE_MEMBER: return ("member"); break;
+ case TLV_NODE_STATE_DEAD: return ("dead"); break;
+ case TLV_NODE_STATE_LEAVING: return ("leaving"); break;
+ }
+
+ return ("Unhandled node state");
+}
+
+const char *
+tlv_tls_supported_to_str(enum tlv_tls_supported tls_supported)
+{
+
+ switch (tls_supported) {
+ case TLV_TLS_UNSUPPORTED: return ("Unsupported"); break;
+ case TLV_TLS_SUPPORTED: return ("Supported"); break;
+ case TLV_TLS_REQUIRED: return ("Required"); break;
+ }
+
+ return ("Unhandled tls supported state");
+}
+
+const char *
+tlv_decision_algorithm_type_to_str(enum tlv_decision_algorithm_type algorithm)
+{
+
+ switch (algorithm) {
+ case TLV_DECISION_ALGORITHM_TYPE_TEST: return ("Test"); break;
+ case TLV_DECISION_ALGORITHM_TYPE_FFSPLIT: return ("Fifty-Fifty split"); break;
+ case TLV_DECISION_ALGORITHM_TYPE_2NODELMS: return ("2 Node LMS"); break;
+ case TLV_DECISION_ALGORITHM_TYPE_LMS: return ("LMS"); break;
+ }
+
+ return ("Unknown algorithm");
+}
+
+const char *
+tlv_heuristics_to_str(enum tlv_heuristics heuristics)
+{
+
+ switch (heuristics) {
+ case TLV_HEURISTICS_UNDEFINED: return ("Undefined"); break;
+ case TLV_HEURISTICS_PASS: return ("Pass"); break;
+ case TLV_HEURISTICS_FAIL: return ("Fail"); break;
+ }
+
+ return ("Unknown heuristics type");
+}
+
+int
+tlv_heuristics_cmp(enum tlv_heuristics h1, enum tlv_heuristics h2)
+{
+ int res;
+
+ res = -2;
+
+ switch (h1) {
+ case TLV_HEURISTICS_UNDEFINED:
+ switch (h2) {
+ case TLV_HEURISTICS_UNDEFINED: res = 0; break;
+ case TLV_HEURISTICS_PASS: res = -1; break;
+ case TLV_HEURISTICS_FAIL: res = 1; break;
+ }
+ break;
+ case TLV_HEURISTICS_PASS:
+ switch (h2) {
+ case TLV_HEURISTICS_UNDEFINED: res = 1; break;
+ case TLV_HEURISTICS_PASS: res = 0; break;
+ case TLV_HEURISTICS_FAIL: res = 1; break;
+ }
+ break;
+ case TLV_HEURISTICS_FAIL:
+ switch (h2) {
+ case TLV_HEURISTICS_UNDEFINED: res = -1; break;
+ case TLV_HEURISTICS_PASS: res = -1; break;
+ case TLV_HEURISTICS_FAIL: res = 0; break;
+ }
+ break;
+ }
+
+ assert(res == -1 || res == 0 || res == 1);
+
+ return (res);
+}
--- /dev/null
+/*
+ * Copyright (c) 2015-2017 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.
+ */
+
+#ifndef _TLV_H_
+#define _TLV_H_
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include "dynar.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum tlv_opt_type {
+ TLV_OPT_MSG_SEQ_NUMBER = 0,
+ TLV_OPT_CLUSTER_NAME = 1,
+ TLV_OPT_TLS_SUPPORTED = 2,
+ TLV_OPT_TLS_CLIENT_CERT_REQUIRED = 3,
+ TLV_OPT_SUPPORTED_MESSAGES = 4,
+ TLV_OPT_SUPPORTED_OPTIONS = 5,
+ TLV_OPT_REPLY_ERROR_CODE = 6,
+ TLV_OPT_SERVER_MAXIMUM_REQUEST_SIZE = 7,
+ TLV_OPT_SERVER_MAXIMUM_REPLY_SIZE = 8,
+ TLV_OPT_NODE_ID = 9,
+ TLV_OPT_SUPPORTED_DECISION_ALGORITHMS = 10,
+ TLV_OPT_DECISION_ALGORITHM = 11,
+ TLV_OPT_HEARTBEAT_INTERVAL = 12,
+ TLV_OPT_RING_ID = 13,
+ TLV_OPT_CONFIG_VERSION = 14,
+ TLV_OPT_DATA_CENTER_ID = 15,
+ TLV_OPT_NODE_STATE = 16,
+ TLV_OPT_NODE_INFO = 17,
+ TLV_OPT_NODE_LIST_TYPE = 18,
+ TLV_OPT_VOTE = 19,
+ TLV_OPT_QUORATE = 20,
+ TLV_OPT_TIE_BREAKER = 21,
+ TLV_OPT_HEURISTICS = 22,
+};
+
+enum tlv_tls_supported {
+ TLV_TLS_UNSUPPORTED = 0,
+ TLV_TLS_SUPPORTED = 1,
+ TLV_TLS_REQUIRED = 2,
+};
+
+enum tlv_reply_error_code {
+ TLV_REPLY_ERROR_CODE_NO_ERROR = 0,
+ TLV_REPLY_ERROR_CODE_UNSUPPORTED_NEEDED_MESSAGE = 1,
+ TLV_REPLY_ERROR_CODE_UNSUPPORTED_NEEDED_OPTION = 2,
+ TLV_REPLY_ERROR_CODE_TLS_REQUIRED = 3,
+ TLV_REPLY_ERROR_CODE_UNSUPPORTED_MESSAGE = 4,
+ TLV_REPLY_ERROR_CODE_MESSAGE_TOO_LONG = 5,
+ TLV_REPLY_ERROR_CODE_PREINIT_REQUIRED = 6,
+ TLV_REPLY_ERROR_CODE_DOESNT_CONTAIN_REQUIRED_OPTION = 7,
+ TLV_REPLY_ERROR_CODE_UNEXPECTED_MESSAGE = 8,
+ TLV_REPLY_ERROR_CODE_ERROR_DECODING_MSG = 9,
+ TLV_REPLY_ERROR_CODE_INTERNAL_ERROR = 10,
+ TLV_REPLY_ERROR_CODE_INIT_REQUIRED = 11,
+ TLV_REPLY_ERROR_CODE_UNSUPPORTED_DECISION_ALGORITHM = 12,
+ TLV_REPLY_ERROR_CODE_INVALID_HEARTBEAT_INTERVAL = 13,
+ TLV_REPLY_ERROR_CODE_UNSUPPORTED_DECISION_ALGORITHM_MESSAGE = 14,
+ TLV_REPLY_ERROR_CODE_TIE_BREAKER_DIFFERS_FROM_OTHER_NODES = 15,
+ TLV_REPLY_ERROR_CODE_ALGORITHM_DIFFERS_FROM_OTHER_NODES = 16,
+ TLV_REPLY_ERROR_CODE_DUPLICATE_NODE_ID = 17,
+ TLV_REPLY_ERROR_CODE_INVALID_CONFIG_NODE_LIST = 18,
+ TLV_REPLY_ERROR_CODE_INVALID_MEMBERSHIP_NODE_LIST = 19,
+};
+
+enum tlv_decision_algorithm_type {
+ TLV_DECISION_ALGORITHM_TYPE_TEST = 0,
+ TLV_DECISION_ALGORITHM_TYPE_FFSPLIT = 1,
+ TLV_DECISION_ALGORITHM_TYPE_2NODELMS = 2,
+ TLV_DECISION_ALGORITHM_TYPE_LMS = 3,
+};
+
+struct tlv_ring_id {
+ uint32_t node_id;
+ uint64_t seq;
+};
+
+enum tlv_node_state {
+ TLV_NODE_STATE_NOT_SET = 0,
+ TLV_NODE_STATE_MEMBER = 1,
+ TLV_NODE_STATE_DEAD = 2,
+ TLV_NODE_STATE_LEAVING = 3,
+};
+
+enum tlv_node_list_type {
+ TLV_NODE_LIST_TYPE_INITIAL_CONFIG = 0,
+ TLV_NODE_LIST_TYPE_CHANGED_CONFIG = 1,
+ TLV_NODE_LIST_TYPE_MEMBERSHIP = 2,
+ TLV_NODE_LIST_TYPE_QUORUM = 3,
+};
+
+enum tlv_vote {
+ TLV_VOTE_UNDEFINED = 0,
+ TLV_VOTE_ACK = 1,
+ TLV_VOTE_NACK = 2,
+ TLV_VOTE_ASK_LATER = 3,
+ TLV_VOTE_WAIT_FOR_REPLY = 4,
+ TLV_VOTE_NO_CHANGE = 5,
+};
+
+enum tlv_quorate {
+ TLV_QUORATE_INQUORATE = 0,
+ TLV_QUORATE_QUORATE = 1,
+};
+
+enum tlv_tie_breaker_mode {
+ TLV_TIE_BREAKER_MODE_LOWEST = 1,
+ TLV_TIE_BREAKER_MODE_HIGHEST = 2,
+ TLV_TIE_BREAKER_MODE_NODE_ID = 3,
+};
+
+struct tlv_tie_breaker {
+ enum tlv_tie_breaker_mode mode;
+ uint32_t node_id;
+};
+
+struct tlv_node_info {
+ uint32_t node_id;
+ uint32_t data_center_id; /* 0 - data center id was not set */
+ enum tlv_node_state node_state; /* TLV_NODE_STATE_NOT_SET - state was not set */
+};
+
+enum tlv_heuristics {
+ TLV_HEURISTICS_UNDEFINED = 0,
+ TLV_HEURISTICS_PASS = 1,
+ TLV_HEURISTICS_FAIL = 2,
+};
+
+struct tlv_iterator {
+ const char *msg;
+ size_t msg_len;
+ size_t current_pos;
+ size_t msg_header_len;
+ int iter_next_called;
+};
+
+extern int tlv_add(struct dynar *msg, enum tlv_opt_type opt_type,
+ uint16_t opt_len, const void *value);
+
+extern int tlv_add_u32(struct dynar *msg, enum tlv_opt_type opt_type,
+ uint32_t u32);
+
+extern int tlv_add_u8(struct dynar *msg, enum tlv_opt_type opt_type,
+ uint8_t u8);
+
+extern int tlv_add_u16(struct dynar *msg, enum tlv_opt_type opt_type,
+ uint16_t u16);
+
+extern int tlv_add_u64(struct dynar *msg, enum tlv_opt_type opt_type,
+ uint64_t u64);
+
+extern int tlv_add_string(struct dynar *msg, enum tlv_opt_type opt_type,
+ const char *str);
+
+extern int tlv_add_u16_array(struct dynar *msg, enum tlv_opt_type opt_type,
+ const uint16_t *array, size_t array_size);
+
+extern int tlv_add_supported_options(struct dynar *msg,
+ const enum tlv_opt_type *supported_options, size_t no_supported_options);
+
+extern int tlv_add_msg_seq_number(struct dynar *msg,
+ uint32_t msg_seq_number);
+
+extern int tlv_add_cluster_name(struct dynar *msg, const char *cluster_name);
+
+extern int tlv_add_tls_supported(struct dynar *msg,
+ enum tlv_tls_supported tls_supported);
+
+extern int tlv_add_tls_client_cert_required(struct dynar *msg,
+ int tls_client_cert_required);
+
+extern int tlv_add_reply_error_code(struct dynar *msg,
+ enum tlv_reply_error_code error_code);
+
+extern int tlv_add_node_id(struct dynar *msg, uint32_t node_id);
+
+extern int tlv_add_server_maximum_request_size(struct dynar *msg,
+ size_t server_maximum_request_size);
+
+extern int tlv_add_server_maximum_reply_size(struct dynar *msg,
+ size_t server_maximum_reply_size);
+
+extern int tlv_add_supported_decision_algorithms(struct dynar *msg,
+ const enum tlv_decision_algorithm_type *supported_algorithms, size_t no_supported_algorithms);
+
+extern int tlv_add_decision_algorithm(struct dynar *msg,
+ enum tlv_decision_algorithm_type decision_algorithm);
+
+extern int tlv_add_heartbeat_interval(struct dynar *msg,
+ uint32_t heartbeat_interval);
+
+extern int tlv_add_ring_id(struct dynar *msg,
+ const struct tlv_ring_id *ring_id);
+
+extern int tlv_add_tie_breaker(struct dynar *msg,
+ const struct tlv_tie_breaker *tie_breaker);
+
+extern int tlv_add_config_version(struct dynar *msg,
+ uint64_t config_version);
+
+extern int tlv_add_data_center_id(struct dynar *msg,
+ uint32_t data_center_id);
+
+extern int tlv_add_node_state(struct dynar *msg,
+ enum tlv_node_state node_state);
+
+extern int tlv_add_node_info(struct dynar *msg,
+ const struct tlv_node_info *node_info);
+
+extern int tlv_add_node_list_type(struct dynar *msg,
+ enum tlv_node_list_type node_list_type);
+
+extern int tlv_add_vote(struct dynar *msg, enum tlv_vote vote);
+
+extern int tlv_add_quorate(struct dynar *msg, enum tlv_quorate quorate);
+
+extern int tlv_add_heuristics(struct dynar *msg,
+ enum tlv_heuristics heuristics);
+
+extern void tlv_iter_init_str(const char *msg, size_t msg_len,
+ size_t msg_header_len, struct tlv_iterator *tlv_iter);
+
+extern void tlv_iter_init(const struct dynar *msg, size_t msg_header_len,
+ struct tlv_iterator *tlv_iter);
+
+extern enum tlv_opt_type tlv_iter_get_type(const struct tlv_iterator *tlv_iter);
+
+extern uint16_t tlv_iter_get_len(const struct tlv_iterator *tlv_iter);
+
+extern const char *tlv_iter_get_data(const struct tlv_iterator *tlv_iter);
+
+extern int tlv_iter_next(struct tlv_iterator *tlv_iter);
+
+extern int tlv_iter_decode_u8(struct tlv_iterator *tlv_iter, uint8_t *res);
+
+extern int tlv_iter_decode_tls_supported(struct tlv_iterator *tlv_iter,
+ enum tlv_tls_supported *tls_supported);
+
+extern int tlv_iter_decode_u32(struct tlv_iterator *tlv_iter,
+ uint32_t *res);
+
+extern int tlv_iter_decode_str(struct tlv_iterator *tlv_iter, char **str,
+ size_t *str_len);
+
+extern int tlv_iter_decode_client_cert_required(
+ struct tlv_iterator *tlv_iter, uint8_t *client_cert_required);
+
+extern int tlv_iter_decode_u16_array(struct tlv_iterator *tlv_iter,
+ uint16_t **u16a, size_t *no_items);
+
+extern int tlv_iter_decode_supported_options(struct tlv_iterator *tlv_iter,
+ enum tlv_opt_type **supported_options, size_t *no_supported_options);
+
+extern int tlv_iter_decode_supported_decision_algorithms(
+ struct tlv_iterator *tlv_iter,
+ enum tlv_decision_algorithm_type **supported_decision_algorithms,
+ size_t *no_supported_decision_algorithms);
+
+extern int tlv_iter_decode_u16(struct tlv_iterator *tlv_iter,
+ uint16_t *u16);
+
+extern int tlv_iter_decode_u64(struct tlv_iterator *tlv_iter,
+ uint64_t *u64);
+
+extern int tlv_iter_decode_reply_error_code(struct tlv_iterator *tlv_iter,
+ enum tlv_reply_error_code *reply_error_code);
+
+extern int tlv_iter_decode_decision_algorithm(struct tlv_iterator *tlv_iter,
+ enum tlv_decision_algorithm_type *decision_algorithm);
+
+extern int tlv_iter_decode_ring_id(struct tlv_iterator *tlv_iter,
+ struct tlv_ring_id *ring_id);
+
+extern int tlv_iter_decode_tie_breaker(struct tlv_iterator *tlv_iter,
+ struct tlv_tie_breaker *tie_breaker);
+
+extern int tlv_iter_decode_node_state(struct tlv_iterator *tlv_iter,
+ enum tlv_node_state *node_state);
+
+extern int tlv_iter_decode_node_info(struct tlv_iterator *tlv_iter,
+ struct tlv_node_info *node_info);
+
+extern int tlv_iter_decode_node_list_type(struct tlv_iterator *tlv_iter,
+ enum tlv_node_list_type *node_list_type);
+
+extern int tlv_iter_decode_vote(struct tlv_iterator *tlv_iter,
+ enum tlv_vote *vote);
+
+extern int tlv_iter_decode_quorate(struct tlv_iterator *tlv_iter,
+ enum tlv_quorate *quorate);
+
+extern int tlv_iter_decode_heuristics(struct tlv_iterator *tlv_iter,
+ enum tlv_heuristics *heuristics);
+
+extern void tlv_get_supported_options(enum tlv_opt_type **supported_options,
+ size_t *no_supported_options);
+
+extern int tlv_ring_id_eq(const struct tlv_ring_id *rid1,
+ const struct tlv_ring_id *rid2);
+
+extern int tlv_tie_breaker_eq(const struct tlv_tie_breaker *tb1,
+ const struct tlv_tie_breaker *tb2);
+
+extern const char *tlv_vote_to_str(enum tlv_vote vote);
+
+extern const char *tlv_node_state_to_str(enum tlv_node_state state);
+
+extern const char *tlv_tls_supported_to_str(enum tlv_tls_supported tls_supported);
+
+extern const char *tlv_decision_algorithm_type_to_str(
+ enum tlv_decision_algorithm_type algorithm);
+
+extern const char *tlv_heuristics_to_str(enum tlv_heuristics heuristics);
+
+/*
+ * Compare h1 and h2. Return -1 if h1 < h2, 0 if h1 == h2 and 1 if h1 > h2
+ */
+extern int tlv_heuristics_cmp(enum tlv_heuristics h1, enum tlv_heuristics h2);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TLV_H_ */
--- /dev/null
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "unix-socket-client-list.h"
+
+void
+unix_socket_client_list_init(struct unix_socket_client_list *client_list)
+{
+
+ TAILQ_INIT(client_list);
+}
+
+struct unix_socket_client *
+unix_socket_client_list_add(struct unix_socket_client_list *client_list,
+ int sock, size_t max_receive_size, size_t max_send_size, void *user_data)
+{
+ struct unix_socket_client *client;
+
+ client = (struct unix_socket_client *)malloc(sizeof(*client));
+ if (client == NULL) {
+ return (NULL);
+ }
+
+ unix_socket_client_init(client, sock, max_receive_size, max_send_size, user_data);
+
+ TAILQ_INSERT_TAIL(client_list, client, entries);
+
+ return (client);
+}
+
+void
+unix_socket_client_list_free(struct unix_socket_client_list *client_list)
+{
+ struct unix_socket_client *client;
+ struct unix_socket_client *client_next;
+
+ client = TAILQ_FIRST(client_list);
+
+ while (client != NULL) {
+ client_next = TAILQ_NEXT(client, entries);
+
+ unix_socket_client_destroy(client);
+ free(client);
+
+ client = client_next;
+ }
+
+ TAILQ_INIT(client_list);
+}
+
+void
+unix_socket_client_list_del(struct unix_socket_client_list *client_list,
+ struct unix_socket_client *client)
+{
+
+ TAILQ_REMOVE(client_list, client, entries);
+ unix_socket_client_destroy(client);
+ free(client);
+}
+
+size_t
+unix_socket_client_list_no_clients(struct unix_socket_client_list *client_list)
+{
+ size_t res;
+ struct unix_socket_client *client;
+
+ res = 0;
+
+ TAILQ_FOREACH(client, client_list, entries) {
+ res++;
+ }
+
+ return (res);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _UNIX_SOCKET_CLIENT_LIST_H_
+#define _UNIX_SOCKET_CLIENT_LIST_H_
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#include "unix-socket-client.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+TAILQ_HEAD(unix_socket_client_list, unix_socket_client);
+
+extern void unix_socket_client_list_init(
+ struct unix_socket_client_list *client_list);
+
+extern struct unix_socket_client *unix_socket_client_list_add(
+ struct unix_socket_client_list *client_list,
+ int sock, size_t max_receive_size, size_t max_send_size, void *user_data);
+
+extern void unix_socket_client_list_free(
+ struct unix_socket_client_list *client_list);
+
+extern void unix_socket_client_list_del(
+ struct unix_socket_client_list *client_list, struct unix_socket_client *client);
+
+extern size_t unix_socket_client_list_no_clients(
+ struct unix_socket_client_list *client_list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UNIX_SOCKET_CLIENT_LIST_H_ */
--- /dev/null
+/*
+ * 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 <errno.h>
+#include <string.h>
+
+#include "unix-socket-client.h"
+#include "unix-socket.h"
+
+#define UNIX_SOCKET_CLIENT_BUFFER 256
+
+void
+unix_socket_client_init(struct unix_socket_client *client, int sock, size_t max_receive_size,
+ size_t max_send_size, void *user_data)
+{
+
+ memset(client, 0, sizeof(*client));
+ client->socket = sock;
+ client->user_data = user_data;
+ dynar_init(&client->receive_buffer, max_receive_size);
+ dynar_init(&client->send_buffer, max_send_size);
+}
+
+void
+unix_socket_client_destroy(struct unix_socket_client *client)
+{
+
+ dynar_destroy(&client->send_buffer);
+ dynar_destroy(&client->receive_buffer);
+}
+
+void
+unix_socket_client_read_line(struct unix_socket_client *client, int enabled)
+{
+
+ client->reading_line = enabled;
+}
+
+void
+unix_socket_client_write_buffer(struct unix_socket_client *client, int enabled)
+{
+
+ client->writing_buffer = enabled;
+}
+
+/*
+ * 1 Full line readed
+ * 0 Partial read (no error)
+ * -1 End of connection
+ * -2 Buffer too long
+ * -3 Unhandled error
+ */
+int
+unix_socket_client_io_read(struct unix_socket_client *client)
+{
+ char buf[UNIX_SOCKET_CLIENT_BUFFER];
+ ssize_t readed;
+ int res;
+ size_t zi;
+
+ res = 0;
+ readed = unix_socket_read(client->socket, buf, sizeof(buf));
+ if (readed > 0) {
+ client->msg_already_received_bytes += readed;
+ if (dynar_cat(&client->receive_buffer, buf, readed) == -1) {
+ res = -2;
+ goto exit_err;
+ }
+
+ for (zi = 0; zi < (size_t)readed; zi++) {
+ if (buf[zi] == '\n') {
+ res = 1;
+ }
+ }
+ }
+
+ if (readed == 0) {
+ res = -1;
+ }
+
+ if (readed < 0 && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
+ res = -3;
+ }
+
+exit_err:
+ return (res);
+}
+
+/*
+ * 1 All data succesfully sent
+ * 0 Partial send (no error)
+ * -1 End of connection
+ * -2 Unhandled error
+ */
+int
+unix_socket_client_io_write(struct unix_socket_client *client)
+{
+ ssize_t sent;
+ size_t to_send;
+ int res;
+
+ res = 0;
+
+ to_send = dynar_size(&client->send_buffer) - client->msg_already_sent_bytes;
+ if (to_send > UNIX_SOCKET_CLIENT_BUFFER) {
+ to_send = UNIX_SOCKET_CLIENT_BUFFER;
+ }
+
+ sent = unix_socket_write(client->socket,
+ dynar_data(&client->send_buffer) + client->msg_already_sent_bytes,
+ to_send);
+
+ if (sent > 0) {
+ client->msg_already_sent_bytes += sent;
+
+ if (client->msg_already_sent_bytes == dynar_size(&client->send_buffer)) {
+ return (1);
+ }
+ }
+
+ if (sent == 0) {
+ res = -1;
+ }
+
+ if (sent < 0 && errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
+ res = -2;
+ }
+
+ return (res);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _UNIX_SOCKET_CLIENT_H_
+#define _UNIX_SOCKET_CLIENT_H_
+
+#include <sys/types.h>
+
+#include <sys/queue.h>
+#include <inttypes.h>
+
+#include "dynar.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct unix_socket_client {
+ int socket;
+ struct dynar receive_buffer;
+ struct dynar send_buffer;
+ size_t msg_already_received_bytes;
+ size_t msg_already_sent_bytes;
+ int reading_line;
+ int writing_buffer;
+ int schedule_disconnect;
+ void *user_data;
+ TAILQ_ENTRY(unix_socket_client) entries;
+};
+
+extern void unix_socket_client_init(struct unix_socket_client *client, int sock,
+ size_t max_receive_size, size_t max_send_size, void *user_data);
+
+extern void unix_socket_client_destroy(struct unix_socket_client *client);
+
+extern void unix_socket_client_read_line(struct unix_socket_client *client, int enabled);
+
+extern void unix_socket_client_write_buffer(struct unix_socket_client *client, int enabled);
+
+extern int unix_socket_client_io_read(struct unix_socket_client *client);
+
+extern int unix_socket_client_io_write(struct unix_socket_client *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UNIX_SOCKET_CLIENT_H_ */
--- /dev/null
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "unix-socket.h"
+#include "unix-socket-ipc.h"
+
+int
+unix_socket_ipc_init(struct unix_socket_ipc *ipc, const char *socket_file_name, int backlog,
+ size_t max_clients, size_t max_receive_size, size_t max_send_size)
+{
+
+ memset(ipc, 0, sizeof(*ipc));
+
+ ipc->socket_file_name = strdup(socket_file_name);
+ if (ipc->socket_file_name == NULL) {
+ return (-1);
+ }
+
+ unix_socket_client_list_init(&ipc->clients);
+
+ ipc->backlog = backlog;
+ ipc->socket = unix_socket_server_create(ipc->socket_file_name, 1,
+ backlog);
+ if (ipc->socket < 0) {
+ free(ipc->socket_file_name);
+ return (-1);
+ }
+
+ ipc->max_clients = max_clients;
+ ipc->max_receive_size = max_receive_size;
+ ipc->max_send_size = max_send_size;
+
+ return (0);
+}
+
+int
+unix_socket_ipc_close(struct unix_socket_ipc *ipc)
+{
+ int res;
+
+ res = 0;
+
+ if (ipc->socket < 0) {
+ return (0);
+ }
+
+ if (unix_socket_server_destroy(ipc->socket, ipc->socket_file_name) != 0) {
+ res = -1;
+ }
+
+ free(ipc->socket_file_name);
+ ipc->socket_file_name = NULL;
+
+ ipc->socket = -1;
+
+ return (res);
+}
+
+int
+unix_socket_ipc_is_closed(struct unix_socket_ipc *ipc)
+{
+
+ return (ipc->socket < 0);
+}
+
+int
+unix_socket_ipc_destroy(struct unix_socket_ipc *ipc)
+{
+
+ if (unix_socket_ipc_close(ipc) != 0) {
+ return (-1);
+ }
+
+ unix_socket_client_list_free(&ipc->clients);
+
+ return (0);
+}
+
+/*
+ * 0 = No error
+ * -1 = Can't accept connection (errno set)
+ * -2 = Too much clients
+ * -3 = Can't add client to list
+ */
+int
+unix_socket_ipc_accept(struct unix_socket_ipc *ipc, struct unix_socket_client **res_client)
+{
+ int client_sock;
+ struct unix_socket_client *client;
+
+ if ((client_sock = unix_socket_server_accept(ipc->socket, 1)) < 0) {
+ return (-1);
+ }
+
+ if (ipc->max_clients != 0 &&
+ unix_socket_client_list_no_clients(&ipc->clients) >= ipc->max_clients) {
+ unix_socket_close(client_sock);
+
+ return (-2);
+ }
+
+ client = unix_socket_client_list_add(&ipc->clients, client_sock, ipc->max_receive_size,
+ ipc->max_send_size, NULL);
+ if (client == NULL) {
+ unix_socket_close(client_sock);
+
+ return (-3);
+ }
+
+ *res_client = client;
+
+ return (0);
+}
+
+void
+unix_socket_ipc_client_disconnect(struct unix_socket_ipc *ipc, struct unix_socket_client *client)
+{
+
+ unix_socket_close(client->socket);
+ unix_socket_client_list_del(&ipc->clients, client);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _UNIX_SOCKET_IPC_H_
+#define _UNIX_SOCKET_IPC_H_
+
+#include "unix-socket-client.h"
+#include "unix-socket-client-list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct unix_socket_ipc {
+ int socket;
+ int backlog;
+ char *socket_file_name;
+ struct unix_socket_client_list clients;
+ size_t max_clients;
+ size_t max_receive_size;
+ size_t max_send_size;
+};
+
+extern int unix_socket_ipc_init(struct unix_socket_ipc *ipc,
+ const char *socket_file_name, int backlog, size_t max_clients, size_t max_receive_size,
+ size_t max_send_size);
+
+extern int unix_socket_ipc_destroy(struct unix_socket_ipc *ipc);
+
+extern int unix_socket_ipc_accept(struct unix_socket_ipc *ipc,
+ struct unix_socket_client **res_client);
+
+void unix_socket_ipc_client_disconnect(struct unix_socket_ipc *ipc,
+ struct unix_socket_client *client);
+
+extern int unix_socket_ipc_close(struct unix_socket_ipc *ipc);
+
+extern int unix_socket_ipc_is_closed(struct unix_socket_ipc *ipc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UNIX_SOCKET_IPC_H_ */
--- /dev/null
+/*
+ * 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 <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "unix-socket.h"
+#include "utils.h"
+
+int
+unix_socket_server_create(const char *path, int non_blocking, int backlog)
+{
+ int s;
+ struct sockaddr_un sun;
+
+ if (strlen(path) >= sizeof(sun.sun_path)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+
+ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ return (-1);
+ }
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+
+ strncpy(sun.sun_path, path, strlen(path));
+ unlink(path);
+ if (bind(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) != 0) {
+ close(s);
+
+ return (-1);
+ }
+
+ if (non_blocking) {
+ if (utils_fd_set_non_blocking(s) != 0) {
+ close(s);
+
+ return (-1);
+ }
+ }
+
+ if (listen(s, backlog) != 0) {
+ close(s);
+
+ return (-1);
+ }
+
+ return (s);
+}
+
+int
+unix_socket_client_create(const char *path, int non_blocking)
+{
+ int s;
+ struct sockaddr_un sun;
+
+ if (strlen(path) >= sizeof(sun.sun_path)) {
+ errno = ENAMETOOLONG;
+ return (-1);
+ }
+
+ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ return (-1);
+ }
+
+ memset(&sun, 0, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+
+ strncpy(sun.sun_path, path, strlen(path));
+
+ if (non_blocking) {
+ if (utils_fd_set_non_blocking(s) != 0) {
+ close(s);
+
+ return (-1);
+ }
+ }
+
+ if (connect(s, (struct sockaddr *)&sun, SUN_LEN(&sun)) != 0) {
+ close(s);
+
+ return (-1);
+ }
+
+ return (s);
+}
+
+
+
+int
+unix_socket_server_destroy(int sock, const char *path)
+{
+ int res;
+
+ res = 0;
+
+ if (close(sock) != 0) {
+ res = -1;
+ }
+
+ if (unlink(path) != 0) {
+ res = -1;
+ }
+
+ return (res);
+}
+
+int
+unix_socket_server_accept(int sock, int non_blocking)
+{
+ struct sockaddr_un sun;
+ socklen_t sun_len;
+ int client_sock;
+
+ sun_len = sizeof(sun);
+ if ((client_sock = accept(sock, (struct sockaddr *)&sun, &sun_len)) < 0) {
+ return (-1);
+ }
+
+ if (non_blocking) {
+ if (utils_fd_set_non_blocking(client_sock) != 0) {
+ close(client_sock);
+
+ return (-1);
+ }
+ }
+
+ return (client_sock);
+}
+
+int
+unix_socket_close(int sock)
+{
+
+ return (close(sock));
+}
+
+ssize_t
+unix_socket_read(int sock, void *buf, size_t len)
+{
+
+ return (recv(sock, buf, len, 0));
+}
+
+ssize_t
+unix_socket_write(int sock, void *buf, size_t len)
+{
+
+ return (send(sock, buf, len, 0));
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _UNIX_SOCKET_H_
+#define _UNIX_SOCKET_H_
+
+#include <string.h>
+#include <sys/types.h>
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int unix_socket_server_create(const char *path, int non_blocking, int backlog);
+
+extern int unix_socket_client_create(const char *path, int non_blocking);
+
+extern int unix_socket_server_destroy(int sock, const char *path);
+
+extern int unix_socket_server_accept(int sock, int non_blocking);
+
+extern int unix_socket_close(int sock);
+
+extern ssize_t unix_socket_read(int sock, void *buf, size_t len);
+
+extern ssize_t unix_socket_write(int sock, void *buf, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UNIX_SOCKET_H_ */
--- /dev/null
+/*
+ * 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 <sys/types.h>
+#include <sys/file.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+
+#include "utils.h"
+
+/*
+ * Check string to value on, off, yes, no, 0, 1. Return 1 if value is on, yes or 1, 0 if
+ * value is off, no or 0 and -1 otherwise.
+ */
+int
+utils_parse_bool_str(const char *str)
+{
+
+ if (strcasecmp(str, "yes") == 0 ||
+ strcasecmp(str, "on") == 0 ||
+ strcasecmp(str, "1") == 0) {
+ return (1);
+ } else if (strcasecmp(str, "no") == 0 ||
+ strcasecmp(str, "off") == 0 ||
+ strcasecmp(str, "0") == 0) {
+ return (0);
+ }
+
+ return (-1);
+}
+
+int
+utils_flock(const char *lockfile, pid_t pid, int *another_instance_running)
+{
+ struct flock lock;
+ char pid_s[17];
+ int fd_flag;
+ int lf;
+ char *dname;
+
+ *another_instance_running = 0;
+
+ /*
+ * lockfile directory may not exists. Creation of directory should
+ * be handled by initscript/tmpfiles.d. But as a last chance it
+ * make sense to try to create it here.
+ */
+ dname = strdup(lockfile);
+ if (dname != NULL) {
+ (void)mkdir(dirname(dname), 0770);
+ free(dname);
+ }
+
+ lf = open(lockfile, O_WRONLY | O_CREAT, 0640);
+ if (lf == -1) {
+ return (-1);
+ }
+
+retry_fcntl:
+ lock.l_type = F_WRLCK;
+ lock.l_start = 0;
+ lock.l_whence = SEEK_SET;
+ lock.l_len = 0;
+ if (fcntl(lf, F_SETLK, &lock) == -1) {
+ switch (errno) {
+ case EINTR:
+ goto retry_fcntl;
+ break;
+ case EAGAIN:
+ case EACCES:
+ *another_instance_running = 1;
+ goto error_close;
+ break;
+ default:
+ goto error_close;
+ break;
+ }
+ }
+
+ if (ftruncate(lf, 0) == -1) {
+ goto error_close_unlink;
+ }
+
+ memset(pid_s, 0, sizeof(pid_s));
+ snprintf(pid_s, sizeof(pid_s) - 1, "%u\n", pid);
+
+retry_write:
+ if (write(lf, pid_s, strlen(pid_s)) != (ssize_t)strlen(pid_s)) {
+ if (errno == EINTR) {
+ goto retry_write;
+ } else {
+ goto error_close_unlink;
+ }
+ }
+
+ if ((fd_flag = fcntl(lf, F_GETFD, 0)) == -1) {
+ goto error_close_unlink;
+ }
+ fd_flag |= FD_CLOEXEC;
+ if (fcntl(lf, F_SETFD, fd_flag) == -1) {
+ goto error_close_unlink;
+ }
+
+ return (lf);
+
+error_close_unlink:
+ unlink(lockfile);
+error_close:
+ close(lf);
+
+ return (-1);
+}
+
+void
+utils_tty_detach(void)
+{
+ int devnull;
+
+ switch (fork()) {
+ case -1:
+ err(1, "Can't create child process");
+ break;
+ case 0:
+ break;
+ default:
+ exit(0);
+ break;
+ }
+
+ /* Create new session */
+ (void)setsid();
+
+ /*
+ * Map stdin/out/err to /dev/null.
+ */
+ devnull = open("/dev/null", O_RDWR);
+ if (devnull == -1) {
+ err(1, "Can't open /dev/null");
+ }
+
+ if (dup2(devnull, 0) < 0 || dup2(devnull, 1) < 0
+ || dup2(devnull, 2) < 0) {
+ close(devnull);
+ err(1, "Can't dup2 stdin/out/err to /dev/null");
+ }
+ close(devnull);
+}
+
+int
+utils_fd_set_non_blocking(int fd)
+{
+ int flags;
+
+ flags = fcntl(fd, F_GETFL, NULL);
+
+ if (flags < 0) {
+ return (-1);
+ }
+
+ flags |= O_NONBLOCK;
+ if (fcntl(fd, F_SETFL, flags) < 0) {
+ return (-1);
+ }
+
+ return (0);
+}
--- /dev/null
+/*
+ * 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.
+ */
+
+#ifndef _UTILS_H_
+#define _UTILS_H_
+
+#include <sys/types.h>
+#include <inttypes.h>
+
+#define UTILS_PRI_NODE_ID "%" PRIu32
+#define UTILS_PRI_DATACENTER_ID "%" PRIu32
+/*
+#define UTILS_PRI_NODE_ID "0x%" PRIx32
+#define UTILS_PRI_DATACENTER_ID "0x%" PRIx32
+*/
+#define UTILS_PRI_MSG_SEQ "%" PRIu32
+#define UTILS_PRI_RING_ID "%" PRIx32 ".%" PRIx64
+#define UTILS_PRI_CONFIG_VERSION "%" PRIu64
+#define UTILS_PRI_EXPECTED_VOTES "%" PRIu32
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int utils_parse_bool_str(const char *str);
+
+extern int utils_flock(const char *lockfile, pid_t pid,
+ int *another_instance_running);
+
+extern void utils_tty_detach(void);
+
+extern int utils_fd_set_non_blocking(int fd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UTILS_H_ */