Initial import from corosync codebase

Used the code from corosync master
(31ddba64a2726bcedf81eb84df2e2da4846832f7)

Signed-off-by: Jan Friesse <jfriesse@redhat.com>
This commit is contained in:
Jan Friesse 2018-01-22 15:59:23 +01:00
commit 9a1955a7d6
201 changed files with 36801 additions and 0 deletions

33
.gitignore vendored Normal file
View File

@ -0,0 +1,33 @@
*.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

58
LICENSE Normal file
View File

@ -0,0 +1,58 @@
-----------------------------------------------------------------------------
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/>.

156
Makefile.am Normal file
View File

@ -0,0 +1,156 @@
# 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

33
README Normal file
View File

@ -0,0 +1,33 @@
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

5
autogen.sh Executable file
View File

@ -0,0 +1,5 @@
#!/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

161
build-aux/git-version-gen Executable file
View File

@ -0,0 +1,161 @@
#!/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:

191
build-aux/gitlog-to-changelog Executable file
View File

@ -0,0 +1,191 @@
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:

75
build-aux/release.mk Normal file
View File

@ -0,0 +1,75 @@
# 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-*

368
configure.ac Normal file
View File

@ -0,0 +1,368 @@
# -*- 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}])

212
corosync-qdevice.spec.in Normal file
View File

@ -0,0 +1,212 @@
@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

8
init/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
corosync
corosync-notifyd
corosync.service
corosync-notifyd.service
corosync-qnetd
corosync-qnetd.service
corosync-qdevice
corosync-qdevice.service

83
init/Makefile.am Normal file
View File

@ -0,0 +1,83 @@
# 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)

164
init/corosync-qdevice.in Executable file
View File

@ -0,0 +1,164 @@
#!@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

View File

@ -0,0 +1,17 @@
[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

View File

@ -0,0 +1,6 @@
# 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=""

171
init/corosync-qnetd.in Executable file
View File

@ -0,0 +1,171 @@
#!@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

View File

@ -0,0 +1,19 @@
[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

View File

@ -0,0 +1,13 @@
# Corosync Qdevice Network daemon init script configuration file
# COROSYNC_QNETD_OPTIONS specifies options passed to corosync-qnetd command
# (default is no options).
# See "man corosync-qnetd" for detailed descriptions of the options.
COROSYNC_QNETD_OPTIONS=""
# COROSYNC_QNETD_RUNAS specifies user under which qnetd daemon should be running
# (not set or empty is default and means "user who executes init script")
# Make sure to set correct owner of directories /etc/corosync/qnetd and
# /var/run/corosync-qnetd
# This has no effect if systemd unit is used (you have to change unit file)
COROSYNC_QNETD_RUNAS=""

2
man/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.html
*.3

92
man/Makefile.am Normal file
View File

@ -0,0 +1,92 @@
# 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

View File

@ -0,0 +1,85 @@
.\"/*
.\" * 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

130
man/corosync-qdevice-tool.8 Normal file
View File

@ -0,0 +1,130 @@
.\"/*
.\" * 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

461
man/corosync-qdevice.8 Normal file
View File

@ -0,0 +1,461 @@
.\"/*
.\" * 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

View File

@ -0,0 +1,73 @@
.\"/*
.\" * 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

128
man/corosync-qnetd-tool.8 Normal file
View File

@ -0,0 +1,128 @@
.\"/*
.\" * 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

227
man/corosync-qnetd.8 Normal file
View File

@ -0,0 +1,227 @@
.\"/*
.\" * 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

7
qdevices/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
corosync-qdevice
corosync-qdevice-tool
corosync-qnetd-certutil
corosync-qdevice-net-certutil
corosync-qnetd
corosync-qnetd-tool
*.test

180
qdevices/Makefile.am Normal file
View File

@ -0,0 +1,180 @@
# 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)

View File

@ -0,0 +1,404 @@
#!@BASHPATH@
#
# Copyright (c) 2015-2016 Red Hat, Inc.
#
# All rights reserved.
#
# Author: Jan Friesse (jfriesse@redhat.com)
#
# This software licensed under BSD license, the text of which follows:
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# - Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# - Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# - Neither the name of the Red Hat, Inc. nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
BASE_DIR="@COROSYSCONFDIR@/qdevice/net"
DB_DIR_QNETD="@COROSYSCONFDIR@/qnetd/nssdb"
DB_DIR_NODE="$BASE_DIR/nssdb"
# Validity of certificate (months)
CRT_VALIDITY=1200
CA_NICKNAME="QNet CA"
SERVER_NICKNAME="QNetd Cert"
CLUSTER_NICKNAME="Cluster Cert"
CA_SUBJECT="CN=QNet CA"
SERVER_SUBJECT="CN=Qnetd Server"
PWD_FILE_BASE="pwdfile.txt"
NOISE_FILE_BASE="noise.txt"
SERIAL_NO_FILE_BASE="serial.txt"
CA_EXPORT_FILE="$DB_DIR_QNETD/qnetd-cacert.crt"
CRQ_FILE_BASE="qdevice-net-node.crq"
CRT_FILE_BASE="" # Generated from cluster name
P12_FILE_BASE="qdevice-net-node.p12"
QNETD_CERTUTIL_CMD="corosync-qnetd-certutil"
usage() {
echo "$0: [-i|-m|-M|-r|-s|-Q] [-c certificate] [-n cluster_name]"
echo
echo " -i Initialize node CA. Needs CA certificate from server"
echo " -m Import cluster certificate on node (needs pk12 certificate)"
echo " -r Generate cluster certificate request"
echo " -M Import signed cluster certificate and export certificate with key to pk12 file"
echo " -Q Quick start. Uses ssh/scp to initialze both qnetd and nodes."
echo ""
echo " -c certificate Ether CA, CRQ, CRT or pk12 certificate (operation dependant)"
echo " -n cluster_name Name of cluster (for -r and -s operations)"
echo ""
echo "Typical usage:"
echo "- Initialize database on QNetd server by running $QNETD_CERTUTIL_CMD -i"
echo "- Copy exported QNetd CA certificate ($CA_EXPORT_FILE) to every node"
echo "- On one of cluster node initialize database by running $0 -i -c `basename $CA_EXPORT_FILE`"
echo "- Generate certificate request: $0 -r -n Cluster (Cluster name must match cluster_name key in the corosync.conf)"
echo "- Copy exported CRQ to QNetd server"
echo "- On QNetd server sign and export cluster certificate by running $QNETD_CERTUTIL_CMD -s -c `basename $CRQ_FILE_BASE` -n Cluster"
echo "- Copy exported CRT to node where certificate request was created"
echo "- Import certificate on node where certificate request was created by running $0 -M -c cluster-Cluster.crt"
echo "- Copy output $P12_FILE_BASE to all other cluster nodes"
echo "- On all other nodes in cluster:"
echo " - Init database by running $0 -i -c `basename $CA_EXPORT_FILE`"
echo " - Import cluster certificate and key: $0 -m -c `basename $P12_FILE_BASE`"
echo ""
echo "It is also possible to use Quick start (-Q). This needs properly configured ssh."
echo " $0 -Q -n Cluster qnetd_server node1 node2 ... nodeN"
exit 0
}
create_new_noise_file() {
local noise_file="$1"
if [ ! -e "$noise_file" ];then
echo "Creating new noise file $noise_file"
(ps -elf; date; w) | sha1sum | (read sha_sum rest; echo $sha_sum) > "$noise_file"
chown root:root "$noise_file"
chmod 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

View File

@ -0,0 +1,281 @@
/*
* 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);
}

298
qdevices/corosync-qdevice.c Normal file
View File

@ -0,0 +1,298 @@
/*
* 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);
}

View File

@ -0,0 +1,215 @@
#!@BASHPATH@
#
# Copyright (c) 2015-2016 Red Hat, Inc.
#
# All rights reserved.
#
# Author: Jan Friesse (jfriesse@redhat.com)
#
# This software licensed under BSD license, the text of which follows:
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# - Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# - Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# - Neither the name of the Red Hat, Inc. nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
CONFIG_DIR="@COROSYSCONFDIR@/qnetd"
DB_DIR="$CONFIG_DIR/nssdb"
# Validity of certificate (months)
CRT_VALIDITY=1200
CA_NICKNAME="QNet CA"
SERVER_NICKNAME="QNetd Cert"
CLUSTER_NICKNAME="Cluster Cert"
CA_SUBJECT="CN=QNet CA"
SERVER_SUBJECT="CN=Qnetd Server"
PWD_FILE="$DB_DIR/pwdfile.txt"
NOISE_FILE="$DB_DIR/noise.txt"
SERIAL_NO_FILE="$DB_DIR/serial.txt"
CA_EXPORT_FILE="$DB_DIR/qnetd-cacert.crt"
CRT_FILE_BASE="" # Generated from cluster name
usage() {
echo "$0: [-i|-s] [-c certificate] [-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

View File

@ -0,0 +1,310 @@
/*
* 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);
}

662
qdevices/corosync-qnetd.c Normal file
View File

@ -0,0 +1,662 @@
/*
* 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);
}

129
qdevices/dynar-getopt-lex.c Normal file
View File

@ -0,0 +1,129 @@
/*
* 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);
}

View File

@ -0,0 +1,61 @@
/*
* 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_ */

203
qdevices/dynar-simple-lex.c Normal file
View File

@ -0,0 +1,203 @@
/*
* 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);
}

View File

@ -0,0 +1,68 @@
/*
* 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_ */

169
qdevices/dynar-str.c Normal file
View File

@ -0,0 +1,169 @@
/*
* 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));
}

66
qdevices/dynar-str.h Normal file
View File

@ -0,0 +1,66 @@
/*
* 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_ */

183
qdevices/dynar.c Normal file
View File

@ -0,0 +1,183 @@
/*
* 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);
}

81
qdevices/dynar.h Normal file
View File

@ -0,0 +1,81 @@
/*
* 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_ */

1095
qdevices/msg.c Normal file

File diff suppressed because it is too large Load Diff

212
qdevices/msg.h Normal file
View File

@ -0,0 +1,212 @@
/*
* 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_ */

218
qdevices/msgio.c Normal file
View File

@ -0,0 +1,218 @@
/*
* 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);
}

60
qdevices/msgio.h Normal file
View File

@ -0,0 +1,60 @@
/*
* 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_ */

211
qdevices/node-list.c Normal file
View File

@ -0,0 +1,211 @@
/*
* 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);
}

91
qdevices/node-list.h Normal file
View File

@ -0,0 +1,91 @@
/*
* 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_ */

479
qdevices/nss-sock.c Normal file
View File

@ -0,0 +1,479 @@
/*
* 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);
}

90
qdevices/nss-sock.h Normal file
View File

@ -0,0 +1,90 @@
/*
* 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_ */

156
qdevices/pr-poll-array.c Normal file
View File

@ -0,0 +1,156 @@
/*
* 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));
}

78
qdevices/pr-poll-array.h Normal file
View File

@ -0,0 +1,78 @@
/*
* 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_ */

621
qdevices/process-list.c Normal file
View File

@ -0,0 +1,621 @@
/*
* 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);
}

120
qdevices/process-list.h Normal file
View File

@ -0,0 +1,120 @@
/*
* 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_ */

View File

@ -0,0 +1,357 @@
/*
* 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);
}

View File

@ -0,0 +1,98 @@
/*
* 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_ */

508
qdevices/qdevice-cmap.c Normal file
View File

@ -0,0 +1,508 @@
/*
* 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);
}

77
qdevices/qdevice-cmap.h Normal file
View File

@ -0,0 +1,77 @@
/*
* 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_ */

116
qdevices/qdevice-config.h Normal file
View File

@ -0,0 +1,116 @@
/*
* 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_ */

View File

@ -0,0 +1,56 @@
/*
* 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_ */

View File

@ -0,0 +1,353 @@
/*
* 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);
}

View File

@ -0,0 +1,60 @@
/*
* 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_ */

View File

@ -0,0 +1,209 @@
/*
* 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);
}

View File

@ -0,0 +1,88 @@
/*
* 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_ */

View File

@ -0,0 +1,48 @@
/*
* 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");
}

View File

@ -0,0 +1,65 @@
/*
* 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_ */

View File

@ -0,0 +1,60 @@
/*
* 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);
}

View File

@ -0,0 +1,82 @@
/*
* 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_ */

View File

@ -0,0 +1,151 @@
/*
* 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);
}

View File

@ -0,0 +1,56 @@
/*
* 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_ */

View File

@ -0,0 +1,173 @@
/*
* 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);
}

View File

@ -0,0 +1,51 @@
/*
* 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_ */

View File

@ -0,0 +1,51 @@
/*
* 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");
}

View File

@ -0,0 +1,55 @@
/*
* 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_ */

View File

@ -0,0 +1,134 @@
/*
* 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);
}

View File

@ -0,0 +1,87 @@
/*
* 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_ */

View File

@ -0,0 +1,384 @@
/*
* 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);
}

View File

@ -0,0 +1,56 @@
/*
* 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_ */

View File

@ -0,0 +1,69 @@
/*
* 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_ */

View File

@ -0,0 +1,96 @@
/*
* 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);
}

View File

@ -0,0 +1,54 @@
/*
* 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_ */

View File

@ -0,0 +1,353 @@
/*
* 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);
}

View File

@ -0,0 +1,55 @@
/*
* 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_ */

View File

@ -0,0 +1,372 @@
/*
* 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);
}

View File

@ -0,0 +1,70 @@
/*
* 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_ */

297
qdevices/qdevice-instance.c Normal file
View File

@ -0,0 +1,297 @@
/*
* 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);
}

136
qdevices/qdevice-instance.h Normal file
View File

@ -0,0 +1,136 @@
/*
* 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_ */

279
qdevices/qdevice-ipc-cmd.c Normal file
View File

@ -0,0 +1,279 @@
/*
* 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);
}

View File

@ -0,0 +1,52 @@
/*
* 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_ */

335
qdevices/qdevice-ipc.c Normal file
View File

@ -0,0 +1,335 @@
/*
* 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;
}
}

80
qdevices/qdevice-ipc.h Normal file
View File

@ -0,0 +1,80 @@
/*
* 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_ */

View File

@ -0,0 +1,56 @@
/*
* 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++;
}
}

View File

@ -0,0 +1,50 @@
/*
* 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_ */

323
qdevices/qdevice-log.c Normal file
View File

@ -0,0 +1,323 @@
/*
* 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();
}

65
qdevices/qdevice-log.h Normal file
View File

@ -0,0 +1,65 @@
/*
* 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_ */

View File

@ -0,0 +1,686 @@
/*
* 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));
}

View File

@ -0,0 +1,82 @@
/*
* 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_ */

View File

@ -0,0 +1,51 @@
/*
* 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_ */

257
qdevices/qdevice-model.c Normal file
View File

@ -0,0 +1,257 @@
/*
* 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");
}

114
qdevices/qdevice-model.h Normal file
View File

@ -0,0 +1,114 @@
/*
* 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_ */

Some files were not shown because too many files have changed in this diff Show More