diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..b769bef --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,17 @@ +#### Additional Information +(The following information is very important in order to help us to help you. Omission of the following details may delay your support request or receive no attention at all.) + +- Version of CHMPX being used (chmpx -version) + ``` + ``` + +- System information (uname -a) + ``` + ``` + +- Distro (cat /etc/issue) + ``` + ``` + +#### Details about issue + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..cc3c613 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,6 @@ +#### Relevant Issue (if applicable) +(If there are Issues related to this PullRequest, please list it.) + +#### Details +(Please describe the details of PullRequest.) + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3929616 --- /dev/null +++ b/.gitignore @@ -0,0 +1,149 @@ +# +# CHMPX +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue July 1 2014 +# REVISION: +# + +# +# Compiled Object files +# +*.slo +*.lo +*.o +*.Po +*.Plo + +# +# Precompiled Headers +# +*.gch +*.pch + +# +# Compiled Dynamic libraries +# +*.so +*.so.* +*.dylib +*.dll + +# +# Fortran module files +# +*.mod + +# +# Compiled Static libraries +# +*.lai +*.la +*.a +*.lib + +# +# Executables +# +*.exe +*.out +*.app + +# +# autotools +# +aclocal.m4 +autom4te.cache +autoscan.log +config.guess +config.h +config.h.in +config.h.in~ +config.log +config.status +config.sub +configure +configure.scan +depcomp +install-sh +libtool +ltmain.sh +m4 +m4/* +missing +stamp-h1 + +# +# automakes +# +Makefile.in +*/Makefile.in +*/*/Makefile.in + + +# +# archive files and backups +# +*.tgz +*.tar.gz +*.rpm +*.swp + +# +# object directories +# +.deps +.libs +*/.deps +*/.deps/* +*/.libs +*/.libs/* + +# +# other files +# +compile +compile/* +test-driver +test-driver/* + +# +# Other built objects +# +*.pc +Makefile +*/Makefile +*/*/Makefile + +# +# project custom +# +RELEASE_VERSION +lib/chmpxversion.cc +*.k2h +src/chmpx +src/chmmain +tests/chmconftest +tests/chmstreamtest +tests/chmpxstatus +tests/chmpxbench + +# +# VIM modelines +# +# vim:set ts=4 fenc=utf-8: +# diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0ce3b4b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "fullock"] + path = fullock + url = https://github.com/yahoojapan/fullock.git +[submodule "k2hash"] + path = k2hash + url = https://github.com/yahoojapan/k2hash.git diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..b4d866f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,57 @@ +# +# FULLOCK - Fast User Level LOCK library by Yahoo! JAPAN +# +# Copyright 2015 Yahoo! JAPAN corporation. +# +# FULLOCK is fast locking library on user level by Yahoo! JAPAN. +# FULLOCK is following specifications. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Wed 7 Dec 2016 +# REVISION: +# + +language: cpp + +sudo: required + +dist: trusty + +cache: apt + +git: + submodules: false + +before_install: + - sudo apt-get update -qq + - sudo apt-get install -qq git autoconf autotools-dev gcc g++ make gdb dh-make fakeroot dpkg-dev devscripts libtool pkg-config libssl-dev libyaml-dev + - git submodule update --init --recursive + - git clone --recursive git://github.com/nakatani-yj/nakatani_yj_test_pj + - cd fullock + - ./autogen.sh + - ./configure --prefix=/usr + - make + - sudo make install + - cd .. + - git clone --recursive git://github.com/nakatani-yj/nakatani_yj_test_pj2 + - cd k2hash + - ./autogen.sh + - ./configure --prefix=/usr + - make + - sudo make install + - cd .. + +script: + - ./autogen.sh + - ./configure --prefix=/usr + - make + - "cd tests && make check-TESTS" + +# +# VIM modelines +# +# vim:set ts=4 fenc=utf-8: +# diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..6a29d8f --- /dev/null +++ b/AUTHORS @@ -0,0 +1,11 @@ +1. Takeshi Nakatani + +Wrote from scratch the initial version of CHMPX. + +2. Tetsuya Mochizuki + +Wrote tools for utilities. + +3. Taku Ishihara + +Porting to multiple languages for k2hash. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..511550f --- /dev/null +++ b/COPYING @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2014 Yahoo Japan Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..4bf05e7 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,6 @@ +chmpx (1.0.50) unstable; urgency=low + + * First version of open sorce on Github + + -- Takeshi Nakatani Wed, 07 Dec 2016 18:31:17 +0900 + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..a1e89e1 --- /dev/null +++ b/INSTALL @@ -0,0 +1,370 @@ +Installation Instructions +************************* + +Copyright (C) 1994-1996, 1999-2002, 2004-2011 Free Software Foundation, +Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + + The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type `make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. + + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..80c7b8e --- /dev/null +++ b/Makefile.am @@ -0,0 +1,31 @@ +# +# CHMPX +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue July 1 2014 +# REVISION: +# + +SUBDIRS=lib src tests docs +ACLOCAL_AMFLAGS = -I m4 +EXTRA_DIST=RELEASE_VERSION make_valiables.sh make_release_version_file.sh make_rev.sh + +# +# VIM modelines +# +# vim:set ts=4 fenc=utf-8: +# diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..d0a37a8 --- /dev/null +++ b/README @@ -0,0 +1,42 @@ +CHMPX +-------- + +CHMPX - Consistent Hashing Mq inProcess data eXchange by Yahoo! JAPAN + +### Overview +CHMPX is inprocess data exchange by MQ with consistent hashing system, +and libraries for clients by Yahoo! JAPAN. +CHMPX is made for the purpose of the construction of original +messaging system and the offer of the client library. CHMPX +transfers messages between the client and the server/slave. CHMPX +based servers are dispersed by consistent hashing and are automatically +layouted. As a result, it provides a high performance, a high +scalability. + +### Feature + - Build up cluster with uniq name by some servers. + - Layouts servers in cluster by consistent hashing. + - Supports interprocess communication across the servers. + - Supports synchronous/asynchronous communication. + - Supports communicating messages in the target specified(HASH). + - Supports plugin Hashing function for target messaging.(k2hash) + - Supports communicating messages in the random. + - Supports SSL communication. + - The message communication possible bypass. + - No message lost during communication failure. + - Broadcast a message communication possible. + - Provision of high-level library for clients. + - Supports multi-thread/process for client programs. + - Supports synchronous communication + - Supports asynchronous communication + - Supports broadcast messages + - Supports data merging automatically + - Supports scaling automatically + +### Doccuments + - https://github.com/yahoojapan/chmpx/wiki + +### License +This software is released under the MIT License, see the LICENSE file. + +Copyright 2014 Yahoo! JAPAN corporation. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7609752 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +CHMPX +-------- +[![Build Status](https://travis-ci.org/yahoojapan/chmpx.svg?branch=master)](https://travis-ci.org/yahoojapan/chmpx) + +CHMPX - Consistent Hashing Mq inProcess data eXchange by Yahoo! JAPAN + +### Overview +CHMPX is inprocess data exchange by MQ with consistent hashing system, +and libraries for clients by Yahoo! JAPAN. +CHMPX is made for the purpose of the construction of original +messaging system and the offer of the client library. CHMPX +transfers messages between the client and the server/slave. CHMPX +based servers are dispersed by consistent hashing and are automatically +layouted. As a result, it provides a high performance, a high +scalability. + +### Feature + - Build up cluster with uniq name by some servers. + - Layouts servers in cluster by consistent hashing. + - Supports interprocess communication across the servers. + - Supports synchronous/asynchronous communication. + - Supports communicating messages in the target specified(HASH). + - Supports plugin Hashing function for target messaging.(k2hash) + - Supports communicating messages in the random. + - Supports SSL communication. + - The message communication possible bypass. + - No message lost during communication failure. + - Broadcast a message communication possible. + - Provision of high-level library for clients. + - Supports multi-thread/process for client programs. + - Supports synchronous communication + - Supports asynchronous communication + - Supports broadcast messages + - Supports data merging automatically + - Supports scaling automatically + +### Doccuments + - [WIKI](https://github.com/yahoojapan/chmpx/wiki) + +### License +This software is released under the MIT License, see the LICENSE file. + +Copyright 2014 Yahoo! JAPAN corporation. diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..6a4ddfc --- /dev/null +++ b/autogen.sh @@ -0,0 +1,142 @@ +#!/bin/sh +# +# Automatically generate configuration - autogen.sh +# +# Copyright 2016 Yahoo! JAPAN corporation. +# +# Templates for customizing screwdriver CPP and autotools. +# This template files are provided by yjcore team. +# +# This script file is for initializing autoconf/autotools +# files. And make version number information file from +# git tag(release), this file is used by other build tools. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Thu, Jun 2 2016 +# REVISION: +# + +# +# Usage: autogen.sh [-noupdate_version_file] [-no_aclocal_force] [-no_check_ver_diff] +# +AUTOGEN_NAME=`basename $0` +AUTOGEN_DIR=`dirname $0` +SRCTOP=`cd ${AUTOGEN_DIR}; pwd` + +echo "** run autogen.sh" + +# +# Parameter +# +NOUPDATE="no" +FORCEPARAM="--force" +PARAMETERS="" +while [ $# -ne 0 ]; do + if [ "X$1" = "X-noupdate_version_file" ]; then + NOUPDATE="yes" + FORCEPARAM="" # do not need force + elif [ "X$1" = "X-no_aclocal_force" ]; then + FORCEPARAM="" + elif [ "X$1" = "X-no_check_ver_diff" ]; then + PARAMETERS="${PARAMETERS} $1" + elif [ "X$1" = "X-h" -o "X$1" = "X--help" ]; then + echo "Usage: ${AUTOGEN_NAME} [-noupdate_version_file] [-no_aclocal_force] [-no_check_ver_diff]" + exit 1 + else + echo "ERROR: Unkown option $1" + echo "Usage: ${AUTOGEN_NAME} [-noupdate_version_file] [-no_aclocal_force] [-no_check_ver_diff]" + exit 1 + fi + shift +done + +# +# update RELEASE_VERSION file +# +if [ "X${NOUPDATE}" = "Xno" ]; then + ${SRCTOP}/make_release_version_file.sh ${PARAMETERS} + if [ $? -ne 0 ]; then + echo "ERROR: update RELEASE_VERSION file" + exit 1 + fi +fi + +# +# Check files +# +if [ ! -f ${SRCTOP}/NEWS ]; then + touch ${SRCTOP}/NEWS +fi +if [ ! -f ${SRCTOP}/README ]; then + touch ${SRCTOP}/README +fi +if [ ! -f ${SRCTOP}/AUTHORS ]; then + touch ${SRCTOP}/AUTHORS +fi +if [ ! -f ${SRCTOP}/ChangeLog ]; then + touch ${SRCTOP}/ChangeLog +fi + +# +# Auto scan +# +if [ ! -f configure.scan -o "X${FORCEPARAM}" != "X" ]; then + echo "--- run autoscan" + autoscan + if [ $? -ne 0 ]; then + echo "ERROR: something error occurred in autoscan" + exit 1 + fi +fi + +# +# Copy libtools +# +libtoolize --force --copy +if [ $? -ne 0 ]; then + echo "ERROR: something error occurred in libtoolize" + exit 1 +fi + +# +# Build configure and Makefile +# +echo "--- run aclocal ${FORCEPARAM}" +aclocal ${FORCEPARAM} +if [ $? -ne 0 ]; then + echo "ERROR: something error occurred in aclocal ${FORCEPARAM}" + exit 1 +fi + +echo "--- run autoheader" +autoheader +if [ $? -ne 0 ]; then + echo "ERROR: something error occurred in autoheader" + exit 1 +fi + +echo "--- run automake -c --add-missing" +automake -c --add-missing +if [ $? -ne 0 ]; then + echo "ERROR: something error occurred in automake -c --add-missing" + exit 1 +fi + +echo "--- run autoconf" +autoconf +if [ $? -ne 0 ]; then + echo "ERROR: something error occurred in autoconf" + exit 1 +fi + +echo "** SUCCEED: autogen" +exit 0 + +# +# VIM modelines +# +# vim:set ts=4 fenc=utf-8: +# diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..01109cc --- /dev/null +++ b/configure.ac @@ -0,0 +1,103 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. +# +# CHMPX +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue July 1 2014 +# REVISION: +# + +AC_PREREQ([2.63]) +AC_INIT(chmpx, m4_esyscmd([tr -d '\n' < $(pwd)/RELEASE_VERSION])) +AM_INIT_AUTOMAKE() +LT_INIT() + +AC_CONFIG_SRCDIR([config.h.in]) +AC_CONFIG_HEADERS([config.h]) + +# Checks for programs. +AC_PROG_CXX +AC_PROG_AWK +AC_PROG_CC +AC_PROG_CPP +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +AC_PROG_RANLIB +AC_PROG_LIBTOOL + +# Checks for libraries. +#AC_CHECK_LIB([chmpx], [main]) + +# Checks for header files. +AC_CHECK_HEADERS([locale.h netdb.h fcntl.h sys/socket.h sys/time.h endian.h sys/endian.h netinet/in.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_INLINE +AC_TYPE_OFF_T +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T +AC_TYPE_UINT64_T +AC_TYPE_UINT32_T +AC_TYPE_MODE_T + +# Checks for library functions. +AC_FUNC_MALLOC +AC_FUNC_MMAP +AC_FUNC_REALLOC +AC_FUNC_MKTIME +AC_FUNC_FORK +AC_CHECK_FUNCS([ftruncate gethostname memset regcomp setlocale strcasecmp strdup strrchr clock_gettime munmap select socket uname]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_FILES([Makefile + lib/Makefile + lib/libchmpx.pc + src/Makefile + tests/Makefile + docs/Makefile + docs/examples/Makefile]) + +# Check for k2hash + libfullock +if test "x${CHECK_K2HASH}" != "xno"; then + PKG_CHECK_MODULES([k2hash], [libk2hash >= 1.0.52], [have_k2hash=yes], [have_k2hash=no]) + if test "x${have_k2hash}" = "xno"; then + AC_MSG_ERROR([not found k2hash package.]) + fi + PKG_CHECK_MODULES([fullock], [libfullock >= 1.0.21], [have_fullock=yes], [have_fullock=no]) + if test "x${have_fullock}" = "xno"; then + AC_MSG_ERROR([not found fullock package.]) + fi +fi + +# CFLAGS/CXXFLAGS +CFLAGS="-Wall $CFLAGS" +CXXFLAGS="-Wall $CXXFLAGS" + +# LIB_VERSION_INFO for libtool library version info +LIB_VERSION_INFO=`$(pwd)/make_valiables.sh -lib_version_info` +AC_SUBST([LIB_VERSION_INFO]) + +AC_OUTPUT + +# +# VIM modelines +# +# vim:set ts=4 fenc=utf-8: +# diff --git a/docs/Makefile.am b/docs/Makefile.am new file mode 100644 index 0000000..443a810 --- /dev/null +++ b/docs/Makefile.am @@ -0,0 +1,35 @@ +# +# CHMPX +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue July 1 2014 +# REVISION: +# + +AUTOMAKE_OPTIONS = foreign no-dependencies + +man_MANS = chmpx.1 chmpxbench.1 + +SUBDIRS = examples + +EXTRA_DIST = $(man_MANS) + +# +# VIM modelines +# +# vim:set ts=4 fenc=utf-8: +# diff --git a/docs/chmpx.1 b/docs/chmpx.1 new file mode 100644 index 0000000..dd0fee9 --- /dev/null +++ b/docs/chmpx.1 @@ -0,0 +1,274 @@ +.TH CHMPX "1" "December 2014" "CHMPX" "Consistent Hashing Mq inProcess data eXchange" +.SH NAME +CHMPX \- Consistent Hashing Mq inProcess data eXchange by Yahoo! JAPAN +.SH SYNOPSIS +.SS executable +.TP +\fBchmpx [options] +.SS library +.TP +\fBlibchmpx.so +.SH DESCRIPTION +.TP 0 +CHMPX is Consistent Hashing Mq inProcess data eXchange system, and libraries for clients by Yahoo! JAPAN. +CHMPX is made for the purpose of the construction of original messaging system and the offer of the client library. CHMPX transfers messages between the client and the server/slave. CHMPX based servers are dispersed by consistent hashing and are automatically layouted. As a result, it provides a high performance, a high scalability. +.TP 2 +CHMPX is following specifications: +.br +\- Build up cluster with uniq name by some servers. +.br +\- Layouts servers in cluster by consistent hashing. +.br +\- Supports interprocess communication across the servers. +.br +\- Supports synchronous/asynchronous communication. +.br +\- Supports communicating messages in the target specified(HASH). +.br +\- Supports plugin Hashing function for target messaging.(k2hash) +.br +\- Supports communicating messages in the random. +.br +\- Supports SSL communication. +.br +\- The message communication possible bypass. +.br +\- No message lost during communication failure. +.br +\- Broadcast a message communication possible. +.br +\- Provision of high-level library for clients. +.br +\- Supports multi-thread/process for client programs. +.br +.SH OPTIONS +.IP \fB\-h\fR 15 +display help +.IP \fB\-v\fR 15 +display version. +.IP \fB\-conf\ [file\ path]\fR 15 +specify configration file(.ini .yaml .json) which is same file for CHMPX server/slave node. +.IP \fB\-json\ [json\ string]\fR 15 +specify configration json string which is same file for CHMPX server/slave node. +.IP \fB\-ctlport\ [control\ port\ number]\fR 15 +specify the self contrl port. if ctlport option is specified, chmpx searches same ctlport in configuration and ignores "CTLPORT" directive in "GLOBAL" section. and chmpx will start in the mode indicated by the server entry that has beed detected. +.IP \fB\-d\fR\ [ERR\ |\ WAN\ |\ INFO\ |\ DUMP]\fR 15 +specify debugging level, chmpx displaies debugging message for itself. +.IP \fB\-dl\ [file\ path]\fR 15 +specify sending message data length by byte. default value and minimum value are 64 bytes. +.br +.SH LIBRARY +.TP 0 +CHMPX has library for programing client program. +.TP 2 +CHMPX C Language interface function: +.br +void chmpx_bump_debug_level(...) +.br +void chmpx_set_debug_level_silent(...) +.br +void chmpx_set_debug_level_error(...) +.br +void chmpx_set_debug_level_warning(...) +.br +void chmpx_set_debug_level_message(...) +.br +void chmpx_set_debug_level_dump(...) +.br +bool chmpx_set_debug_file(...) +.br +bool chmpx_unset_debug_file(...) +.br +bool chmpx_load_debug_env(...) +.TP 2 +.BR +bool chmpx_load_hash_library(...) +.br +bool chmpx_unload_hash_library(...) +.TP 2 +.BR +chmpx_h chmpx_create(...) +.br +bool chmpx_destroy(...) +.TP 2 +.BR +bool chmpx_svr_send(...) +.br +bool chmpx_svr_send_kvp(...) +.br +bool chmpx_svr_send_kvp_ex(...) +.br +bool chmpx_svr_send_kv(...) +.br +bool chmpx_svr_send_kv_ex(...) +.br +bool chmpx_svr_broadcast(...) +.br +bool chmpx_svr_broadcast_ex(...) +.br +bool chmpx_svr_receive(...) +.TP 2 +.BR +msgid_t chmpx_open(...) +.br +bool chmpx_close(...) +.br +bool chmpx_msg_send(...) +.br +bool chmpx_msg_send_kvp(...) +.br +bool chmpx_msg_send_kv(...) +.br +bool chmpx_msg_broadcast(...) +.br +bool chmpx_msg_receive(...) +.TP 2 +.BR +bool chmpx_msg_reply(...) +.br +bool chmpx_msg_reply_kvp(...) +.br +bool chmpx_msg_reply_kv(...) +.TP 2 +.BR +bool is_chmpx_proc_exists(...) +.TP 2 +.BR +void chmpx_print_version(...) +.TP 2 +.BR +unsigned char* cvt_kvp_bin(...) +.br +bool cvt_bin_kvp(...) +.br +chmhash_t make_chmbin_hash(...) +.br +chmhash_t make_kvp_hash(...) +.PP +You can see these function prototypes in chmpx.h +.TP +CHMPX C++ Language interface is implemented almost in chmcntrl.h. You can use public ChmCntrl class methods like C Language interface. +.br +.SH CONFIGRATION +.TP 0 +The chmpx program is main program for transferring message. You can run chmpx program with "\-h" option, then you can see options for chmpx. The chmpx program and clients program with library which is made by you are loading the configration file when these program start to run. The configration file is formatted "INI" file. +.TP 2 +Following keys in configration file: +.IP [GLOBAL] 20 +global common section. +.IP [SVRNODE] 20 +server nodes section. +.IP [SLVNODE] 20 +slave nodes section. +.br +.BR +.br +.IP FILEVERSION 20 +serial number for configration file version. +.IP DATE 20 +date by RFC 2822 +.IP GROUP 20 +CHMPX group name as claster name. +.IP MODE 20 +server or slave +.IP DELIVERMODE 20 +random or hash, random means random transferred messages, hash is sending message to one of node. +.IP MAXCHMPX 20 +maximum chmpx server in claster +.IP REPLICA 20 +duplicate messages +.IP MAXMQSERVER 20 +maximum MQ(POSIX Message Queue) on one server process. +.IP MAXMQCLIENT 20 +maximum MQ(POSIX Message Queue) for all slave processes. +.IP MQPERATTACH 20 +maximum attaching MQ count at time on server/slave process. +.IP MAXQPERCLIENTMQ 20 +maximum queue in MQ by each MQ on client process. +.IP MAXMQPERCLIENT 20 +maximum MQ count by each client process. +.IP MAXHISTLOG 20 +maximum history log count. +.IP PORT 20 +default chmpx port number. +.IP CTLPORT 20 +default chmpx control port number. +.IP SELFCTLPORT 20 +own chmpx control port number. +.IP RWTIMEOUT 20 +timeout for reading/writing on socket by us. +.IP RETRYCNT 20 +retry count for recovering connecting/sending/receiving on socket. +.IP CONTIMEOUT 20 +timeout for connecting by us. +.IP MQRWTIMEOUT 20 +timeout for reading/writing on MQ by us. +.IP MQRETRYCNT 20 +retry count for recovering connecting/sending/receiving on MQ. +.IP MQACK 20 +send/receive the ack at comminicating on MQ. +.IP SOCKTHREADCNT 20 +thread count for receiving data on socket. +.IP MQTHREADCNT 20 +thread count for receiving data on MQ. +.IP MAXSOCKPOOL 20 +maximum socket pool count for each server node on server node. +.IP SOCKPOOLTIMEOUT 20 +time limit for each socket pool. +.IP DOMERGE 20 +auto merging mode, MUST be true. +.IP AUTOMERGE 20 +merging data on server node automatically. +.IP MERGETIMEOUT 20 +time limit for merging. +.IP SSL 20 +default SSL mode. +.IP SSL_VERIFY_PEER 20 +default verify peer mode on SSL. +.IP CAPATH 20 +default CA certs file path or directory path. +.IP SERVER_CERT 20 +default server cert file path. +.IP SERVER_PRIKEY 20 +default server priovate key file path. +.IP SLAVE_CERT 20 +default slave cert file path. +.IP SLAVE_PRIKEY 20 +default slave priovate key file path. +.IP K2HFULLMAP 20 +k2hash option which is used by chmpx.(see man k2hash) +.IP K2HMASKBIT 20 +k2hash option which is used by chmpx.(see man k2hash) +.IP K2HCMASKBIT 20 +k2hash option which is used by chmpx.(see man k2hash) +.IP K2HMAXELE 20 +k2hash option which is used by chmpx.(see man k2hash) +.br +.BR +.br +.IP NAME 20 +server/slave name by FQDN. you can use regular expression(for slave name) or easiness regular expression(for server name) +.TP +You can see the sample of configration file in docs/exsample directory. +.SH ENVIRONMENT +.TP 2 +CHMPX use some environments automatically, plese take care these environments. +.IP CHMDBGMODE 20 +specifies debugging level: INFO/WAN/ERR/SILENT +.IP CHMDBGFILE 20 +output file path for message(default: stderr) +.IP CHMCONFFILE 20 +specify configuratin file by environment, chmpx uses this value when configuration file or json string is not specified in command line. +.IP CHMJSONCONF 20 +specify configuratin json string by environment, chmpx uses this value when configuration file or json string is not specified in command line. +.SH NOTES +.TP 0 +chmpxbench is tool for k2hash file/memory. +.SH SEE ALSO +.TP +chmpxbench(1) +.SH BUGS +.TP +Please report owner and comitter. +.SH AUTHOR +CHMPX has been written by Takeshi Nakatani . diff --git a/docs/chmpxbench.1 b/docs/chmpxbench.1 new file mode 100644 index 0000000..14217cc --- /dev/null +++ b/docs/chmpxbench.1 @@ -0,0 +1,57 @@ +.TH CHMPXBENCH "1" "May 2015" "CHMPX" "Consistent Hashing Mq inProcess data eXchange" +.SH NAME +CHMPXBENCH \- Bench mark Tool for CHMPX by Yahoo! JAPAN +.SH SYNOPSIS +.B chmpxbench +[ \-s | \-c ] [options] +.SH DESCRIPTION +.PP +chmpxbench is bench mark test tool, this can test benchmark on CHMPX server/slave node. Then this tool send/receive data through CHMPX for bench mark on multi process/thread. You can see usage this tool by typing "-h". +.SH OPTIONS +.TP +\fB\-h\fR +display help +.TP +\fB\-s\fR +join CHMPX server node(run chmpxbench on server node) +.TP +\fB\-c\fR +join CHMPX slave node(run chmpxbench on slave node) +.TP +\fB\-f\fR [file path] +specify configration file(.ini .yaml .json) which is same file for CHMPX server/slave node. +.TP +\fB\-json\fR [file path] +specify configration json string which is same file for CHMPX server/slave node. +.TP +\fB\-l\fR [loop count] +specify loop count for each thread/process. default count is 1. 0 means no limit for loop. +.TP +\fB\-proccnt\fR [count] +specify process count. default process count is 1. +.TP +\fB\-threadcnt\fR [count] +specify thread count for each process. default thread count is 1. +.TP +\fB\-ta\fR +specify Turn Around mode. the message is terned around on server to slave. +.TP +\fB\-b\fR +specify Broadcast mode for only chmpxbench on slave node. +.TP +\fB\-dl\fR +specify sending message data length by byte. default value and minimum value are 64 bytes. +.TP +\fB\-pr\fR +print sending/recieving messages during bench mark. +.TP +\fB\-d\fR [ERR | WAN | INFO | DUMP] +specify debugging level, chmpxbench displaies debugging message for itself. +.SH SEE ALSO +.TP +chmpx(1) +.SH BUGS +.TP +Please report owner and comitter. +.SH AUTHOR +chmpxbench has been written by Takeshi Nakatani . diff --git a/docs/examples/Makefile.am b/docs/examples/Makefile.am new file mode 100644 index 0000000..dd006fa --- /dev/null +++ b/docs/examples/Makefile.am @@ -0,0 +1,33 @@ +# +# CHMPX +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue July 1 2014 +# REVISION: +# + +AUTOMAKE_OPTIONS = foreign no-dependencies + +doc_DATA = sample.ini + +EXTRA_DIST = $(doc_DATA) + +# +# VIM modelines +# +# vim:set ts=4 fenc=utf-8: +# diff --git a/docs/examples/sample.ini b/docs/examples/sample.ini new file mode 100644 index 0000000..ce24f02 --- /dev/null +++ b/docs/examples/sample.ini @@ -0,0 +1,97 @@ +# +# CHMPX SAMPLE CONFIGRATION FILE +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue July 1 2014 +# REVISION: +# + +# +# GLOBAL SECTION +# +[GLOBAL] +FILEVERSION = 2 +DATE = Tue, 12 May 2015 18:10:19 +0900 +GROUP = SAMPLE +MODE = SERVER +DELIVERMODE = RANDOM +#DELIVERMODE = HASH +MAXCHMPX = 256 +REPLICA = 0 +MAXMQSERVER = 1 +MAXMQCLIENT = 1024 +MQPERATTACH = 1 +MAXQPERCLIENTMQ = 1 +MAXMQPERCLIENT = 5 +MAXHISTLOG = 10000 +PORT = 8020 +CTLPORT = 8021 +SELFCTLPORT = 8021 +RWTIMEOUT = 100 +RETRYCNT = 1000 +CONTIMEOUT = 500000 +MQRWTIMEOUT = 500 +MQRETRYCNT = 2000 +MQACK = yes +DOMERGE = on +AUTOMERGE = on +MERGETIMEOUT = 0 +SOCKTHREADCNT = 0 +MQTHREADCNT = 0 +MAXSOCKPOOL = 1 +SOCKPOOLTIMEOUT = 0 +#SSL = no +#SSL_VERIFY_PEER = no +#CAPATH = null +#SERVER_CERT = null +#SERVER_PRIKEY = null +#SLAVE_CERT = null +#SLAVE_PRIKEY = null +#K2HFULLMAP = on +K2HMASKBIT = 8 +K2HCMASKBIT = 4 +K2HMAXELE = 8 + +# +# SERVER NODES SECTION +# +[SVRNODE] +NAME = test[0-9][0-9].server.chmpx.yahoo.co.jp +#PORT = 8020 +#CTLPORT = 8021 +#SSL = no +#SSL_VERIFY_PEER = no +#CAPATH = null +#SERVER_CERT = null +#SERVER_PRIKEY = null +#SLAVE_CERT = null +#SLAVE_PRIKEY = null + +# +# SLAVE NODES SECTION +# +[SLVNODE] +NAME = [.]* +#NAME = test[0-9][0-9].slave.chmpx.yahoo.co.jp +#CTLPORT = 8021 +#CAPATH = null +#SLAVE_CERT = null +#SLAVE_PRIKEY = null + +# +# EOF +# diff --git a/fullock b/fullock new file mode 160000 index 0000000..9157151 --- /dev/null +++ b/fullock @@ -0,0 +1 @@ +Subproject commit 9157151ba9da56cfc565721b9861c28884e4808e diff --git a/k2hash b/k2hash new file mode 160000 index 0000000..bcd8209 --- /dev/null +++ b/k2hash @@ -0,0 +1 @@ +Subproject commit bcd82094621d6e9070b13fd04d2e84bb34a5c3b9 diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..4d18eed --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,53 @@ +# +# CHMPX +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue July 1 2014 +# REVISION: +# + +## AUTOMAKE_OPTIONS = + +pkginclude_HEADERS = chmpx.h chmcntrl.h chmcommon.h chmcomstructure.h chmconf.h chmconfutil.h chmdbg.h chmeventbase.h chmeventmq.h chmeventsock.h chmeventshm.h chmhash.h chmimdata.h chmnetdb.h chmregex.h chmsigcntrl.h chmstructure.h chmutil.h chmopts.h chmthread.h chmlock.h chmkvp.h chmstructure.tcc chmstream.h chmlockmap.tcc +pkgincludedir = $(includedir)/chmpx + +EXTRA_DIST = + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libchmpx.pc +DISTCLEANFILES = $(pkgconfig_DATA) + +lib_LTLIBRARIES = libchmpx.la +libchmpx_la_SOURCES = chmpx.cc chmcntrl.cc chmconf.cc chmconfutil.cc chmdbg.cc chmeventbase.cc chmeventmq.cc chmeventsock.cc chmeventshm.cc chmhash.cc chmimdata.cc chmnetdb.cc chmregex.cc chmsigcntrl.cc chmutil.cc chmopts.cc chmthread.cc chmlock.cc chmkvp.cc chmpxversion.cc +libchmpx_la_LDFLAGS = -version-info $(LIB_VERSION_INFO) +libchmpx_la_LIBADD = $(k2hash_LIBS) $(fullock_LIBS) -lrt -lpthread -lssl -lcrypto -lyaml + +ACLOCAL_AMFLAGS = -I m4 +AM_CFLAGS = $(k2hash_CFLAGS) $(fullock_CFLAGS) +AM_CPPFLAGS = $(k2hash_CFLAGS) $(fullock_CFLAGS) + +### version(commit hash) +.PHONY: chmpxversion + +chmpxversion.cc: chmpxversion + @../make_rev.sh chmpxversion.cc chmpx_commit_hash + +# +# VIM modelines +# +# vim:set ts=4 fenc=utf-8: +# diff --git a/lib/chmcntrl.cc b/lib/chmcntrl.cc new file mode 100644 index 0000000..7fa19ab --- /dev/null +++ b/lib/chmcntrl.cc @@ -0,0 +1,1250 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include +#include +#include +#include + +#include "chmcommon.h" +#include "chmcntrl.h" +#include "chmsigcntrl.h" +#include "chmconfutil.h" +#include "chmdbg.h" + +using namespace std; + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +// The MQ object is allowed making/registing many. +// +#define EVMAP_KEY_CONF 0 +#define EVMAP_KEY_SOCK 1 +#define EVMAP_KEY_MQ 2 +#define EVMAP_KEY_SHM 3 +#define EVMAP_MAX_SIZE 4 + +#define CVT_EVOBJTYPE_TO_EVMAP(type) ( EVOBJ_TYPE_CONF == type ? EVMAP_KEY_CONF: \ + EVOBJ_TYPE_EVSOCK == type ? EVMAP_KEY_SOCK: \ + EVOBJ_TYPE_EVMQ == type ? EVMAP_KEY_MQ : EVMAP_KEY_SHM ) + +#define CVT_EVMAP_TO_EVOBJTYPE(key) ( EVMAP_KEY_CONF == key ? EVOBJ_TYPE_CONF : \ + EVMAP_KEY_SOCK == key ? EVOBJ_TYPE_EVSOCK : \ + EVMAP_KEY_MQ == key ? EVOBJ_TYPE_EVMQ : EVOBJ_TYPE_EVSHM ) + +//--------------------------------------------------------- +// Class Variable +//--------------------------------------------------------- +const int ChmCntrl::DEFAULT_MAX_EVENT_CNT; +const int ChmCntrl::DEFAULT_EVENT_TIMEOUT; +const int ChmCntrl::EVENT_NOWAIT; +volatile bool ChmCntrl::DoLoop = true; + +//--------------------------------------------------------- +// Class Methods +//--------------------------------------------------------- +void ChmCntrl::LoopBreakHandler(int signum) +{ + ChmCntrl::DoLoop = false; +} + +//--------------------------------------------------------- +// Constructor/Destructor +//--------------------------------------------------------- +ChmCntrl::ChmCntrl(void) : chmcntrltype(CHMCHNTL_TYPE_CHMPXPROC), eqfd(CHM_INVALID_HANDLE), is_ssl_init(false), pConfObj(NULL), pEventMq(NULL), pEventSock(NULL), pEventShm(NULL), auto_rejoin(false), bup_cfg(""), is_close_notify(false) +{ +} + +ChmCntrl::~ChmCntrl(void) +{ + Clean(); +} + +//--------------------------------------------------------- +// Methods +//--------------------------------------------------------- +bool ChmCntrl::Clean(bool is_clean_bup) +{ + if(pEventShm){ + pEventShm->Clean(); + CHM_Delete(pEventShm); + } + if(pEventSock){ + pEventSock->Clean(); + CHM_Delete(pEventSock); + } + if(pEventMq){ + pEventMq->Clean(); + CHM_Delete(pEventMq); + } + if(pConfObj){ + pConfObj->Clean(); + CHM_Delete(pConfObj); + } + ImData.Close(); + CHM_CLOSE(eqfd); + + if(is_clean_bup){ + bup_cfg.erase(); + } + // SSL + if(is_ssl_init){ + ERR_free_strings(); + is_ssl_init = false; + } + return true; +} + +bool ChmCntrl::IsInitialized(void) const +{ + if( CHM_INVALID_SOCK == eqfd || + !ImData.IsInitialized() || + !pConfObj || pConfObj->IsEmpty() || + (CHMCHNTL_TYPE_CHMPXPROC == chmcntrltype && EVMAP_MAX_SIZE != evobjmap.size()) || + (CHMCHNTL_TYPE_CHMPXPROC != chmcntrltype && 1 != evobjmap.size()) ) + { + return false; + } + return true; +} + +ChmEventBase* ChmCntrl::GetEventBaseObj(EVOBJTYPE type) +{ + int key = CVT_EVOBJTYPE_TO_EVMAP(type); + ChmEventBase* pEvObj = NULL; + + if(evobjmap.end() != evobjmap.find(key)){ + pEvObj = evobjmap.find(key)->second; + } + return pEvObj; +} + +ChmEventBase* ChmCntrl::FindEventBaseObj(int fd) +{ + if(CHM_INVALID_HANDLE == fd){ + return NULL; + } + + ChmEventBase* pEvObj = NULL; + for(evobj_map_t::const_iterator iter = evobjmap.begin(); iter != evobjmap.end(); ++iter){ + if((iter->second)->IsEventQueueFd(fd)){ + pEvObj = iter->second; + break; + } + } + return pEvObj; +} + +bool ChmCntrl::GetEventBaseObjType(const ChmEventBase* pEvObj, EVOBJTYPE& type) +{ + if(!pEvObj){ + return false; + } + + bool result = false; + for(evobj_map_t::const_iterator iter = evobjmap.begin(); iter != evobjmap.end(); ++iter){ + if(iter->second == pEvObj){ + type = CVT_EVMAP_TO_EVOBJTYPE(iter->first); + result = true; + break; + } + } + return result; +} + +// [NOTE] +// The cfgfile parameter can be specified two type variable. +// One is configuration file(.ini/.json/.yaml) path, the other is JSON string. +// It will be judged automatically in this method. +// This specification has been added because of support JSON format later added. +// +bool ChmCntrl::Initialize(const char* cfgfile, CHMCNTRLTYPE type, bool is_auto_rejoin, chm_merge_get_cb mgetfp, chm_merge_set_cb setfp, chm_merge_lastts_cb mlastfp, short ctlport) +{ + // set type + chmcntrltype = type; + + // epoll + if(!InitializeEventFd()){ + ERR_CHMPRN("Could not initialize epoll fd."); + Clean(bup_cfg.empty()); + return false; + } + + // Load configuration + string confval(""); + if(NULL == (pConfObj = CHMConf::GetCHMConf(eqfd, this, cfgfile, ctlport, true, &confval))){ + ERR_CHMPRN("Failed to make configration object from %s", ISEMPTYSTR(cfgfile) ? "empty" : cfgfile); + Clean(bup_cfg.empty()); + return false; + } + const CHMCFGINFO* pchmcfg = pConfObj->GetConfiguration(); + if(!pchmcfg){ + ERR_CHMPRN("Failed to load configration from %s", cfgfile); + Clean(bup_cfg.empty()); + return false; + } + if(CHMCHNTL_TYPE_CHMPXPROC == chmcntrltype){ + // set inotify(only chmpx proc) + if(!pConfObj->SetEventQueue()){ + ERR_CHMPRN("Failed to set/initialize confobj."); + Clean(bup_cfg.empty()); + return false; + } + } + if(CHMCHNTL_TYPE_CLIENT_ONSLAVE == chmcntrltype){ + // If client process joining on slave chmpx, MQPERATTACH and MAXQPERCLIENTMQ should be "1". + // But you can set not "1", so put a message here. + // + if(1 != pchmcfg->mqcnt_per_attach || 1 != pchmcfg->max_q_per_clientmq){ + MSG_CHMPRN("Client process on slave chmpx must be 1 for MQPERATTACH and MAXQPERCLIENTMQ in configuration."); + } + } + + // SSL library initialize + if(pConfObj->IsSsl()){ + SSL_load_error_strings(); + SSL_library_init(); + is_ssl_init = true; + } + + // Initialize Internal Memory data + if(!ImData.Initialize(pConfObj, eqfd, (CHMCHNTL_TYPE_CHMPXPROC == chmcntrltype))){ + ERR_CHMPRN("Failed to initialize internal memory data."); + Clean(bup_cfg.empty()); + return false; + } + + // Initialize Sockets + if(CHMCHNTL_TYPE_CHMPXPROC == chmcntrltype){ + pEventSock = new ChmEventSock(eqfd, this); + if(!pEventSock->SetEventQueue()){ + ERR_CHMPRN("Failed to set/initialize socket event obj."); + Clean(bup_cfg.empty()); + return false; + } + }else{ + pEventSock = NULL; + } + + // Initialize MQ + if(CHMCHNTL_TYPE_CHMPXPROC == chmcntrltype){ + pEventMq = new ChmEventMq(eqfd, this); + }else{ + pEventMq = new ChmEventMq(eqfd, this, mgetfp, setfp, mlastfp); + } + if(!pEventMq->SetEventQueue()){ + ERR_CHMPRN("Failed to set/initialize mq event obj."); + Clean(bup_cfg.empty()); + return false; + } + + // Initialize client process HUP + pid_t pid = getpid(); + if(CHMCHNTL_TYPE_CHMPXPROC == chmcntrltype){ + pEventShm = new ChmEventShm(eqfd, this, ImData.GetShmPath()); + if(!pEventShm->SetEventQueue()){ + ERR_CHMPRN("Failed to set/initialize chmshm event obj."); + Clean(bup_cfg.empty()); + return false; + } + }else{ + pEventShm = NULL; + + // add own(client) pid to CHMSHM + ImData.AddClientPid(pid); + } + + if(CHMCHNTL_TYPE_CHMPXPROC == chmcntrltype){ + evobjmap[EVMAP_KEY_CONF] = pConfObj; + } + evobjmap[EVOBJ_TYPE_EVMQ] = pEventMq; + if(CHMCHNTL_TYPE_CHMPXPROC == chmcntrltype){ + evobjmap[EVMAP_KEY_SOCK] = pEventSock; + } + if(CHMCHNTL_TYPE_CHMPXPROC == chmcntrltype){ + evobjmap[EVMAP_KEY_SHM] = pEventShm; + } + + // backup configration file path. + bup_cfg = confval; + + // rejoin mode + if(CHMCHNTL_TYPE_CHMPXPROC == chmcntrltype){ + auto_rejoin = false; + }else{ + auto_rejoin = is_auto_rejoin; + } + + // Send notification for client joining. + if(CHMCHNTL_TYPE_CHMPXPROC != chmcntrltype){ + if(!pEventMq->PxCltSendJoinNotify(pid)){ + ERR_CHMPRN("Failed to send notification for client joining to chmpx process(server/slave), but continue..."); + } + } + +#if 0 + // Dump for DEBUG + if(CHMDBG_MSG == GetChmDbgMode()){ + stringstream ss; + ImData.Dump(ss); + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), "[DUMP] IMDATA{\n"); + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), "%s", ss.str().c_str()); + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), "}\n"); + } +#endif + + return true; +} + +bool ChmCntrl::ReInitialize(long waitms, int trycnt) +{ + if(CHMCHNTL_TYPE_CHMPXPROC == chmcntrltype){ + MSG_CHMPRN("This is chmpx process, so could not re-initialize."); + return false; + } + if(IsInitialized()){ + MSG_CHMPRN("Already initialized, so do not re-initialize."); + return false; + } + if(!IsChmpxExit()){ + MSG_CHMPRN("There is no backup cfgfile path, so could not re-initialize."); + return false; + } + if(!auto_rejoin){ + MSG_CHMPRN("Not retry to join mode."); + return false; + } + + struct timespec sleeptime = {0, 0}; + if(0 < waitms){ + SET_TIMESPEC(&sleeptime, (waitms / 1000), ((waitms % 1000) * 1000 * 1000)); + } + + bool no_give_uo = false; + if(0 == trycnt){ + no_give_uo = true; + } + + do{ + if(0 < waitms){ + nanosleep(&sleeptime, NULL); + } + // try to join + if(Initialize(bup_cfg.c_str(), chmcntrltype, auto_rejoin)){ + return true; + } + }while(no_give_uo || 0 < --trycnt); + + return false; +} + +// [NOTE] +// This method initializes ChmCntrl object for only reading chmshm. +// Initialized object by this methods can be used only dumping chmshm +// +// And the cfgfile parameter can be specified two type variable. +// One is configuration file(.ini/.json/.yaml) path, the other is JSON string. +// It will be judged automatically in this method. +// This specification has been added because of support JSON format later added. +// +bool ChmCntrl::OnlyAttachInitialize(const char* cfgfile, short ctlport) +{ + // set type + chmcntrltype = CHMCHNTL_TYPE_CLIENT_ONSLAVE; // Dummy + + // Load configuration + string confval(""); + if(NULL == (pConfObj = CHMConf::GetCHMConf(eqfd, this, cfgfile, ctlport, true, &confval))){ + ERR_CHMPRN("Failed to make configration object from %s", ISEMPTYSTR(cfgfile) ? "empty" : cfgfile); + Clean(bup_cfg.empty()); + return false; + } + + // Initialize Internal Memory data + if(!ImData.Initialize(pConfObj, CHM_INVALID_HANDLE, false)){ + ERR_CHMPRN("Failed to initialize internal memory data."); + Clean(bup_cfg.empty()); + return false; + } + + // Initialize Sockets + pEventSock = NULL; + + // Initialize MQ + pEventMq = NULL; + + // Initialize client process HUP + pEventShm = NULL; + + // backup configration file path. + bup_cfg = confval; + + // rejoin mode + auto_rejoin = false; + +#if 0 + // Dump for DEBUG + if(CHMDBG_MSG == GetChmDbgMode()){ + stringstream ss; + ImData.Dump(ss); + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), "[DUMP] IMDATA{\n"); + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), "%s", ss.str().c_str()); + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), "}\n"); + } +#endif + + return true; +} + +bool ChmCntrl::InitializeEventFd(void) +{ + CHM_CLOSE(eqfd); + + // create epoll event fd + if(-1 == (eqfd = epoll_create1(EPOLL_CLOEXEC))){ + ERR_CHMPRN("Failed to create epoll fd, errno=%d", errno); + return false; + } + return true; +} + +// +// This method is called from CHMConf object when configuration file changing. +// +bool ChmCntrl::ConfigrationUpdateNotify(void) +{ + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return false; + } + + // First, reload configuration for ImData data + if(!ImData.ReloadConfigration()){ + ERR_CHMPRN("Failed to reinitialize internal data for ImData."); + return false; + } + + // Second, update all objects without conf. + bool result = true; + if(!pEventSock->UpdateInternalData()){ + ERR_CHMPRN("Failed to reinitialize internal data for Socket Object."); + result = false; + } + if(!pEventMq->UpdateInternalData()){ + ERR_CHMPRN("Failed to reinitialize internal data for MQ Object."); + result = false; + } + if(!pEventShm->UpdateInternalData()){ + ERR_CHMPRN("Failed to reinitialize internal data for Shm Object."); + result = false; + } + return result; +} + +// [NOTICE] +// This method is only for debugging SSL. +// +void ChmCntrl::AllowSelfCert(void) +{ + if(pEventSock){ + pEventSock->AllowSelfSignedCert(); + } +} + +// +// Event loop for chmpx process. +// +bool ChmCntrl::EventLoop(void) +{ + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return false; + } + if(CHMCHNTL_TYPE_CHMPXPROC != chmcntrltype){ + ERR_CHMPRN("This object is not initialized for CHMPX process."); + return false; + } + + // Get signal mask + ChmSigCntrl sigcntrl; + sigset_t sigset; + if(!sigcntrl.GetSignalMask(sigset)){ + ERR_CHMPRN("Could not get signal mask value."); + return false; + } + + // Loop + struct epoll_event events[ChmCntrl::DEFAULT_MAX_EVENT_CNT]; + int max_events = ChmCntrl::DEFAULT_MAX_EVENT_CNT; + int timeout = ChmCntrl::DEFAULT_EVENT_TIMEOUT; + bool is_slave = ImData.IsSlaveMode(); + while(ChmCntrl::DoLoop){ + // check all client closing. + if(is_close_notify && pEventSock){ + is_close_notify = false; + + // Set status SUSPEND(if merging, stop it) + if(!pEventSock->DoSuspend()){ + ERR_CHMPRN("Failed to set status SUSPEND."); + } + } + + // wait event + int eventcnt = epoll_pwait(eqfd, events, max_events, timeout, &sigset); + + if(-1 == eventcnt){ + if(EINTR != errno){ + ERR_CHMPRN("Failed to wait epoll by errno=%d", errno); + break; + } + MSG_CHMPRN("Break waiting epoll by SIGNAL, continue to loop."); + continue; + }else if(0 == eventcnt){ + //MSG_CHMPRN("Timeouted for waiting epoll event."); + if(is_slave && pEventSock){ + if(0L == ImData.GetUpServerCount()){ + // Check any server up + if(pEventSock->InitialAllServerStatus()){ + if(0L < ImData.GetUpServerCount()){ + // Try to connect servers + //MSG_CHMPRN("Try to connect servers on RING(mode is SLAVE)."); + pEventSock->ConnectServers(); // not need to check result. + }else{ + // sleep + struct timespec sleeptime = {0, 100 * 1000 * 1000}; // 100ms + nanosleep(&sleeptime, NULL); + } + } + } + } + continue; + } + + // loop for dispatching event + for(int cnt = 0; cnt < eventcnt; cnt++){ + //MSG_CHMPRN("Event type(%lu) on event fd(%d).", events[cnt].events, events[cnt].data.fd); + + ChmEventBase* pEvObj = FindEventBaseObj(events[cnt].data.fd); + if(!pEvObj){ + MSG_CHMPRN("Unknown event(%d) on fd(%d), so skip this event and close this fd.", events[cnt].events, events[cnt].data.fd); + // Most of this case is occured on the server side socket. + // Because that socket set epoll with EPOLLIN and EPOLLRDHUP, then maybe those event + // are occured at same time. So that, one of event occured after the other event. + // Thus we close it here. + epoll_ctl(eqfd, EPOLL_CTL_DEL, events[cnt].data.fd, NULL); + close(events[cnt].data.fd); + continue; + } + //MSG_CHMPRN("Get event fd(%d) - type(0x%x).", events[cnt].data.fd, events[cnt].events); + + // do event + if( EPOLLRDHUP == (events[cnt].events & EPOLLRDHUP)|| + EPOLLERR == (events[cnt].events & EPOLLERR) || + EPOLLHUP == (events[cnt].events & EPOLLHUP) ) + { + // Hungup fd + // + if(!pEvObj->NotifyHup(events[cnt].data.fd)){ + ERR_CHMPRN("Failed to notify HUP from event fd(%d).", events[cnt].data.fd); + } + }else{ + // Receive & Processing(EPOLLIN, etc...) + // + // [NOTE] + // If the receive data needs processing, the event object calls Processing() + // method by itself. + // + if(!pEvObj->Receive(events[cnt].data.fd)){ + ERR_CHMPRN("Failed to receive data from event fd(%d).", events[cnt].data.fd); + } + } + } + } + return true; +} + +// +// Event Processing +// +// [NOTE] +// This method is called from Event objects for processing events after receiving. +// If the data is received on chmpx process and the process uses multi-thread, +// this method is called not main thread bu another thread. +// +bool ChmCntrl::Processing(PCOMPKT pComPkt, EVOBJTYPE call_ev_type) +{ + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return false; + } + if(!pComPkt){ + MSG_CHMPRN("pComPkt is NULL."); + return true; + } + + // processing object + ChmEventBase* pProcessObj = NULL; + + if(COM_C2C == pComPkt->head.type){ + // set processing event object by input event type + if(EVOBJ_TYPE_EVMQ == call_ev_type){ + // Input from MQ, so processing do to socket + // + pProcessObj = GetEventBaseObj(EVOBJ_TYPE_EVSOCK); + + }else if(EVOBJ_TYPE_EVSOCK == call_ev_type){ + // Input from Socket, so processing do to MQ + // + pProcessObj = GetEventBaseObj(EVOBJ_TYPE_EVMQ); + } + + }else if(COM_PX2PX == pComPkt->head.type){ + pProcessObj = GetEventBaseObj(EVOBJ_TYPE_EVSOCK); + + }else if(COM_S2PX == pComPkt->head.type){ + // + ERR_CHMPRN("This library does not implement for transfer signal by ComPkt type=%" PRIu64 "(dept=%" PRIu64 ":%" PRIu64 ", term=%" PRIu64 ":%" PRIu64 "), so skip this.", + pComPkt->head.type, pComPkt->head.dept_ids.chmpxid, pComPkt->head.dept_ids.msgid, pComPkt->head.term_ids.chmpxid, pComPkt->head.term_ids.msgid); + + }else if(COM_F2PX == pComPkt->head.type){ + // + ERR_CHMPRN("This library does not implement for transfer inotify by ComPkt type=%" PRIu64 "(dept=%" PRIu64 ":%" PRIu64 ", term=%" PRIu64 ":%" PRIu64 "), so skip this.", + pComPkt->head.type, pComPkt->head.dept_ids.chmpxid, pComPkt->head.dept_ids.msgid, pComPkt->head.term_ids.chmpxid, pComPkt->head.term_ids.msgid); + + }else if(COM_C2PX == pComPkt->head.type || COM_PX2C == pComPkt->head.type){ + // Input from MQ, so processing do on MQ + // + PPXCLT_ALL pCltAll = CVT_CLT_ALL_PTR_PXCOMPKT(pComPkt); + if(IS_PXCLT_TYPE(pCltAll, CHMPX_CLT_JOIN_NOTIFY)){ + // CHMPX_CLT_JOIN_NOTIFY type is processing by ChmEventSock. + pProcessObj = GetEventBaseObj(EVOBJ_TYPE_EVSOCK); + }else{ + pProcessObj = GetEventBaseObj(EVOBJ_TYPE_EVMQ); + } + + }else{ // COM_UNKNOWN + ERR_CHMPRN("Unknown COMPKT type(%" PRIu64 ").", pComPkt->head.type); + } + + // dispatching + if(!pProcessObj){ + MSG_CHMPRN("Nothing to do for ComPkt type=%" PRIu64 "(dept=%" PRIu64 ":%" PRIu64 ", term=%" PRIu64 ":%" PRIu64 ") after receiving.", + pComPkt->head.type, pComPkt->head.dept_ids.chmpxid, pComPkt->head.dept_ids.msgid, pComPkt->head.term_ids.chmpxid, pComPkt->head.term_ids.msgid); + + }else if(!pProcessObj->Processing(pComPkt)){ + ERR_CHMPRN("Failed to processing for ComPkt type=%" PRIu64 "(dept=%" PRIu64 ":%" PRIu64 ", term=%" PRIu64 ":%" PRIu64 ") after receiving.", + pComPkt->head.type, pComPkt->head.dept_ids.chmpxid, pComPkt->head.dept_ids.msgid, pComPkt->head.term_ids.chmpxid, pComPkt->head.term_ids.msgid); + return false; + } + return true; +} + +// +// Receive COMPKT for client joining server chmpx. +// +// [Return value] +// true: succeed to read / could not read event. +// false: something error occured +// +// * If returned true, must check *ppComPkt value which is not NULL for succeed. +// *ppComPkt is NULL, it means timeout. +// +// [Usage] +// If you get COMPKT and body data for each, specify ppbody and plength argument. +// If you get COMPKT for all of head and body, specify these are NULL. +// +bool ChmCntrl::Receive(PCOMPKT* ppComPkt, unsigned char** ppbody, size_t* plength, int timeout_ms, bool no_giveup_rejoin) +{ + if(!ppComPkt || (!ppbody && plength) || (ppbody && !plength)){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(CHMCHNTL_TYPE_CLIENT_ONSERVER != chmcntrltype){ + ERR_CHMPRN("This object is not initialized for server."); + return false; + } + if(!IsInitialized()){ + if(!ReInitialize(no_giveup_rejoin ? timeout_ms : 0L, no_giveup_rejoin ? 0 : 1)){ + ERR_CHMPRN("This object is not initialized yet."); + return false; + } + } + + // initialize values + *ppComPkt = NULL; + if(ppbody){ + *ppbody = NULL; + *plength= 0L; + } + + // Get signal mask + ChmSigCntrl sigcntrl; + sigset_t sigset; + if(!sigcntrl.GetSignalMask(sigset)){ + ERR_CHMPRN("Could not get signal mask value."); + return false; + } + + // Loop for read not expected event + do{ + struct epoll_event readevent; // One event + + // wait event + int eventcnt = epoll_pwait(eqfd, &readevent, 1, timeout_ms, &sigset); + if(-1 == eventcnt){ + if(EINTR == errno){ + MSG_CHMPRN("Break waiting epoll by SIGNAL, continue to loop."); + continue; + } + ERR_CHMPRN("Failed to wait epoll by errno=%d", errno); + return false; + + }else if(0 == eventcnt){ + // there is no event now, check chmpx process down + if(!ImData.IsNeedDettach()){ + return true; + } + MSG_CHMPRN("Found notification, Chmpx process down."); + + // Dettach + Clean(false); + + // Re-join + if(!ReInitialize(timeout_ms, no_giveup_rejoin ? 0 : 1)){ // timeout(ms) + MSG_CHMPRN("Try to rejoin, but could not join."); + return false; + } + continue; + } + + //MSG_CHMPRN("Event type(%lu) on event fd(%d).", readevent.events, readevent.data.fd); + + ChmEventBase* pEvObj = FindEventBaseObj(readevent.data.fd); + if(!pEvObj){ + MSG_CHMPRN("Unknown event(%d) on fd(%d), so skip this event and close this fd.", readevent.events, readevent.data.fd); + // Most of this case is occured on the server side socket. + // Because that socket set epoll with EPOLLIN and EPOLLRDHUP, then maybe those event + // are occured at same time. So that, one of event occured after the other event. + // Thus we close it here. + epoll_ctl(eqfd, EPOLL_CTL_DEL, readevent.data.fd, NULL); + close(readevent.data.fd); + continue; + } + MSG_CHMPRN("Get event fd(%d) - type(%d).", readevent.data.fd, readevent.events); + + // do event + if(EPOLLRDHUP == (readevent.events & EPOLLRDHUP)){ + // Hungup fd + if(!pEvObj->NotifyHup(readevent.data.fd)){ + ERR_CHMPRN("event fd(%d) is hangup, but failed to do.", readevent.data.fd); + } + // retry to read. + continue; + } + + // EPOLLIN, etc... + EVOBJTYPE type = EVOBJ_TYPE_EVMQ; // tmp + if(!GetEventBaseObjType(pEvObj, type)){ + ERR_CHMPRN("Why? somthing wrong with event type."); + return false; + } + + // Receive + if(EVOBJ_TYPE_EVMQ == type){ + ChmEventMq* pEvMqObj = reinterpret_cast(pEvObj); + + // get composed msgid + COMPOSEDMSGID composed; + if(!pEvMqObj->ReceiveComposedMsgid(readevent.data.fd, composed)){ + ERR_CHMPRN("Failed to read composed msgid from MQ(%d).", readevent.data.fd); + return false; + } + // read only head + if(!pEvMqObj->ReceiveHead(readevent.data.fd, composed, ppComPkt)){ + ERR_CHMPRN("Failed to receive head data from msgid(0x%016" PRIx64 ") by MQ(%d).", composed.msgid, readevent.data.fd); + return false; + } + + // check COM_C2C packet + if(pEvMqObj->CheckProcessing(*ppComPkt)){ + // Processing COM_PX2C/COM_C2PX/etc packet, so wait next packet. + CHM_Free(*ppComPkt); + continue; + + }else{ + // COM_C2C : read body + if(ppbody){ + if(!pEvMqObj->ReceiveBody(*ppComPkt, ppbody, *plength, true)){ // remove k2hash + ERR_CHMPRN("Failed to receive data from event fd(%d).", readevent.data.fd); + CHM_Free(*ppComPkt); + return false; + } + }else{ + PCOMPKT pTmp = pEventMq->ReceiveBody(*ppComPkt, true); + if(!pTmp){ + ERR_CHMPRN("Something error occured reading body from event fd(%d).", readevent.data.fd); + CHM_Free(*ppComPkt); + return false; + } + *ppComPkt = pTmp; + } + break; + } + + }else{ + // why unsupport read event object type + WAN_CHMPRN("Got a event is not MQ event."); + + if(!pEvObj->Receive(readevent.data.fd)){ + ERR_CHMPRN("Failed to receive data from event fd(%d).", readevent.data.fd); + return false; + } + break; + } + }while(ChmCntrl::DoLoop); + + return true; +} + +// +// Receive COMPKT for client joining server/slave chmpx. +// +msgid_t ChmCntrl::Open(bool no_giveup_rejoin) +{ + if(CHMCHNTL_TYPE_CLIENT_ONSLAVE != chmcntrltype){ + ERR_CHMPRN("This object is not initialized for client."); + return CHM_INVALID_MSGID; + } + if(!IsInitialized()){ + if(!ReInitialize(no_giveup_rejoin ? ChmCntrl::DEFAULT_EVENT_TIMEOUT : 0L, no_giveup_rejoin ? 0 : 1)){ + ERR_CHMPRN("This object is not initialized yet."); + return CHM_INVALID_MSGID; + } + } + + // activating + msgid_t msgid; + if(CHM_INVALID_MSGID == (msgid = pEventMq->ActivatedMsgId())){ + ERR_CHMPRN("Failed to activating MQ."); + } + + return msgid; +} + +// +// Receive COMPKT for client joining server/slave chmpx. +// +bool ChmCntrl::Close(msgid_t msgid) +{ + if(CHM_INVALID_MSGID == msgid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(CHMCHNTL_TYPE_CLIENT_ONSLAVE != chmcntrltype){ + ERR_CHMPRN("This object is not initialized for client."); + return false; + } + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return false; + } + + // disactivating + if(!pEventMq->DisactivatedMsgId(msgid)){ + ERR_CHMPRN("Failed to disactivating MQ msgid(0x%016" PRIx64 ").", msgid); + return false; + } + return true; +} + +// +// Receive COMPKT for client joining slave chmpx. +// +// [NOTICE] +// This method access to MQ fd directly, thus not use event queue. +// This class on "client on library" mode sets MQ fd into event queue, +// but not use it.(be carefule) +// +// [Return value] +// true: succeed to read / could not read event. +// false: something error occured +// +// * If returned true, must check *ppComPkt value which is not NULL for succeed. +// *ppComPkt is NULL, it means timeout. +// +// [Usage] +// If you get COMPKT and body data for each, specify ppbody and plength argument. +// If you get COMPKT for all of head and body, specify these are NULL. +// +bool ChmCntrl::Receive(msgid_t msgid, PCOMPKT* ppComPkt, unsigned char** ppbody, size_t* plength, int timeout_ms) +{ + if(CHM_INVALID_MSGID == msgid || !ppComPkt || (!ppbody && plength) || (ppbody && !plength)){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(CHMCHNTL_TYPE_CLIENT_ONSLAVE != chmcntrltype){ + ERR_CHMPRN("This object is not initialized for client."); + return false; + } + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return false; + } + + // initialize values + *ppComPkt = NULL; + if(ppbody){ + *ppbody = NULL; + *plength= 0L; + } + + // read loop + bool CanContinueWait; + struct timespec sleeptime = {0, 10 * 1000}; // 10us + int retrycnt = timeout_ms <= 0 ? 1 : (timeout_ms * 1000 / 10); // retrycnt * 10us = timeout_ms; + for(int cnt = 0; cnt < retrycnt && ChmCntrl::DoLoop; ){ + // Read Header + // if not readable data, retry after sleep 100us. + CanContinueWait = false; + if(!pEventMq->ReceiveHeadByMsgId(msgid, ppComPkt, &CanContinueWait) || NULL == (*ppComPkt)){ + // check chmpx process down + if(ImData.IsNeedDettach()){ + ERR_CHMPRN("Found notification, Chmpx process down."); + // Dettach + Clean(false); + return false; + } + if(CanContinueWait){ + if(cnt + 1 < retrycnt){ + nanosleep(&sleeptime, NULL); + } + cnt++; + continue; + } + ERR_CHMPRN("Something error occured reading head from MQ msgid(0x%016" PRIx64 ").", msgid); + return false; + } + + // check COM_C2C packet + if(!pEventMq->CheckProcessing(*ppComPkt)){ + // Read after + if(!ppbody){ + PCOMPKT pTmp = pEventMq->ReceiveBody(*ppComPkt, true); + if(!pTmp){ + ERR_CHMPRN("Something error occured reading body from MQ msgid(0x%016" PRIx64 ").", msgid);; + CHM_Free(*ppComPkt); + return false; + } + *ppComPkt = pTmp; + }else{ + if(!pEventMq->ReceiveBody(*ppComPkt, ppbody, (*plength), true)){ + ERR_CHMPRN("Something error occured reading body from MQ msgid(0x%016" PRIx64 ").", msgid); + CHM_Free(*ppComPkt); + return false; + } + } + // Succeed receive C2C packet. + break; + } + + // Processing COM_PX2C/COM_C2PX/etc packet, so wait next packet. + // and cnt is not increament. + CHM_Free(*ppComPkt); + } + + // means timeout or break loop. + return true; +} + +long ChmCntrl::PlaningReceiverCount(chmhash_t hash, c2ctype_t c2ctype) +{ + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return -1L; + } + // get target chmpxid list + chmpxidlist_t chmpxids; + long chmpxcnt = ImData.GetReceiverChmpxids(hash, c2ctype, chmpxids); + if(0 > chmpxcnt){ + ERR_CHMPRN("Failed to get target chmpxids by something error occurred."); + }else if(0 == chmpxcnt){ + WAN_CHMPRN("There are no target chmpxids, but method returns with success."); + } + return chmpxcnt; +} + +// +// Get all assigned chmpx's chmhash list for manual replication. +// +bool ChmCntrl::GetAllReplicateChmHashs(chmhash_t hash, chmhashlist_t& basehashs, bool with_pending, bool without_down, bool without_suspend) +{ + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return -1L; + } + return ImData.GetServerChmHashsByHashs(hash, basehashs, with_pending, without_down, without_suspend); +} + +// +// Send COMPKT for client joining slave chmpx. +// +bool ChmCntrl::RawSendOnSlave(msgid_t msgid, const unsigned char* pbody, size_t blength, chmhash_t hash, long* preceivercnt, c2ctype_t c2ctype) +{ + if(!IsClientOnSlvType()){ + ERR_CHMPRN("This is not client joining slave chmpx process."); + return false; + } + if(preceivercnt){ + if(0 > ((*preceivercnt) = PlaningReceiverCount(hash, c2ctype))){ + ERR_CHMPRN("Could not plan reciver chmpx server count."); + return false; + } + } + return RawSend(msgid, NULL, pbody, blength, hash, c2ctype); +} + +// +// Send COMPKT for client joining server chmpx proc. +// +bool ChmCntrl::RawSendOnServer(const unsigned char* pbody, size_t blength, chmhash_t hash, c2ctype_t c2ctype, bool without_self) +{ + if(!IsClientOnSvrType()){ + ERR_CHMPRN("This is not client joining server chmpx process."); + return false; + } + // Supported C2C from server to server now. + // (after 1.0.19) + if(IS_C2CTYPE_IGNORE(c2ctype)){ + ERR_CHMPRN("COM_C2C type(%s) is invalid.", STRCOMC2CTYPE(c2ctype)); + return false; + } + if(without_self){ + SET_C2CTYPE_NOT_SELF(c2ctype); + }else{ + SET_C2CTYPE_SELF(c2ctype); + } + return RawSend(CHM_INVALID_MSGID, NULL, pbody, blength, hash, c2ctype); +} + +// +// Reply COMPKT for all client. +// +bool ChmCntrl::Send(PCOMPKT pComPkt, const unsigned char* pbody, size_t blength) +{ + if(IsChmpxType()){ + ERR_CHMPRN("This is chmpx process."); + return false; + } + return RawSend(CHM_INVALID_MSGID, pComPkt, pbody, blength, 0L, COM_C2C_NORMAL); +} + +// +// RawSend COMPKT +// +bool ChmCntrl::RawSend(msgid_t msgid, PCOMPKT pComPkt, const unsigned char* pbody, size_t blength, chmhash_t hash, c2ctype_t c2ctype) +{ + if(!pbody || 0L == blength){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(CHMCHNTL_TYPE_CHMPXPROC == chmcntrltype){ + ERR_CHMPRN("This object is not initialized for chmpx."); + return false; + } + if(IS_C2CTYPE_IGNORE(c2ctype)){ + ERR_CHMPRN("COM_C2C type(%s) is invalid.", STRCOMC2CTYPE(c2ctype)); + return false; + } + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return false; + } + + // make head + COMPKT NewComPkt; + if(!pComPkt){ + // new message + // + if(!pEventMq->BuildC2CSendHead(&(NewComPkt.head), CHM_INVALID_CHMPXID, hash, c2ctype, msgid)){ + ERR_CHMPRN("Could not make COMHEAD for sending new message through MQ."); + return false; + } + }else{ + // renponse message + if(!pEventMq->BuildC2CResponseHead(&(NewComPkt.head), &(pComPkt->head))){ + ERR_CHMPRN("Could not make COMHEAD for responding message through MQ."); + return false; + } + } + NewComPkt.length = sizeof(COMPKT); + NewComPkt.offset = sizeof(COMPKT); + + if(!pEventMq->Send(&NewComPkt, pbody, blength)){ + // check chmpx process down + if(ImData.IsNeedDettach()){ + MSG_CHMPRN("Found notification, Chmpx process down."); + // Dettach + Clean(false); + } + return false; + } + return true; +} + +bool ChmCntrl::IsChmpxExit(void) +{ + if(IsInitialized()){ + return false; + } + if(bup_cfg.empty()){ + return false; + } + return true; +} + +bool ChmCntrl::MergeGetLastTime(struct timespec& lastts) +{ + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return CHM_INVALID_PID; + } + if(CHMCHNTL_TYPE_CHMPXPROC != chmcntrltype){ + ERR_CHMPRN("This object is not initialized for chmpx process."); + return false; + } + return pEventMq->PxCltSendMergeGetLastTime(lastts); +} + +bool ChmCntrl::MergeRequestUpdateData(const PPXCOMMON_MERGE_PARAM pmerge_param) +{ + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return CHM_INVALID_PID; + } + if(CHMCHNTL_TYPE_CHMPXPROC != chmcntrltype){ + ERR_CHMPRN("This object is not initialized for chmpx process."); + return false; + } + return pEventMq->PxCltSendRequestUpdateData(pmerge_param); +} + +bool ChmCntrl::MergeResponseUpdateData(chmpxid_t chmpxid, size_t length, const unsigned char* pdata, const struct timespec* pts) +{ + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return CHM_INVALID_PID; + } + if(CHMCHNTL_TYPE_CHMPXPROC != chmcntrltype){ + ERR_CHMPRN("This object is not initialized for chmpx process."); + return false; + } + return pEventSock->PxComSendResUpdateData(chmpxid, length, pdata, pts); +} + +bool ChmCntrl::MergeSetUpdateData(size_t length, const unsigned char* pdata, const struct timespec* pts) +{ + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return CHM_INVALID_PID; + } + if(CHMCHNTL_TYPE_CHMPXPROC != chmcntrltype){ + ERR_CHMPRN("This object is not initialized for chmpx process."); + return false; + } + return pEventMq->PxCltSendSetUpdateData(length, pdata, pts); +} + +bool ChmCntrl::MergeResultUpdateData(chmpxid_t chmpxid, reqidmapflag_t result_updatedata) +{ + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return CHM_INVALID_PID; + } + if(CHMCHNTL_TYPE_CHMPXPROC != chmcntrltype){ + ERR_CHMPRN("This object is not initialized for chmpx process."); + return false; + } + return pEventSock->PxComSendResultUpdateData(chmpxid, result_updatedata); +} + +bool ChmCntrl::MergeAbortUpdateData(void) +{ + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return CHM_INVALID_PID; + } + if(CHMCHNTL_TYPE_CHMPXPROC != chmcntrltype){ + ERR_CHMPRN("This object is not initialized for chmpx process."); + return false; + } + return pEventMq->PxCltSendAbortUpdateData(); +} + +bool ChmCntrl::DumpSelfChmpxSvr(stringstream& sstream) const +{ + if(!IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return false; + } + if(!ImData.DumpSelfChmpxSvr(sstream)){ + ERR_CHMPRN("Could not dump self chmpx information."); + return false; + } + return true; +} + +PCHMINFOEX ChmCntrl::DupAllChmInfo(void) +{ + if(!ImData.IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return false; + } + return ImData.DupAllChmInfo(); +} + +PCHMPX ChmCntrl::DupSelfChmpxInfo(void) +{ + if(!ImData.IsInitialized()){ + ERR_CHMPRN("This object is not initialized yet."); + return false; + } + return ImData.DupSelfChmpxInfo(); +} + +void ChmCntrl::FreeDupAllChmInfo(PCHMINFOEX ptr) +{ + ChmIMData::FreeDupAllChmInfo(ptr); +} + +void ChmCntrl::FreeDupSelfChmpxInfo(PCHMPX ptr) +{ + ChmIMData::FreeDupSelfChmpxInfo(ptr); +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmcntrl.h b/lib/chmcntrl.h new file mode 100644 index 0000000..971e7c0 --- /dev/null +++ b/lib/chmcntrl.h @@ -0,0 +1,175 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMCNTRL_H +#define CHMCNTRL_H + +#include +#include + +#include "chmconf.h" +#include "chmimdata.h" +#include "chmeventmq.h" +#include "chmeventsock.h" +#include "chmeventshm.h" + +//--------------------------------------------------------- +// ChmCntrl Class +//--------------------------------------------------------- +class ChmCntrl +{ + friend class ChmEventBase; + friend class CHMConf; + friend class ChmEventMq; + friend class ChmEventSock; + friend class ChmEventShm; + + protected: + typedef enum _chmcntrl_type{ + CHMCHNTL_TYPE_CHMPXPROC, + CHMCHNTL_TYPE_CLIENT_ONSERVER, + CHMCHNTL_TYPE_CLIENT_ONSLAVE + }CHMCNTRLTYPE; + + typedef enum _evobj_type{ + EVOBJ_TYPE_CONF, + EVOBJ_TYPE_EVSOCK, + EVOBJ_TYPE_EVMQ, + EVOBJ_TYPE_EVSHM + }EVOBJTYPE; + + typedef std::map evobj_map_t; // Key is number which is kind of event object. + + static const int DEFAULT_MAX_EVENT_CNT = 32; // receive max event at onece for CHMCHNTL_TYPE_CHMPXPROC + static const int DEFAULT_EVENT_TIMEOUT = 100; // timeout for epoll wait(100ms) for CHMCHNTL_TYPE_CHMPXPROC + static const int EVENT_NOWAIT = 0; // no wait for epoll + static volatile bool DoLoop; // Break loop flag + + CHMCNTRLTYPE chmcntrltype; // process type + int eqfd; // event queue fd + bool is_ssl_init; // + ChmIMData ImData; // + CHMConf* pConfObj; // + ChmEventMq* pEventMq; // + ChmEventSock* pEventSock; // [NOTICE] chmpx only has this value. + ChmEventShm* pEventShm; // [NOTICE] chmpx only has this value. + bool auto_rejoin; // retry to join if chmpx process is down. + std::string bup_cfg; // backup for configuration file path or json string. + evobj_map_t evobjmap; // Object mapping + volatile bool is_close_notify; // client closing notify flag + + protected: + ChmEventBase* GetEventBaseObj(EVOBJTYPE type); + ChmEventBase* FindEventBaseObj(int fd); + bool GetEventBaseObjType(const ChmEventBase* pEvObj, EVOBJTYPE& type); + + ChmIMData* GetImDataObj(void) { return (ImData.IsInitialized() ? &ImData : NULL); } + CHMConf* GetConfObj(void) { return pConfObj; } + ChmEventMq* GetEventMqObj(void) { return pEventMq; } + ChmEventSock* GetEventSockObj(void) { return pEventSock; } + ChmEventShm* GetEventShmObj(void) { return pEventShm; } + + bool Initialize(const char* cfgfile, CHMCNTRLTYPE type, bool is_auto_rejoin = false, chm_merge_get_cb getfp = NULL, chm_merge_set_cb setfp = NULL, chm_merge_lastts_cb lastupdatefp = NULL, short ctlport = CHM_INVALID_PORT); + bool ReInitialize(long waitms = 0L, int trycnt = 1); + bool IsInitialized(void) const; + bool InitializeEventFd(void); + bool ConfigrationUpdateNotify(void); + + bool Processing(PCOMPKT pComPkt, EVOBJTYPE call_ev_type); + + long PlaningReceiverCount(chmhash_t hash, c2ctype_t c2ctype); + bool RawSendOnSlave(msgid_t msgid, const unsigned char* pbody, size_t blength, chmhash_t hash, long* preceivercnt, c2ctype_t c2ctype); + bool RawSendOnServer(const unsigned char* pbody, size_t blength, chmhash_t hash, c2ctype_t c2ctype, bool without_self); + bool RawSend(msgid_t msgid, PCOMPKT pComPkt, const unsigned char* pbody, size_t blength, chmhash_t hash, c2ctype_t c2ctype); + + // For merging + bool MergeGetLastTime(struct timespec& lastts); + bool MergeRequestUpdateData(const PPXCOMMON_MERGE_PARAM pmerge_param); + bool MergeResponseUpdateData(chmpxid_t chmpxid, size_t length, const unsigned char* pdata, const struct timespec* pts); + bool MergeSetUpdateData(size_t length, const unsigned char* pdata, const struct timespec* pts); + bool MergeResultUpdateData(chmpxid_t chmpxid, reqidmapflag_t result_updatedata); + bool MergeAbortUpdateData(void); + + public: + static void LoopBreakHandler(int signum); + static void FreeDupAllChmInfo(PCHMINFOEX ptr); // for free memory allocated by DupAllChmInfo() + static void FreeDupSelfChmpxInfo(PCHMPX ptr); // for free memory allocated by DupSelfChmpxInfo() + + ChmCntrl(void); + virtual ~ChmCntrl(); + + bool Clean(bool is_clean_bup = true); + // For chmpx process + bool InitializeOnChmpx(const char* cfgfile, short ctlport = CHM_INVALID_PORT) { return Initialize(cfgfile, CHMCHNTL_TYPE_CHMPXPROC, false, NULL, NULL, NULL, ctlport); } + // For server process on library + bool InitializeOnServer(const char* cfgfile, bool is_auto_rejoin = false, chm_merge_get_cb getfp = NULL, chm_merge_set_cb setfp = NULL, chm_merge_lastts_cb lastupdatefp = NULL, short ctlport = CHM_INVALID_PORT) { return Initialize(cfgfile, CHMCHNTL_TYPE_CLIENT_ONSERVER, is_auto_rejoin, getfp, setfp, lastupdatefp, ctlport); } + // For client process on library + bool InitializeOnSlave(const char* cfgfile, bool is_auto_rejoin = false, short ctlport = CHM_INVALID_PORT) { return Initialize(cfgfile, CHMCHNTL_TYPE_CLIENT_ONSLAVE, is_auto_rejoin, NULL, NULL, NULL, ctlport); } + bool OnlyAttachInitialize(const char* cfgfile, short ctlport = CHM_INVALID_PORT); + + void AllowSelfCert(void); // For only debugging + + // where process running on + bool IsChmpxType(void) const { return (CHMCHNTL_TYPE_CHMPXPROC == chmcntrltype); } + bool IsClientOnSvrType(void) const { return (CHMCHNTL_TYPE_CLIENT_ONSERVER == chmcntrltype); } + bool IsClientOnSlvType(void) const { return (CHMCHNTL_TYPE_CLIENT_ONSLAVE == chmcntrltype); } + + // Receive for chmpx process + bool EventLoop(void); + + // Receive/Send for client on server chmpx process + bool Receive(PCOMPKT* ppComPkt, unsigned char** ppbody = NULL, size_t* plength = NULL, int timeout_ms = ChmCntrl::EVENT_NOWAIT, bool no_giveup_rejoin = false); + bool Send(const unsigned char* pbody, size_t blength, chmhash_t hash, bool is_routing = true, bool without_self = false) { return RawSendOnServer(pbody, blength, hash, is_routing ? COM_C2C_ROUTING : COM_C2C_NORMAL, without_self); } + bool Broadcast(const unsigned char* pbody, size_t blength, chmhash_t hash, bool without_self = false) { return RawSendOnServer(pbody, blength, hash, COM_C2C_BROADCAST, without_self); } + bool Replicate(const unsigned char* pbody, size_t blength, chmhash_t hash, bool without_self = true) { return RawSendOnServer(pbody, blength, hash, COM_C2C_RBROADCAST, without_self); } + + // Open/Close/Receive/Send for client on slave chmpx process + msgid_t Open(bool no_giveup_rejoin = false); + bool Close(msgid_t msgid); + bool Receive(msgid_t msgid, PCOMPKT* ppComPkt, unsigned char** ppbody, size_t* plength, int timeout_ms = 0); // 0 is no wait + bool Send(msgid_t msgid, const unsigned char* pbody, size_t blength, chmhash_t hash, long* preceivercnt = NULL, bool is_routing = true) { return RawSendOnSlave(msgid, pbody, blength, hash, preceivercnt, is_routing ? COM_C2C_ROUTING : COM_C2C_NORMAL); } + bool Broadcast(msgid_t msgid, const unsigned char* pbody, size_t blength, chmhash_t hash, long* preceivercnt = NULL) { return RawSendOnSlave(msgid, pbody, blength, hash, preceivercnt, COM_C2C_BROADCAST); } + bool Replicate(msgid_t msgid, const unsigned char* pbody, size_t blength, chmhash_t hash, long* preceivercnt = NULL) { return RawSendOnSlave(msgid, pbody, blength, hash, preceivercnt, COM_C2C_RBROADCAST); } + + // Reply for all client + bool Send(PCOMPKT pComPkt, const unsigned char* pbody, size_t blength); + bool Reply(PCOMPKT pComPkt, const unsigned char* pbody, size_t blength) { return Send(pComPkt, pbody, blength); } + + // Check error type as Chmpx process exiting + bool IsChmpxExit(void); + + // Utility for manual replication + bool GetAllReplicateChmHashs(chmhash_t hash, chmhashlist_t& basehashs, bool with_pending = true, bool without_down = true, bool without_suspend = true); + + // Dump/Get internal information + bool DumpSelfChmpxSvr(std::stringstream& sstream) const; + PCHMINFOEX DupAllChmInfo(void); + PCHMPX DupSelfChmpxInfo(void); +}; + +#endif // CHMCNTRL_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmcommon.h b/lib/chmcommon.h new file mode 100644 index 0000000..ccc234d --- /dev/null +++ b/lib/chmcommon.h @@ -0,0 +1,131 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMCOMMON_H +#define CHMCOMMON_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +//--------------------------------------------------------- +// Macros for compiler +//--------------------------------------------------------- +#ifndef CHMPX_NOWEAK +#define CHMPX_ATTR_WEAK __attribute__ ((weak,unused)) +#else +#define CHMPX_ATTR_WEAK +#endif + +#ifndef CHMPX_NOPADDING +#define CHMPX_ATTR_PACKED __attribute__ ((packed)) +#else +#define CHMPX_ATTR_PACKED +#endif + +#if defined(__cplusplus) +#define DECL_EXTERN_C_START extern "C" { +#define DECL_EXTERN_C_END } +#else // __cplusplus +#define DECL_EXTERN_C_START +#define DECL_EXTERN_C_END +#endif // __cplusplus + +//--------------------------------------------------------- +// Templates & macros +//--------------------------------------------------------- +#if defined(__cplusplus) +template inline bool CHMEMPTYSTR(const T& pstr) +{ + return (NULL == (pstr) || '\0' == *(pstr)) ? true : false; +} +#else // __cplusplus +#define CHMEMPTYSTR(pstr) (NULL == (pstr) || '\0' == *(pstr)) +#endif // __cplusplus + +#define CHMPXSTRJOIN(first, second) first ## second + +#if defined(__cplusplus) +#define CHM_OFFSET(baseaddr, offset, type) (offset ? reinterpret_cast(reinterpret_cast(baseaddr) + offset) : reinterpret_cast(baseaddr)) // convert pointer with offset +#define CHM_ABS(baseaddr, offset, type) (offset ? reinterpret_cast(reinterpret_cast(baseaddr) + reinterpret_cast(offset)) : 0) // To Absorute address +#define CHM_REL(baseaddr, address, type) (address ? reinterpret_cast(reinterpret_cast(address) - reinterpret_cast(baseaddr)) : 0) // To Relative address +#else // __cplusplus +#define CHM_OFFSET(baseaddr, offset, type) (offset ? (type)((off_t)baseaddr + offset) : (type)baseaddr) // convert pointer with offset +#define CHM_ABS(baseaddr, offset, type) (offset ? (type)((off_t)baseaddr + (off_t)offset) : 0) // To Absorute address +#define CHM_REL(baseaddr, address, type) (address ? (type)((off_t)address - (off_t)baseaddr) : 0) // To Relative address +#endif // __cplusplus + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +#define CHM_INVALID_HANDLE (-1) +#define CHM_INVALID_SOCK (-1) +#define CHM_INVALID_TID 0 +#define CHM_MAX_PATH_LEN 1024 + +#if defined(__cplusplus) +#define CHM_INVALID_CHMPXHANDLE static_cast(CHM_INVALID_HANDLE) +#else // __cplusplus +#define CHM_INVALID_CHMPXHANDLE (uint64_t)(CHM_INVALID_HANDLE) +#endif // __cplusplus + +//--------------------------------------------------------- +// For endian +//--------------------------------------------------------- +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#define SET_LOCAL_BSD_SOURCE 1 +#endif + +#ifdef HAVE_ENDIAN_H +#include +#else +#ifdef HAVE_SYS_ENDIAN_H +#include +#endif +#endif + +#ifdef SET_LOCAL_BSD_SOURCE +#undef _BSD_SOURCE +#endif + +//--------------------------------------------------------- +// Compatibility +//--------------------------------------------------------- +// For clock_gettime +#ifndef CLOCK_BOOTTIME +#define CLOCK_BOOTTIME CLOCK_MONOTONIC +#endif + +//--------------------------------------------------------- +// types +//--------------------------------------------------------- +#define __STDC_FORMAT_MACROS +#include + +#endif // CHMCOMMON_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmcomstructure.h b/lib/chmcomstructure.h new file mode 100644 index 0000000..d41a330 --- /dev/null +++ b/lib/chmcomstructure.h @@ -0,0 +1,975 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMCOMSTRUCTURE_H +#define CHMCOMSTRUCTURE_H + +#include "chmcommon.h" +#include "chmstructure.h" +#include "chmdbg.h" + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +#define MAX_K2HASH_MQ_KEY_LENGTH 128 // (uint64_t(32) + '-' + uint64_t(32) + '-' + uint64_t(32) + suffix(5) + null) + some bytes. +#define MQ_PRIORITY_NOTICE 10 // COM_PX2C, COM_C2PX +#define MQ_PRIORITY_MSG 10 // COM_C2C + +// +// The two higher bits in serial number is for tha ACK on MQ. +// +#define MQ_ACK_TYPE_MASK (static_cast(0x80000000) & MASK64_LOWBIT) +#define MQ_ACK_CODE_MASK (static_cast(0x40000000) & MASK64_LOWBIT) +#define MQ_ACK_MASK (MQ_ACK_TYPE_MASK | MQ_ACK_CODE_MASK) +#define MQ_ACK_SUCCESS (MQ_ACK_TYPE_MASK) +#define MQ_ACK_FAILURE (MQ_ACK_TYPE_MASK | MQ_ACK_CODE_MASK) + +#define MIN_MQ_SERIAL_NUMBER (0L) +#define MAX_MQ_SERIAL_NUMBER (static_cast(0x3FFFFFFF) & MASK64_LOWBIT) + +#define IS_MQ_ACK(val) (MQ_ACK_TYPE_MASK == (((val) & MASK64_LOWBIT) & MQ_ACK_TYPE_MASK)) +#define IS_MQ_ACK_SUCCESS(val) (MQ_ACK_SUCCESS == (((val) & MASK64_LOWBIT) & MQ_ACK_MASK)) +#define IS_MQ_ACK_FAILURE(val) (MQ_ACK_FAILURE == (((val) & MASK64_LOWBIT) & MQ_ACK_MASK)) +#define GET_MQ_SERIAL_NUMBER(val) (((val) & MASK64_LOWBIT) & ~MQ_ACK_MASK) + +//--------------------------------------------------------- +// Common structure +//--------------------------------------------------------- +typedef struct chmpx_common_merge_param{ + chmpxid_t chmpxid; // requester chmpx + struct timespec startts; // start time for update datas + chmhash_t max_pending_hash; // max panding hash value + chmhash_t pending_hash; // new(pending) hash value + chmhash_t max_base_hash; // max base hash value + chmhash_t base_hash; // base hash value + long replica_count; // replica count + bool is_expire_check; // whether checking expire time +}CHMPX_ATTR_PACKED PXCOMMON_MERGE_PARAM, *PPXCOMMON_MERGE_PARAM; + +#define COPY_COMMON_MERGE_PARAM(pdest, psrc) \ + { \ + (pdest)->chmpxid = (psrc)->chmpxid; \ + (pdest)->startts.tv_sec = (psrc)->startts.tv_sec; \ + (pdest)->startts.tv_nsec = (psrc)->startts.tv_nsec; \ + (pdest)->max_pending_hash = (psrc)->max_pending_hash; \ + (pdest)->pending_hash = (psrc)->pending_hash; \ + (pdest)->max_base_hash = (psrc)->max_base_hash; \ + (pdest)->base_hash = (psrc)->base_hash; \ + (pdest)->replica_count = (psrc)->replica_count; \ + (pdest)->is_expire_check = (psrc)->is_expire_check; \ + } + +//--------------------------------------------------------- +// COM_C2C: Communication Packets +//--------------------------------------------------------- +// This type does not have structure. so data is binary. +// + +//--------------------------------------------------------- +// COM_C2PX, COM_PX2C: Communication Packets +//--------------------------------------------------------- +// These packets are used for comminicating on MQ. +// So do not change bity order. +// +typedef uint64_t pxclttype_t; // type for packet +typedef uint64_t reqidmapflag_t; // flag type for request update status for each chmpxid + +// Type for COM_C2PX, COM_PX2C +#define CHMPX_CLT_UNKNOWN 0 // Unknown +#define CHMPX_CLT_CLOSE_NOTIFY 1 // Notification of closing MQ +#define CHMPX_CLT_JOIN_NOTIFY 2 // Notification of joining MQ from client to chmpx +#define CHMPX_CLT_MERGE_GETLASTTS 3 // Command for getting lastest update time(chmpx server node to server client) +#define CHMPX_CLT_MERGE_RESLASTTS 4 // Response for getting lastest update time(server client to chmpx server node) +#define CHMPX_CLT_REQ_UPDATEDATA 5 // Command for request update datas(chmpx server node to server client) +#define CHMPX_CLT_RES_UPDATEDATA 6 // Response for request update datas(server client to chmpx server node) +#define CHMPX_CLT_SET_UPDATEDATA 7 // Command for set update datas(chmpx server node to server client) +#define CHMPX_CLT_RESULT_UPDATEDATA 8 // Resonse for result of update data(server client to chmpx server node) +#define CHMPX_CLT_ABORT_UPDATEDATA 9 // Command for abort update data(chmpx server node to server client) + +#define STRPXCLTTYPE(type) ( CHMPX_CLT_UNKNOWN == type ? "CHMPX_CLT_UNKNOWN" : \ + CHMPX_CLT_CLOSE_NOTIFY == type ? "CHMPX_CLT_CLOSE_NOTIFY" : \ + CHMPX_CLT_JOIN_NOTIFY == type ? "CHMPX_CLT_JOIN_NOTIFY" : \ + CHMPX_CLT_MERGE_GETLASTTS == type ? "CHMPX_CLT_MERGE_GETLASTTS" : \ + CHMPX_CLT_MERGE_RESLASTTS == type ? "CHMPX_CLT_MERGE_RESLASTTS" : \ + CHMPX_CLT_REQ_UPDATEDATA == type ? "CHMPX_CLT_REQ_UPDATEDATA" : \ + CHMPX_CLT_RES_UPDATEDATA == type ? "CHMPX_CLT_RES_UPDATEDATA" : \ + CHMPX_CLT_SET_UPDATEDATA == type ? "CHMPX_CLT_SET_UPDATEDATA" : \ + CHMPX_CLT_RESULT_UPDATEDATA == type ? "CHMPX_CLT_RESULT_UPDATEDATA" : \ + CHMPX_CLT_ABORT_UPDATEDATA == type ? "CHMPX_CLT_ABORT_UPDATEDATA" : \ + "NOT_DEFINED_TYPE" ) + +//------------------------------- +// Structures +// +typedef struct chmpx_client_head{ + pxclttype_t type; // Command type + size_t length; // Length for this packet +}CHMPX_ATTR_PACKED PXCLT_HEAD, *PPXCLT_HEAD; + +typedef struct chmpx_client_close_notify{ + PXCLT_HEAD head; + long count; + msgid_t* pmsgids; // out of this structure area. +}CHMPX_ATTR_PACKED PXCLT_CLOSE_NOTIFY, *PPXCLT_CLOSE_NOTIFY; + +typedef struct chmpx_client_join_notify{ + PXCLT_HEAD head; + pid_t pid; // client pid +}CHMPX_ATTR_PACKED PXCLT_JOIN_NOTIFY, *PPXCLT_JOIN_NOTIFY; + +typedef struct chmpx_client_merge_getlastts{ + PXCLT_HEAD head; +}CHMPX_ATTR_PACKED PXCLT_MERGE_GETLASTTS, *PPXCLT_MERGE_GETLASTTS; + +typedef struct chmpx_client_merge_reslastts{ + PXCLT_HEAD head; + struct timespec lastupdatets; // using by only response +}CHMPX_ATTR_PACKED PXCLT_MERGE_RESLASTTS, *PPXCLT_MERGE_RESLASTTS; + +typedef struct chmpx_client_req_updatedata{ + PXCLT_HEAD head; + PXCOMMON_MERGE_PARAM merge_param; +}CHMPX_ATTR_PACKED PXCLT_REQ_UPDATEDATA, *PPXCLT_REQ_UPDATEDATA; + +typedef struct chmpx_client_res_updatedata{ + PXCLT_HEAD head; + chmpxid_t chmpxid; // requester chmpx + struct timespec ts; // updated time is also in this time at least + size_t length; + unsigned char* byptr; // out of this structure area. +}CHMPX_ATTR_PACKED PXCLT_RES_UPDATEDATA, *PPXCLT_RES_UPDATEDATA; + +typedef struct chmpx_client_set_updatedata{ + PXCLT_HEAD head; + struct timespec ts; // updated time is also in this time at least + size_t length; + unsigned char* byptr; // out of this structure area. +}CHMPX_ATTR_PACKED PXCLT_SET_UPDATEDATA, *PPXCLT_SET_UPDATEDATA; + +typedef struct chmpx_client_result_updatedata{ + PXCLT_HEAD head; + chmpxid_t chmpxid; + reqidmapflag_t result; // result +}CHMPX_ATTR_PACKED PXCLT_RESULT_UPDATEDATA, *PPXCLT_RESULT_UPDATEDATA; + +typedef struct chmpx_client_abort_updatedata{ + PXCLT_HEAD head; +}CHMPX_ATTR_PACKED PXCLT_ABORT_UPDATEDATA, *PPXCLT_ABORT_UPDATEDATA; + +typedef union chmpx_clt_all{ + PXCLT_HEAD val_head; + PXCLT_CLOSE_NOTIFY val_close_notify; + PXCLT_JOIN_NOTIFY val_join_notify; + PXCLT_MERGE_GETLASTTS val_merge_getlastts; + PXCLT_MERGE_RESLASTTS val_merge_reslastts; + PXCLT_REQ_UPDATEDATA val_req_updatedata; + PXCLT_RES_UPDATEDATA val_res_updatedata; + PXCLT_SET_UPDATEDATA val_set_updatedata; + PXCLT_RESULT_UPDATEDATA val_result_updatedata; + PXCLT_ABORT_UPDATEDATA val_abort_updatedata; +}CHMPX_ATTR_PACKED PXCLT_ALL, *PPXCLT_ALL; + +//------------ +// Utility Macros +//------------ +#define CVT_CLTPTR_CLOSE_NOTIFY(pCltAll) &((pCltAll)->val_close_notify) +#define CVT_CLTPTR_JOIN_NOTIFY(pCltAll) &((pCltAll)->val_join_notify) +#define CVT_CLTPTR_MERGE_GETLASTTS(pCltAll) &((pCltAll)->val_merge_getlastts) +#define CVT_CLTPTR_MERGE_RESLASTTS(pCltAll) &((pCltAll)->val_merge_reslastts) +#define CVT_CLTPTR_REQ_UPDATEDATA(pCltAll) &((pCltAll)->val_req_updatedata) +#define CVT_CLTPTR_RES_UPDATEDATA(pCltAll) &((pCltAll)->val_res_updatedata) +#define CVT_CLTPTR_SET_UPDATEDATA(pCltAll) &((pCltAll)->val_set_updatedata) +#define CVT_CLTPTR_RESULT_UPDATEDATA(pCltAll) &((pCltAll)->val_result_updatedata) +#define CVT_CLTPTR_ABORT_UPDATEDATA(pCltAll) &((pCltAll)->val_abort_updatedata) +#define CVT_CLT_ALL_PTR_PXCOMPKT(pComPkt) ( CHM_OFFSET((pComPkt), sizeof(COMPKT), PPXCLT_ALL) ) + +#define SIZEOF_CHMPX_CLT(type) ( CHMPX_CLT_CLOSE_NOTIFY == type ? sizeof(PXCLT_CLOSE_NOTIFY) : \ + CHMPX_CLT_JOIN_NOTIFY == type ? sizeof(PXCLT_JOIN_NOTIFY) : \ + CHMPX_CLT_MERGE_GETLASTTS == type ? sizeof(PXCLT_MERGE_GETLASTTS) : \ + CHMPX_CLT_MERGE_RESLASTTS == type ? sizeof(PXCLT_MERGE_RESLASTTS) : \ + CHMPX_CLT_REQ_UPDATEDATA == type ? sizeof(PXCLT_REQ_UPDATEDATA) : \ + CHMPX_CLT_RES_UPDATEDATA == type ? sizeof(PXCLT_RES_UPDATEDATA) : \ + CHMPX_CLT_SET_UPDATEDATA == type ? sizeof(PXCLT_SET_UPDATEDATA) : \ + CHMPX_CLT_RESULT_UPDATEDATA == type ? sizeof(PXCLT_RESULT_UPDATEDATA) : \ + CHMPX_CLT_ABORT_UPDATEDATA == type ? sizeof(PXCLT_ABORT_UPDATEDATA) : \ + 0L) + +#define IS_PXCLT_TYPE(pCltAll, clttype) ( pCltAll && pCltAll->val_head.type == clttype ) + +#define SET_PXCLTPKT(pComPkt, is_chmpx_proc, clttype, dept_msgid, term_msgid, serial_num, extlength) \ + { \ + (pComPkt)->length = sizeof(COMPKT) + SIZEOF_CHMPX_CLT(clttype) + extlength; \ + (pComPkt)->offset = sizeof(COMPKT); \ + (pComPkt)->head.version = COM_VERSION_1; \ + (pComPkt)->head.type = is_chmpx_proc ? COM_PX2C : COM_C2PX; \ + (pComPkt)->head.c2ctype = COM_C2C_IGNORE; \ + (pComPkt)->head.dept_ids.chmpxid= CHM_INVALID_CHMPXID; \ + (pComPkt)->head.dept_ids.msgid = dept_msgid; \ + (pComPkt)->head.term_ids.chmpxid= CHM_INVALID_CHMPXID; \ + (pComPkt)->head.term_ids.msgid = term_msgid; \ + (pComPkt)->head.peer_dept_msgid = dept_msgid; \ + (pComPkt)->head.peer_term_msgid = term_msgid; \ + (pComPkt)->head.mq_serial_num = serial_num; \ + (pComPkt)->head.hash = CHM_INVALID_HASHVAL; \ + if(!RT_TIMESPEC(&((pComPkt)->head.reqtime))){ \ + WAN_CHMPRN("Could not get timespec(errno=%d), but continue...", errno); \ + INIT_TIMESPEC(&((pComPkt)->head.reqtime)); \ + } \ + } + +//--------------------------------------------------------- +// COM_S2PX: Communication Packets +//--------------------------------------------------------- +typedef struct chmpx_signal_com{ + int signalno; +}CHMPX_ATTR_PACKED PXSIG_COM, *PPXSIG_COM; + +//--------------------------------------------------------- +// COM_F2PX: Communication Packets +//--------------------------------------------------------- +typedef struct chmpx_inotify_com{ + int wfd; + uint32_t mask; +}CHMPX_ATTR_PACKED PXFILE_COM, *PPXFILE_COM; + +//--------------------------------------------------------- +// COM_PX2PX: Communication Packets +//--------------------------------------------------------- +// Following pxcomtype_t is long(64) instead of enum because +// PXCOM_HEAD should be alignment for datas after it. +// +typedef uint64_t pxcomtype_t; // type for packet +typedef uint64_t pxcomres_t; // result code for PXCOM_XXX + +// [NOTICE] +// CHMPX_COM_STATUS_REQ & CHMPX_COM_STATUS_RES is pull-type for status and server information updating. +// CHMPX_COM_STATUS_UPDATE is push to all server for updating by sending server's all status. +// So CHMPX_COM_STATUS_UPDATE is not used now because of taking very careful(no recovering if failed). +// +#define CHMPX_COM_UNKNOWN 0 // Unknown +#define CHMPX_COM_STATUS_REQ 1 // Get server nodes status +#define CHMPX_COM_STATUS_RES 2 // Result(Response) server nodes status +#define CHMPX_COM_CONINIT_REQ 3 // Send initial connect information +#define CHMPX_COM_CONINIT_RES 4 // Result(Response) initial connect information +#define CHMPX_COM_JOIN_RING 5 // [loop in RING] server up & join ring +#define CHMPX_COM_STATUS_UPDATE 6 // [loop in RING] status update +#define CHMPX_COM_STATUS_CONFIRM 7 // [loop in RING] status confirm +#define CHMPX_COM_STATUS_CHANGE 8 // [loop in RING] status change(notice) +#define CHMPX_COM_MERGE_START 9 // [loop in RING] start merging +#define CHMPX_COM_MERGE_ABORT 10 // [loop in RING] abort merging +#define CHMPX_COM_MERGE_COMPLETE 11 // [loop in RING] complete merging +#define CHMPX_COM_SERVER_DOWN 12 // [loop in RING] notify server down +#define CHMPX_COM_REQ_UPDATEDATA 13 // [loop in RING] request update datas +#define CHMPX_COM_RES_UPDATEDATA 14 // Result(Response) request update datas +#define CHMPX_COM_RESULT_UPDATEDATA 15 // Result(Response) request update datas + +#define STRPXCOMTYPE(type) ( CHMPX_COM_UNKNOWN == type ? "CHMPX_COM_UNKNOWN" : \ + CHMPX_COM_STATUS_REQ == type ? "CHMPX_COM_STATUS_REQ" : \ + CHMPX_COM_STATUS_RES == type ? "CHMPX_COM_STATUS_RES" : \ + CHMPX_COM_CONINIT_REQ == type ? "CHMPX_COM_CONINIT_REQ" : \ + CHMPX_COM_CONINIT_RES == type ? "CHMPX_COM_CONINIT_RES" : \ + CHMPX_COM_JOIN_RING == type ? "CHMPX_COM_JOIN_RING" : \ + CHMPX_COM_STATUS_UPDATE == type ? "CHMPX_COM_STATUS_UPDATE" : \ + CHMPX_COM_STATUS_CONFIRM == type ? "CHMPX_COM_STATUS_CONFIRM" : \ + CHMPX_COM_STATUS_CHANGE == type ? "CHMPX_COM_STATUS_CHANGE" : \ + CHMPX_COM_MERGE_START == type ? "CHMPX_COM_MERGE_START" : \ + CHMPX_COM_MERGE_ABORT == type ? "CHMPX_COM_MERGE_ABORT" : \ + CHMPX_COM_MERGE_COMPLETE == type ? "CHMPX_COM_MERGE_COMPLETE" : \ + CHMPX_COM_SERVER_DOWN == type ? "CHMPX_COM_SERVER_DOWN" : \ + CHMPX_COM_REQ_UPDATEDATA == type ? "CHMPX_COM_REQ_UPDATEDATA" : \ + CHMPX_COM_RES_UPDATEDATA == type ? "CHMPX_COM_RES_UPDATEDATA" : \ + CHMPX_COM_RESULT_UPDATEDATA == type ? "CHMPX_COM_RESULT_UPDATEDATA" : \ + "NOT_DEFINED_TYPE" ) + +#define CHMPX_COM_RES_SUCCESS 0 // no error +#define CHMPX_COM_RES_ERROR 1 // error + +//------------------------------- +// status for each chmpxid in CHMPX_COM_REQ_UPDATEDATA +// +#define CHMPX_COM_REQ_UPDATE_INIVAL 0 // initial value +#define CHMPX_COM_REQ_UPDATE_DO 1 // target can run to update data +#define CHMPX_COM_REQ_UPDATE_NOACT 2 // target can not run to update data +#define CHMPX_COM_REQ_UPDATE_SUCCESS 3 // succeed to update data +#define CHMPX_COM_REQ_UPDATE_FAIL 4 // failed to update data + +#define IS_PXCOM_REQ_UPDATE_RESULT(res) ((CHMPX_COM_REQ_UPDATE_NOACT == res) || (CHMPX_COM_REQ_UPDATE_SUCCESS == res) || (CHMPX_COM_REQ_UPDATE_FAIL == res)) +#define STRPXCOM_REQ_UPDATEDATA(res) ( CHMPX_COM_REQ_UPDATE_INIVAL == res ? "INIVAL" : \ + CHMPX_COM_REQ_UPDATE_DO == res ? "DO" : \ + CHMPX_COM_REQ_UPDATE_NOACT == res ? "NOACT" : \ + CHMPX_COM_REQ_UPDATE_SUCCESS == res ? "SUCCESS" : \ + CHMPX_COM_REQ_UPDATE_FAIL == res ? "FAIL" : \ + "UNKNOWN" ) + +//------------------------------- +// Structures +// +typedef struct chmpx_com_head{ + pxcomtype_t type; // Command type + pxcomres_t result; // Command result + size_t length; // Length for this packet +}CHMPX_ATTR_PACKED PXCOM_HEAD, *PPXCOM_HEAD; + +typedef struct chmpx_com_status_req{ + PXCOM_HEAD head; +}CHMPX_ATTR_PACKED PXCOM_STATUS_REQ, *PPXCOM_STATUS_REQ; + +typedef struct chmpx_com_status_res{ + PXCOM_HEAD head; + long count; + off_t pchmpxsvr_offset; +}CHMPX_ATTR_PACKED PXCOM_STATUS_RES, *PPXCOM_STATUS_RES; + +typedef struct chmpx_com_coninit_req{ + PXCOM_HEAD head; + chmpxid_t chmpxid; + short ctlport; +}CHMPX_ATTR_PACKED PXCOM_CONINIT_REQ, *PPXCOM_CONINIT_REQ; + +typedef struct chmpx_com_coninit_res{ + PXCOM_HEAD head; +}CHMPX_ATTR_PACKED PXCOM_CONINIT_RES, *PPXCOM_CONINIT_RES; + +typedef struct chmpx_com_join_ring{ + PXCOM_HEAD head; + CHMPXSVR server; +}CHMPX_ATTR_PACKED PXCOM_JOIN_RING, *PPXCOM_JOIN_RING; + +typedef struct chmpx_com_status_update{ // same as STATUS_RES + PXCOM_HEAD head; + long count; + off_t pchmpxsvr_offset; +}CHMPX_ATTR_PACKED PXCOM_STATUS_UPDATE, *PPXCOM_STATUS_UPDATE; + +typedef struct chmpx_com_status_confirm{ // same as STATUS_RES + PXCOM_HEAD head; + long count; + off_t pchmpxsvr_offset; +}CHMPX_ATTR_PACKED PXCOM_STATUS_CONFIRM, *PPXCOM_STATUS_CONFIRM; + +typedef struct chmpx_com_status_change{ // same as JOIN_RING + PXCOM_HEAD head; + CHMPXSVR server; +}CHMPX_ATTR_PACKED PXCOM_STATUS_CHANGE, *PPXCOM_STATUS_CHANGE; + +typedef struct chmpx_com_merge_start{ // same as STATUS_REQ + PXCOM_HEAD head; +}CHMPX_ATTR_PACKED PXCOM_MERGE_START, *PPXCOM_MERGE_START; + +typedef struct chmpx_com_merge_abort{ // same as STATUS_REQ + PXCOM_HEAD head; +}CHMPX_ATTR_PACKED PXCOM_MERGE_ABORT, *PPXCOM_MERGE_ABORT; + +typedef struct chmpx_com_merge_complete{ // same as STATUS_REQ + PXCOM_HEAD head; +}CHMPX_ATTR_PACKED PXCOM_MERGE_COMPLETE, *PPXCOM_MERGE_COMPLETE; + +typedef struct chmpx_com_server_down{ + PXCOM_HEAD head; + chmpxid_t chmpxid; +}CHMPX_ATTR_PACKED PXCOM_SERVER_DOWN, *PPXCOM_SERVER_DOWN; + +typedef struct chmpx_com_req_idmap{ + chmpxid_t chmpxid; + reqidmapflag_t req_status; +}CHMPX_ATTR_PACKED PXCOM_REQ_IDMAP, *PPXCOM_REQ_IDMAP; + +typedef struct chmpx_com_req_updatedata{ + PXCOM_HEAD head; + PXCOMMON_MERGE_PARAM merge_param; // common parameter for update data + long count; // extended data count + off_t plist_offset; // offset for PPXCOM_REQ_IDMAP +}CHMPX_ATTR_PACKED PXCOM_REQ_UPDATEDATA, *PPXCOM_REQ_UPDATEDATA; + +typedef struct chmpx_com_res_updatedata{ + PXCOM_HEAD head; + struct timespec ts; // updated time is also in this time at least + size_t length; + off_t pdata_offset; // offset for binary data +}CHMPX_ATTR_PACKED PXCOM_RES_UPDATEDATA, *PPXCOM_RES_UPDATEDATA; + +typedef struct chmpx_com_result_updatedata{ + PXCOM_HEAD head; + chmpxid_t chmpxid; + reqidmapflag_t result; // result +}CHMPX_ATTR_PACKED PXCOM_RESULT_UPDATEDATA, *PPXCOM_RESULT_UPDATEDATA; + +typedef union chmpx_com_all{ + PXCOM_HEAD val_head; + PXCOM_STATUS_REQ val_status_req; + PXCOM_STATUS_RES val_status_res; + PXCOM_CONINIT_REQ val_coninit_req; + PXCOM_CONINIT_RES val_coninit_res; + PXCOM_JOIN_RING val_join_ring; + PXCOM_STATUS_UPDATE val_status_update; + PXCOM_STATUS_CONFIRM val_status_confirm; + PXCOM_STATUS_CHANGE val_status_change; + PXCOM_MERGE_START val_merge_start; + PXCOM_MERGE_ABORT val_merge_abort; + PXCOM_MERGE_COMPLETE val_merge_complete; + PXCOM_SERVER_DOWN val_server_down; + PXCOM_REQ_UPDATEDATA val_req_updatedata; + PXCOM_RES_UPDATEDATA val_res_updatedata; + PXCOM_RESULT_UPDATEDATA val_result_updatedata; +}CHMPX_ATTR_PACKED PXCOM_ALL, *PPXCOM_ALL; + +//------------ +// Converter +//------------ +#define HTON_PPXCOM_HEAD(pdata) \ + { \ + (pdata)->type = htobe64((pdata)->type); \ + (pdata)->result = htobe64((pdata)->result); \ + (pdata)->length = htobe64((pdata)->length); \ + } + +#define NTOH_PPXCOM_HEAD(pdata) \ + { \ + (pdata)->type = be64toh((pdata)->type); \ + (pdata)->result = be64toh((pdata)->result); \ + (pdata)->length = be64toh((pdata)->length); \ + } + +#define HTON_PPXCOM_STATUS_REQ(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + } + +#define NTOH_PPXCOM_STATUS_REQ(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + } + +#define HTON_PPXCOM_STATUS_RES(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + (pdata)->count = htobe64((pdata)->count); \ + (pdata)->pchmpxsvr_offset = htobe64((pdata)->pchmpxsvr_offset); \ + } + +#define NTOH_PPXCOM_STATUS_RES(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + (pdata)->count = be64toh((pdata)->count); \ + (pdata)->pchmpxsvr_offset = be64toh((pdata)->pchmpxsvr_offset); \ + } + +#define HTON_PPXCOM_CONINIT_REQ(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + (pdata)->chmpxid = htobe64((pdata)->chmpxid); \ + (pdata)->ctlport = htobe16((pdata)->ctlport); \ + } + +#define NTOH_PPXCOM_CONINIT_REQ(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + (pdata)->chmpxid = be64toh((pdata)->chmpxid); \ + (pdata)->ctlport = be16toh((pdata)->ctlport); \ + } + +#define HTON_PPXCOM_CONINIT_RES(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + } + +#define NTOH_PPXCOM_CONINIT_RES(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + } + +#define HTON_PPXCOM_JOIN_RING(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + HTON_PCHMPXSVR(&((pdata)->server)); \ + } + +#define NTOH_PPXCOM_JOIN_RING(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + NTOH_PCHMPXSVR(&((pdata)->server)); \ + } + +#define HTON_PPXCOM_STATUS_UPDATE(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + (pdata)->count = htobe64((pdata)->count); \ + (pdata)->pchmpxsvr_offset = htobe64((pdata)->pchmpxsvr_offset); \ + } + +#define NTOH_PPXCOM_STATUS_UPDATE(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + (pdata)->count = be64toh((pdata)->count); \ + (pdata)->pchmpxsvr_offset = be64toh((pdata)->pchmpxsvr_offset); \ + } + +#define HTON_PPXCOM_STATUS_CONFIRM(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + (pdata)->count = htobe64((pdata)->count); \ + (pdata)->pchmpxsvr_offset = htobe64((pdata)->pchmpxsvr_offset); \ + } + +#define NTOH_PPXCOM_STATUS_CONFIRM(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + (pdata)->count = be64toh((pdata)->count); \ + (pdata)->pchmpxsvr_offset = be64toh((pdata)->pchmpxsvr_offset); \ + } + +#define HTON_PPXCOM_STATUS_CHANGE(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + HTON_PCHMPXSVR(&((pdata)->server)); \ + } + +#define NTOH_PPXCOM_STATUS_CHANGE(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + NTOH_PCHMPXSVR(&((pdata)->server)); \ + } + +#define HTON_PPXCOM_MERGE_START(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + } + +#define NTOH_PPXCOM_MERGE_START(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + } + +#define HTON_PPXCOM_MERGE_ABORT(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + } + +#define NTOH_PPXCOM_MERGE_ABORT(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + } + +#define HTON_PPXCOM_MERGE_COMPLETE(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + } + +#define NTOH_PPXCOM_MERGE_COMPLETE(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + } + +#define HTON_PPXCOM_SERVER_DOWN(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + (pdata)->chmpxid= htobe64((pdata)->chmpxid); \ + } + +#define NTOH_PPXCOM_SERVER_DOWN(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + (pdata)->chmpxid= be64toh((pdata)->chmpxid); \ + } + +#define HTON_PPXCOM_REQ_IDMAP(pdata) \ + { \ + (pdata)->chmpxid = htobe64((pdata)->chmpxid); \ + (pdata)->req_status = htobe64((pdata)->req_status); \ + } + +#define NTOH_PPXCOM_REQ_IDMAP(pdata) \ + { \ + (pdata)->chmpxid = be64toh((pdata)->chmpxid); \ + (pdata)->req_status = be64toh((pdata)->req_status); \ + } + +#define HTON_PXCOMMON_MERGE_PARAM(pdata) \ + { \ + (pdata)->chmpxid = htobe64((pdata)->chmpxid); \ + HTON_TIMESPEC(&((pdata)->startts)); \ + (pdata)->max_pending_hash = htobe64((pdata)->max_pending_hash); \ + (pdata)->pending_hash = htobe64((pdata)->pending_hash); \ + (pdata)->max_base_hash = htobe64((pdata)->max_base_hash); \ + (pdata)->base_hash = htobe64((pdata)->base_hash); \ + (pdata)->replica_count = htobe64((pdata)->replica_count); \ + } + +#define NTOH_PXCOMMON_MERGE_PARAM(pdata) \ + { \ + (pdata)->chmpxid = be64toh((pdata)->chmpxid); \ + NTOH_TIMESPEC(&((pdata)->startts)); \ + (pdata)->max_pending_hash = be64toh((pdata)->max_pending_hash); \ + (pdata)->pending_hash = be64toh((pdata)->pending_hash); \ + (pdata)->max_base_hash = be64toh((pdata)->max_base_hash); \ + (pdata)->base_hash = be64toh((pdata)->base_hash); \ + (pdata)->replica_count = be64toh((pdata)->replica_count); \ + } + +#define HTON_PPXCOM_REQ_UPDATEDATA(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + HTON_PXCOMMON_MERGE_PARAM(&((pdata)->merge_param)); \ + (pdata)->count = htobe64((pdata)->count); \ + (pdata)->plist_offset = htobe64((pdata)->plist_offset); \ + } + +#define NTOH_PPXCOM_REQ_UPDATEDATA(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + NTOH_PXCOMMON_MERGE_PARAM(&((pdata)->merge_param)); \ + (pdata)->count = be64toh((pdata)->count); \ + (pdata)->plist_offset = be64toh((pdata)->plist_offset); \ + } + +#define HTON_PPXCOM_RES_UPDATEDATA(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + HTON_TIMESPEC(&((pdata)->ts)); \ + (pdata)->length = htobe64((pdata)->length); \ + (pdata)->pdata_offset = htobe64((pdata)->pdata_offset); \ + } + +#define NTOH_PPXCOM_RES_UPDATEDATA(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + NTOH_TIMESPEC(&((pdata)->ts)); \ + (pdata)->length = be64toh((pdata)->length); \ + (pdata)->pdata_offset = be64toh((pdata)->pdata_offset); \ + } + +#define HTON_PPXCOM_RESULT_UPDATEDATA(pdata) \ + { \ + HTON_PPXCOM_HEAD(&((pdata)->head)); \ + (pdata)->chmpxid = htobe64((pdata)->chmpxid); \ + (pdata)->result = htobe64((pdata)->result); \ + } + +#define NTOH_PPXCOM_RESULT_UPDATEDATA(pdata) \ + { \ + NTOH_PPXCOM_HEAD(&((pdata)->head)); \ + (pdata)->chmpxid = be64toh((pdata)->chmpxid); \ + (pdata)->result = be64toh((pdata)->result); \ + } + +//--------------------------------------------------------- +// Communication Packets +//--------------------------------------------------------- +// Following pxcomtype_t is long(64) instead of enum because +// PXCOM_HEAD should be alignment for datas after it. +// +typedef uint64_t comtype_t; // type for packet +typedef uint64_t c2ctype_t; // COM_C2C type for duplication +typedef uint64_t comver_t; // Protocol version + +// Version +#define COM_VERSION_1 1L // First version 1 + +// Type for packet +#define COM_UNKNOWN 0 // Unknown +#define COM_C2C 1 // Client Process to Client Process +#define COM_PX2PX 2 // CHMPX to CHMPX(Internal communication) +#define COM_S2PX 3 // Signal to CHMPX +#define COM_F2PX 4 // Inotify to CHMPX +#define COM_C2PX 5 // Client Process to CHMPX(Notification from Client process) +#define COM_PX2C 6 // CHMPX to Client Process(Notification from CHMPX) + +#define STRCOMTYPE(type) ( COM_UNKNOWN == type ? "COM_UNKNOWN" : \ + COM_C2C == type ? "COM_C2C" : \ + COM_PX2PX == type ? "COM_PX2PX" : \ + COM_S2PX == type ? "COM_S2PX" : \ + COM_F2PX == type ? "COM_F2PX" : \ + COM_C2PX == type ? "COM_C2PX" : \ + COM_PX2C == type ? "COM_PX2C" : "NOT_DEFINED_TYPE" ) + +// COM_C2C Type for duplication +#define COM_C2C_IGNORE 0x0 // ignore(not COM_C2C packet) +#define COM_C2C_NORMAL 0x1 // send to single routing +#define COM_C2C_ROUTING 0x2 // send to duplicated routing +#define COM_C2C_BROADCAST 0x3 // send broadcast +#define COM_C2C_RBROADCAST 0x4 // send broadcast to duplicate in routing +#define COM_C2C_MODE_MASK 0x7 // mask for C2C TYPE +#define COM_C2C_NOT_SELF 0x10 // flag for excepting self + +#define GET_C2CTYPE(type) (COM_C2C_MODE_MASK & type) +#define SET_C2CTYPE_SELF(type) (type = GET_C2CTYPE(type)) +#define SET_C2CTYPE_NOT_SELF(type) (type = GET_C2CTYPE(type) | COM_C2C_NOT_SELF) +#define IS_C2CTYPE_IGNORE(type) (COM_C2C_IGNORE == GET_C2CTYPE(type)) +#define IS_C2CTYPE_NORMAL(type) (COM_C2C_NORMAL == GET_C2CTYPE(type)) +#define IS_C2CTYPE_ROUTING(type) (COM_C2C_ROUTING == GET_C2CTYPE(type)) +#define IS_C2CTYPE_BROADCAST(type) (COM_C2C_BROADCAST == GET_C2CTYPE(type)) +#define IS_C2CTYPE_RBROADCAST(type) (COM_C2C_RBROADCAST == GET_C2CTYPE(type)) +#define IS_C2CTYPE_SELF(type) (COM_C2C_NOT_SELF != (COM_C2C_NOT_SELF & type)) +#define IS_C2CTYPE_NOT_SELF(type) (COM_C2C_NOT_SELF == (COM_C2C_NOT_SELF & type)) +#define STRCOMC2CTYPE(type) ( IS_C2CTYPE_IGNORE(type) ? "COM_C2C_IGNORE" : \ + IS_C2CTYPE_NORMAL(type) ? ((IS_C2CTYPE_SELF(type) ? "COM_C2C_NORMAL" : "COM_C2C_NORMAL_NOT_SELF")) : \ + IS_C2CTYPE_ROUTING(type) ? ((IS_C2CTYPE_SELF(type) ? "COM_C2C_ROUTING" : "COM_C2C_ROUTING_NOT_SELF")) : \ + IS_C2CTYPE_BROADCAST(type) ? ((IS_C2CTYPE_SELF(type) ? "COM_C2C_BROADCAST" : "COM_C2C_BROADCAST_NOT_SELF")) : \ + IS_C2CTYPE_RBROADCAST(type) ? ((IS_C2CTYPE_SELF(type) ? "COM_C2C_RBROADCAST": "COM_C2C_RBROADCAST_NOT_SELF")) : "NOT_DEFINED_TYPE" ) + +typedef struct packed_ids{ + chmpxid_t chmpxid; + msgid_t msgid; +}CHMPX_ATTR_PACKED PCKID, *PPCKID; + +typedef struct com_head{ + comver_t version; // version + comtype_t type; + c2ctype_t c2ctype; + PCKID dept_ids; // "From" of End to end(departure) + PCKID term_ids; // "To" of End to end(terminus) + msgid_t peer_dept_msgid; // "From" of Peer to Peer + msgid_t peer_term_msgid; // "To" of Peer to Peer + serial_t mq_serial_num; // serial number on MQ + chmhash_t hash; + struct timespec reqtime; +}CHMPX_ATTR_PACKED COMHEAD, *PCOMHEAD; + +typedef struct com_packet{ + COMHEAD head; + size_t length; // this structure length and detail data length after this structure + off_t offset; // offset for detail data after this structure from this structure top +}CHMPX_ATTR_PACKED COMPKT, *PCOMPKT; + +//------------ +// Macros +//------------ +#define COPY_PKID(pdest, psrc) \ + { \ + (pdest)->chmpxid = (psrc)->chmpxid; \ + (pdest)->msgid = (psrc)->msgid; \ + } + +#define COPY_COMHEAD(pdest, psrc) \ + { \ + COPY_PKID(&((pdest)->dept_ids), &((psrc)->dept_ids)); \ + COPY_PKID(&((pdest)->term_ids), &((psrc)->term_ids)); \ + (pdest)->version = (psrc)->version; \ + (pdest)->type = (psrc)->type; \ + (pdest)->c2ctype = (psrc)->c2ctype; \ + (pdest)->peer_dept_msgid = (psrc)->peer_dept_msgid; \ + (pdest)->peer_term_msgid = (psrc)->peer_term_msgid; \ + (pdest)->mq_serial_num = (psrc)->mq_serial_num; \ + (pdest)->hash = (psrc)->hash; \ + COPY_TIMESPEC(&((pdest)->reqtime), &((psrc)->reqtime)); \ + } + +#define COPY_COMPKT(pdest, psrc) \ + { \ + COPY_COMHEAD(&((pdest)->head), &((psrc)->head)); \ + (pdest)->length = (psrc)->length; \ + (pdest)->offset = (psrc)->offset; \ + } + +//------------ +// Converter +//------------ +#define HTON_PPCKID(pdata) \ + { \ + (pdata)->chmpxid= htobe64((pdata)->chmpxid); \ + (pdata)->msgid = htobe64((pdata)->msgid); \ + } + +#define NTOH_PPCKID(pdata) \ + { \ + (pdata)->chmpxid= be64toh((pdata)->chmpxid); \ + (pdata)->msgid = be64toh((pdata)->msgid); \ + } + +#define HTON_TIMESPEC(ptr) \ + { \ + (ptr)->tv_sec = htobe64((ptr)->tv_sec); \ + (ptr)->tv_nsec = htobe64((ptr)->tv_nsec); \ + } \ + +#define NTOH_TIMESPEC(ptr) \ + { \ + (ptr)->tv_sec = be64toh((ptr)->tv_sec); \ + (ptr)->tv_nsec = be64toh((ptr)->tv_nsec); \ + } \ + +#define HTON_PCOMHEAD(pdata) \ + { \ + HTON_PPCKID(&((pdata)->dept_ids)); \ + HTON_PPCKID(&((pdata)->term_ids)); \ + (pdata)->version = htobe64((pdata)->version); \ + (pdata)->type = htobe64((pdata)->type); \ + (pdata)->c2ctype = htobe64((pdata)->c2ctype); \ + (pdata)->peer_dept_msgid = htobe64((pdata)->peer_dept_msgid); \ + (pdata)->peer_term_msgid = htobe64((pdata)->peer_term_msgid); \ + (pdata)->mq_serial_num = htobe64((pdata)->mq_serial_num); \ + (pdata)->hash = htobe64((pdata)->hash); \ + HTON_TIMESPEC(&((pdata)->reqtime)); \ + } + +#define NTOH_PCOMHEAD(pdata) \ + { \ + NTOH_PPCKID(&((pdata)->dept_ids)); \ + NTOH_PPCKID(&((pdata)->term_ids)); \ + (pdata)->version = be64toh((pdata)->version); \ + (pdata)->type = be64toh((pdata)->type); \ + (pdata)->c2ctype = be64toh((pdata)->c2ctype); \ + (pdata)->peer_dept_msgid = be64toh((pdata)->peer_dept_msgid); \ + (pdata)->peer_term_msgid = be64toh((pdata)->peer_term_msgid); \ + (pdata)->mq_serial_num = be64toh((pdata)->mq_serial_num); \ + (pdata)->hash = be64toh((pdata)->hash); \ + NTOH_TIMESPEC(&((pdata)->reqtime)); \ + } + +#define HTON_PCOMPKT(pdata) \ + { \ + HTON_PCOMHEAD(&((pdata)->head)); \ + (pdata)->length = htobe64((pdata)->length); \ + (pdata)->offset = htobe64((pdata)->offset); \ + } + +#define NTOH_PCOMPKT(pdata) \ + { \ + NTOH_PCOMHEAD(&((pdata)->head)); \ + (pdata)->length = be64toh((pdata)->length); \ + (pdata)->offset = be64toh((pdata)->offset); \ + } + +//------------ +// Utility Macros +//------------ +#define CVT_COMPTR_STATUS_REQ(pComAll) &((pComAll)->val_status_req) +#define CVT_COMPTR_STATUS_RES(pComAll) &((pComAll)->val_status_res) +#define CVT_COMPTR_CONINIT_REQ(pComAll) &((pComAll)->val_coninit_req) +#define CVT_COMPTR_CONINIT_RES(pComAll) &((pComAll)->val_coninit_res) +#define CVT_COMPTR_JOIN_RING(pComAll) &((pComAll)->val_join_ring) +#define CVT_COMPTR_STATUS_UPDATE(pComAll) &((pComAll)->val_status_update) +#define CVT_COMPTR_STATUS_CONFIRM(pComAll) &((pComAll)->val_status_confirm) +#define CVT_COMPTR_STATUS_CHANGE(pComAll) &((pComAll)->val_status_change) +#define CVT_COMPTR_MERGE_START(pComAll) &((pComAll)->val_merge_start) +#define CVT_COMPTR_MERGE_ABORT(pComAll) &((pComAll)->val_merge_abort) +#define CVT_COMPTR_MERGE_COMPLETE(pComAll) &((pComAll)->val_merge_complete) +#define CVT_COMPTR_SERVER_DOWN(pComAll) &((pComAll)->val_server_down) +#define CVT_COMPTR_REQ_UPDATEDATA(pComAll) &((pComAll)->val_req_updatedata) +#define CVT_COMPTR_RES_UPDATEDATA(pComAll) &((pComAll)->val_res_updatedata) +#define CVT_COMPTR_RESULT_UPDATEDATA(pComAll) &((pComAll)->val_result_updatedata) + +#define SIZEOF_CHMPX_COM(type) ( CHMPX_COM_STATUS_REQ == type ? sizeof(PXCOM_STATUS_REQ) : \ + CHMPX_COM_STATUS_RES == type ? sizeof(PXCOM_STATUS_RES) : \ + CHMPX_COM_CONINIT_REQ == type ? sizeof(PXCOM_CONINIT_REQ) : \ + CHMPX_COM_CONINIT_RES == type ? sizeof(PXCOM_CONINIT_RES) : \ + CHMPX_COM_JOIN_RING == type ? sizeof(PXCOM_JOIN_RING) : \ + CHMPX_COM_STATUS_UPDATE == type ? sizeof(PXCOM_STATUS_UPDATE) : \ + CHMPX_COM_STATUS_CONFIRM == type ? sizeof(PXCOM_STATUS_CONFIRM) : \ + CHMPX_COM_STATUS_CHANGE == type ? sizeof(PXCOM_STATUS_CHANGE) : \ + CHMPX_COM_MERGE_START == type ? sizeof(PXCOM_MERGE_START) : \ + CHMPX_COM_MERGE_ABORT == type ? sizeof(PXCOM_MERGE_ABORT) : \ + CHMPX_COM_MERGE_COMPLETE == type ? sizeof(PXCOM_MERGE_COMPLETE) : \ + CHMPX_COM_SERVER_DOWN == type ? sizeof(PXCOM_SERVER_DOWN) : \ + CHMPX_COM_REQ_UPDATEDATA == type ? sizeof(PXCOM_REQ_UPDATEDATA) : \ + CHMPX_COM_RES_UPDATEDATA == type ? sizeof(PXCOM_RES_UPDATEDATA) : \ + CHMPX_COM_RESULT_UPDATEDATA == type ? sizeof(PXCOM_RESULT_UPDATEDATA) : \ + 0L) + +#define CVT_COM_ALL_PTR_PXCOMPKT(pComPkt) ( CHM_OFFSET((pComPkt), sizeof(COMPKT), PPXCOM_ALL) ) + +#define SET_PXCOMPKT(pComPkt, comtype, dept_chmpxid, term_chmpxid, is_timespec, extlength) \ + { \ + (pComPkt)->length = sizeof(COMPKT) + SIZEOF_CHMPX_COM(comtype) + extlength; \ + (pComPkt)->offset = sizeof(COMPKT); \ + (pComPkt)->head.version = COM_VERSION_1; \ + (pComPkt)->head.type = COM_PX2PX; \ + (pComPkt)->head.c2ctype = COM_C2C_IGNORE; \ + (pComPkt)->head.dept_ids.chmpxid= dept_chmpxid; \ + (pComPkt)->head.dept_ids.msgid = CHM_INVALID_MSGID; \ + (pComPkt)->head.term_ids.chmpxid= term_chmpxid; \ + (pComPkt)->head.term_ids.msgid = CHM_INVALID_MSGID; \ + (pComPkt)->head.peer_dept_msgid = CHM_INVALID_MSGID; \ + (pComPkt)->head.peer_term_msgid = CHM_INVALID_MSGID; \ + (pComPkt)->head.mq_serial_num = MIN_MQ_SERIAL_NUMBER; \ + (pComPkt)->head.hash = CHM_INVALID_HASHVAL; \ + if(is_timespec){ \ + if(!RT_TIMESPEC(&((pComPkt)->head.reqtime))){ \ + WAN_CHMPRN("Could not get timespec(errno=%d), but continue...", errno); \ + INIT_TIMESPEC(&((pComPkt)->head.reqtime)); \ + } \ + }else{ \ + INIT_TIMESPEC(&((pComPkt)->head.reqtime)); \ + } \ + } + +//------------ +// Debug Macros +//------------ +#define DUMPCOM_COMPKT_TYPE(action, pComPkt) \ + if(chm_debug_mode >= CHMDBG_DUMP){ \ + if(COM_PX2PX == (pComPkt)->head.type){ \ + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT((pComPkt)); \ + MSG_CHMPRN("%s COMPKT type(%s) : PXCOM_ALL type(%s).", action ? action : "", STRCOMTYPE((pComPkt)->head.type), STRPXCOMTYPE(pComAll->val_head.type)); \ + }else if(COM_C2C == (pComPkt)->head.type){ \ + MSG_CHMPRN("%s COMPKT type(%s) : COM_C2C type(%s).", action ? action : "", STRCOMTYPE((pComPkt)->head.type), STRCOMC2CTYPE((pComPkt)->head.c2ctype)); \ + }else{ \ + MSG_CHMPRN("%s COMPKT type(%s).", action ? action : "", STRCOMTYPE((pComPkt)->head.type)); \ + } \ + } + +#define DUMPCOM_COMPKT(headmsg, pComPkt) \ + if(chm_debug_mode >= CHMDBG_DUMP){ \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), "DUMP COMPKT(%s) = {\n", headmsg ? headmsg : "notag"); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " head = {\n"); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " version = 0x%016" PRIx64 "\n", (pComPkt)->head.version); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " type = 0x%016" PRIx64 "\n", (pComPkt)->head.type); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " c2ctype = 0x%016" PRIx64 "\n", (pComPkt)->head.c2ctype); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " dept_ids = {\n"); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " chmpxid = 0x%016" PRIx64 "\n", (pComPkt)->head.dept_ids.chmpxid); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " msgid = 0x%016" PRIx64 "\n", (pComPkt)->head.dept_ids.msgid); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " }\n"); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " term_ids = {\n"); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " chmpxid = 0x%016" PRIx64 "\n", (pComPkt)->head.term_ids.chmpxid); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " msgid = 0x%016" PRIx64 "\n", (pComPkt)->head.term_ids.msgid); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " }\n"); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " peer_dept_msgid = 0x%016" PRIx64 "\n", (pComPkt)->head.peer_dept_msgid); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " peer_term_msgid = 0x%016" PRIx64 "\n", (pComPkt)->head.peer_term_msgid); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " mq_serial_num = 0x%016" PRIx64 "\n", (pComPkt)->head.mq_serial_num); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " hash = 0x%016" PRIx64 "\n", (pComPkt)->head.hash); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " reqtime = {\n"); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " tv_sec = %jd\n", static_cast((pComPkt)->head.reqtime.tv_sec)); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " tv_nsec = %ld\n", (pComPkt)->head.reqtime.tv_nsec); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " }\n"); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " }\n"); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " length = %zu\n", (pComPkt)->length); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " offset = %jd\n", static_cast((pComPkt)->offset)); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), "}\n"); \ + } + +#define DUMPCOM_PXCLT(headmsg, pCltAll) \ + if(chm_debug_mode >= CHMDBG_DUMP){ \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), "DUMP PXCTL(%s) = {\n", headmsg ? headmsg : "notag"); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " val_head = {\n"); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " type = 0x%016" PRIx64 "\n", (pCltAll)->val_head.type); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " length = 0x%016" PRIx64 "\n", (pCltAll)->val_head.length); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), " }\n"); \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), "}\n"); \ + } + +#endif // CHMCOMSTRUCTURE_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmconf.cc b/lib/chmconf.cc new file mode 100644 index 0000000..8f58d99 --- /dev/null +++ b/lib/chmconf.cc @@ -0,0 +1,3174 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "chmcommon.h" +#include "chmconf.h" +#include "chmconfutil.h" +#include "chmcntrl.h" +#include "chmstructure.h" +#include "chmutil.h" +#include "chmnetdb.h" +#include "chmregex.h" +#include "chmeventsock.h" +#include "chmdbg.h" + +using namespace std; + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +// Keywards +#define CFG_GLOBAL_SEC_STR "GLOBAL" +#define CFG_SVRNODE_SEC_STR "SVRNODE" +#define CFG_SLVNODE_SEC_STR "SLVNODE" + +#define INICFG_COMMENT_CHAR '#' +#define INICFG_SEC_START_CHAR '[' +#define INICFG_SEC_END_CHAR ']' +#define INICFG_GLOBAL_SEC_STR "[" CFG_GLOBAL_SEC_STR "]" +#define INICFG_SVRNODE_SEC_STR "[" CFG_SVRNODE_SEC_STR "]" +#define INICFG_SLVNODE_SEC_STR "[" CFG_SLVNODE_SEC_STR "]" +#define INICFG_GROUP_STR "GROUP" +#define INICFG_VERSION_STR "FILEVERSION" +#define INICFG_MODE_STR "MODE" +#define INICFG_DELIVERMODE_STR "DELIVERMODE" +#define INICFG_REPLICA_STR "REPLICA" +#define INICFG_MAXCHMPX_STR "MAXCHMPX" +#define INICFG_MAXMQSERVER_STR "MAXMQSERVER" +#define INICFG_MAXMQCLIENT_STR "MAXMQCLIENT" +#define INICFG_MQPERATTACH_STR "MQPERATTACH" +#define INICFG_MAXQPERSERVERMQ_STR "MAXQPERSERVERMQ" +#define INICFG_MAXQPERCLIENTMQ_STR "MAXQPERCLIENTMQ" +#define INICFG_MAXMQPERCLIENT_STR "MAXMQPERCLIENT" +#define INICFG_MAXHISTLOG_STR "MAXHISTLOG" +#define INICFG_DATE_STR "DATE" +#define INICFG_PORT_STR "PORT" +#define INICFG_CTLPORT_STR "CTLPORT" +#define INICFG_SELFCTLPORT_STR "SELFCTLPORT" +#define INICFG_RWTIMEOUT_STR "RWTIMEOUT" +#define INICFG_RETRYCNT_STR "RETRYCNT" +#define INICFG_MQRWTIMEOUT_STR "MQRWTIMEOUT" +#define INICFG_MQRETRYCNT_STR "MQRETRYCNT" +#define INICFG_MQACK_STR "MQACK" +#define INICFG_CONTIMEOUT_STR "CONTIMEOUT" +#define INICFG_AUTOMERGE_STR "AUTOMERGE" +#define INICFG_DOMERGE_STR "DOMERGE" +#define INICFG_MERGETIMEOUT_STR "MERGETIMEOUT" +#define INICFG_SOCKTHREADCNT_STR "SOCKTHREADCNT" +#define INICFG_MQTHREADCNT_STR "MQTHREADCNT" +#define INICFG_MAXSOCKPOOL_STR "MAXSOCKPOOL" +#define INICFG_SOCKPOOLTIMEOUT_STR "SOCKPOOLTIMEOUT" +#define INICFG_SSL_STR "SSL" +#define INICFG_SSL_VERIFY_PEER_STR "SSL_VERIFY_PEER" +#define INICFG_CAPATH_STR "CAPATH" +#define INICFG_SERVER_CERT_STR "SERVER_CERT" +#define INICFG_SERVER_PRIKEY_STR "SERVER_PRIKEY" +#define INICFG_SLAVE_CERT_STR "SLAVE_CERT" +#define INICFG_SLAVE_PRIKEY_STR "SLAVE_PRIKEY" +#define INICFG_K2HFULLMAP_STR "K2HFULLMAP" +#define INICFG_K2HMASKBIT_STR "K2HMASKBIT" +#define INICFG_K2HCMASKBIT_STR "K2HCMASKBIT" +#define INICFG_K2HMAXELE_STR "K2HMAXELE" +#define INICFG_NAME_STR "NAME" + +#define INICFG_INCLUDE_STR "INCLUDE" +#define INICFG_KV_SEP "=" + +#define INICFG_BOOL_ON "ON" +#define INICFG_BOOL_YES "YES" +#define INICFG_BOOL_OFF "OFF" +#define INICFG_BOOL_NO "NO" +#define INICFG_STRING_NULL "NULL" +#define INICFG_MODE_SERVER_STR "server" +#define INICFG_MODE_SLAVE_STR "slave" +#define INICFG_DELIVERMODE_RANDOM_STR "random" +#define INICFG_DELIVERMODE_HASH_STR "hash" + +// for analizing .ini file +#define INICFG_INSEC_NOT 0 +#define INICFG_INSEC_GLOBAL 1 +#define INICFG_INSEC_SVRNODE 2 +#define INICFG_INSEC_SLVNODE 3 +#define INICFG_INSEC_UNKNOWN 1000 + +//--------------------------------------------------------- +// CHMConf Class Factory +//--------------------------------------------------------- +CHMConf* CHMConf::GetCHMConf(int eventqfd, ChmCntrl* pcntrl, const char* config, short ctlport, bool is_check_env, string* normalize_config) +{ + string tmpconf(""); + CHMCONFTYPE conftype = check_chmconf_type_ex(config, (is_check_env ? CHM_CONFFILE_ENV_NAME : NULL), (is_check_env ? CHM_JSONCONF_ENV_NAME : NULL), &tmpconf); + + CHMConf* result = NULL; + switch(conftype){ + case CHMCONF_TYPE_INI_FILE: + result = new CHMIniConf(eventqfd, pcntrl, tmpconf.c_str(), ctlport); + break; + case CHMCONF_TYPE_YAML_FILE: + result = new CHMYamlConf(eventqfd, pcntrl, tmpconf.c_str(), ctlport); + break; + case CHMCONF_TYPE_JSON_FILE: + result = new CHMJsonConf(eventqfd, pcntrl, tmpconf.c_str(), ctlport); + break; + case CHMCONF_TYPE_JSON_STRING: + result = new CHMJsonStringConf(eventqfd, pcntrl, tmpconf.c_str(), ctlport); + break; + case CHMCONF_TYPE_UNKNOWN: + case CHMCONF_TYPE_NULL: + default: + ERR_CHMPRN("configuration \"%s\" is unknown type.", CHMEMPTYSTR(config) ? "empty" : config); + break; + } + if(result && normalize_config){ + (*normalize_config) = tmpconf; + } + return result; +} + +//--------------------------------------------------------- +// CHMConf Class +//--------------------------------------------------------- +CHMConf::CHMConf(int eventqfd, ChmCntrl* pcntrl, const char* file, short ctlport, const char* pJson) : ChmEventBase(eventqfd, pcntrl), ctlport_param(ctlport), type(CONF_UNKNOWN), inotifyfd(CHM_INVALID_HANDLE), watchfd(CHM_INVALID_HANDLE), pchmcfginfo(NULL) +{ + // [NOTE] Either file or json string. + // + if(!CHMEMPTYSTR(file)){ + cfgfile = file; + }else if(!CHMEMPTYSTR(pJson)){ + strjson = pJson; + } +} + +CHMConf::~CHMConf() +{ + Clean(); +} + +bool CHMConf::Clean(void) +{ + if(IsWatching()){ + MSG_CHMPRN("Should call RemoveNotify function before calling this destructor."); + UnsetEventQueue(); + } + CHM_Delete(pchmcfginfo); + + return ChmEventBase::Clean(); +} + +bool CHMConf::GetEventQueueFds(event_fds_t& fds) +{ + if(!IsWatching()){ + if(IsJsonStringType()){ + // Json string type --> return true + return true; + } + MSG_CHMPRN("There is no watching file."); + return false; + } + fds.clear(); + fds.push_back(inotifyfd); + return true; +} + +bool CHMConf::SetEventQueue(void) +{ + if(cfgfile.empty()){ + if(IsJsonStringType()){ + // Json string type --> return true + return true; + } + ERR_CHMPRN("This object does not have file path."); + return false; + } + if(CHM_INVALID_HANDLE == eqfd){ + ERR_CHMPRN("event fd is invalid."); + return false; + } + if(IsWatching()){ + if(!UnsetEventQueue()){ + return false; + } + } + + // create inotify + if(-1 == (inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC))){ + ERR_CHMPRN("Failed to create inotify, error %d", errno); + return false; + } + + // add inotify + if(CHM_INVALID_HANDLE == (watchfd = inotify_add_watch(inotifyfd, cfgfile.c_str(), IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF))){ + ERR_CHMPRN("Could not watch file %s (errno=%d)", cfgfile.c_str(), errno); + CHM_CLOSE(inotifyfd); + return false; + } + + // add event + struct epoll_event epoolev; + memset(&epoolev, 0, sizeof(struct epoll_event)); + epoolev.data.fd = inotifyfd; + epoolev.events = EPOLLIN | EPOLLET | EPOLLRDHUP; // EPOLLRDHUP is set + + if(-1 == epoll_ctl(eqfd, EPOLL_CTL_ADD, inotifyfd, &epoolev)){ + ERR_CHMPRN("Failed to add inotifyfd(%d)-watchfd(%d) to event fd(%d), error=%d", inotifyfd, watchfd, eqfd, errno); + inotify_rm_watch(inotifyfd, watchfd); + watchfd = CHM_INVALID_HANDLE; + CHM_CLOSE(inotifyfd); + return false; + } + return true; +} + +bool CHMConf::UnsetEventQueue(void) +{ + if(!IsWatching()){ + if(IsJsonStringType()){ + // Json string type --> return true + return true; + } + MSG_CHMPRN("There is no watching file."); + return false; + } + bool result = true; + + if(CHM_INVALID_HANDLE == inotifyfd){ + WAN_CHMPRN("inotifyfd is invalid."); + }else{ + epoll_ctl(eqfd, EPOLL_CTL_DEL, inotifyfd, NULL); + + if(CHM_INVALID_HANDLE == inotify_rm_watch(inotifyfd, watchfd)){ + if(EINVAL == errno){ + WAN_CHMPRN("Failed to remove watching fd(%d) from inotify fd(%d), because watchfd is invalid. It maybe removed file, so continue...", watchfd, inotifyfd); + }else{ + ERR_CHMPRN("Could not remove watching fd(%d) from inotify fd(%d), errno=%d", watchfd, inotifyfd, errno); + result = false; + } + } + CHM_CLOSE(inotifyfd); + } + inotifyfd = CHM_INVALID_HANDLE; + watchfd = CHM_INVALID_HANDLE; + + return result; +} + + +bool CHMConf::IsEventQueueFd(int fd) +{ + if(CHM_INVALID_HANDLE == fd){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!IsWatching()){ + if(!IsJsonStringType()){ + MSG_CHMPRN("There is no watching file."); + } + return false; + } + // check + if(inotifyfd != fd){ + return false; + } + return true; +} + +bool CHMConf::ResetEventQueue(void) +{ + if(IsWatching()){ + if(!UnsetEventQueue()){ + return false; + } + } + return SetEventQueue(); +} + +bool CHMConf::Receive(int fd) +{ + if(!IsWatching()){ + if(!IsJsonStringType()){ + ERR_CHMPRN("There is no watching file."); + } + return false; + } + if(!IsEventQueueFd(fd)){ + ERR_CHMPRN("fd(%d) is not this object event fd.", fd); + return false; + } + + // get type + bool is_check_file = false; + bool is_reload = false; + uint type = CheckNotifyEvent(); + if(type & IN_CLOSE_WRITE){ + is_reload = true; + } + if(type & IN_MODIFY){ + is_reload = true; + } + if(type & IN_DELETE_SELF){ + is_check_file = true; + } + if(type & IN_MOVE_SELF){ + is_check_file = true; + } + + // configration file is moved or deleted + if(is_check_file){ + struct timespec sleeptime; + SET_TIMESPEC(&sleeptime, 0, (1000 * 1000)); // 1ms + + // wait for 500ms + for(int cnt = 0; !CheckConfFile() && cnt < 500; nanosleep(&sleeptime, NULL), cnt++); + + // try to reset event + if(!ResetEventQueue()){ + ERR_CHMPRN("Failed to set inotify event for configuration file(%s), then no more watching it.", cfgfile.c_str()); + return false; + } + is_reload = true; + } + + // reload file & check update + if(is_reload){ + if(CheckUpdate()){ + // somthing changed, so update internal data. + if(!pChmCntrl->ConfigrationUpdateNotify()){ + ERR_CHMPRN("Failed to reinitialize internal data."); + return false; + } + }else{ + MSG_CHMPRN("Reloaded configuration file(%s), but it is not changed.", cfgfile.c_str()); + } + } + return true; +} + +bool CHMConf::Send(PCOMPKT pComPkt, const unsigned char* pbody, size_t blength) +{ + MSG_CHMPRN("Nothing to do in this object for this event.(Not implement thie event in this class)"); + return true; +} + +bool CHMConf::NotifyHup(int fd) +{ + if(!IsWatching()){ + if(!IsJsonStringType()){ + MSG_CHMPRN("There is no watching file."); + } + return false; + } + if(!IsEventQueueFd(fd)){ + ERR_CHMPRN("fd(%d) is not this object event fd.", fd); + return false; + } + return UnsetEventQueue(); +} + +bool CHMConf::Processing(PCOMPKT pComPkt) +{ + MSG_CHMPRN("Nothing to do in this object for this event.(Not implement thie event in this class)"); + return true; +} + +// +// Return: 0 - Nothing to do +// IN_CLOSE_WRITE - The configuration file is wrote, should reload it. +// IN_DELETE_SELF - The configuration file is deleted, check it and reload assap. +// IN_MOVE_SELF - The configuration file is moved, check it and reload assap. +// +// Other event type is not handled here, because handled event type in this method is caught +// after those event. +// +uint CHMConf::CheckNotifyEvent(void) +{ + if(!IsWatching()){ + if(!IsJsonStringType()){ + MSG_CHMPRN("There is no watching file."); + } + return 0; + } + + // read from inotify event + unsigned char* pevent; + size_t bytes; + if(NULL == (pevent = chm_read(inotifyfd, &bytes))){ + WAN_CHMPRN("read no inotify event, no more inotify event data."); + return 0; + } + + // analize event types + struct inotify_event* in_event = NULL; + uint result = 0; + for(unsigned char* ptr = pevent; (ptr + sizeof(struct inotify_event)) <= (pevent + bytes); ptr += sizeof(struct inotify_event) + in_event->len){ + in_event = reinterpret_cast(ptr); + + if(watchfd != in_event->wd){ + continue; + } + if(in_event->mask & IN_CLOSE_WRITE){ + MSG_CHMPRN("Configration file %s is wrote(%d).", cfgfile.c_str(), in_event->mask); + result |= IN_CLOSE_WRITE; + }else if(in_event->mask & IN_DELETE_SELF){ + MSG_CHMPRN("Configration file %s is deleted(%d).", cfgfile.c_str(), in_event->mask); + result |= IN_DELETE_SELF; + }else if(in_event->mask & IN_MOVE_SELF){ + MSG_CHMPRN("Configration file %s is moved(%d).", cfgfile.c_str(), in_event->mask); + result |= IN_MOVE_SELF; + }else if(in_event->mask & IN_MODIFY){ + MSG_CHMPRN("Configration file %s is modified(%d).", cfgfile.c_str(), in_event->mask); + result |= IN_MODIFY; + }else{ + WAN_CHMPRN("inotify event type(%u) is not handled because of waiting another event after it.", in_event->mask); + } + } + CHM_Free(pevent); + return result; +} + +bool CHMConf::CheckConfFile(void) const +{ + if(!IsFileType() || !is_file_safe_exist(cfgfile.c_str())){ + return false; + } + return true; +} + +bool CHMConf::CheckUpdate(void) +{ + if(pchmcfginfo && !CheckConfFile()){ + return false; + } + PCHMCFGINFO pnewinfo = new CHMCFGINFO; + + // Load configration file. + if(!LoadConfigration(*pnewinfo)){ + ERR_CHMPRN("Failed to load configuration from %s.", cfgfile.c_str()); + CHM_Delete(pnewinfo); + return false; + } + + bool result = false; + if(pchmcfginfo){ + if(!pchmcfginfo->compare(*pnewinfo)){ + // not same + result = true; + } + CHM_Delete(pchmcfginfo); + }else{ + result = true; + } + pchmcfginfo = pnewinfo; + + return result; +} + +const CHMCFGINFO* CHMConf::GetConfiguration(bool is_check_update) +{ + if(IsFileType() && !CheckConfFile()){ + return NULL; + } + // Load & Check configration file. + if(is_check_update || !pchmcfginfo){ + CheckUpdate(); + } + return pchmcfginfo; +} + +bool CHMConf::GetServerInfo(const char* hostname, short ctlport, CHMNODE_CFGINFO& svrnodeinfo, bool is_check_update) +{ + if(CHMEMPTYSTR(hostname)){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsFileType() && !CheckConfFile()){ + return false; + } + + // Load & Check configration file. + if(is_check_update || !pchmcfginfo){ + CheckUpdate(); + } + // change hostname to fqdn + string globalname; + if(!ChmNetDb::Get()->GetHostname(hostname, globalname, true)){ + MSG_CHMPRN("Could not convert hostname to global hostanme."); + return false; + } + for(chmnode_cfginfos_t::const_iterator iter = pchmcfginfo->servers.begin(); iter != pchmcfginfo->servers.end(); ++iter){ + if(globalname == iter->name){ + if(CHM_INVALID_PORT == ctlport || ctlport == iter->ctlport){ + // found + svrnodeinfo = *iter; + return true; + } + } + } + return false; +} + +bool CHMConf::GetSelfServerInfo(CHMNODE_CFGINFO& svrnodeinfo, bool is_check_update) +{ + return GetServerInfo("localhost", pchmcfginfo->self_ctlport, svrnodeinfo, is_check_update); +} + +bool CHMConf::GetSlaveInfo(const char* hostname, short ctlport, CHMNODE_CFGINFO& slvnodeinfo, bool is_check_update) +{ + if(CHMEMPTYSTR(hostname)){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsFileType() && !CheckConfFile()){ + return false; + } + + // Load & Check configration file. + if(is_check_update || !pchmcfginfo){ + CheckUpdate(); + } + + string globalname; + strlst_t slave_list; + for(chmnode_cfginfos_t::const_iterator iter = pchmcfginfo->slaves.begin(); iter != pchmcfginfo->slaves.end(); ++iter){ + slave_list.clear(); + slave_list.push_back(iter->name); + if(IsMatchHostname(hostname, slave_list, globalname)){ + if(CHM_INVALID_PORT == ctlport || ctlport == iter->ctlport){ + // found + slvnodeinfo.name = globalname; + slvnodeinfo.port = iter->port; + slvnodeinfo.ctlport = iter->ctlport; + slvnodeinfo.is_ssl = iter->is_ssl; + slvnodeinfo.verify_peer = iter->verify_peer; + slvnodeinfo.is_ca_file = iter->is_ca_file; + slvnodeinfo.capath = iter->capath; + slvnodeinfo.server_cert = iter->server_cert; + slvnodeinfo.server_prikey = iter->server_prikey; + slvnodeinfo.slave_cert = iter->slave_cert; + slvnodeinfo.slave_prikey = iter->slave_prikey; + return true; + } + } + } + return false; +} + +bool CHMConf::GetSelfSlaveInfo(CHMNODE_CFGINFO& slvnodeinfo, bool is_check_update) +{ + return GetServerInfo("localhost", pchmcfginfo->self_ctlport, slvnodeinfo, is_check_update); +} + +bool CHMConf::GetNodeInfo(const char* hostname, short ctlport, CHMNODE_CFGINFO& nodeinfo, bool is_only_server, bool is_check_update) +{ + if(IsFileType() && !CheckConfFile()){ + return false; + } + // Load & Check configration file. + if(is_check_update || !pchmcfginfo){ + CheckUpdate(); + } + + if(GetServerInfo(hostname, ctlport, nodeinfo, false)){ // already checked update + return true; + } + if(is_only_server){ + return false; + } + // if not only server, next check slave nodes. + return GetSlaveInfo(hostname, ctlport, nodeinfo, false); // already checked update +} + +bool CHMConf::GetSelfNodeInfo(CHMNODE_CFGINFO& nodeinfo, bool is_check_update) +{ + return GetNodeInfo("localhost", pchmcfginfo->self_ctlport, nodeinfo, pchmcfginfo->is_server_mode, is_check_update); +} + +bool CHMConf::GetServerList(strlst_t& server_list) +{ + if(NULL == GetConfiguration()){ + ERR_CHMPRN("Could not get configuration file(%s) contents.", cfgfile.c_str()); + return false; + } + server_list.clear(); + for(chmnode_cfginfos_t::const_iterator iter = pchmcfginfo->servers.begin(); iter != pchmcfginfo->servers.end(); ++iter){ + server_list.push_back(iter->name); + } + server_list.sort(strarr_sort()); + + return true; +} + +bool CHMConf::IsServerList(const char* hostname, string& fqdn) +{ + strlst_t server_list; + if(!GetServerList(server_list)){ + ERR_CHMPRN("Could not get server list."); + return false; + } + if(!IsInHostnameList(hostname, server_list, fqdn)){ + return false; + } + return true; +} + +bool CHMConf::IsServerList(string& fqdn) +{ + return IsServerList("locahost", fqdn); +} + +bool CHMConf::GetSlaveList(strlst_t& slave_list) +{ + if(NULL == GetConfiguration()){ + ERR_CHMPRN("Could not get configuration file(%s) contents.", cfgfile.c_str()); + return false; + } + slave_list.clear(); + + for(chmnode_cfginfos_t::const_iterator iter = pchmcfginfo->slaves.begin(); iter != pchmcfginfo->slaves.end(); ++iter){ + slave_list.push_back(iter->name); + } + slave_list.sort(strarr_sort()); + + return true; +} + +bool CHMConf::IsSlaveList(const char* hostname, string& fqdn) +{ + strlst_t slave_list; + if(!GetSlaveList(slave_list)){ + ERR_CHMPRN("Could not get slave list."); + return false; + } + if(!IsMatchHostname(hostname, slave_list, fqdn)){ + return false; + } + return true; +} + +bool CHMConf::IsSlaveList(string& fqdn) +{ + return IsSlaveList("locahost", fqdn); +} + +bool CHMConf::IsSsl(void) const +{ + if(!pchmcfginfo){ + MSG_CHMPRN("This object does not loading configration file yet."); + return false; + } + + for(chmnode_cfginfos_t::const_iterator iter = pchmcfginfo->servers.begin(); iter != pchmcfginfo->servers.end(); ++iter){ + if(iter->is_ssl){ + return true; + } + } + return false; +} + +//--------------------------------------------------------- +// CHMIniConf Class +//--------------------------------------------------------- +CHMIniConf::CHMIniConf(int eventqfd, ChmCntrl* pcntrl, const char* file, short ctlport) : CHMConf(eventqfd, pcntrl, file, ctlport, NULL) +{ + type = CHMConf::CONF_INI; +} + +CHMIniConf::~CHMIniConf() +{ +} + +bool CHMIniConf::ReadFileContents(const string& filename, strlst_t& linelst, strlst_t& allfiles) const +{ + if(0 == filename.length()){ + ERR_CHMPRN("Configuration file path is wrong."); + return false; + } + + ifstream cfgstream(filename.c_str(), ios::in); + if(!cfgstream.good()){ + ERR_CHMPRN("Could not open(read only) file(%s)", filename.c_str()); + return false; + } + + string line; + int lineno; + for(lineno = 1; cfgstream.good() && getline(cfgstream, line); lineno++){ + line = trim(line); + if(0 == line.length()){ + continue; + } + + // check only include + string::size_type pos; + if(string::npos != (pos = line.find(INICFG_INCLUDE_STR))){ + string value = trim(line.substr(pos + 1)); + string key = trim(line.substr(0, pos)); + if(key == INICFG_INCLUDE_STR){ + // found include. + bool found_same_file = false; + for(strlst_t::const_iterator iter = allfiles.begin(); iter != allfiles.end(); ++iter){ + if(value == (*iter)){ + found_same_file = true; + break; + } + } + if(found_same_file){ + WAN_CHMPRN("%s keyword in %s(%d) is filepath(%s) which already read!", INICFG_INCLUDE_STR, filename.c_str(), lineno, value.c_str()); + }else{ + // reentrant + allfiles.push_back(value); + if(!ReadFileContents(value, linelst, allfiles)){ + ERR_CHMPRN("Failed to load include file(%s)", value.c_str()); + cfgstream.close(); + return false; + } + } + continue; + } + } + // add + linelst.push_back(line); + } + cfgstream.close(); + + return true; +} + +bool CHMIniConf::LoadConfigrationRaw(CFGRAW& chmcfgraw) const +{ + if(0 == cfgfile.length()){ + ERR_CHMPRN("Configuration file path is not set."); + return false; + } + + // Load all file contents(with include file) + strlst_t linelst; + strlst_t allfiles; + allfiles.push_back(cfgfile); + if(!ReadFileContents(cfgfile, linelst, allfiles)){ + ERR_CHMPRN("Could not load configration file(%s) contents.", cfgfile.c_str()); + return false; + } + + string line; + int section = INICFG_INSEC_NOT; + int next_section; + strmap_t tmpmap; + for(strlst_t::const_iterator iter = linelst.begin(); iter != linelst.end(); ++iter){ + line = trim((*iter)); + if(0 == line.length()){ + continue; + } + + // check section keywords + if(INICFG_COMMENT_CHAR == line[0]){ + continue; + }else if(line == INICFG_GLOBAL_SEC_STR){ + next_section = INICFG_INSEC_GLOBAL; + }else if(line == INICFG_SVRNODE_SEC_STR){ + next_section = INICFG_INSEC_SVRNODE; + }else if(line == INICFG_SLVNODE_SEC_STR){ + next_section = INICFG_INSEC_SLVNODE; + }else if(INICFG_SEC_START_CHAR == line[0] && INICFG_SEC_END_CHAR == line[line.length() - 1]){ + next_section = INICFG_INSEC_UNKNOWN; + }else{ + next_section = INICFG_INSEC_NOT; + } + + if(INICFG_INSEC_NOT == next_section && INICFG_INSEC_NOT == section){ + // found not section keywords, but now out of section. + WAN_CHMPRN("cfg file(%s) invalide value(%s), no section.", cfgfile.c_str(), line.c_str()); + continue; + } + + if(INICFG_INSEC_NOT != next_section){ + // start new section, closing before section + if(INICFG_INSEC_GLOBAL == section){ + merge_strmap(chmcfgraw.global, tmpmap); + }else if(INICFG_INSEC_SVRNODE == section){ + if(!sorted_insert_strmaparr(chmcfgraw.server_nodes, tmpmap, INICFG_NAME_STR, INICFG_CTLPORT_STR)){ + WAN_CHMPRN("cfg file(%s) invalide server node data, probably %s is not specified.", cfgfile.c_str(), INICFG_NAME_STR); + } + }else if(INICFG_INSEC_SLVNODE == section){ + if(!sorted_insert_strmaparr(chmcfgraw.slave_nodes, tmpmap, INICFG_NAME_STR, INICFG_CTLPORT_STR)){ + WAN_CHMPRN("cfg file(%s) invalide slave node data, probably %s is not specified.", cfgfile.c_str(), INICFG_NAME_STR); + } + }else{ // INICFG_INSEC_UNKNOWN == section + // Unknown section, nothing to do. + } + section = next_section; + tmpmap.clear(); + + }else if(INICFG_INSEC_UNKNOWN != section){ + // not section keywords, continue in before section. + string value(""); + string::size_type pos; + if(string::npos != (pos = line.find(INICFG_KV_SEP))){ + value = trim(line.substr(pos + 1)); + line = trim(line.substr(0, pos)); + } + if(!extract_conf_value(value) || !extract_conf_value(line)){ + WAN_CHMPRN("cfg file(%s) invalide data, could not extract key or value.", cfgfile.c_str()); + }else{ + if(0 == line.length()){ + WAN_CHMPRN("cfg file(%s) invalide data, key is not found.", cfgfile.c_str()); + }else{ + if(tmpmap.end() != tmpmap.find(line)){ + WAN_CHMPRN("cfg file(%s) invalide data, key(%s: value=%s) is already specified, and overwrited.", cfgfile.c_str(), line.c_str(), value.c_str()); + } + tmpmap[line] = value; + } + } + } + } + + if(0 < tmpmap.size()){ + if(INICFG_INSEC_GLOBAL == section){ + merge_strmap(chmcfgraw.global, tmpmap); + }else if(INICFG_INSEC_SVRNODE == section){ + if(!sorted_insert_strmaparr(chmcfgraw.server_nodes, tmpmap, INICFG_NAME_STR, INICFG_CTLPORT_STR)){ + WAN_CHMPRN("cfg file(%s) invalide server node data, probably %s is not specified.", cfgfile.c_str(), INICFG_NAME_STR); + } + }else if(INICFG_INSEC_SLVNODE == section){ + if(!sorted_insert_strmaparr(chmcfgraw.slave_nodes, tmpmap, INICFG_NAME_STR, INICFG_CTLPORT_STR)){ + WAN_CHMPRN("cfg file(%s) invalide slave node data, probably %s is not specified.", cfgfile.c_str(), INICFG_NAME_STR); + } + }else{ // INICFG_INSEC_NOT == section + // before section is nothing(first section), nothing to do. + } + } + return true; +} + +bool CHMIniConf::LoadConfigration(CHMCFGINFO& chmcfginfo) const +{ + if(0 == cfgfile.length()){ + ERR_CHMPRN("Configuration file path is not set."); + return false; + } + + CFGRAW chmcfgraw; + if(!LoadConfigrationRaw(chmcfgraw)){ + ERR_CHMPRN("Failed to read configuration file(%s).", cfgfile.c_str()); + return false; + } + + // Common values + CHMCONF_CCV ccvals; + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_GROUP_STR)){ + ERR_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_GROUP_STR, INICFG_GLOBAL_SEC_STR); + return false; + } + chmcfginfo.groupname = chmcfgraw.global[INICFG_GROUP_STR]; + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_VERSION_STR)){ + ERR_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_VERSION_STR, INICFG_GLOBAL_SEC_STR); + return false; + } + chmcfginfo.revision = static_cast(atoi(chmcfgraw.global[INICFG_VERSION_STR].c_str())); + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MODE_STR)){ + WAN_CHMPRN("configuration file(%s) does not have \"%s\" in %s section, but seaching automatically by contrl port.", cfgfile.c_str(), INICFG_MODE_STR, INICFG_GLOBAL_SEC_STR); + }else{ + if(0 == strcasecmp(chmcfgraw.global[INICFG_MODE_STR].c_str(), INICFG_MODE_SERVER_STR)){ + chmcfginfo.is_server_mode = true; + }else if(0 == strcasecmp(chmcfgraw.global[INICFG_MODE_STR].c_str(), INICFG_MODE_SLAVE_STR)){ + chmcfginfo.is_server_mode = false; + }else{ + ERR_CHMPRN("configuration file(%s) have \"%s\" in %s section, but value %s does not defined.", cfgfile.c_str(), INICFG_MODE_STR, INICFG_GLOBAL_SEC_STR, chmcfgraw.global[INICFG_MODE_STR].c_str()); + return false; + } + ccvals.server_mode = true; + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_DELIVERMODE_STR)){ + ERR_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_DELIVERMODE_STR, INICFG_GLOBAL_SEC_STR); + return false; + }else{ + if(0 == strcasecmp(chmcfgraw.global[INICFG_DELIVERMODE_STR].c_str(), INICFG_DELIVERMODE_RANDOM_STR)){ + chmcfginfo.is_random_mode = true; + }else if(0 == strcasecmp(chmcfgraw.global[INICFG_DELIVERMODE_STR].c_str(), INICFG_DELIVERMODE_HASH_STR)){ + chmcfginfo.is_random_mode = false; + }else{ + ERR_CHMPRN("configuration file(%s) have \"%s\" in %s section, but value %s does not defined.", cfgfile.c_str(), INICFG_DELIVERMODE_STR, INICFG_GLOBAL_SEC_STR, chmcfgraw.global[INICFG_DELIVERMODE_STR].c_str()); + return false; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MAXCHMPX_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_MAXCHMPX_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.max_chmpx_count = DEFAULT_CHMPX_COUNT; + }else{ + chmcfginfo.max_chmpx_count = static_cast(atoi(chmcfgraw.global[INICFG_MAXCHMPX_STR].c_str())); + if(MAX_CHMPX_COUNT < chmcfginfo.max_chmpx_count){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MAXCHMPX_STR, chmcfginfo.max_chmpx_count, MAX_CHMPX_COUNT); + chmcfginfo.max_chmpx_count = MAX_CHMPX_COUNT; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_REPLICA_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_REPLICA_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.replica_count = DEFAULT_REPLICA_COUNT; + }else{ + chmcfginfo.replica_count = static_cast(atoi(chmcfgraw.global[INICFG_REPLICA_STR].c_str())); + if(chmcfginfo.is_random_mode){ + if(DEFAULT_REPLICA_COUNT != chmcfginfo.replica_count){ + WAN_CHMPRN("\"%s\" value(%ld) is not set, because random mode does not do replication. This value should be %d on random mode.", INICFG_REPLICA_STR, chmcfginfo.replica_count, DEFAULT_REPLICA_COUNT); + chmcfginfo.replica_count = DEFAULT_REPLICA_COUNT; + } + }else{ + if(chmcfginfo.max_chmpx_count < chmcfginfo.replica_count){ + WAN_CHMPRN("\"%s\" value(%ld) is over maximum chmpx count(%ld), so set value to maximum chmpx count.", INICFG_REPLICA_STR, chmcfginfo.replica_count, chmcfginfo.max_chmpx_count); + chmcfginfo.replica_count = chmcfginfo.max_chmpx_count; + } + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MAXMQSERVER_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_MAXMQSERVER_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.max_server_mq_cnt = DEFAULT_SERVER_MQ_CNT; + }else{ + chmcfginfo.max_server_mq_cnt = static_cast(atoi(chmcfgraw.global[INICFG_MAXMQSERVER_STR].c_str())); + if(MAX_SERVER_MQ_CNT < chmcfginfo.max_server_mq_cnt){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MAXMQSERVER_STR, chmcfginfo.max_server_mq_cnt, MAX_SERVER_MQ_CNT); + chmcfginfo.max_server_mq_cnt = MAX_SERVER_MQ_CNT; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MAXMQCLIENT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_MAXMQCLIENT_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.max_client_mq_cnt = DEFAULT_CLIENT_MQ_CNT; + }else{ + chmcfginfo.max_client_mq_cnt = static_cast(atoi(chmcfgraw.global[INICFG_MAXMQCLIENT_STR].c_str())); + if(MAX_CLIENT_MQ_CNT < chmcfginfo.max_client_mq_cnt){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MAXMQCLIENT_STR, chmcfginfo.max_client_mq_cnt, MAX_CLIENT_MQ_CNT); + chmcfginfo.max_client_mq_cnt = MAX_CLIENT_MQ_CNT; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MQPERATTACH_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_MQPERATTACH_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.mqcnt_per_attach = DEFAULT_MQ_PER_ATTACH; + }else{ + chmcfginfo.mqcnt_per_attach = static_cast(atoi(chmcfgraw.global[INICFG_MQPERATTACH_STR].c_str())); + if(MAX_MQ_PER_ATTACH < chmcfginfo.mqcnt_per_attach){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MQPERATTACH_STR, chmcfginfo.mqcnt_per_attach, MAX_MQ_PER_ATTACH); + chmcfginfo.mqcnt_per_attach = MAX_MQ_PER_ATTACH; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MAXQPERSERVERMQ_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_MAXQPERSERVERMQ_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.max_q_per_servermq = DEFAULT_QUEUE_PER_SERVERMQ; + }else{ + chmcfginfo.max_q_per_servermq = static_cast(atoi(chmcfgraw.global[INICFG_MAXQPERSERVERMQ_STR].c_str())); + if(MAX_QUEUE_PER_SERVERMQ < chmcfginfo.max_q_per_servermq){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MAXQPERSERVERMQ_STR, chmcfginfo.max_q_per_servermq, MAX_QUEUE_PER_SERVERMQ); + chmcfginfo.max_q_per_servermq = MAX_QUEUE_PER_SERVERMQ; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MAXQPERCLIENTMQ_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_MAXQPERCLIENTMQ_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.max_q_per_clientmq = DEFAULT_QUEUE_PER_CLIENTMQ; + }else{ + chmcfginfo.max_q_per_clientmq = static_cast(atoi(chmcfgraw.global[INICFG_MAXQPERCLIENTMQ_STR].c_str())); + if(MAX_QUEUE_PER_CLIENTMQ < chmcfginfo.max_q_per_clientmq){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MAXQPERCLIENTMQ_STR, chmcfginfo.max_q_per_clientmq, MAX_QUEUE_PER_CLIENTMQ); + chmcfginfo.max_q_per_clientmq = MAX_QUEUE_PER_CLIENTMQ; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MAXMQPERCLIENT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_MAXMQPERCLIENT_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.max_mq_per_client = DEFAULT_MQ_PER_CLIENT; + }else{ + chmcfginfo.max_mq_per_client = static_cast(atoi(chmcfgraw.global[INICFG_MAXMQPERCLIENT_STR].c_str())); + if(MAX_MQ_PER_CLIENT < chmcfginfo.max_mq_per_client){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MAXMQPERCLIENT_STR, chmcfginfo.max_mq_per_client, MAX_MQ_PER_CLIENT); + chmcfginfo.max_mq_per_client = MAX_MQ_PER_CLIENT; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MAXHISTLOG_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_MAXHISTLOG_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.max_histlog_count = DEFAULT_HISTLOG_COUNT; + }else{ + chmcfginfo.max_histlog_count = static_cast(atoi(chmcfgraw.global[INICFG_MAXHISTLOG_STR].c_str())); + if(MAX_HISTLOG_COUNT < chmcfginfo.max_histlog_count){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MAXHISTLOG_STR, chmcfginfo.max_histlog_count, MAX_HISTLOG_COUNT); + chmcfginfo.max_histlog_count = MAX_HISTLOG_COUNT; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_DATE_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_DATE_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.date = 0L; + }else{ + chmcfginfo.date = rfcdate_time(chmcfgraw.global[INICFG_DATE_STR].c_str()); + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_PORT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_PORT_STR, INICFG_GLOBAL_SEC_STR); + }else{ + ccvals.port = static_cast(atoi(chmcfgraw.global[INICFG_PORT_STR].c_str())); + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_CTLPORT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_CTLPORT_STR, INICFG_GLOBAL_SEC_STR); + }else{ + ccvals.ctlport = static_cast(atoi(chmcfgraw.global[INICFG_CTLPORT_STR].c_str())); + } + + if(CHM_INVALID_PORT == ctlport_param){ + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_SELFCTLPORT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_SELFCTLPORT_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.self_ctlport = CHM_INVALID_PORT; + }else{ + chmcfginfo.self_ctlport = static_cast(atoi(chmcfgraw.global[INICFG_SELFCTLPORT_STR].c_str())); + } + }else{ + chmcfginfo.self_ctlport = ctlport_param; + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_RETRYCNT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_RETRYCNT_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.retrycnt = CHMEVENTSOCK_RETRY_DEFAULT; + }else{ + chmcfginfo.retrycnt = atoi(chmcfgraw.global[INICFG_RETRYCNT_STR].c_str()); + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MQRETRYCNT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_MQRETRYCNT_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.mq_retrycnt = ChmEventMq::DEFAULT_RETRYCOUNT; + }else{ + chmcfginfo.mq_retrycnt = atoi(chmcfgraw.global[INICFG_MQRETRYCNT_STR].c_str()); + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MQACK_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_MQACK_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.mq_ack = true; + }else{ + if(0 == strcasecmp(chmcfgraw.global[INICFG_MQACK_STR].c_str(), INICFG_BOOL_ON) || 0 == strcasecmp(chmcfgraw.global[INICFG_MQACK_STR].c_str(), INICFG_BOOL_YES)){ + chmcfginfo.mq_ack = true; + }else if(0 == strcasecmp(chmcfgraw.global[INICFG_MQACK_STR].c_str(), INICFG_BOOL_OFF) || 0 == strcasecmp(chmcfgraw.global[INICFG_MQACK_STR].c_str(), INICFG_BOOL_NO)){ + chmcfginfo.mq_ack = false; + }else{ + ERR_CHMPRN("configuration file(%s) have wrong \"%s\" value(%s) in %s section.", cfgfile.c_str(), INICFG_MQACK_STR, chmcfgraw.global[INICFG_MQACK_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_RWTIMEOUT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_RWTIMEOUT_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.timeout_wait_socket = CHMEVENTSOCK_TIMEOUT_DEFAULT; + }else{ + chmcfginfo.timeout_wait_socket = atoi(chmcfgraw.global[INICFG_RWTIMEOUT_STR].c_str()); + if(chmcfginfo.timeout_wait_socket < CHMEVENTSOCK_TIMEOUT_DEFAULT){ + chmcfginfo.timeout_wait_socket = CHMEVENTSOCK_TIMEOUT_DEFAULT; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_CONTIMEOUT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_CONTIMEOUT_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.timeout_wait_connect = CHMEVENTSOCK_TIMEOUT_DEFAULT; + }else{ + chmcfginfo.timeout_wait_connect = atoi(chmcfgraw.global[INICFG_CONTIMEOUT_STR].c_str()); + if(chmcfginfo.timeout_wait_connect < CHMEVENTSOCK_TIMEOUT_DEFAULT){ + chmcfginfo.timeout_wait_connect = CHMEVENTSOCK_TIMEOUT_DEFAULT; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MQRWTIMEOUT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_MQRWTIMEOUT_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.timeout_wait_mq = CHMEVENTMQ_TIMEOUT_DEFAULT; + }else{ + chmcfginfo.timeout_wait_mq = atoi(chmcfgraw.global[INICFG_MQRWTIMEOUT_STR].c_str()); + if(chmcfginfo.timeout_wait_mq < CHMEVENTMQ_TIMEOUT_DEFAULT){ + chmcfginfo.timeout_wait_mq = CHMEVENTMQ_TIMEOUT_DEFAULT; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_AUTOMERGE_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_AUTOMERGE_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.is_auto_merge = false; + }else{ + if(0 == strcasecmp(chmcfgraw.global[INICFG_AUTOMERGE_STR].c_str(), INICFG_BOOL_ON) || 0 == strcasecmp(chmcfgraw.global[INICFG_AUTOMERGE_STR].c_str(), INICFG_BOOL_YES)){ + chmcfginfo.is_auto_merge = true; + }else if(0 == strcasecmp(chmcfgraw.global[INICFG_AUTOMERGE_STR].c_str(), INICFG_BOOL_OFF) || 0 == strcasecmp(chmcfgraw.global[INICFG_AUTOMERGE_STR].c_str(), INICFG_BOOL_NO)){ + chmcfginfo.is_auto_merge = false; + }else{ + ERR_CHMPRN("configuration file(%s) have wrong \"%s\" value(%s) in %s section.", cfgfile.c_str(), INICFG_AUTOMERGE_STR, chmcfgraw.global[INICFG_AUTOMERGE_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_DOMERGE_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_DOMERGE_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.is_do_merge = false; + }else{ + if(0 == strcasecmp(chmcfgraw.global[INICFG_DOMERGE_STR].c_str(), INICFG_BOOL_ON) || 0 == strcasecmp(chmcfgraw.global[INICFG_DOMERGE_STR].c_str(), INICFG_BOOL_YES)){ + chmcfginfo.is_do_merge = true; + }else if(0 == strcasecmp(chmcfgraw.global[INICFG_DOMERGE_STR].c_str(), INICFG_BOOL_OFF) || 0 == strcasecmp(chmcfgraw.global[INICFG_DOMERGE_STR].c_str(), INICFG_BOOL_NO)){ + chmcfginfo.is_do_merge = false; + }else{ + ERR_CHMPRN("configuration file(%s) have wrong \"%s\" value(%s) in %s section.", cfgfile.c_str(), INICFG_DOMERGE_STR, chmcfgraw.global[INICFG_DOMERGE_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MERGETIMEOUT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_MERGETIMEOUT_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.timeout_merge = CHMEVENTMQ_TIMEOUT_DEFAULT; + }else{ + chmcfginfo.timeout_merge = static_cast(atoi(chmcfgraw.global[INICFG_MERGETIMEOUT_STR].c_str())); + if(chmcfginfo.timeout_merge < CHMEVENTMQ_TIMEOUT_DEFAULT){ + chmcfginfo.timeout_merge = CHMEVENTMQ_TIMEOUT_DEFAULT; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_SOCKTHREADCNT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_SOCKTHREADCNT_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.sock_thread_cnt = ChmEventSock::DEFAULT_SOCK_THREAD_CNT; + }else{ + chmcfginfo.sock_thread_cnt = atoi(chmcfgraw.global[INICFG_SOCKTHREADCNT_STR].c_str()); + if(chmcfginfo.sock_thread_cnt < ChmEventSock::DEFAULT_SOCK_THREAD_CNT){ + ERR_CHMPRN("configuration file(%s) have wrong \"%s\" value(%s) in %s section.", cfgfile.c_str(), INICFG_SOCKTHREADCNT_STR, chmcfgraw.global[INICFG_SOCKTHREADCNT_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MQTHREADCNT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_MQTHREADCNT_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.mq_thread_cnt = ChmEventMq::DEFAULT_MQ_THREAD_CNT; + }else{ + chmcfginfo.mq_thread_cnt = atoi(chmcfgraw.global[INICFG_MQTHREADCNT_STR].c_str()); + if(chmcfginfo.mq_thread_cnt < ChmEventMq::DEFAULT_MQ_THREAD_CNT){ + ERR_CHMPRN("configuration file(%s) have wrong \"%s\" value(%s) in %s section.", cfgfile.c_str(), INICFG_MQTHREADCNT_STR, chmcfgraw.global[INICFG_MQTHREADCNT_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_MAXSOCKPOOL_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_MAXSOCKPOOL_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.max_sock_pool = ChmEventSock::DEFAULT_MAX_SOCK_POOL; + }else{ + chmcfginfo.max_sock_pool = static_cast(atoi(chmcfgraw.global[INICFG_MAXSOCKPOOL_STR].c_str())); + if(chmcfginfo.max_sock_pool < ChmEventSock::DEFAULT_MAX_SOCK_POOL){ + ERR_CHMPRN("configuration file(%s) have wrong \"%s\" value(%s) in %s section.", cfgfile.c_str(), INICFG_MAXSOCKPOOL_STR, chmcfgraw.global[INICFG_MAXSOCKPOOL_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_SOCKPOOLTIMEOUT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_SOCKPOOLTIMEOUT_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.sock_pool_timeout = ChmEventSock::DEFAULT_SOCK_POOL_TIMEOUT; + }else{ + chmcfginfo.sock_pool_timeout = atoi(chmcfgraw.global[INICFG_SOCKPOOLTIMEOUT_STR].c_str()); + if(chmcfginfo.sock_pool_timeout < ChmEventSock::NO_SOCK_POOL_TIMEOUT){ + ERR_CHMPRN("configuration file(%s) have wrong \"%s\" value(%s) in %s section.", cfgfile.c_str(), INICFG_SOCKPOOLTIMEOUT_STR, chmcfgraw.global[INICFG_SOCKPOOLTIMEOUT_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + } + + // SSL + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_SSL_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_SSL_STR, INICFG_GLOBAL_SEC_STR); + }else{ + if(0 == strcasecmp(chmcfgraw.global[INICFG_SSL_STR].c_str(), INICFG_BOOL_ON) || 0 == strcasecmp(chmcfgraw.global[INICFG_SSL_STR].c_str(), INICFG_BOOL_YES)){ + ccvals.is_ssl = true; + }else if(0 == strcasecmp(chmcfgraw.global[INICFG_SSL_STR].c_str(), INICFG_BOOL_OFF) || 0 == strcasecmp(chmcfgraw.global[INICFG_SSL_STR].c_str(), INICFG_BOOL_NO)){ + ccvals.is_ssl = false; + }else{ + ERR_CHMPRN("configuration file(%s) have wrong \"%s\" value(%s) in %s section.", cfgfile.c_str(), INICFG_SSL_STR, chmcfgraw.global[INICFG_SSL_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + } + + // SSL_VERIFY_PEER + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_SSL_VERIFY_PEER_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_SSL_VERIFY_PEER_STR, INICFG_GLOBAL_SEC_STR); + }else{ + if(0 == strcasecmp(chmcfgraw.global[INICFG_SSL_VERIFY_PEER_STR].c_str(), INICFG_BOOL_ON) || 0 == strcasecmp(chmcfgraw.global[INICFG_SSL_VERIFY_PEER_STR].c_str(), INICFG_BOOL_YES)){ + if(!ccvals.is_ssl){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but \"%s\" is OFF.", cfgfile.c_str(), INICFG_SSL_VERIFY_PEER_STR, chmcfgraw.global[INICFG_SSL_VERIFY_PEER_STR].c_str(), INICFG_GLOBAL_SEC_STR, INICFG_SSL_STR); + return false; + } + ccvals.verify_peer = true; + }else if(0 == strcasecmp(chmcfgraw.global[INICFG_SSL_VERIFY_PEER_STR].c_str(), INICFG_BOOL_OFF) || 0 == strcasecmp(chmcfgraw.global[INICFG_SSL_VERIFY_PEER_STR].c_str(), INICFG_BOOL_NO)){ + ccvals.verify_peer = false; + }else{ + ERR_CHMPRN("configuration file(%s) have wrong \"%s\" value(%s) in %s section.", cfgfile.c_str(), INICFG_SSL_VERIFY_PEER_STR, chmcfgraw.global[INICFG_SSL_VERIFY_PEER_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + } + + // SSL_CAPATH + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_CAPATH_STR) || 0 == strcasecmp(chmcfgraw.global[INICFG_CAPATH_STR].c_str(), INICFG_STRING_NULL)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_CAPATH_STR, INICFG_GLOBAL_SEC_STR); + }else{ + if(is_dir_exist(chmcfgraw.global[INICFG_CAPATH_STR].c_str())){ + ccvals.is_ca_file = false; + }else{ + if(is_file_safe_exist(chmcfgraw.global[INICFG_CAPATH_STR].c_str())){ + ccvals.is_ca_file = true; + }else{ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is not directory or file.", cfgfile.c_str(), INICFG_CAPATH_STR, chmcfgraw.global[INICFG_CAPATH_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + } + if(CHM_MAX_PATH_LEN <= chmcfgraw.global[INICFG_CAPATH_STR].length()){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is length(%zd) is over max length(1024).", cfgfile.c_str(), INICFG_CAPATH_STR, chmcfgraw.global[INICFG_CAPATH_STR].c_str(), INICFG_GLOBAL_SEC_STR, chmcfgraw.global[INICFG_CAPATH_STR].length()); + return false; + } + ccvals.capath = chmcfgraw.global[INICFG_CAPATH_STR]; + } + + // SSL_SERVER_CERT + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_SERVER_CERT_STR) || 0 == strcasecmp(chmcfgraw.global[INICFG_SERVER_CERT_STR].c_str(), INICFG_STRING_NULL)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_SERVER_CERT_STR, INICFG_GLOBAL_SEC_STR); + }else{ + if(!is_file_safe_exist(chmcfgraw.global[INICFG_SERVER_CERT_STR].c_str())){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is not safe file.", cfgfile.c_str(), INICFG_SERVER_CERT_STR, chmcfgraw.global[INICFG_SERVER_CERT_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + if(!ccvals.is_ssl){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but \"%s\" is OFF.", cfgfile.c_str(), INICFG_SERVER_CERT_STR, chmcfgraw.global[INICFG_SERVER_CERT_STR].c_str(), INICFG_GLOBAL_SEC_STR, INICFG_SSL_STR); + return false; + } + if(CHM_MAX_PATH_LEN <= chmcfgraw.global[INICFG_SERVER_CERT_STR].length()){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is length(%zd) is over max length(1024).", cfgfile.c_str(), INICFG_SERVER_CERT_STR, chmcfgraw.global[INICFG_SERVER_CERT_STR].c_str(), INICFG_GLOBAL_SEC_STR, chmcfgraw.global[INICFG_SERVER_CERT_STR].length()); + return false; + } + ccvals.server_cert = chmcfgraw.global[INICFG_SERVER_CERT_STR]; + } + + // SSL_SERVER_PRIKEY + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_SERVER_PRIKEY_STR) || 0 == strcasecmp(chmcfgraw.global[INICFG_SERVER_PRIKEY_STR].c_str(), INICFG_STRING_NULL)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_SERVER_PRIKEY_STR, INICFG_GLOBAL_SEC_STR); + }else{ + if(!is_file_safe_exist(chmcfgraw.global[INICFG_SERVER_PRIKEY_STR].c_str())){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is not safe file.", cfgfile.c_str(), INICFG_SERVER_PRIKEY_STR, chmcfgraw.global[INICFG_SERVER_PRIKEY_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + if(!ccvals.is_ssl){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but \"%s\" is OFF.", cfgfile.c_str(), INICFG_SERVER_PRIKEY_STR, chmcfgraw.global[INICFG_SERVER_PRIKEY_STR].c_str(), INICFG_GLOBAL_SEC_STR, INICFG_SSL_STR); + return false; + } + if(CHM_MAX_PATH_LEN <= chmcfgraw.global[INICFG_SERVER_PRIKEY_STR].length()){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is length(%zd) is over max length(1024).", cfgfile.c_str(), INICFG_SERVER_PRIKEY_STR, chmcfgraw.global[INICFG_SERVER_PRIKEY_STR].c_str(), INICFG_GLOBAL_SEC_STR, chmcfgraw.global[INICFG_SERVER_PRIKEY_STR].length()); + return false; + } + ccvals.server_prikey = chmcfgraw.global[INICFG_SERVER_PRIKEY_STR]; + } + + // SSL_SLAVE_CERT + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_SLAVE_CERT_STR) || 0 == strcasecmp(chmcfgraw.global[INICFG_SLAVE_CERT_STR].c_str(), INICFG_STRING_NULL)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_SLAVE_CERT_STR, INICFG_GLOBAL_SEC_STR); + }else{ + if(!is_file_safe_exist(chmcfgraw.global[INICFG_SLAVE_CERT_STR].c_str())){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is not safe file.", cfgfile.c_str(), INICFG_SLAVE_CERT_STR, chmcfgraw.global[INICFG_SLAVE_CERT_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + if(CHM_MAX_PATH_LEN <= chmcfgraw.global[INICFG_SLAVE_CERT_STR].length()){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is length(%zd) is over max length(1024).", cfgfile.c_str(), INICFG_SLAVE_CERT_STR, chmcfgraw.global[INICFG_SLAVE_CERT_STR].c_str(), INICFG_GLOBAL_SEC_STR, chmcfgraw.global[INICFG_SLAVE_CERT_STR].length()); + return false; + } + ccvals.slave_cert = chmcfgraw.global[INICFG_SLAVE_CERT_STR]; + } + + // SSL_SLAVE_PRIKEY + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_SLAVE_PRIKEY_STR) || 0 == strcasecmp(chmcfgraw.global[INICFG_SLAVE_PRIKEY_STR].c_str(), INICFG_STRING_NULL)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_SLAVE_PRIKEY_STR, INICFG_GLOBAL_SEC_STR); + }else{ + if(!is_file_safe_exist(chmcfgraw.global[INICFG_SLAVE_PRIKEY_STR].c_str())){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is not safe file.", cfgfile.c_str(), INICFG_SLAVE_PRIKEY_STR, chmcfgraw.global[INICFG_SLAVE_PRIKEY_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + if(CHM_MAX_PATH_LEN <= chmcfgraw.global[INICFG_SLAVE_PRIKEY_STR].length()){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is length(%zd) is over max length(1024).", cfgfile.c_str(), INICFG_SLAVE_PRIKEY_STR, chmcfgraw.global[INICFG_SLAVE_PRIKEY_STR].c_str(), INICFG_GLOBAL_SEC_STR, chmcfgraw.global[INICFG_SLAVE_PRIKEY_STR].length()); + return false; + } + ccvals.slave_prikey = chmcfgraw.global[INICFG_SLAVE_PRIKEY_STR]; + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_K2HFULLMAP_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_K2HFULLMAP_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.k2h_fullmap = true; + }else{ + if(0 == strcasecmp(chmcfgraw.global[INICFG_K2HFULLMAP_STR].c_str(), INICFG_BOOL_ON) || 0 == strcasecmp(chmcfgraw.global[INICFG_K2HFULLMAP_STR].c_str(), INICFG_BOOL_YES)){ + chmcfginfo.k2h_fullmap = true; + }else if(0 == strcasecmp(chmcfgraw.global[INICFG_K2HFULLMAP_STR].c_str(), INICFG_BOOL_OFF) || 0 == strcasecmp(chmcfgraw.global[INICFG_K2HFULLMAP_STR].c_str(), INICFG_BOOL_NO)){ + chmcfginfo.k2h_fullmap = false; + }else{ + ERR_CHMPRN("configuration file(%s) have wrong \"%s\" value(%s) in %s section.", cfgfile.c_str(), INICFG_K2HFULLMAP_STR, chmcfgraw.global[INICFG_K2HFULLMAP_STR].c_str(), INICFG_GLOBAL_SEC_STR); + return false; + } + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_K2HMASKBIT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_K2HMASKBIT_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.k2h_mask_bitcnt = K2HShm::DEFAULT_MASK_BITCOUNT; + }else{ + chmcfginfo.k2h_mask_bitcnt = atoi(chmcfgraw.global[INICFG_K2HMASKBIT_STR].c_str()); + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_K2HCMASKBIT_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_K2HCMASKBIT_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.k2h_cmask_bitcnt = K2HShm::DEFAULT_COLLISION_MASK_BITCOUNT; + }else{ + chmcfginfo.k2h_cmask_bitcnt = atoi(chmcfgraw.global[INICFG_K2HCMASKBIT_STR].c_str()); + } + + if(chmcfgraw.global.end() == chmcfgraw.global.find(INICFG_K2HMAXELE_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_K2HMAXELE_STR, INICFG_GLOBAL_SEC_STR); + chmcfginfo.k2h_max_element = K2HShm::DEFAULT_MAX_ELEMENT_CNT; + }else{ + chmcfginfo.k2h_max_element = atoi(chmcfgraw.global[INICFG_K2HMAXELE_STR].c_str()); + } + + // server node section + if(0 == chmcfgraw.server_nodes.size()){ + ERR_CHMPRN("configuration file(%s) does not have %s section.", cfgfile.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + + string localhost_hostname; + ChmNetDb::GetLocalHostname(localhost_hostname); + for(strmaparr_t::iterator iter = chmcfgraw.server_nodes.begin(); iter != chmcfgraw.server_nodes.end(); ++iter){ + CHMNODE_CFGINFO svrnode; + + if(iter->end() == iter->find(INICFG_NAME_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_NAME_STR, INICFG_SVRNODE_SEC_STR); + return false; + }else{ + svrnode.name = (*iter)[INICFG_NAME_STR]; + } + + if(iter->end() == iter->find(INICFG_PORT_STR)){ + if(CHM_INVALID_PORT == ccvals.port){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s server node in %s section.", cfgfile.c_str(), INICFG_PORT_STR, svrnode.name.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + svrnode.port = ccvals.port; + }else{ + svrnode.port = static_cast(atoi((*iter)[INICFG_PORT_STR].c_str())); + } + + if(iter->end() == iter->find(INICFG_CTLPORT_STR)){ + if(CHM_INVALID_PORT == ccvals.ctlport){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s server node in %s section.", cfgfile.c_str(), INICFG_CTLPORT_STR, svrnode.name.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + svrnode.ctlport = ccvals.ctlport; + }else{ + svrnode.ctlport = static_cast(atoi((*iter)[INICFG_CTLPORT_STR].c_str())); + } + + // SSL + // + if(iter->end() == iter->find(INICFG_SSL_STR)){ + svrnode.is_ssl = ccvals.is_ssl; + }else{ + if(0 == strcasecmp((*iter)[INICFG_SSL_STR].c_str(), INICFG_BOOL_ON) || 0 == strcasecmp((*iter)[INICFG_SSL_STR].c_str(), INICFG_BOOL_YES)){ + svrnode.is_ssl = true; + }else if(0 == strcasecmp((*iter)[INICFG_SSL_STR].c_str(), INICFG_BOOL_OFF) || 0 == strcasecmp((*iter)[INICFG_SSL_STR].c_str(), INICFG_BOOL_NO)){ + svrnode.is_ssl = false; + }else{ + ERR_CHMPRN("configuration file(%s) have wrong \"%s\" value(%s) in %s server node in %s section.", cfgfile.c_str(), INICFG_SSL_STR, (*iter)[INICFG_SSL_STR].c_str(), svrnode.name.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + } + if(svrnode.is_ssl){ + ccvals.found_ssl = true; // Keep SSL flag for checking after this loop + } + + // SSL_VERIFY_PEER + // + if(iter->end() == iter->find(INICFG_SSL_VERIFY_PEER_STR)){ + svrnode.verify_peer = ccvals.verify_peer; + }else{ + if(0 == strcasecmp((*iter)[INICFG_SSL_VERIFY_PEER_STR].c_str(), INICFG_BOOL_ON) || 0 == strcasecmp((*iter)[INICFG_SSL_VERIFY_PEER_STR].c_str(), INICFG_BOOL_YES)){ + svrnode.verify_peer = true; + }else if(0 == strcasecmp((*iter)[INICFG_SSL_VERIFY_PEER_STR].c_str(), INICFG_BOOL_OFF) || 0 == strcasecmp((*iter)[INICFG_SSL_VERIFY_PEER_STR].c_str(), INICFG_BOOL_NO)){ + svrnode.verify_peer = false; + }else{ + ERR_CHMPRN("configuration file(%s) have wrong \"%s\" value(%s) in %s server node in %s section.", cfgfile.c_str(), INICFG_SSL_VERIFY_PEER_STR, (*iter)[INICFG_SSL_VERIFY_PEER_STR].c_str(), svrnode.name.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + } + if(!svrnode.is_ssl && svrnode.verify_peer){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s server node in %s section, but \"%s\" is OFF.", cfgfile.c_str(), INICFG_SSL_VERIFY_PEER_STR, (*iter)[INICFG_SSL_VERIFY_PEER_STR].c_str(), svrnode.name.c_str(), INICFG_SVRNODE_SEC_STR, INICFG_SSL_STR); + return false; + } + if(svrnode.verify_peer){ + ccvals.found_ssl_verify_peer = true; // Keep verify peer flag for checking after this loop + } + + // SSL_CAPATH + // + // [NOTE] : This value is checked after this loop. + // + if(iter->end() == iter->find(INICFG_CAPATH_STR)){ + svrnode.is_ca_file = ccvals.is_ca_file; + svrnode.capath = ccvals.capath; + }else{ + if(0 == strcasecmp((*iter)[INICFG_CAPATH_STR].c_str(), INICFG_STRING_NULL)){ + svrnode.is_ca_file = false; + svrnode.capath = ""; + }else{ + if(is_dir_exist((*iter)[INICFG_CAPATH_STR].c_str())){ + svrnode.is_ca_file = false; + }else{ + if(is_file_safe_exist((*iter)[INICFG_CAPATH_STR].c_str())){ + svrnode.is_ca_file = true; + }else{ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s server node in %s section, but it is not directory or file.", cfgfile.c_str(), INICFG_CAPATH_STR, (*iter)[INICFG_CAPATH_STR].c_str(), svrnode.name.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + } + if(CHM_MAX_PATH_LEN <= (*iter)[INICFG_CAPATH_STR].length()){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is length(%zd) is over max length(1024).", cfgfile.c_str(), INICFG_CAPATH_STR, (*iter)[INICFG_CAPATH_STR].c_str(), INICFG_GLOBAL_SEC_STR, (*iter)[INICFG_CAPATH_STR].length()); + return false; + } + svrnode.capath = (*iter)[INICFG_CAPATH_STR].c_str(); + } + } + + // SSL_SERVER_CERT, SSL_SERVER_PRIKEY + // + if(iter->end() == iter->find(INICFG_SERVER_CERT_STR)){ + svrnode.server_cert = ccvals.server_cert; + }else{ + if(0 == strcasecmp((*iter)[INICFG_SERVER_CERT_STR].c_str(), INICFG_STRING_NULL)){ + svrnode.server_cert = ""; + }else{ + if(!is_file_safe_exist((*iter)[INICFG_SERVER_CERT_STR].c_str())){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s server node in %s section, but it is not safe file.", cfgfile.c_str(), INICFG_SERVER_CERT_STR, (*iter)[INICFG_SERVER_CERT_STR].c_str(), svrnode.name.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + if(CHM_MAX_PATH_LEN <= (*iter)[INICFG_SERVER_CERT_STR].length()){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is length(%zd) is over max length(1024).", cfgfile.c_str(), INICFG_SERVER_CERT_STR, (*iter)[INICFG_SERVER_CERT_STR].c_str(), INICFG_GLOBAL_SEC_STR, (*iter)[INICFG_SERVER_CERT_STR].length()); + return false; + } + svrnode.server_cert = (*iter)[INICFG_SERVER_CERT_STR].c_str(); + } + } + if(svrnode.is_ssl == svrnode.server_cert.empty()){ + ERR_CHMPRN("configuration file(%s) is \"%s\"=value(%s) in %s server node in %s section, but \"%s\" is %s.", cfgfile.c_str(), INICFG_SERVER_CERT_STR, svrnode.server_cert.c_str(), svrnode.name.c_str(), INICFG_SVRNODE_SEC_STR, INICFG_SSL_STR, (svrnode.is_ssl ? "ON" : "OFF")); + return false; + } + if(iter->end() == iter->find(INICFG_SERVER_PRIKEY_STR)){ + svrnode.server_prikey = ccvals.server_prikey; + }else{ + if(0 == strcasecmp((*iter)[INICFG_SERVER_PRIKEY_STR].c_str(), INICFG_STRING_NULL)){ + svrnode.server_prikey = ""; + }else{ + if(!is_file_safe_exist((*iter)[INICFG_SERVER_PRIKEY_STR].c_str())){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s server node in %s section, but it is not safe file.", cfgfile.c_str(), INICFG_SERVER_PRIKEY_STR, (*iter)[INICFG_SERVER_PRIKEY_STR].c_str(), svrnode.name.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + if(CHM_MAX_PATH_LEN <= (*iter)[INICFG_SERVER_PRIKEY_STR].length()){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is length(%zd) is over max length(1024).", cfgfile.c_str(), INICFG_SERVER_PRIKEY_STR, (*iter)[INICFG_SERVER_PRIKEY_STR].c_str(), INICFG_GLOBAL_SEC_STR, (*iter)[INICFG_SERVER_PRIKEY_STR].length()); + return false; + } + svrnode.server_prikey = (*iter)[INICFG_SERVER_PRIKEY_STR].c_str(); + } + } + if(svrnode.is_ssl == svrnode.server_prikey.empty()){ + ERR_CHMPRN("configuration file(%s) is \"%s\"=value(%s) in %s server node in %s section, but \"%s\" is %s.", cfgfile.c_str(), INICFG_SERVER_PRIKEY_STR, svrnode.server_prikey.c_str(), svrnode.name.c_str(), INICFG_SVRNODE_SEC_STR, INICFG_SSL_STR, (svrnode.is_ssl ? "ON" : "OFF")); + return false; + } + + // SSL_SLAVE_CERT, SSL_SLAVE_PRIKEY + // + // [NOTE] : This value is checked after this loop. + // + if(iter->end() == iter->find(INICFG_SLAVE_CERT_STR)){ + svrnode.slave_cert = ccvals.slave_cert; + }else{ + if(0 == strcasecmp((*iter)[INICFG_SLAVE_CERT_STR].c_str(), INICFG_STRING_NULL)){ + svrnode.slave_cert = ""; + }else{ + if(!is_file_safe_exist((*iter)[INICFG_SLAVE_CERT_STR].c_str())){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s server node in %s section, but it is not safe file.", cfgfile.c_str(), INICFG_SLAVE_CERT_STR, (*iter)[INICFG_SLAVE_CERT_STR].c_str(), svrnode.name.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + if(CHM_MAX_PATH_LEN <= (*iter)[INICFG_SLAVE_CERT_STR].length()){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is length(%zd) is over max length(1024).", cfgfile.c_str(), INICFG_SLAVE_CERT_STR, (*iter)[INICFG_SLAVE_CERT_STR].c_str(), INICFG_GLOBAL_SEC_STR, (*iter)[INICFG_SLAVE_CERT_STR].length()); + return false; + } + svrnode.slave_cert = (*iter)[INICFG_SLAVE_CERT_STR].c_str(); + } + } + + if(iter->end() == iter->find(INICFG_SLAVE_PRIKEY_STR)){ + svrnode.slave_prikey = ccvals.slave_prikey; + }else{ + if(0 == strcasecmp((*iter)[INICFG_SLAVE_PRIKEY_STR].c_str(), INICFG_STRING_NULL)){ + svrnode.slave_prikey = ""; + }else{ + if(!is_file_safe_exist((*iter)[INICFG_SLAVE_PRIKEY_STR].c_str())){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s server node in %s section, but it is not safe file.", cfgfile.c_str(), INICFG_SLAVE_PRIKEY_STR, (*iter)[INICFG_SLAVE_PRIKEY_STR].c_str(), svrnode.name.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + if(CHM_MAX_PATH_LEN <= (*iter)[INICFG_SLAVE_PRIKEY_STR].length()){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is length(%zd) is over max length(1024).", cfgfile.c_str(), INICFG_SLAVE_PRIKEY_STR, (*iter)[INICFG_SLAVE_PRIKEY_STR].c_str(), INICFG_GLOBAL_SEC_STR, (*iter)[INICFG_SLAVE_PRIKEY_STR].length()); + return false; + } + svrnode.slave_prikey = (*iter)[INICFG_SLAVE_PRIKEY_STR].c_str(); + } + } + if(svrnode.slave_cert.empty() != svrnode.slave_prikey.empty()){ + // SSL_SLAVE_CERT, SSL_SLAVE_PRIKEY must be set or not set. + ERR_CHMPRN("configuration file(%s) have \"%s\"=value(%s) and \"%s\"=value(%s) in %s server node in %s section.", cfgfile.c_str(), INICFG_SLAVE_CERT_STR, svrnode.slave_cert.c_str(), INICFG_SLAVE_PRIKEY_STR, svrnode.slave_prikey.c_str(), svrnode.name.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + + // Expand name(simple regex) + // Need to expand for server node, because node name is compared directly and is used by connecting. + // So that, we expand server node name here. + // + strlst_t expand_svrnodes; + expand_svrnodes.clear(); + if(!ExpandSimpleRegxHostname(svrnode.name.c_str(), expand_svrnodes, true, true, false)){ // convert localhost to server name and query FQDN. + MSG_CHMPRN("Failed to expand server node name(%s).", svrnode.name.c_str()); + return false; + } + + // Add each expanded server node name. + for(strlst_t::const_iterator svrnodeiter = expand_svrnodes.begin(); svrnodeiter != expand_svrnodes.end(); ++svrnodeiter){ + svrnode.name = (*svrnodeiter); + chmcfginfo.servers.push_back(svrnode); + + // whichever server mode or not? + if(!ccvals.is_server_by_ctlport && chmcfginfo.self_ctlport == svrnode.ctlport && 0 == strcasecmp(svrnode.name.c_str(), localhost_hostname.c_str())){ + ccvals.is_server_by_ctlport = true; + } + } + } + + // Re-Check SSL_CAPATH, SSL_SLAVE_CERT, SSL_SLAVE_PRIKEY + // + for(chmnode_cfginfos_t::iterator iter = chmcfginfo.servers.begin(); iter != chmcfginfo.servers.end(); ++iter){ + if(!ccvals.found_ssl){ + // If there is no ssl server, all servers should not have CApath. + if(!iter->capath.empty()){ + ERR_CHMPRN("configuration file(%s) have \"%s\"=value(%s) in %s server node in %s section.", cfgfile.c_str(), INICFG_CAPATH_STR, iter->capath.c_str(), iter->name.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + // If there is no ssl servers and no verify peer, any servers should not have client cert and private key. + if(!iter->slave_cert.empty() || !iter->slave_prikey.empty()){ + ERR_CHMPRN("configuration file(%s) have \"%s\"=value(%s) and \"%s\"=value(%s) in %s server node in %s section.", cfgfile.c_str(), INICFG_SLAVE_CERT_STR, iter->slave_cert.c_str(), INICFG_SLAVE_PRIKEY_STR, iter->slave_prikey.c_str(), iter->name.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + }else{ + if(ccvals.found_ssl_verify_peer){ + // If there are ssl servers with verify peer, all servers must have client cert and private key. + if(iter->slave_cert.empty() || iter->slave_prikey.empty()){ + ERR_CHMPRN("configuration file(%s) have \"%s\"=value(%s) and \"%s\"=value(%s) in %s server node in %s section.", cfgfile.c_str(), INICFG_SLAVE_CERT_STR, iter->slave_cert.c_str(), INICFG_SLAVE_PRIKEY_STR, iter->slave_prikey.c_str(), iter->name.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + }else{ + // If there are ssl servers without verify peer, any servers should not have client cert and private key. + if(!iter->slave_cert.empty() || !iter->slave_prikey.empty()){ + ERR_CHMPRN("configuration file(%s) have \"%s\"=value(%s) and \"%s\"=value(%s) in %s server node in %s section.", cfgfile.c_str(), INICFG_SLAVE_CERT_STR, iter->slave_cert.c_str(), INICFG_SLAVE_PRIKEY_STR, iter->slave_prikey.c_str(), iter->name.c_str(), INICFG_SVRNODE_SEC_STR); + return false; + } + + } + } + } + // [NOTE] + // The server list might have duplicate server name & port. + // Because the port number can not be specified in configration file, so if there is some server nodes on same server + // and one specifies port and the other does not specify port(using default port). + // On this case, the list have duplicate server. + // + chmcfginfo.servers.unique(chm_node_cfg_info_same_name_port()); // uniq about server node must be name and port + chmcfginfo.servers.sort(chm_node_cfg_info_sort()); + + // slave node section + if(0 == chmcfgraw.slave_nodes.size()){ + ERR_CHMPRN("configuration file(%s) does not have %s section.", cfgfile.c_str(), INICFG_SLVNODE_SEC_STR); + return false; + } + for(strmaparr_t::iterator iter = chmcfgraw.slave_nodes.begin(); iter != chmcfgraw.slave_nodes.end(); ++iter){ + CHMNODE_CFGINFO slvnode; + + if(iter->end() == iter->find(INICFG_NAME_STR)){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s section.", cfgfile.c_str(), INICFG_NAME_STR, INICFG_SLVNODE_SEC_STR); + return false; + }else{ + // slave server name is allowed regexed format. + // so do not convert name to global name(can not convert regexed name). + // + slvnode.name = (*iter)[INICFG_NAME_STR]; + } + if(iter->end() == iter->find(INICFG_CTLPORT_STR)){ + if(CHM_INVALID_PORT == ccvals.ctlport){ + MSG_CHMPRN("configuration file(%s) does not have \"%s\" in %s slave node in %s section.", cfgfile.c_str(), INICFG_CTLPORT_STR, slvnode.name.c_str(), INICFG_SLVNODE_SEC_STR); + return false; + } + slvnode.ctlport = ccvals.ctlport; + }else{ + slvnode.ctlport = static_cast(atoi((*iter)[INICFG_CTLPORT_STR].c_str())); + } + slvnode.port = CHM_INVALID_PORT; + slvnode.is_ssl = false; + slvnode.verify_peer = false; + + // SSL_CAPATH + // + if(iter->end() == iter->find(INICFG_CAPATH_STR)){ + slvnode.is_ca_file = ccvals.is_ca_file; + slvnode.capath = ccvals.capath; + }else{ + if(0 == strcasecmp((*iter)[INICFG_CAPATH_STR].c_str(), INICFG_STRING_NULL)){ + slvnode.is_ca_file = false; + slvnode.capath = ""; + }else{ + if(is_dir_exist((*iter)[INICFG_CAPATH_STR].c_str())){ + slvnode.is_ca_file = false; + }else{ + if(is_file_safe_exist((*iter)[INICFG_CAPATH_STR].c_str())){ + slvnode.is_ca_file = true; + }else{ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s server node in %s section, but it is not directory or file.", cfgfile.c_str(), INICFG_CAPATH_STR, (*iter)[INICFG_CAPATH_STR].c_str(), slvnode.name.c_str(), INICFG_SLVNODE_SEC_STR); + return false; + } + } + if(CHM_MAX_PATH_LEN <= (*iter)[INICFG_CAPATH_STR].length()){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is length(%zd) is over max length(1024).", cfgfile.c_str(), INICFG_CAPATH_STR, (*iter)[INICFG_CAPATH_STR].c_str(), INICFG_SLVNODE_SEC_STR, (*iter)[INICFG_CAPATH_STR].length()); + return false; + } + slvnode.capath = (*iter)[INICFG_CAPATH_STR].c_str(); + } + } + if(!ccvals.found_ssl && !slvnode.capath.empty()){ + // There is not SSL, but CApath is set. + ERR_CHMPRN("configuration file(%s) have \"%s\"=value(%s) in %s server node in %s section.", cfgfile.c_str(), INICFG_CAPATH_STR, slvnode.capath.c_str(), slvnode.name.c_str(), INICFG_SLVNODE_SEC_STR); + return false; + } + + // SSL_SLAVE_CERT, SSL_SLAVE_PRIKEY + // + if(iter->end() == iter->find(INICFG_SLAVE_CERT_STR)){ + slvnode.slave_cert = ccvals.slave_cert; + }else{ + if(0 == strcasecmp((*iter)[INICFG_SLAVE_CERT_STR].c_str(), INICFG_STRING_NULL)){ + slvnode.slave_cert = ""; + }else{ + if(!is_file_safe_exist((*iter)[INICFG_SLAVE_CERT_STR].c_str())){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s server node in %s section, but it is not safe file.", cfgfile.c_str(), INICFG_SLAVE_CERT_STR, (*iter)[INICFG_SLAVE_CERT_STR].c_str(), slvnode.name.c_str(), INICFG_SLVNODE_SEC_STR); + return false; + } + if(CHM_MAX_PATH_LEN <= (*iter)[INICFG_SLAVE_CERT_STR].length()){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is length(%zd) is over max length(1024).", cfgfile.c_str(), INICFG_SLAVE_CERT_STR, (*iter)[INICFG_SLAVE_CERT_STR].c_str(), INICFG_SLVNODE_SEC_STR, (*iter)[INICFG_SLAVE_CERT_STR].length()); + return false; + } + slvnode.slave_cert = (*iter)[INICFG_SLAVE_CERT_STR].c_str(); + } + } + if(iter->end() == iter->find(INICFG_SLAVE_PRIKEY_STR)){ + slvnode.slave_prikey = ccvals.slave_prikey; + }else{ + if(0 == strcasecmp((*iter)[INICFG_SLAVE_PRIKEY_STR].c_str(), INICFG_STRING_NULL)){ + slvnode.slave_prikey = ""; + }else{ + if(!is_file_safe_exist((*iter)[INICFG_SLAVE_PRIKEY_STR].c_str())){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s server node in %s section, but it is not safe file.", cfgfile.c_str(), INICFG_SLAVE_PRIKEY_STR, (*iter)[INICFG_SLAVE_PRIKEY_STR].c_str(), slvnode.name.c_str(), INICFG_SLVNODE_SEC_STR); + return false; + } + if(CHM_MAX_PATH_LEN <= (*iter)[INICFG_SLAVE_PRIKEY_STR].length()){ + ERR_CHMPRN("configuration file(%s) have \"%s\" value(%s) in %s section, but it is length(%zd) is over max length(1024).", cfgfile.c_str(), INICFG_SLAVE_PRIKEY_STR, (*iter)[INICFG_SLAVE_PRIKEY_STR].c_str(), INICFG_SLVNODE_SEC_STR, (*iter)[INICFG_SLAVE_PRIKEY_STR].length()); + return false; + } + slvnode.slave_prikey = (*iter)[INICFG_SLAVE_PRIKEY_STR].c_str(); + } + } + if(slvnode.slave_cert.empty() != slvnode.slave_prikey.empty()){ + // SSL_SLAVE_CERT, SSL_SLAVE_PRIKEY must be set or not set. + ERR_CHMPRN("configuration file(%s) have \"%s\"=value(%s) and \"%s\"=value(%s) in %s server node in %s section.", cfgfile.c_str(), INICFG_SLAVE_CERT_STR, slvnode.slave_cert.c_str(), INICFG_SLAVE_PRIKEY_STR, slvnode.slave_prikey.c_str(), slvnode.name.c_str(), INICFG_SLVNODE_SEC_STR); + return false; + } + if(ccvals.found_ssl_verify_peer == slvnode.slave_cert.empty()){ + // If There is SSL_VERIFY_PEER, but client cert(and private key) must be set.(nor so on) + ERR_CHMPRN("configuration file(%s) have \"%s\"=value(%s) and \"%s\"=value(%s) in %s server node in %s section.", cfgfile.c_str(), INICFG_SLAVE_CERT_STR, slvnode.slave_cert.c_str(), INICFG_SLAVE_PRIKEY_STR, slvnode.slave_prikey.c_str(), slvnode.name.c_str(), INICFG_SLVNODE_SEC_STR); + return false; + } + + chmcfginfo.slaves.push_back(slvnode); + } + chmcfginfo.slaves.unique(chm_node_cfg_info_same_name_port()); // uniq about slave node must be name and port + chmcfginfo.slaves.sort(chm_node_cfg_info_sort()); + + // Re-check for server/slave mode by self control port number. + // + if(ccvals.is_server_by_ctlport){ + if(!ccvals.server_mode){ + WAN_CHMPRN("configuration file(%s) does not have \"%s\" in %s section, but self control port(%d) found in server list, so run server mode.", cfgfile.c_str(), INICFG_MODE_STR, INICFG_GLOBAL_SEC_STR, chmcfginfo.self_ctlport); + chmcfginfo.is_server_mode = true; + }else{ + if(!chmcfginfo.is_server_mode){ + ERR_CHMPRN("configuration file(%s) have \"%s\" as slave mode in %s section, but self control port(%d) found in server list.", cfgfile.c_str(), INICFG_MODE_STR, INICFG_GLOBAL_SEC_STR, chmcfginfo.self_ctlport); + return false; + } + } + }else{ + if(!ccvals.server_mode){ + WAN_CHMPRN("configuration file(%s) does not have \"%s\" in %s section, but self control port(%d) found in slave list, so run slave mode.", cfgfile.c_str(), INICFG_MODE_STR, INICFG_GLOBAL_SEC_STR, chmcfginfo.self_ctlport); + chmcfginfo.is_server_mode = false; + }else{ + if(chmcfginfo.is_server_mode){ + ERR_CHMPRN("configuration file(%s) have \"%s\" as server mode in %s section, but self control port(%d) not found in server list.", cfgfile.c_str(), INICFG_MODE_STR, INICFG_GLOBAL_SEC_STR, chmcfginfo.self_ctlport); + return false; + } + } + } + + // check merge flags + if(chmcfginfo.is_random_mode){ + if(chmcfginfo.is_auto_merge){ + WAN_CHMPRN("Specified %s=ON and %s=ON. These options can not be specified at the same time, so SET %s=OFF.", INICFG_DELIVERMODE_STR, INICFG_AUTOMERGE_STR, INICFG_AUTOMERGE_STR); + chmcfginfo.is_auto_merge = false; + } + if(chmcfginfo.is_do_merge){ + WAN_CHMPRN("Specified %s=ON and %s=ON. These options can not be specified at the same time, so SET %s=OFF.", INICFG_DELIVERMODE_STR, INICFG_DOMERGE_STR, INICFG_DOMERGE_STR); + chmcfginfo.is_do_merge = false; + } + }else{ + if(!chmcfginfo.is_do_merge && chmcfginfo.is_auto_merge){ + WAN_CHMPRN("Specified %s=ON and %s=ON. These options can not be specified at the same time, so SET %s=OFF.", INICFG_DOMERGE_STR, INICFG_AUTOMERGE_STR, INICFG_AUTOMERGE_STR); + chmcfginfo.is_auto_merge = false; + } + } + + return true; +} + +//--------------------------------------------------------- +// Loading Yaml Utilities for CHMYamlBaseConf Class +//--------------------------------------------------------- +static bool ChmYamlLoadConfigrationGlobalSec(yaml_parser_t& yparser, CHMCFGINFO& chmcfginfo, CHMCONF_CCV& ccvals, short default_ctlport) +{ + // Must start yaml mapping event. + yaml_event_t yevent; + if(!yaml_parser_parse(&yparser, &yevent)){ + ERR_CHMPRN("Could not parse event. errno = %d", errno); + return false; + } + if(YAML_MAPPING_START_EVENT != yevent.type){ + ERR_CHMPRN("Parsed event type is not start mapping(%d)", yevent.type); + yaml_event_delete(&yevent); + return false; + } + yaml_event_delete(&yevent); + + // Set default value in GLOBAL section + chmcfginfo.max_chmpx_count = DEFAULT_CHMPX_COUNT; + chmcfginfo.replica_count = DEFAULT_REPLICA_COUNT; + chmcfginfo.max_server_mq_cnt = DEFAULT_SERVER_MQ_CNT; + chmcfginfo.max_client_mq_cnt = DEFAULT_CLIENT_MQ_CNT; + chmcfginfo.mqcnt_per_attach = DEFAULT_MQ_PER_ATTACH; + chmcfginfo.max_q_per_servermq = DEFAULT_QUEUE_PER_SERVERMQ; + chmcfginfo.max_q_per_clientmq = DEFAULT_QUEUE_PER_CLIENTMQ; + chmcfginfo.max_mq_per_client = DEFAULT_MQ_PER_CLIENT; + chmcfginfo.max_histlog_count = DEFAULT_HISTLOG_COUNT; + chmcfginfo.date = 0L; + chmcfginfo.self_ctlport = default_ctlport; + chmcfginfo.retrycnt = CHMEVENTSOCK_RETRY_DEFAULT; + chmcfginfo.mq_retrycnt = ChmEventMq::DEFAULT_RETRYCOUNT; + chmcfginfo.mq_ack = true; + chmcfginfo.timeout_wait_socket = CHMEVENTSOCK_TIMEOUT_DEFAULT; + chmcfginfo.timeout_wait_connect = CHMEVENTSOCK_TIMEOUT_DEFAULT; + chmcfginfo.timeout_wait_mq = CHMEVENTMQ_TIMEOUT_DEFAULT; + chmcfginfo.is_auto_merge = false; + chmcfginfo.is_do_merge = false; + chmcfginfo.timeout_merge = CHMEVENTMQ_TIMEOUT_DEFAULT; + chmcfginfo.sock_thread_cnt = ChmEventSock::DEFAULT_SOCK_THREAD_CNT; + chmcfginfo.mq_thread_cnt = ChmEventMq::DEFAULT_MQ_THREAD_CNT; + chmcfginfo.max_sock_pool = ChmEventSock::DEFAULT_MAX_SOCK_POOL; + chmcfginfo.sock_pool_timeout = ChmEventSock::DEFAULT_SOCK_POOL_TIMEOUT; + chmcfginfo.k2h_fullmap = true; + chmcfginfo.k2h_mask_bitcnt = K2HShm::DEFAULT_MASK_BITCOUNT; + chmcfginfo.k2h_cmask_bitcnt = K2HShm::DEFAULT_COLLISION_MASK_BITCOUNT; + chmcfginfo.k2h_max_element = K2HShm::DEFAULT_MAX_ELEMENT_CNT; + + // Clear default values + ccvals.port = CHM_INVALID_PORT; + ccvals.ctlport = CHM_INVALID_PORT; + ccvals.server_mode = false; + ccvals.is_ssl = false; + ccvals.verify_peer = false; + ccvals.is_ca_file = false; + ccvals.capath = ""; + ccvals.server_cert = ""; + ccvals.server_prikey = ""; + ccvals.slave_cert = ""; + ccvals.slave_prikey = ""; + + // Loading + string key(""); + bool result = true; + for(bool is_loop = true; is_loop && result; ){ + // get event + if(!yaml_parser_parse(&yparser, &yevent)){ + ERR_CHMPRN("Could not parse event. errno = %d", errno); + result = false; + continue; + } + + // check event + if(YAML_MAPPING_END_EVENT == yevent.type){ + // End of mapping event + is_loop = false; + + }else if(YAML_SCALAR_EVENT == yevent.type){ + // Load key & value + if(key.empty()){ + key = reinterpret_cast(yevent.data.scalar.value); + }else{ + // + // Compare key and set value + // + if(0 == strcasecmp(INICFG_GROUP_STR, key.c_str())){ + chmcfginfo.groupname = reinterpret_cast(yevent.data.scalar.value); + + }else if(0 == strcasecmp(INICFG_VERSION_STR, key.c_str())){ + chmcfginfo.revision = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + + }else if(0 == strcasecmp(INICFG_MODE_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_MODE_SERVER_STR)){ + chmcfginfo.is_server_mode = true; + }else if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_MODE_SLAVE_STR)){ + chmcfginfo.is_server_mode = false; + }else{ + ERR_CHMPRN("Found %s in %s section, but value %s does not defined.", INICFG_MODE_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + ccvals.server_mode = true; + + }else if(0 == strcasecmp(INICFG_DELIVERMODE_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_DELIVERMODE_RANDOM_STR)){ + chmcfginfo.is_random_mode = true; + }else if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_DELIVERMODE_HASH_STR)){ + chmcfginfo.is_random_mode = false; + }else{ + ERR_CHMPRN("Found %s in %s section, but value %s does not defined.", INICFG_DELIVERMODE_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + + }else if(0 == strcasecmp(INICFG_MAXCHMPX_STR, key.c_str())){ + chmcfginfo.max_chmpx_count = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + if(MAX_CHMPX_COUNT < chmcfginfo.max_chmpx_count){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MAXCHMPX_STR, chmcfginfo.max_chmpx_count, MAX_CHMPX_COUNT); + chmcfginfo.max_chmpx_count = MAX_CHMPX_COUNT; + } + + }else if(0 == strcasecmp(INICFG_REPLICA_STR, key.c_str())){ + chmcfginfo.replica_count = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + if(chmcfginfo.is_random_mode){ + if(DEFAULT_REPLICA_COUNT != chmcfginfo.replica_count){ + WAN_CHMPRN("\"%s\" value(%ld) is not set, because random mode does not do replication. This value should be %d on random mode.", INICFG_REPLICA_STR, chmcfginfo.replica_count, DEFAULT_REPLICA_COUNT); + chmcfginfo.replica_count = DEFAULT_REPLICA_COUNT; + } + }else{ + if(chmcfginfo.max_chmpx_count < chmcfginfo.replica_count){ + WAN_CHMPRN("\"%s\" value(%ld) is over maximum chmpx count(%ld), so set value to maximum chmpx count.", INICFG_REPLICA_STR, chmcfginfo.replica_count, chmcfginfo.max_chmpx_count); + chmcfginfo.replica_count = chmcfginfo.max_chmpx_count; + } + } + + }else if(0 == strcasecmp(INICFG_MAXMQSERVER_STR, key.c_str())){ + chmcfginfo.max_server_mq_cnt = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + if(MAX_SERVER_MQ_CNT < chmcfginfo.max_server_mq_cnt){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MAXMQSERVER_STR, chmcfginfo.max_server_mq_cnt, MAX_SERVER_MQ_CNT); + chmcfginfo.max_server_mq_cnt = MAX_SERVER_MQ_CNT; + } + + }else if(0 == strcasecmp(INICFG_MAXMQCLIENT_STR, key.c_str())){ + chmcfginfo.max_client_mq_cnt = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + if(MAX_CLIENT_MQ_CNT < chmcfginfo.max_client_mq_cnt){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MAXMQCLIENT_STR, chmcfginfo.max_client_mq_cnt, MAX_CLIENT_MQ_CNT); + chmcfginfo.max_client_mq_cnt = MAX_CLIENT_MQ_CNT; + } + + }else if(0 == strcasecmp(INICFG_MQPERATTACH_STR, key.c_str())){ + chmcfginfo.mqcnt_per_attach = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + if(MAX_MQ_PER_ATTACH < chmcfginfo.mqcnt_per_attach){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MQPERATTACH_STR, chmcfginfo.mqcnt_per_attach, MAX_MQ_PER_ATTACH); + chmcfginfo.mqcnt_per_attach = MAX_MQ_PER_ATTACH; + } + + }else if(0 == strcasecmp(INICFG_MAXQPERSERVERMQ_STR, key.c_str())){ + chmcfginfo.max_q_per_servermq = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + if(MAX_QUEUE_PER_SERVERMQ < chmcfginfo.max_q_per_servermq){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MAXQPERSERVERMQ_STR, chmcfginfo.max_q_per_servermq, MAX_QUEUE_PER_SERVERMQ); + chmcfginfo.max_q_per_servermq = MAX_QUEUE_PER_SERVERMQ; + } + + }else if(0 == strcasecmp(INICFG_MAXQPERCLIENTMQ_STR, key.c_str())){ + chmcfginfo.max_q_per_clientmq = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + if(MAX_QUEUE_PER_CLIENTMQ < chmcfginfo.max_q_per_clientmq){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MAXQPERCLIENTMQ_STR, chmcfginfo.max_q_per_clientmq, MAX_QUEUE_PER_CLIENTMQ); + chmcfginfo.max_q_per_clientmq = MAX_QUEUE_PER_CLIENTMQ; + } + + }else if(0 == strcasecmp(INICFG_MAXMQPERCLIENT_STR, key.c_str())){ + chmcfginfo.max_mq_per_client = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + if(MAX_MQ_PER_CLIENT < chmcfginfo.max_mq_per_client){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MAXMQPERCLIENT_STR, chmcfginfo.max_mq_per_client, MAX_MQ_PER_CLIENT); + chmcfginfo.max_mq_per_client = MAX_MQ_PER_CLIENT; + } + + }else if(0 == strcasecmp(INICFG_MAXHISTLOG_STR, key.c_str())){ + chmcfginfo.max_histlog_count = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + if(MAX_HISTLOG_COUNT < chmcfginfo.max_histlog_count){ + WAN_CHMPRN("\"%s\" value(%ld) is over upper limit(%d), so set upper limit.", INICFG_MAXHISTLOG_STR, chmcfginfo.max_histlog_count, MAX_HISTLOG_COUNT); + chmcfginfo.max_histlog_count = MAX_HISTLOG_COUNT; + } + + }else if(0 == strcasecmp(INICFG_DATE_STR, key.c_str())){ + chmcfginfo.date = rfcdate_time(reinterpret_cast(yevent.data.scalar.value)); + + }else if(0 == strcasecmp(INICFG_PORT_STR, key.c_str())){ + ccvals.port = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + + }else if(0 == strcasecmp(INICFG_CTLPORT_STR, key.c_str())){ + ccvals.ctlport = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + + }else if(0 == strcasecmp(INICFG_SELFCTLPORT_STR, key.c_str())){ + if(CHM_INVALID_PORT == default_ctlport){ + chmcfginfo.self_ctlport = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + } + + }else if(0 == strcasecmp(INICFG_RETRYCNT_STR, key.c_str())){ + chmcfginfo.retrycnt = atoi(reinterpret_cast(yevent.data.scalar.value)); + + }else if(0 == strcasecmp(INICFG_MQRETRYCNT_STR, key.c_str())){ + chmcfginfo.mq_retrycnt = atoi(reinterpret_cast(yevent.data.scalar.value)); + + }else if(0 == strcasecmp(INICFG_MQACK_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_ON) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_YES)){ + chmcfginfo.mq_ack = true; + }else if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_OFF) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_NO)){ + chmcfginfo.mq_ack = false; + }else{ + ERR_CHMPRN("Found %s in %s section, but value %s does not defined.", INICFG_MQACK_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + + }else if(0 == strcasecmp(INICFG_RWTIMEOUT_STR, key.c_str())){ + chmcfginfo.timeout_wait_socket = atoi(reinterpret_cast(yevent.data.scalar.value)); + if(chmcfginfo.timeout_wait_socket < CHMEVENTSOCK_TIMEOUT_DEFAULT){ + chmcfginfo.timeout_wait_socket = CHMEVENTSOCK_TIMEOUT_DEFAULT; + } + + }else if(0 == strcasecmp(INICFG_CONTIMEOUT_STR, key.c_str())){ + chmcfginfo.timeout_wait_connect = atoi(reinterpret_cast(yevent.data.scalar.value)); + if(chmcfginfo.timeout_wait_connect < CHMEVENTSOCK_TIMEOUT_DEFAULT){ + chmcfginfo.timeout_wait_connect = CHMEVENTSOCK_TIMEOUT_DEFAULT; + } + + }else if(0 == strcasecmp(INICFG_MQRWTIMEOUT_STR, key.c_str())){ + chmcfginfo.timeout_wait_mq = atoi(reinterpret_cast(yevent.data.scalar.value)); + if(chmcfginfo.timeout_wait_mq < CHMEVENTMQ_TIMEOUT_DEFAULT){ + chmcfginfo.timeout_wait_mq = CHMEVENTMQ_TIMEOUT_DEFAULT; + } + + }else if(0 == strcasecmp(INICFG_AUTOMERGE_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_ON) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_YES)){ + chmcfginfo.is_auto_merge = true; + }else if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_OFF) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_NO)){ + chmcfginfo.is_auto_merge = false; + }else{ + ERR_CHMPRN("Found %s in %s section, but value %s does not defined.", INICFG_AUTOMERGE_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + + }else if(0 == strcasecmp(INICFG_DOMERGE_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_ON) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_YES)){ + chmcfginfo.is_do_merge = true; + }else if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_OFF) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_NO)){ + chmcfginfo.is_do_merge = false; + }else{ + ERR_CHMPRN("Found %s in %s section, but value %s does not defined.", INICFG_DOMERGE_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + + }else if(0 == strcasecmp(INICFG_MERGETIMEOUT_STR, key.c_str())){ + chmcfginfo.timeout_merge = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + if(chmcfginfo.timeout_merge < CHMEVENTMQ_TIMEOUT_DEFAULT){ + chmcfginfo.timeout_merge = CHMEVENTMQ_TIMEOUT_DEFAULT; + } + + }else if(0 == strcasecmp(INICFG_SOCKTHREADCNT_STR, key.c_str())){ + chmcfginfo.sock_thread_cnt = atoi(reinterpret_cast(yevent.data.scalar.value)); + if(chmcfginfo.sock_thread_cnt < ChmEventSock::DEFAULT_SOCK_THREAD_CNT){ + ERR_CHMPRN("Found %s in %s section, but value %s is wrong.", INICFG_SOCKTHREADCNT_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + + }else if(0 == strcasecmp(INICFG_MQTHREADCNT_STR, key.c_str())){ + chmcfginfo.mq_thread_cnt = atoi(reinterpret_cast(yevent.data.scalar.value)); + if(chmcfginfo.mq_thread_cnt < ChmEventMq::DEFAULT_MQ_THREAD_CNT){ + ERR_CHMPRN("Found %s in %s section, but value %s is wrong.", INICFG_MQTHREADCNT_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + + }else if(0 == strcasecmp(INICFG_MAXSOCKPOOL_STR, key.c_str())){ + chmcfginfo.max_sock_pool = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + if(chmcfginfo.max_sock_pool < ChmEventSock::DEFAULT_MAX_SOCK_POOL){ + ERR_CHMPRN("Found %s in %s section, but value %s is wrong.", INICFG_MAXSOCKPOOL_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + + }else if(0 == strcasecmp(INICFG_SOCKPOOLTIMEOUT_STR, key.c_str())){ + chmcfginfo.sock_pool_timeout = atoi(reinterpret_cast(yevent.data.scalar.value)); + if(chmcfginfo.sock_pool_timeout < ChmEventSock::NO_SOCK_POOL_TIMEOUT){ + ERR_CHMPRN("Found %s in %s section, but value %s is wrong.", INICFG_SOCKPOOLTIMEOUT_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + + }else if(0 == strcasecmp(INICFG_SSL_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_ON) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_YES)){ + ccvals.is_ssl = true; + }else if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_OFF) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_NO)){ + ccvals.is_ssl = false; + }else{ + ERR_CHMPRN("Found %s in %s section, but value %s does not defined.", INICFG_SSL_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + + }else if(0 == strcasecmp(INICFG_SSL_VERIFY_PEER_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_ON) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_YES)){ + if(!ccvals.is_ssl){ + ERR_CHMPRN("Found %s in %s section with value(%s), but %s is OFF.", INICFG_SSL_VERIFY_PEER_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value), INICFG_SSL_STR); + result = false; + }else{ + ccvals.verify_peer = true; + } + }else if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_OFF) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_NO)){ + ccvals.verify_peer = false; + }else{ + ERR_CHMPRN("Found %s in %s section, but value %s is wrong.", INICFG_SSL_VERIFY_PEER_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + + }else if(0 == strcasecmp(INICFG_CAPATH_STR, key.c_str())){ + if(0 != strcasecmp(INICFG_STRING_NULL, reinterpret_cast(yevent.data.scalar.value))){ // set only when value is not "NULL" + if(is_dir_exist(reinterpret_cast(yevent.data.scalar.value))){ + ccvals.is_ca_file = false; + }else{ + if(is_file_safe_exist(reinterpret_cast(yevent.data.scalar.value))){ + ccvals.is_ca_file = true; + }else{ + ERR_CHMPRN("Found %s in %s section with value %s, but it is not directory or file.", INICFG_CAPATH_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + } + if(CHM_MAX_PATH_LEN <= strlen(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but its length(%zd) is over max length(1024).", INICFG_CAPATH_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value), strlen(reinterpret_cast(yevent.data.scalar.value))); + result = false; + }else{ + ccvals.capath = reinterpret_cast(yevent.data.scalar.value); + } + } + + }else if(0 == strcasecmp(INICFG_SERVER_CERT_STR, key.c_str())){ + if(0 != strcasecmp(INICFG_STRING_NULL, reinterpret_cast(yevent.data.scalar.value))){ // set only when value is not "NULL" + if(!is_file_safe_exist(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but it is not safe file.", INICFG_SERVER_CERT_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + }else{ + if(!ccvals.is_ssl){ + ERR_CHMPRN("Found %s in %s section with value(%s), but %s is OFF.", INICFG_SERVER_CERT_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value), INICFG_SSL_STR); + result = false; + }else{ + if(CHM_MAX_PATH_LEN <= strlen(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but its length(%zd) is over max length(1024).", INICFG_SERVER_CERT_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value), strlen(reinterpret_cast(yevent.data.scalar.value))); + result = false; + }else{ + ccvals.server_cert = reinterpret_cast(yevent.data.scalar.value); + } + } + } + } + + }else if(0 == strcasecmp(INICFG_SERVER_PRIKEY_STR, key.c_str())){ + if(0 != strcasecmp(INICFG_STRING_NULL, reinterpret_cast(yevent.data.scalar.value))){ // set only when value is not "NULL" + if(!is_file_safe_exist(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but it is not safe file.", INICFG_SERVER_PRIKEY_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + }else{ + if(!ccvals.is_ssl){ + ERR_CHMPRN("Found %s in %s section with value(%s), but %s is OFF.", INICFG_SERVER_PRIKEY_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value), INICFG_SSL_STR); + result = false; + }else{ + if(CHM_MAX_PATH_LEN <= strlen(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but its length(%zd) is over max length(1024).", INICFG_SERVER_PRIKEY_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value), strlen(reinterpret_cast(yevent.data.scalar.value))); + result = false; + }else{ + ccvals.server_prikey = reinterpret_cast(yevent.data.scalar.value); + } + } + } + } + + }else if(0 == strcasecmp(INICFG_SLAVE_CERT_STR, key.c_str())){ + if(0 != strcasecmp(INICFG_STRING_NULL, reinterpret_cast(yevent.data.scalar.value))){ // set only when value is not "NULL" + if(!is_file_safe_exist(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but it is not safe file.", INICFG_SLAVE_CERT_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + }else{ + if(CHM_MAX_PATH_LEN <= strlen(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but its length(%zd) is over max length(1024).", INICFG_SLAVE_CERT_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value), strlen(reinterpret_cast(yevent.data.scalar.value))); + result = false; + }else{ + ccvals.slave_cert = reinterpret_cast(yevent.data.scalar.value); + } + } + } + + }else if(0 == strcasecmp(INICFG_SLAVE_PRIKEY_STR, key.c_str())){ + if(0 != strcasecmp(INICFG_STRING_NULL, reinterpret_cast(yevent.data.scalar.value))){ // set only when value is not "NULL" + if(!is_file_safe_exist(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but it is not safe file.", INICFG_SLAVE_PRIKEY_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + }else{ + if(CHM_MAX_PATH_LEN <= strlen(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but its length(%zd) is over max length(1024).", INICFG_SLAVE_PRIKEY_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value), strlen(reinterpret_cast(yevent.data.scalar.value))); + result = false; + }else{ + ccvals.slave_prikey = reinterpret_cast(yevent.data.scalar.value); + } + } + } + + }else if(0 == strcasecmp(INICFG_K2HFULLMAP_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_ON) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_YES)){ + chmcfginfo.k2h_fullmap = true; + }else if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_OFF) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_NO)){ + chmcfginfo.k2h_fullmap = false; + }else{ + ERR_CHMPRN("Found %s in %s section, but value %s is wrong.", INICFG_K2HFULLMAP_STR, CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + + }else if(0 == strcasecmp(INICFG_K2HMASKBIT_STR, key.c_str())){ + chmcfginfo.k2h_mask_bitcnt = atoi(reinterpret_cast(yevent.data.scalar.value)); + + }else if(0 == strcasecmp(INICFG_K2HCMASKBIT_STR, key.c_str())){ + chmcfginfo.k2h_cmask_bitcnt = atoi(reinterpret_cast(yevent.data.scalar.value)); + + }else if(0 == strcasecmp(INICFG_K2HMAXELE_STR, key.c_str())){ + chmcfginfo.k2h_max_element = atoi(reinterpret_cast(yevent.data.scalar.value)); + + }else{ + WAN_CHMPRN("Found unexpected key(%s) in %s section, thus skip this key and value.", key.c_str(), CFG_GLOBAL_SEC_STR); + } + key.clear(); + } + }else{ + // [TODO] Now not support alias(anchor) event + // + ERR_CHMPRN("Found unexpected yaml event(%d) in %s section.", yevent.type, CFG_GLOBAL_SEC_STR); + result = false; + } + + // delete event + if(is_loop){ + is_loop = yevent.type != YAML_STREAM_END_EVENT; + } + yaml_event_delete(&yevent); + } + + return result; +} + +static bool ChmYamlLoadConfigrationSvrnodeSec(yaml_parser_t& yparser, CHMCFGINFO& chmcfginfo, CHMCONF_CCV& ccvals) +{ + // Must start yaml sequence(for mapping array) -> mapping event. + yaml_event_t yevent; + { + // sequence + if(!yaml_parser_parse(&yparser, &yevent)){ + ERR_CHMPRN("Could not parse event. errno = %d", errno); + return false; + } + if(YAML_SEQUENCE_START_EVENT != yevent.type){ + ERR_CHMPRN("Parsed event type is not start sequence(%d)", yevent.type); + yaml_event_delete(&yevent); + return false; + } + yaml_event_delete(&yevent); + } + + // Clear default values + ccvals.is_server_by_ctlport = false; // for check server/slave mode by checking control port and server name. + ccvals.found_ssl = false; + ccvals.found_ssl_verify_peer = false; + + // temporary data + CHMNODE_CFGINFO svrnode; + string localhost_hostname; + ChmNetDb::GetLocalHostname(localhost_hostname); + + // Loading + string key(""); + bool result = true; + for(bool is_loop = true, in_mapping = false; is_loop && result; ){ + // get event + if(!yaml_parser_parse(&yparser, &yevent)){ + ERR_CHMPRN("Could not parse event. errno = %d", errno); + result = false; + continue; // break loop assap. + } + + // check event + if(YAML_MAPPING_START_EVENT == yevent.type){ + // Start mapping event + if(in_mapping){ + ERR_CHMPRN("Already start yaml mapping event in %s section loop.", CFG_SVRNODE_SEC_STR); + result = false; + }else{ + in_mapping = true; + + // Set default value for each server node + svrnode.name.clear(); + svrnode.port = ccvals.port; + svrnode.ctlport = ccvals.ctlport; + svrnode.is_ssl = ccvals.is_ssl; + svrnode.verify_peer = ccvals.verify_peer; + svrnode.is_ca_file = ccvals.is_ca_file; + svrnode.capath = ccvals.capath; + svrnode.server_cert = ccvals.server_cert; + svrnode.server_prikey = ccvals.server_prikey; + svrnode.slave_cert = ccvals.slave_cert; + svrnode.slave_prikey = ccvals.slave_prikey; + } + + }else if(YAML_MAPPING_END_EVENT == yevent.type){ + // End mapping event + if(!in_mapping){ + ERR_CHMPRN("Already stop yaml mapping event in %s section loop.", CFG_SVRNODE_SEC_STR); + result = false; + }else{ + // Finish one server node configuration. + // + if(svrnode.is_ssl){ + ccvals.found_ssl = true; // Keep SSL flag for checking after this methods + } + if(svrnode.verify_peer){ + ccvals.found_ssl_verify_peer = true; // Keep verify peer flag for checking after this loop + } + + // check values + if(svrnode.name.empty()){ + ERR_CHMPRN("Found some value in %s section, but NAME is empty.", CFG_SVRNODE_SEC_STR); + result = false; + } + if(CHM_INVALID_PORT == svrnode.port){ + ERR_CHMPRN("Invalid port number for %s server node.", svrnode.name.c_str()); + result = false; + } + if(CHM_INVALID_PORT == svrnode.ctlport){ + ERR_CHMPRN("Invalid control port number for %s server node.", svrnode.name.c_str()); + result = false; + } + if(!svrnode.is_ssl && svrnode.verify_peer){ + ERR_CHMPRN("Found %s with value(ON) in %s section, but %s is OFF.", INICFG_SSL_VERIFY_PEER_STR, CFG_SVRNODE_SEC_STR, INICFG_SSL_STR); + result = false; + } + if(svrnode.is_ssl == svrnode.server_cert.empty()){ + ERR_CHMPRN("Found %s with value(%s) in %s section, but %s is %s.", INICFG_SERVER_CERT_STR, svrnode.server_cert.c_str(), CFG_SVRNODE_SEC_STR, INICFG_SSL_STR, (svrnode.is_ssl ? "ON" : "OFF")); + result = false; + } + if(svrnode.is_ssl == svrnode.server_prikey.empty()){ + ERR_CHMPRN("Found %s with value(%s) in %s section, but %s is %s.", INICFG_SERVER_PRIKEY_STR, svrnode.server_prikey.c_str(), CFG_SVRNODE_SEC_STR, INICFG_SSL_STR, (svrnode.is_ssl ? "ON" : "OFF")); + result = false; + } + if(svrnode.slave_cert.empty() != svrnode.slave_prikey.empty()){ + // SSL_SLAVE_CERT, SSL_SLAVE_PRIKEY must be set or not set. + ERR_CHMPRN("Found %s with value(%s) and %s with(%s) in %s server node in %s section.", INICFG_SLAVE_CERT_STR, svrnode.slave_cert.c_str(), INICFG_SLAVE_PRIKEY_STR, svrnode.slave_prikey.c_str(), svrnode.name.c_str(), INICFG_SVRNODE_SEC_STR); + result = false; + } + + // Expand name(simple regex) + // Need to expand for server node, because node name is compared directly and is used by connecting. + // So that, we expand server node name here. + // + strlst_t expand_svrnodes; + if(!ExpandSimpleRegxHostname(svrnode.name.c_str(), expand_svrnodes, true, true, false)){ // convert localhost to server name and query FQDN. + ERR_CHMPRN("Failed to expand server node name(%s).", svrnode.name.c_str()); + result = false; + + }else{ + // Add each expanded server node name. + for(strlst_t::const_iterator svrnodeiter = expand_svrnodes.begin(); svrnodeiter != expand_svrnodes.end(); ++svrnodeiter){ + svrnode.name = (*svrnodeiter); + chmcfginfo.servers.push_back(svrnode); + + // whichever server mode or not? + if(!ccvals.is_server_by_ctlport && chmcfginfo.self_ctlport == svrnode.ctlport && 0 == strcasecmp(svrnode.name.c_str(), localhost_hostname.c_str())){ + ccvals.is_server_by_ctlport = true; + } + } + } + + in_mapping = false; + } + + }else if(YAML_SEQUENCE_END_EVENT == yevent.type){ + // End sequence(for mapping) event + if(in_mapping){ + ERR_CHMPRN("Found yaml sequence event, but not stop yaml mapping event in %s section loop.", CFG_SVRNODE_SEC_STR); + result = false; + }else{ + // Finish loop without error. + // + is_loop = false; + } + + }else if(YAML_SCALAR_EVENT == yevent.type){ + // Load key & value + if(key.empty()){ + key = reinterpret_cast(yevent.data.scalar.value); + }else{ + // + // Compare key and set value + // + if(0 == strcasecmp(INICFG_NAME_STR, key.c_str())){ + svrnode.name = reinterpret_cast(yevent.data.scalar.value); + + }else if(0 == strcasecmp(INICFG_PORT_STR, key.c_str())){ + svrnode.port = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + + }else if(0 == strcasecmp(INICFG_CTLPORT_STR, key.c_str())){ + svrnode.ctlport = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + + }else if(0 == strcasecmp(INICFG_SSL_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_ON) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_YES)){ + svrnode.is_ssl = true; + }else if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_OFF) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_NO)){ + svrnode.is_ssl = false; + }else{ + ERR_CHMPRN("Found %s in %s section, but value %s does not defined.", INICFG_SSL_STR, CFG_SVRNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + + }else if(0 == strcasecmp(INICFG_SSL_VERIFY_PEER_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_ON) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_YES)){ + svrnode.verify_peer = true; + }else if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_OFF) || 0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_BOOL_NO)){ + svrnode.verify_peer = false; + }else{ + ERR_CHMPRN("Found %s in %s section, but value %s does not defined.", INICFG_SSL_VERIFY_PEER_STR, CFG_SVRNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + + }else if(0 == strcasecmp(INICFG_CAPATH_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_STRING_NULL)){ + svrnode.is_ca_file = false; + svrnode.capath = ""; + }else{ + if(is_dir_exist(reinterpret_cast(yevent.data.scalar.value))){ + svrnode.is_ca_file = false; + }else{ + if(is_file_safe_exist(reinterpret_cast(yevent.data.scalar.value))){ + svrnode.is_ca_file = true; + }else{ + ERR_CHMPRN("Found %s in %s section with value %s, but it is not directory or file.", INICFG_CAPATH_STR, CFG_SVRNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + } + if(CHM_MAX_PATH_LEN <= strlen(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but its length(%zd) is over max length(1024).", INICFG_CAPATH_STR, CFG_SVRNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value), strlen(reinterpret_cast(yevent.data.scalar.value))); + result = false; + }else{ + svrnode.capath = reinterpret_cast(yevent.data.scalar.value); + } + } + + }else if(0 == strcasecmp(INICFG_SERVER_CERT_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_STRING_NULL)){ + svrnode.server_cert = ""; + }else{ + if(!is_file_safe_exist(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but it is not directory or file.", INICFG_SERVER_CERT_STR, CFG_SVRNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + }else{ + if(CHM_MAX_PATH_LEN <= strlen(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but its length(%zd) is over max length(1024).", INICFG_SERVER_CERT_STR, CFG_SVRNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value), strlen(reinterpret_cast(yevent.data.scalar.value))); + result = false; + }else{ + svrnode.server_cert = reinterpret_cast(yevent.data.scalar.value); + } + } + } + + }else if(0 == strcasecmp(INICFG_SERVER_PRIKEY_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_STRING_NULL)){ + svrnode.server_prikey = ""; + }else{ + if(!is_file_safe_exist(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but it is not directory or file.", INICFG_SERVER_PRIKEY_STR, CFG_SVRNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + }else{ + if(CHM_MAX_PATH_LEN <= strlen(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but its length(%zd) is over max length(1024).", INICFG_SERVER_PRIKEY_STR, CFG_SVRNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value), strlen(reinterpret_cast(yevent.data.scalar.value))); + result = false; + }else{ + svrnode.server_prikey = reinterpret_cast(yevent.data.scalar.value); + } + } + } + + }else if(0 == strcasecmp(INICFG_SLAVE_CERT_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_STRING_NULL)){ + svrnode.slave_cert = ""; + }else{ + if(!is_file_safe_exist(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but it is not directory or file.", INICFG_SLAVE_CERT_STR, CFG_SVRNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + }else{ + if(CHM_MAX_PATH_LEN <= strlen(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but its length(%zd) is over max length(1024).", INICFG_SLAVE_CERT_STR, CFG_SVRNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value), strlen(reinterpret_cast(yevent.data.scalar.value))); + result = false; + }else{ + svrnode.slave_cert = reinterpret_cast(yevent.data.scalar.value); + } + } + } + + }else if(0 == strcasecmp(INICFG_SLAVE_PRIKEY_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_STRING_NULL)){ + svrnode.slave_prikey = ""; + }else{ + if(!is_file_safe_exist(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but it is not directory or file.", INICFG_SLAVE_PRIKEY_STR, CFG_SVRNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + }else{ + if(CHM_MAX_PATH_LEN <= strlen(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but its length(%zd) is over max length(1024).", INICFG_SLAVE_PRIKEY_STR, CFG_SVRNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value), strlen(reinterpret_cast(yevent.data.scalar.value))); + result = false; + }else{ + svrnode.slave_prikey = reinterpret_cast(yevent.data.scalar.value); + } + } + } + + }else{ + WAN_CHMPRN("Found unexpected key(%s) in %s section, thus skip this key and value.", key.c_str(), CFG_SVRNODE_SEC_STR); + } + key.clear(); + } + }else{ + // [TODO] Now not support alias(anchor) event + // + ERR_CHMPRN("Found unexpected yaml event(%d) in %s section.", yevent.type, CFG_GLOBAL_SEC_STR); + result = false; + } + + // delete event + if(is_loop){ + is_loop = yevent.type != YAML_STREAM_END_EVENT; + } + yaml_event_delete(&yevent); + } + + if(result){ + // Re-Check SSL_CAPATH, SSL_SLAVE_CERT, SSL_SLAVE_PRIKEY + // + for(chmnode_cfginfos_t::iterator iter = chmcfginfo.servers.begin(); iter != chmcfginfo.servers.end(); ++iter){ + if(!ccvals.found_ssl){ + // If there is no ssl server, all servers should not have CApath. + if(!iter->capath.empty()){ + ERR_CHMPRN("Found %s with value(%s) in %s server node in %s section.", INICFG_CAPATH_STR, iter->capath.c_str(), iter->name.c_str(), CFG_SVRNODE_SEC_STR); + result = false; + break; + } + // If there is no ssl servers and no verify peer, any servers should not have client cert and private key. + if(!iter->slave_cert.empty() || !iter->slave_prikey.empty()){ + ERR_CHMPRN("Found %s with value(%s) and %s with value(%s) in %s server node in %s section.", INICFG_SLAVE_CERT_STR, iter->slave_cert.c_str(), INICFG_SLAVE_PRIKEY_STR, iter->slave_prikey.c_str(), iter->name.c_str(), INICFG_SVRNODE_SEC_STR); + result = false; + break; + } + }else{ + if(ccvals.found_ssl_verify_peer){ + // If there are ssl servers with verify peer, all servers must have client cert and private key. + if(iter->slave_cert.empty() || iter->slave_prikey.empty()){ + ERR_CHMPRN("Found %s with value(%s) and %s with value(%s) in %s server node in %s section.", INICFG_SLAVE_CERT_STR, iter->slave_cert.c_str(), INICFG_SLAVE_PRIKEY_STR, iter->slave_prikey.c_str(), iter->name.c_str(), INICFG_SVRNODE_SEC_STR); + result = false; + break; + } + }else{ + // If there are ssl servers without verify peer, any servers should not have client cert and private key. + if(!iter->slave_cert.empty() || !iter->slave_prikey.empty()){ + ERR_CHMPRN("Found %s with value(%s) and %s with value(%s) in %s server node in %s section.", INICFG_SLAVE_CERT_STR, iter->slave_cert.c_str(), INICFG_SLAVE_PRIKEY_STR, iter->slave_prikey.c_str(), iter->name.c_str(), INICFG_SVRNODE_SEC_STR); + result = false; + break; + } + + } + } + } + if(result){ + // [NOTE] + // The server list might have duplicate server name & port. + // Because the port number can not be specified in configration file, so if there is some server nodes on same server + // and one specifies port and the other does not specify port(using default port). + // On this case, the list have duplicate server. + // + chmcfginfo.servers.unique(chm_node_cfg_info_same_name_port()); // uniq about server node must be name and port + chmcfginfo.servers.sort(chm_node_cfg_info_sort()); + } + } + + return result; +} + +static bool ChmYamlLoadConfigrationSlvnodeSec(yaml_parser_t& yparser, CHMCFGINFO& chmcfginfo, CHMCONF_CCV& ccvals) +{ + // Must start yaml sequence(for mapping array) -> mapping event. + yaml_event_t yevent; + { + // sequence + if(!yaml_parser_parse(&yparser, &yevent)){ + ERR_CHMPRN("Could not parse event. errno = %d", errno); + return false; + } + if(YAML_SEQUENCE_START_EVENT != yevent.type){ + ERR_CHMPRN("Parsed event type is not start sequence(%d)", yevent.type); + yaml_event_delete(&yevent); + return false; + } + yaml_event_delete(&yevent); + } + + // temporary data + CHMNODE_CFGINFO slvnode; + + // Loading + string key(""); + bool result = true; + for(bool is_loop = true, in_mapping = false; is_loop && result; ){ + // get event + if(!yaml_parser_parse(&yparser, &yevent)){ + ERR_CHMPRN("Could not parse event. errno = %d", errno); + result = false; + continue; // break loop assap. + } + + // check event + if(YAML_MAPPING_START_EVENT == yevent.type){ + // Start mapping event + if(in_mapping){ + ERR_CHMPRN("Already start yaml mapping event in %s section loop.", CFG_SLVNODE_SEC_STR); + result = false; + }else{ + in_mapping = true; + + // Set default value for each server node + slvnode.name.clear(); + slvnode.server_cert.clear(); + slvnode.server_prikey.clear(); + slvnode.port = CHM_INVALID_PORT; + slvnode.ctlport = ccvals.ctlport; + slvnode.is_ssl = false; + slvnode.verify_peer = false; + slvnode.is_ca_file = ccvals.is_ca_file; + slvnode.capath = ccvals.capath; + slvnode.slave_cert = ccvals.slave_cert; + slvnode.slave_prikey = ccvals.slave_prikey; + } + + }else if(YAML_MAPPING_END_EVENT == yevent.type){ + // End mapping event + if(!in_mapping){ + ERR_CHMPRN("Already stop yaml mapping event in %s section loop.", CFG_SLVNODE_SEC_STR); + result = false; + }else{ + // Finish one server node configuration. + // + + // check values + if(slvnode.name.empty()){ + ERR_CHMPRN("Found some value in %s section, but NAME is empty.", CFG_SLVNODE_SEC_STR); + result = false; + } + if(!ccvals.found_ssl && !slvnode.capath.empty()){ + // There is not SSL, but CApath is set. + ERR_CHMPRN("Found %s with value(%s) in %s slave node in %s section.", INICFG_CAPATH_STR, slvnode.capath.c_str(), slvnode.name.c_str(), INICFG_SLVNODE_SEC_STR); + result = false; + } + if(slvnode.slave_cert.empty() != slvnode.slave_prikey.empty()){ + // SSL_SLAVE_CERT, SSL_SLAVE_PRIKEY must be set or not set. + ERR_CHMPRN("Found %s with value(%s) and %s with(%s) in %s slave node in %s section.", INICFG_SLAVE_CERT_STR, slvnode.slave_cert.c_str(), INICFG_SLAVE_PRIKEY_STR, slvnode.slave_prikey.c_str(), slvnode.name.c_str(), INICFG_SLVNODE_SEC_STR); + result = false; + } + if(ccvals.found_ssl_verify_peer == slvnode.slave_cert.empty()){ + // If There is SSL_VERIFY_PEER, but client cert(and private key) must be set.(nor so on) + ERR_CHMPRN("Found %s with value(%s) and %s with(%s) in %s slave node in %s section.", INICFG_SLAVE_CERT_STR, slvnode.slave_cert.c_str(), INICFG_SLAVE_PRIKEY_STR, slvnode.slave_prikey.c_str(), slvnode.name.c_str(), INICFG_SLVNODE_SEC_STR); + result = false; + } + + // set value + chmcfginfo.slaves.push_back(slvnode); + + in_mapping = false; + } + + }else if(YAML_SEQUENCE_END_EVENT == yevent.type){ + // End sequence(for mapping) event + if(in_mapping){ + ERR_CHMPRN("Found yaml sequence event, but not stop yaml mapping event in %s section loop.", CFG_SLVNODE_SEC_STR); + result = false; + }else{ + // Finish loop without error. + // + is_loop = false; + } + + }else if(YAML_SCALAR_EVENT == yevent.type){ + // Load key & value + if(key.empty()){ + key = reinterpret_cast(yevent.data.scalar.value); + }else{ + // + // Compare key and set value + // + if(0 == strcasecmp(INICFG_NAME_STR, key.c_str())){ + // slave server name is allowed regexed format. + // so do not convert name to global name(can not convert regexed name). + // + slvnode.name = reinterpret_cast(yevent.data.scalar.value); + + }else if(0 == strcasecmp(INICFG_CTLPORT_STR, key.c_str())){ + slvnode.ctlport = static_cast(atoi(reinterpret_cast(yevent.data.scalar.value))); + + }else if(0 == strcasecmp(INICFG_CAPATH_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_STRING_NULL)){ + slvnode.is_ca_file = false; + slvnode.capath = ""; + }else{ + if(is_dir_exist(reinterpret_cast(yevent.data.scalar.value))){ + slvnode.is_ca_file = false; + }else{ + if(is_file_safe_exist(reinterpret_cast(yevent.data.scalar.value))){ + slvnode.is_ca_file = true; + }else{ + ERR_CHMPRN("Found %s in %s section with value %s, but it is not directory or file.", INICFG_CAPATH_STR, CFG_SLVNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + } + } + if(CHM_MAX_PATH_LEN <= strlen(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but its length(%zd) is over max length(1024).", INICFG_CAPATH_STR, CFG_SLVNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value), strlen(reinterpret_cast(yevent.data.scalar.value))); + result = false; + }else{ + slvnode.capath = reinterpret_cast(yevent.data.scalar.value); + } + } + + }else if(0 == strcasecmp(INICFG_SLAVE_CERT_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_STRING_NULL)){ + slvnode.slave_cert = ""; + }else{ + if(!is_file_safe_exist(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but it is not directory or file.", INICFG_SLAVE_CERT_STR, CFG_SLVNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + }else{ + if(CHM_MAX_PATH_LEN <= strlen(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but its length(%zd) is over max length(1024).", INICFG_SLAVE_CERT_STR, CFG_SLVNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value), strlen(reinterpret_cast(yevent.data.scalar.value))); + result = false; + }else{ + slvnode.slave_cert = reinterpret_cast(yevent.data.scalar.value); + } + } + } + + }else if(0 == strcasecmp(INICFG_SLAVE_PRIKEY_STR, key.c_str())){ + if(0 == strcasecmp(reinterpret_cast(yevent.data.scalar.value), INICFG_STRING_NULL)){ + slvnode.slave_prikey = ""; + }else{ + if(!is_file_safe_exist(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but it is not directory or file.", INICFG_SLAVE_PRIKEY_STR, CFG_SLVNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value)); + result = false; + }else{ + if(CHM_MAX_PATH_LEN <= strlen(reinterpret_cast(yevent.data.scalar.value))){ + ERR_CHMPRN("Found %s in %s section with value %s, but its length(%zd) is over max length(1024).", INICFG_SLAVE_PRIKEY_STR, CFG_SLVNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value), strlen(reinterpret_cast(yevent.data.scalar.value))); + result = false; + }else{ + slvnode.slave_prikey = reinterpret_cast(yevent.data.scalar.value); + } + } + } + + }else{ + WAN_CHMPRN("Found unexpected key(%s) in %s section, thus skip this key and value.", key.c_str(), CFG_SLVNODE_SEC_STR); + } + key.clear(); + } + }else{ + // [TODO] Now not support alias(anchor) event + // + ERR_CHMPRN("Found unexpected yaml event(%d) in %s section.", yevent.type, CFG_GLOBAL_SEC_STR); + result = false; + } + + // delete event + if(is_loop){ + is_loop = yevent.type != YAML_STREAM_END_EVENT; + } + yaml_event_delete(&yevent); + } + + if(result){ + chmcfginfo.slaves.unique(chm_node_cfg_info_same_name_port()); // uniq about slave node must be name and port + chmcfginfo.slaves.sort(chm_node_cfg_info_sort()); + } + + return result; +} + +static bool ChmYamlLoadConfigrationTopLevel(yaml_parser_t& yparser, CHMCFGINFO& chmcfginfo, short default_ctlport) +{ + CHMYamlDataStack other_stack; + CHMCONF_CCV ccvals; + bool is_set_global = false; + bool is_set_svrnode = false; + bool is_set_slvnode = false; + bool result = true; + for(bool is_loop = true, in_stream = false, in_document = false, in_toplevel = false; is_loop && result; ){ + // get event + yaml_event_t yevent; + if(!yaml_parser_parse(&yparser, &yevent)){ + ERR_CHMPRN("Could not parse event. errno = %d", errno); + result = false; + continue; + } + + // check event + switch(yevent.type){ + case YAML_NO_EVENT: + MSG_CHMPRN("There is no yaml event in loop"); + break; + + case YAML_STREAM_START_EVENT: + if(!other_stack.empty()){ + MSG_CHMPRN("Found start yaml stream event in skipping event loop"); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else if(in_stream){ + MSG_CHMPRN("Already start yaml stream event in loop, Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else{ + MSG_CHMPRN("Start yaml stream event in loop"); + in_stream = true; + } + break; + + case YAML_STREAM_END_EVENT: + if(!other_stack.empty()){ + MSG_CHMPRN("Found stop yaml stream event in skipping event loop"); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else if(!in_stream){ + MSG_CHMPRN("Already stop yaml stream event in loop, Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else{ + MSG_CHMPRN("Stop yaml stream event in loop"); + in_stream = false; + } + break; + + case YAML_DOCUMENT_START_EVENT: + if(!other_stack.empty()){ + MSG_CHMPRN("Found start yaml document event in skipping event loop"); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else if(!in_stream){ + MSG_CHMPRN("Found start yaml document event before yaml stream event in loop, Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else if(in_document){ + MSG_CHMPRN("Already start yaml document event in loop, Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else{ + MSG_CHMPRN("Start yaml document event in loop"); + in_document = true; + } + break; + + case YAML_DOCUMENT_END_EVENT: + if(!other_stack.empty()){ + MSG_CHMPRN("Found stop yaml document event in skipping event loop"); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else if(!in_document){ + MSG_CHMPRN("Already stop yaml document event in loop, Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else{ + MSG_CHMPRN("Stop yaml document event in loop"); + in_document = false; + } + break; + + case YAML_MAPPING_START_EVENT: + if(!other_stack.empty()){ + MSG_CHMPRN("Found start yaml mapping event in skipping event loop"); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else if(!in_stream){ + MSG_CHMPRN("Found start yaml mapping event before yaml stream event in loop, Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else if(!in_document){ + MSG_CHMPRN("Found start yaml mapping event before yaml document event in loop, Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else if(in_toplevel){ + MSG_CHMPRN("Already start yaml mapping event in loop, Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else{ + MSG_CHMPRN("Start yaml mapping event in loop"); + in_toplevel = true; + } + break; + + case YAML_MAPPING_END_EVENT: + if(!other_stack.empty()){ + MSG_CHMPRN("Found stop yaml mapping event in skipping event loop"); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else if(!in_toplevel){ + MSG_CHMPRN("Already stop yaml mapping event in loop, Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else{ + MSG_CHMPRN("Stop yaml mapping event in loop"); + in_toplevel = false; + } + break; + + case YAML_SEQUENCE_START_EVENT: + // always stacking + // + if(!other_stack.empty()){ + MSG_CHMPRN("Found start yaml sequence event in skipping event loop"); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else{ + MSG_CHMPRN("Found start yaml sequence event before top level event in loop, Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + } + break; + + case YAML_SEQUENCE_END_EVENT: + // always stacking + // + if(!other_stack.empty()){ + MSG_CHMPRN("Found stop yaml sequence event in skipping event loop"); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else{ + MSG_CHMPRN("Found stop yaml sequence event before top level event in loop, Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + } + break; + + case YAML_SCALAR_EVENT: + if(!other_stack.empty()){ + MSG_CHMPRN("Got yaml scalar event in skipping event loop"); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else if(!in_stream){ + MSG_CHMPRN("Got yaml scalar event before yaml stream event in loop, Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else if(!in_document){ + MSG_CHMPRN("Got yaml scalar event before yaml document event in loop, Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else if(!in_toplevel){ + MSG_CHMPRN("Got yaml scalar event before yaml mapping event in loop, Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else{ + // Found Top Level Keywards, start to loading + if(0 == strcasecmp(CFG_GLOBAL_SEC_STR, reinterpret_cast(yevent.data.scalar.value))){ + if(is_set_global){ + MSG_CHMPRN("Got yaml scalar event in loop, but already loading %s top level. Thus stacks this event.", CFG_GLOBAL_SEC_STR); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else{ + // Load GLOBAL section + if(!ChmYamlLoadConfigrationGlobalSec(yparser, chmcfginfo, ccvals, default_ctlport)){ + ERR_CHMPRN("Something error occured in loading %s section.", CFG_GLOBAL_SEC_STR); + result = false; + } + } + + }else if(0 == strcasecmp(CFG_SVRNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value))){ + if(is_set_svrnode){ + MSG_CHMPRN("Got yaml scalar event in loop, but already loading SVRNODE top level. Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else{ + // Load SVRNODE section + if(!ChmYamlLoadConfigrationSvrnodeSec(yparser, chmcfginfo, ccvals)){ + ERR_CHMPRN("Something error occured in loading %s section.", CFG_SVRNODE_SEC_STR); + result = false; + } + } + + }else if(0 == strcasecmp(CFG_SLVNODE_SEC_STR, reinterpret_cast(yevent.data.scalar.value))){ + if(is_set_slvnode){ + MSG_CHMPRN("Got yaml scalar event in loop, but already loading SLVNODE top level. Thus stacks this event."); + if(!other_stack.add(yevent.type)){ + result = false; + } + }else{ + // Load SLVNODE section + if(!ChmYamlLoadConfigrationSlvnodeSec(yparser, chmcfginfo, ccvals)){ + ERR_CHMPRN("Something error occured in loading %s section.", CFG_SLVNODE_SEC_STR); + result = false; + } + } + + }else{ + MSG_CHMPRN("Got yaml scalar event in loop, but unknown keyward(%s) for top level target. Thus stacks this event.", reinterpret_cast(yevent.data.scalar.value)); + if(!other_stack.add(yevent.type)){ + result = false; + } + } + } + break; + + case YAML_ALIAS_EVENT: + // [TODO] + // Now we do not supports alias(anchor) event. + // + MSG_CHMPRN("Got yaml alias(anchor) event in loop, but we does not support this event. Thus skip this event."); + break; + } + + // delete event + is_loop = yevent.type != YAML_STREAM_END_EVENT; + yaml_event_delete(&yevent); + } + + if(result){ + // Re-check for server/slave mode by self control port number. + // + if(ccvals.is_server_by_ctlport){ + if(!ccvals.server_mode){ + WAN_CHMPRN("There is no \"%s\" in %s section, but self control port(%d) found in server list, so run server mode.", INICFG_MODE_STR, INICFG_GLOBAL_SEC_STR, chmcfginfo.self_ctlport); + chmcfginfo.is_server_mode = true; + }else{ + if(!chmcfginfo.is_server_mode){ + ERR_CHMPRN("Found \"%s\" as slave mode in %s section, but self control port(%d) found in server list.", INICFG_MODE_STR, INICFG_GLOBAL_SEC_STR, chmcfginfo.self_ctlport); + result = false; + } + } + }else{ + if(!ccvals.server_mode){ + WAN_CHMPRN("There is no \"%s\" in %s section, but self control port(%d) found in slave list, so run slave mode.", INICFG_MODE_STR, INICFG_GLOBAL_SEC_STR, chmcfginfo.self_ctlport); + chmcfginfo.is_server_mode = false; + }else{ + if(chmcfginfo.is_server_mode){ + ERR_CHMPRN("Found \"%s\" as server mode in %s section, but self control port(%d) not found in server list.", INICFG_MODE_STR, INICFG_GLOBAL_SEC_STR, chmcfginfo.self_ctlport); + result = false; + } + } + } + + // check merge flags + if(chmcfginfo.is_random_mode){ + if(chmcfginfo.is_auto_merge){ + WAN_CHMPRN("Found %s=ON and %s=ON. These options can not be specified at the same time, so SET %s=OFF.", INICFG_DELIVERMODE_STR, INICFG_AUTOMERGE_STR, INICFG_AUTOMERGE_STR); + chmcfginfo.is_auto_merge = false; + } + if(chmcfginfo.is_do_merge){ + WAN_CHMPRN("Found %s=ON and %s=ON. These options can not be specified at the same time, so SET %s=OFF.", INICFG_DELIVERMODE_STR, INICFG_DOMERGE_STR, INICFG_DOMERGE_STR); + chmcfginfo.is_do_merge = false; + } + }else{ + if(!chmcfginfo.is_do_merge && chmcfginfo.is_auto_merge){ + WAN_CHMPRN("Found %s=ON and %s=ON. These options can not be specified at the same time, so SET %s=OFF.", INICFG_DOMERGE_STR, INICFG_AUTOMERGE_STR, INICFG_AUTOMERGE_STR); + chmcfginfo.is_auto_merge = false; + } + } + } + return result; +} + +//--------------------------------------------------------- +// CHMYamlBaseConf Class +//--------------------------------------------------------- +CHMYamlBaseConf::CHMYamlBaseConf(int eventqfd, ChmCntrl* pcntrl, const char* file, short ctlport, const char* pJson) : CHMConf(eventqfd, pcntrl, file, ctlport, pJson) +{ +} + +CHMYamlBaseConf::~CHMYamlBaseConf() +{ +} + +bool CHMYamlBaseConf::LoadConfigration(CHMCFGINFO& chmcfginfo) const +{ + if(CONF_JSON != type && CONF_JSON_STR != type && CONF_YAML != type){ + ERR_CHMPRN("Class type(%d) does not JSON/JSON_STR/YAML.", type); + return false; + } + if(CONF_JSON == type || CONF_YAML == type){ + if(cfgfile.empty()){ + ERR_CHMPRN("Configuration file path is not set."); + return false; + } + }else{ // JSON_STR + if(strjson.empty()){ + ERR_CHMPRN("JSON string is not set."); + return false; + } + } + + // initialize yaml parser + yaml_parser_t yparser; + if(!yaml_parser_initialize(&yparser)){ + ERR_CHMPRN("Failed to initialize yaml parser"); + return false; + } + + FILE* fp = NULL; + if(CONF_JSON == type || CONF_YAML == type){ + // open configuration file + if(NULL == (fp = fopen(cfgfile.c_str(), "r"))){ + ERR_CHMPRN("Could not open configuration file(%s). errno = %d", cfgfile.c_str(), errno); + return false; + } + + // set file to parser + yaml_parser_set_input_file(&yparser, fp); + + }else{ // JSON_STR + // set string to parser + yaml_parser_set_input_string(&yparser, reinterpret_cast(strjson.c_str()), strjson.length()); + } + + // Do parsing + bool result = ChmYamlLoadConfigrationTopLevel(yparser, chmcfginfo, ctlport_param); + + yaml_parser_delete(&yparser); + if(fp){ + fclose(fp); + } + return result; +} + +//--------------------------------------------------------- +// CHMJsonConf Class +//--------------------------------------------------------- +CHMJsonConf::CHMJsonConf(int eventqfd, ChmCntrl* pcntrl, const char* file, short ctlport) : CHMYamlBaseConf(eventqfd, pcntrl, file, ctlport, NULL) +{ + type = CHMConf::CONF_JSON; +} + +CHMJsonConf::~CHMJsonConf() +{ +} + +//--------------------------------------------------------- +// CHMJsonStringConf Class +//--------------------------------------------------------- +CHMJsonStringConf::CHMJsonStringConf(int eventqfd, ChmCntrl* pcntrl, const char* pJson, short ctlport) : CHMYamlBaseConf(eventqfd, pcntrl, NULL, ctlport, pJson) +{ + type = CHMConf::CONF_JSON_STR; // default +} + +CHMJsonStringConf::~CHMJsonStringConf() +{ +} + +//--------------------------------------------------------- +// CHMYamlConf Class +//--------------------------------------------------------- +CHMYamlConf::CHMYamlConf(int eventqfd, ChmCntrl* pcntrl, const char* file, short ctlport) : CHMYamlBaseConf(eventqfd, pcntrl, file, ctlport, NULL) +{ + type = CHMConf::CONF_YAML; +} + +CHMYamlConf::~CHMYamlConf() +{ +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmconf.h b/lib/chmconf.h new file mode 100644 index 0000000..c8ad1f4 --- /dev/null +++ b/lib/chmconf.h @@ -0,0 +1,401 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMCONF_H +#define CHMCONF_H + +#include +#include +#include + +#include "chmcommon.h" +#include "chmutil.h" +#include "chmstructure.h" +#include "chmeventbase.h" + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +#define UNINITIALIZE_REVISION (-1) + +//--------------------------------------------------------- +// Structures +//--------------------------------------------------------- +// configuration information for node +typedef struct chm_node_cfg_info{ + std::string name; + short port; + short ctlport; + bool is_ssl; + bool verify_peer; // verify ssl client peer on server + bool is_ca_file; + std::string capath; + std::string server_cert; + std::string server_prikey; + std::string slave_cert; + std::string slave_prikey; + + chm_node_cfg_info() : name(""), port(0), ctlport(0), is_ssl(false), verify_peer(false), is_ca_file(false), capath(""), server_cert(""), server_prikey(""), slave_cert(""), slave_prikey("") {} + + bool compare(const struct chm_node_cfg_info& other) const + { + if( name == other.name && + port == other.port && + ctlport == other.ctlport && + is_ssl == other.is_ssl && + verify_peer == other.verify_peer && + is_ca_file == other.is_ca_file && + capath == other.capath && + server_cert == other.server_cert && + server_prikey == other.server_prikey && + slave_cert == other.slave_cert && + slave_prikey == other.slave_prikey ) + { + return true; + } + return false; + } + bool operator==(const struct chm_node_cfg_info& other) const + { + return compare(other); + } + bool operator!=(const struct chm_node_cfg_info& other) const + { + return !compare(other); + } +}CHMNODE_CFGINFO, *PCHMNODE_CFGINFO; + +typedef std::list chmnode_cfginfos_t; + +struct chm_node_cfg_info_sort +{ + bool operator()(const CHMNODE_CFGINFO& lchmnodecfginfo, const CHMNODE_CFGINFO& rchmnodecfginfo) const + { + if(lchmnodecfginfo.name == rchmnodecfginfo.name){ + if(lchmnodecfginfo.port == rchmnodecfginfo.port){ + return lchmnodecfginfo.ctlport < rchmnodecfginfo.ctlport; + }else{ + return lchmnodecfginfo.port < rchmnodecfginfo.port; + } + }else{ + return lchmnodecfginfo.name < rchmnodecfginfo.name; + } + } +}; + +struct chm_node_cfg_info_same_name +{ + bool operator()(const CHMNODE_CFGINFO& lchmnodecfginfo, const CHMNODE_CFGINFO& rchmnodecfginfo) const + { + return lchmnodecfginfo.name == rchmnodecfginfo.name; + } +}; + +struct chm_node_cfg_info_same_name_port +{ + bool operator()(const CHMNODE_CFGINFO& lchmnodecfginfo, const CHMNODE_CFGINFO& rchmnodecfginfo) const + { + return (lchmnodecfginfo.name == rchmnodecfginfo.name && lchmnodecfginfo.ctlport == rchmnodecfginfo.ctlport); + } +}; + +// configuration information for all +typedef struct chm_cfg_info{ + std::string groupname; + long revision; + bool is_server_mode; + bool is_random_mode; + short self_ctlport; + long max_chmpx_count; + long replica_count; + long max_server_mq_cnt; // MQ count for server/slave node + long max_client_mq_cnt; // MQ count for client process + long mqcnt_per_attach; // MQ count at each attaching from each client process + long max_q_per_servermq; // Queue count in one MQ on server/slave node + long max_q_per_clientmq; // Queue count in one MQ on client process + long max_mq_per_client; // max MQ count for one client process + long max_histlog_count; + int retrycnt; + int mq_retrycnt; + bool mq_ack; + int timeout_wait_socket; + int timeout_wait_connect; + int timeout_wait_mq; + bool is_auto_merge; + bool is_do_merge; + time_t timeout_merge; + int sock_thread_cnt; + int mq_thread_cnt; + int max_sock_pool; + time_t sock_pool_timeout; + bool k2h_fullmap; + int k2h_mask_bitcnt; + int k2h_cmask_bitcnt; + int k2h_max_element; + time_t date; + chmnode_cfginfos_t servers; + chmnode_cfginfos_t slaves; + + chm_cfg_info() : groupname(""), revision(UNINITIALIZE_REVISION), is_server_mode(false), is_random_mode(false), self_ctlport(CHM_INVALID_PORT), + max_chmpx_count(0L), replica_count(0L), max_server_mq_cnt(0L), max_client_mq_cnt(0L), mqcnt_per_attach(0L), max_q_per_servermq(0L), + max_q_per_clientmq(0L), max_mq_per_client(0L), max_histlog_count(0L), retrycnt(-1), mq_retrycnt(-1), mq_ack(true), timeout_wait_socket(-1), + timeout_wait_connect(-1), timeout_wait_mq(-1), is_auto_merge(false), is_do_merge(false), timeout_merge(0), sock_thread_cnt(0), + mq_thread_cnt(0), max_sock_pool(1), sock_pool_timeout(0), k2h_fullmap(true), k2h_mask_bitcnt(K2HShm::DEFAULT_MASK_BITCOUNT), + k2h_cmask_bitcnt(K2HShm::DEFAULT_COLLISION_MASK_BITCOUNT), k2h_max_element(K2HShm::DEFAULT_MAX_ELEMENT_CNT), date(0L) {} + + bool compare(const struct chm_cfg_info& other) const + { + if( groupname == other.groupname && + revision == other.revision && + is_server_mode == other.is_server_mode && + is_random_mode == other.is_random_mode && + self_ctlport == other.self_ctlport && + max_chmpx_count == other.max_chmpx_count && + replica_count == other.replica_count && + max_server_mq_cnt == other.max_server_mq_cnt && + max_client_mq_cnt == other.max_client_mq_cnt && + mqcnt_per_attach == other.mqcnt_per_attach && + max_q_per_servermq == other.max_q_per_servermq && + max_q_per_clientmq == other.max_q_per_clientmq && + max_mq_per_client == other.max_mq_per_client && + max_histlog_count == other.max_histlog_count && + retrycnt == other.retrycnt && + mq_retrycnt == other.mq_retrycnt && + mq_ack == other.mq_ack && + timeout_wait_socket == other.timeout_wait_socket && + timeout_wait_connect== other.timeout_wait_connect && + timeout_wait_mq == other.timeout_wait_mq && + is_auto_merge == other.is_auto_merge && + is_do_merge == other.is_do_merge && + timeout_merge == other.timeout_merge && + sock_thread_cnt == other.sock_thread_cnt && + mq_thread_cnt == other.mq_thread_cnt && + max_sock_pool == other.max_sock_pool && + sock_pool_timeout == other.sock_pool_timeout && + //k2h_fullmap == other.k2h_fullmap && // [NOTICE] k2hash parameter is not compared + //k2h_mask_bitcnt == other.k2h_mask_bitcnt && + //k2h_cmask_bitcnt == other.k2h_cmask_bitcnt && + //k2h_max_element == other.k2h_max_element && + //date == other.date && // [NOTICE] date is not compared + servers == other.servers && + slaves == other.slaves ) + { + return true; + } + return false; + } + bool operator==(const struct chm_cfg_info& other) const + { + return compare(other); + } + bool operator!=(const struct chm_cfg_info& other) const + { + return !compare(other); + } +}CHMCFGINFO, *PCHMCFGINFO; + +// raw all configuration +typedef struct cfg_raw{ + strmap_t global; + strmaparr_t server_nodes; + strmaparr_t slave_nodes; +}CFGRAW, *PCFGRAW; + +// For using temporary carry common values for loading +// +typedef struct chm_conf_common_carry_value{ + short port; + short ctlport; + bool server_mode; + bool is_ssl; + bool verify_peer; + bool is_ca_file; + std::string capath; + std::string server_cert; + std::string server_prikey; + std::string slave_cert; + std::string slave_prikey; + bool is_server_by_ctlport; // for check server/slave mode by checking control port and server name. + bool found_ssl; + bool found_ssl_verify_peer; + + chm_conf_common_carry_value() : port(CHM_INVALID_PORT), ctlport(CHM_INVALID_PORT), server_mode(false), is_ssl(false), verify_peer(false), + is_ca_file(false), capath(""), server_cert(""), server_prikey(""), slave_cert(""), slave_prikey(""), + is_server_by_ctlport(false), found_ssl(false), found_ssl_verify_peer(false) {} +}CHMCONF_CCV, *PCHMCONF_CCV; + +//--------------------------------------------------------- +// Class CHMConf +//--------------------------------------------------------- +class CHMConf : public ChmEventBase +{ + public: + enum ConfType{ + CONF_UNKNOWN = 0, + CONF_INI, + CONF_JSON, + CONF_JSON_STR, // JSON string type(not file), On this case, this class does not use inotify. + CONF_YAML + }; + + protected: + std::string cfgfile; + std::string strjson; + short ctlport_param; + CHMConf::ConfType type; + int inotifyfd; + int watchfd; + PCHMCFGINFO pchmcfginfo; + + protected: + CHMConf(int eventqfd = CHM_INVALID_HANDLE, ChmCntrl* pcntrl = NULL, const char* file = NULL, short ctlport = CHM_INVALID_PORT, const char* pJson = NULL); + + virtual bool LoadConfigration(CHMCFGINFO& chmcfginfo) const = 0; + + bool GetServerInfo(const char* hostname, short ctlport, CHMNODE_CFGINFO& svrnodeinfo, bool is_check_update = false); + bool GetSelfServerInfo(CHMNODE_CFGINFO& svrnodeinfo, bool is_check_update = false); + bool GetSlaveInfo(const char* hostname, short ctlport, CHMNODE_CFGINFO& slvnodeinfo, bool is_check_update = false); + bool GetSelfSlaveInfo(CHMNODE_CFGINFO& slvnodeinfo, bool is_check_update = false); + + bool IsWatching(void) const { return (CHM_INVALID_HANDLE != watchfd); } + uint CheckNotifyEvent(void); + bool ResetEventQueue(void); + + bool IsFileType(void) const { return (CONF_INI == type || CONF_YAML == type || CONF_JSON == type); } + bool IsJsonStringType(void) const { return (CONF_JSON_STR == type); } + + public: + static CHMConf* GetCHMConf(int eventqfd, ChmCntrl* pcntrl, const char* config, short ctlport = CHM_INVALID_PORT, bool is_check_env = true, std::string* normalize_config = NULL); // Class Factory + virtual ~CHMConf(); + + virtual bool Clean(void); + + virtual bool GetEventQueueFds(event_fds_t& fds); + virtual bool SetEventQueue(void); + virtual bool UnsetEventQueue(void); + virtual bool IsEventQueueFd(int fd); + + virtual bool Send(PCOMPKT pComPkt, const unsigned char* pbody, size_t blength); + virtual bool Receive(int fd); + virtual bool NotifyHup(int fd); + virtual bool Processing(PCOMPKT pComPkt); + + bool CheckConfFile(void) const; + bool CheckUpdate(void); + const CHMCFGINFO* GetConfiguration(bool is_check_update = false); + + bool GetNodeInfo(const char* hostname, short ctlport, CHMNODE_CFGINFO& nodeinfo, bool is_only_server, bool is_check_update = false); + bool GetSelfNodeInfo(CHMNODE_CFGINFO& nodeinfo, bool is_check_update = false); + + bool GetServerList(strlst_t& server_list); + bool IsServerList(const char* hostname, std::string& fqdn); + bool IsServerList(std::string& fqdn); + + bool GetSlaveList(strlst_t& slave_list); + bool IsSlaveList(const char* hostname, std::string& fqdn); + bool IsSlaveList(std::string& fqdn); + bool IsSsl(void) const; +}; + +//--------------------------------------------------------- +// Class CHMIniConf +//--------------------------------------------------------- +class CHMIniConf : public CHMConf +{ + friend class CHMConf; + + protected: + CHMIniConf(int eventqfd = CHM_INVALID_HANDLE, ChmCntrl* pcntrl = NULL, const char* file = NULL, short ctlport = CHM_INVALID_PORT); + + virtual bool LoadConfigration(CHMCFGINFO& chmcfginfo) const; + bool LoadConfigrationRaw(CFGRAW& chmcfgraw) const; + bool ReadFileContents(const std::string& filename, strlst_t& linelst, strlst_t& allfiles) const; + + public: + virtual ~CHMIniConf(); +}; + +//--------------------------------------------------------- +// Class CHMYamlBaseConf +//--------------------------------------------------------- +class CHMYamlBaseConf : public CHMConf +{ + friend class CHMConf; + + protected: + CHMYamlBaseConf(int eventqfd = CHM_INVALID_HANDLE, ChmCntrl* pcntrl = NULL, const char* file = NULL, short ctlport = CHM_INVALID_PORT, const char* pJson = NULL); + + virtual bool LoadConfigration(CHMCFGINFO& chmcfginfo) const; + + public: + virtual ~CHMYamlBaseConf(); +}; + +//--------------------------------------------------------- +// Class CHMJsonConf +//--------------------------------------------------------- +class CHMJsonConf : public CHMYamlBaseConf +{ + friend class CHMConf; + + protected: + CHMJsonConf(int eventqfd = CHM_INVALID_HANDLE, ChmCntrl* pcntrl = NULL, const char* file = NULL, short ctlport = CHM_INVALID_PORT); + + public: + virtual ~CHMJsonConf(); +}; + +//--------------------------------------------------------- +// Class CHMJsonStringConf +//--------------------------------------------------------- +class CHMJsonStringConf : public CHMYamlBaseConf +{ + friend class CHMConf; + + protected: + CHMJsonStringConf(int eventqfd = CHM_INVALID_HANDLE, ChmCntrl* pcntrl = NULL, const char* pJson = NULL, short ctlport = CHM_INVALID_PORT); + + public: + virtual ~CHMJsonStringConf(); +}; + +//--------------------------------------------------------- +// Class CHMYamlConf +//--------------------------------------------------------- +class CHMYamlConf : public CHMYamlBaseConf +{ + friend class CHMConf; + + protected: + CHMYamlConf(int eventqfd = CHM_INVALID_HANDLE, ChmCntrl* pcntrl = NULL, const char* file = NULL, short ctlport = CHM_INVALID_PORT); + + public: + virtual ~CHMYamlConf(); +}; + +#endif // CHMCONF_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmconfutil.cc b/lib/chmconfutil.cc new file mode 100644 index 0000000..c61f1fc --- /dev/null +++ b/lib/chmconfutil.cc @@ -0,0 +1,423 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Thu Nor 17 2016 + * REVISION: + * + */ + +#include +#include + +#include "chmconfutil.h" +#include "chmutil.h" +#include "chmdbg.h" + +using namespace std; + +//--------------------------------------------------------- +// Utility Functions +//--------------------------------------------------------- +// [NOTE] +// This function checks target string which is json format for not file path. +// +bool right_check_json_string(const char* target) +{ + if(CHMEMPTYSTR(target)){ + return false; + } + for(const char* ptr = target; ptr && '\0' != *ptr; ++ptr){ + if(0 == isspace(*ptr) && '\\' != *ptr){ + if('{' == *ptr || '[' == *ptr || ':' == *ptr || '}' == *ptr || ']' == *ptr || '\\' == *ptr || '\'' == *ptr || '\"' == *ptr){ + return true; + } + break; + } + } + return false; +} + +// +// extract for configuration ini file +// +bool extract_conf_value(string& value) +{ + value = trim(value); // trim + + string result(""); + bool is_single_quart = false; + bool is_double_quart = false; + bool is_escape_char = false; + bool is_before_space = false; + for(const char* ptr = value.c_str(); ptr && '\0' != *ptr; ++ptr){ + if(is_single_quart){ + if('\"' == *ptr){ + result += *ptr; + }else if('\'' == *ptr){ + is_single_quart = false; // end of single quart area + }else if('#' == *ptr){ + result += *ptr; // not comment + }else if('\\' == *ptr){ + result += *ptr; // not to escape + }else if(0 != isspace(*ptr)){ + result += *ptr; + }else{ + result += *ptr; + } + }else if(is_double_quart){ + if('\"' == *ptr){ + if(is_escape_char){ + result += *ptr; + is_escape_char = false; + }else{ + is_double_quart = false; // end of double quart area + } + }else if('\'' == *ptr){ + if(is_escape_char){ + result += '\\'; + is_escape_char = false; + } + result += *ptr; + }else if('#' == *ptr){ + if(is_escape_char){ + result += '\\'; + is_escape_char = false; + } + result += *ptr; + }else if('\\' == *ptr){ + if(is_escape_char){ + result += *ptr; + is_escape_char = false; + }else{ + is_escape_char = true; + } + }else if(0 != isspace(*ptr)){ + if(is_escape_char){ + result += '\\'; + is_escape_char = false; + } + result += *ptr; + }else{ + if(is_escape_char){ + result += '\\'; + is_escape_char = false; + } + result += *ptr; + } + }else{ + if('\"' == *ptr){ + if(is_escape_char){ + result += *ptr; + is_escape_char = false; + }else{ + is_double_quart = true; + } + }else if('\'' == *ptr){ + if(is_escape_char){ + result += *ptr; + is_escape_char = false; + }else{ + is_single_quart = true; + } + }else if('#' == *ptr){ + if(!is_before_space){ + // need "not space" before '#' + result += *ptr; + }else{ + // after here is all comment. + break; + } + }else if('\\' == *ptr){ + if(is_escape_char){ + result += *ptr; + is_escape_char = false; + }else{ + is_escape_char = true; + } + }else if(0 != isspace(*ptr)){ + result += *ptr; + is_before_space = true; + is_escape_char = false; + }else{ + result += *ptr; + is_before_space = false; + is_escape_char = false; + } + } + } + if(is_single_quart || is_double_quart || is_escape_char){ + ERR_CHMPRN("Could not extract string(%s)", result.c_str()); + return false; + } + value = trim(result); + return true; +} + +// +// Environment +// +bool have_env_chm_conf(void) +{ + string value(""); + return ((k2h_getenv(CHM_CONFFILE_ENV_NAME, value) && !value.empty()) || (k2h_getenv(CHM_JSONCONF_ENV_NAME, value) && !value.empty())); +} + +bool getenv_chm_conffile(string& value) +{ + value.clear(); + return (k2h_getenv(CHM_CONFFILE_ENV_NAME, value) && !value.empty()); +} + +bool getenv_chm_jsonconf(string& value) +{ + value.clear(); + return (k2h_getenv(CHM_JSONCONF_ENV_NAME, value) && !value.empty()); +} + +// +// Check configuration type +// +CHMCONFTYPE check_chmconf_type(const char* config) +{ + return check_chmconf_type_ex(config, NULL, NULL, NULL); +} + +// +// Check configuration type with environments +// +CHMCONFTYPE check_chmconf_type_ex(const char* config, const char* env_conf_name, const char* env_json_name, string* normalize_config) +{ + string tmpconfig(""); + if(CHMEMPTYSTR(config)){ + // check environment if needs. + if( !(!CHMEMPTYSTR(env_conf_name) && k2h_getenv(env_conf_name, tmpconfig) && !tmpconfig.empty()) && + !(!CHMEMPTYSTR(env_json_name) && k2h_getenv(env_json_name, tmpconfig) && !tmpconfig.empty()) ) + { + return CHMCONF_TYPE_NULL; + } + }else{ + tmpconfig = config; + } + + // check type(file or json string) + CHMCONFTYPE result_type = CHMCONF_TYPE_UNKNOWN; + if(right_check_json_string(tmpconfig.c_str())){ + // json string type + MSG_CHMPRN("configuration parameter is json string."); + result_type = CHMCONF_TYPE_JSON_STRING; + }else{ + // file type + MSG_CHMPRN("configuration parameter(%s) is file path.", tmpconfig.c_str()); + + if(!is_file_safe_exist(tmpconfig.c_str())){ + ERR_CHMPRN("configuration file %s is not existed.", tmpconfig.c_str()); + result_type = CHMCONF_TYPE_UNKNOWN; + }else{ + char* pTmp = strdup(tmpconfig.c_str()); + char* base = basename(pTmp); + char* pos; + if(CHMEMPTYSTR(base) || NULL == (pos = strrchr(base, '.'))){ + WAN_CHMPRN("configuration file %s does not have file extention, thus type is set default(INI).", tmpconfig.c_str()); + result_type = CHMCONF_TYPE_INI_FILE; + }else{ + ++pos; + if(0 == strcasecmp(pos, "ini")){ + result_type = CHMCONF_TYPE_INI_FILE; + }else if(0 == strcasecmp(pos, "json")){ + result_type = CHMCONF_TYPE_JSON_FILE; + }else if(0 == strcasecmp(pos, "yaml") || 0 == strcasecmp(pos, "yml")){ + result_type = CHMCONF_TYPE_YAML_FILE; + }else{ + ERR_CHMPRN("configuration file %s extension is unknown(should be .ini .json .yaml).", tmpconfig.c_str()); + result_type = CHMCONF_TYPE_UNKNOWN; + } + } + CHM_Free(pTmp); + } + } + + if(CHMCONF_TYPE_UNKNOWN != result_type && normalize_config){ + (*normalize_config) = tmpconfig; + } + return result_type; +} + +//--------------------------------------------------------- +// Helper Class CHMYamlDataStack +//--------------------------------------------------------- +// +// utility macros +// +#define CHMYAML_STACK_IS_NOTYPE(type) ( YAML_NO_EVENT == type ) + +#define CHMYAML_STACK_IS_OPENED_TYPE(type) ( YAML_STREAM_START_EVENT == type || \ + YAML_DOCUMENT_START_EVENT == type || \ + YAML_MAPPING_START_EVENT == type || \ + YAML_SEQUENCE_START_EVENT == type ) + + +#define CHMYAML_STACK_IS_CLOSED_TYPE(type) ( YAML_STREAM_END_EVENT == type || \ + YAML_DOCUMENT_END_EVENT == type || \ + YAML_MAPPING_END_EVENT == type || \ + YAML_SEQUENCE_END_EVENT == type || \ + YAML_SCALAR_EVENT == type || \ + YAML_ALIAS_EVENT == type ) + +#define CHMYAML_STACK_GET_SYMMETRY_TYPE(type) ( YAML_NO_EVENT == type ? YAML_NO_EVENT : \ + YAML_STREAM_START_EVENT == type ? YAML_STREAM_END_EVENT : \ + YAML_STREAM_END_EVENT == type ? YAML_STREAM_START_EVENT : \ + YAML_DOCUMENT_START_EVENT == type ? YAML_DOCUMENT_END_EVENT : \ + YAML_DOCUMENT_END_EVENT == type ? YAML_DOCUMENT_START_EVENT : \ + YAML_MAPPING_START_EVENT == type ? YAML_MAPPING_END_EVENT : \ + YAML_MAPPING_END_EVENT == type ? YAML_MAPPING_START_EVENT : \ + YAML_SEQUENCE_START_EVENT == type ? YAML_SEQUENCE_END_EVENT : \ + YAML_SEQUENCE_END_EVENT == type ? YAML_SEQUENCE_START_EVENT : \ + YAML_SCALAR_EVENT == type ? YAML_SCALAR_EVENT : \ + YAML_ALIAS_EVENT == type ? YAML_ALIAS_EVENT : YAML_NO_EVENT ) + +#define CHMYAML_STACK_CEHCK_SYMMETRY_TYPE(type1, type2) (type1 == CHMYAML_STACK_GET_SYMMETRY_TYPE(type2)) + +#define CHMYAML_STACK_IS_EMPTY (0 >= stack.size()) +#define CHMYAML_STACK_FIRST (stack[stack.size() - 1].first) +#define CHMYAML_STACK_SECOND (stack[stack.size() - 1].second) +#define CHMYAML_STACK_SET_FIRST(type) stack[stack.size() - 1].first = type +#define CHMYAML_STACK_SET_SECOND(type) stack[stack.size() - 1].second = type + +bool CHMYamlDataStack::add(const yaml_event_type_t& type) +{ + switch(type){ + case YAML_NO_EVENT: + break; + + case YAML_DOCUMENT_START_EVENT: + case YAML_STREAM_START_EVENT: + stack.push_back(CHM_YAML_DATAPAIR(type)); + break; + + case YAML_DOCUMENT_END_EVENT: + case YAML_STREAM_END_EVENT: + if(CHMYAML_STACK_IS_EMPTY){ + ERR_CHMPRN("Yaml parser stack : there is no start section before end section type.") + return false; + }else{ + if(CHMYAML_STACK_CEHCK_SYMMETRY_TYPE(type, CHMYAML_STACK_FIRST)){ + ERR_CHMPRN("Yaml parser stack : there is no symmetory section for type.") + return false; + } + stack.pop_back(); + } + break; + + case YAML_MAPPING_START_EVENT: + case YAML_SEQUENCE_START_EVENT: + if(CHMYAML_STACK_IS_EMPTY){ + stack.push_back(CHM_YAML_DATAPAIR(type)); + + }else if(!CHMYAML_STACK_IS_NOTYPE(CHMYAML_STACK_SECOND)){ + if(CHMYAML_STACK_IS_CLOSED_TYPE(CHMYAML_STACK_SECOND)){ + WAN_CHMPRN("Yaml parser stack : internal warning - why come here, try to recover...") + stack.pop_back(); + return add(type); // Recursive call + }else{ // CHMYAML_STACK_SECOND is opened + stack.push_back(CHM_YAML_DATAPAIR(type)); + } + }else{ // CHMYAML_STACK_SECOND is empty + if(CHMYAML_STACK_IS_NOTYPE(CHMYAML_STACK_FIRST)){ + WAN_CHMPRN("Yaml parser stack : internal warning - why come here, try to recover...") + CHMYAML_STACK_SET_FIRST(type); // overwrite + + }else if(CHMYAML_STACK_IS_CLOSED_TYPE(CHMYAML_STACK_FIRST)){ + CHMYAML_STACK_SET_SECOND(type); + + }else{ // CHMYAML_STACK_FIRST is opened + stack.push_back(CHM_YAML_DATAPAIR(type)); + } + } + break; + + case YAML_MAPPING_END_EVENT: + case YAML_SEQUENCE_END_EVENT: + if(CHMYAML_STACK_IS_EMPTY){ + ERR_CHMPRN("Yaml parser stack : there is no start section before end section type.") + return false; + + }else if(!CHMYAML_STACK_IS_NOTYPE(CHMYAML_STACK_SECOND)){ + if(CHMYAML_STACK_CEHCK_SYMMETRY_TYPE(type, CHMYAML_STACK_SECOND)){ + stack.pop_back(); + + }else if(CHMYAML_STACK_IS_CLOSED_TYPE(CHMYAML_STACK_SECOND)){ + WAN_CHMPRN("Yaml parser stack : internal warning - why come here, try to recover...") + stack.pop_back(); + return add(type); // Recursive call + + }else{ // CHMYAML_STACK_SECOND is opened + ERR_CHMPRN("Yaml parser stack : internal error - why come here") + return false; + } + }else{ // CHMYAML_STACK_SECOND is empty + if(CHMYAML_STACK_CEHCK_SYMMETRY_TYPE(type, CHMYAML_STACK_FIRST)){ + CHMYAML_STACK_SET_FIRST(type); + + }else if(CHMYAML_STACK_IS_CLOSED_TYPE(CHMYAML_STACK_FIRST)){ + // should close this stack level. + stack.pop_back(); + return add(type); // Recursive call + + }else{ // CHMYAML_STACK_FIRST is opened + ERR_CHMPRN("Yaml parser stack : internal error - why come here") + return false; + } + } + break; + + case YAML_SCALAR_EVENT: + case YAML_ALIAS_EVENT: // not support ALIAS now. + if(CHMYAML_STACK_IS_EMPTY){ + stack.push_back(CHM_YAML_DATAPAIR(type)); + + }else if(!CHMYAML_STACK_IS_NOTYPE(CHMYAML_STACK_SECOND)){ + if(CHMYAML_STACK_IS_CLOSED_TYPE(CHMYAML_STACK_SECOND)){ + WAN_CHMPRN("Yaml parser stack : internal warning - why come here, try to recover...") + stack.pop_back(); + return add(type); // Recursive call + + }else{ // CHMYAML_STACK_SECOND is opened + stack.push_back(CHM_YAML_DATAPAIR(type)); + } + }else{ // CHMYAML_STACK_SECOND is empty + if(!CHMYAML_STACK_IS_NOTYPE(CHMYAML_STACK_FIRST)){ + if(CHMYAML_STACK_IS_CLOSED_TYPE(CHMYAML_STACK_FIRST)){ + stack.pop_back(); + }else{ // CHMYAML_STACK_FIRST is opened + stack.push_back(CHM_YAML_DATAPAIR(type)); + } + }else{ // CHMYAML_STACK_FIRST is empty + WAN_CHMPRN("Yaml parser stack : internal warning - why come here, try to recover...") + CHMYAML_STACK_SET_FIRST(type); + } + } + break; + } + return true; +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmconfutil.h b/lib/chmconfutil.h new file mode 100644 index 0000000..cded14f --- /dev/null +++ b/lib/chmconfutil.h @@ -0,0 +1,104 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Thu Nor 17 2016 + * REVISION: + * + */ + +#ifndef CHMCONFUTIL_H +#define CHMCONFUTIL_H + +#include +#include +#include +#include "chmcommon.h" + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +// Environment +#define CHM_CONFFILE_ENV_NAME "CHMCONFFILE" +#define CHM_JSONCONF_ENV_NAME "CHMJSONCONF" + +//--------------------------------------------------------- +// Enum +//--------------------------------------------------------- +// enum for check_chmconf_type(_ex) functions +// +typedef enum chm_conf_type{ + CHMCONF_TYPE_UNKNOWN = -1, + CHMCONF_TYPE_NULL = 0, + CHMCONF_TYPE_INI_FILE, + CHMCONF_TYPE_YAML_FILE, + CHMCONF_TYPE_JSON_FILE, + CHMCONF_TYPE_JSON_STRING +}CHMCONFTYPE; + +//--------------------------------------------------------- +// Utility Functions +//--------------------------------------------------------- +DECL_EXTERN_C_START + +bool right_check_json_string(const char* target); +bool have_env_chm_conf(void); + +DECL_EXTERN_C_END + +bool extract_conf_value(std::string& value); +bool getenv_chm_conffile(std::string& value); +bool getenv_chm_jsonconf(std::string& value); +CHMCONFTYPE check_chmconf_type(const char* config); +CHMCONFTYPE check_chmconf_type_ex(const char* config, const char* env_conf_name, const char* env_json_name, std::string* normalize_config); + +//--------------------------------------------------------- +// Structure for CHMYamlDataStack +//--------------------------------------------------------- +typedef struct chm_yaml_data_pair_type{ + yaml_event_type_t first; + yaml_event_type_t second; + + chm_yaml_data_pair_type(yaml_event_type_t first_type = YAML_NO_EVENT) : first(first_type), second(YAML_NO_EVENT) {} + +}CHM_YAML_DATAPAIR; + +typedef std::vector chm_yaml_datas_t; + +//--------------------------------------------------------- +// Class CHMYamlDataStack +//--------------------------------------------------------- +class CHMYamlDataStack +{ + private: + chm_yaml_datas_t stack; + + public: + CHMYamlDataStack() {} + virtual ~CHMYamlDataStack() {} + + bool empty(void) const { return stack.empty(); } + bool add(const yaml_event_type_t& type); +}; + +#endif // CHMCONFUTIL_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmdbg.cc b/lib/chmdbg.cc new file mode 100644 index 0000000..76f470d --- /dev/null +++ b/lib/chmdbg.cc @@ -0,0 +1,187 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include +#include + +#include "chmcommon.h" +#include "chmdbg.h" + +//--------------------------------------------------------- +// Class CHMDbgControl +//--------------------------------------------------------- +class CHMDbgControl +{ + protected: + static const char* DBGENVNAME; + static const char* DBGENVFILE; + static CHMDbgControl singleton; + + public: + static bool LoadEnv(void); + static bool LoadEnvName(void); + static bool LoadEnvFile(void); + + CHMDbgControl(); + virtual ~CHMDbgControl(); +}; + +// Class valiables +const char* CHMDbgControl::DBGENVNAME = "CHMDBGMODE"; +const char* CHMDbgControl::DBGENVFILE = "CHMDBGFILE"; +CHMDbgControl CHMDbgControl::singleton; + +// Constructor / Destructor +CHMDbgControl::CHMDbgControl() +{ + CHMDbgControl::LoadEnv(); +} +CHMDbgControl::~CHMDbgControl() +{ +} + +// Class Methods +bool CHMDbgControl::LoadEnv(void) +{ + if(!CHMDbgControl::LoadEnvName() || !CHMDbgControl::LoadEnvFile()){ + return false; + } + return true; +} + +bool CHMDbgControl::LoadEnvName(void) +{ + char* pEnvVal; + if(NULL == (pEnvVal = getenv(CHMDbgControl::DBGENVNAME))){ + MSG_CHMPRN("%s ENV is not set.", CHMDbgControl::DBGENVNAME); + return true; + } + if(0 == strcasecmp(pEnvVal, "SILENT")){ + SetChmDbgMode(CHMDBG_SILENT); + }else if(0 == strcasecmp(pEnvVal, "ERR")){ + SetChmDbgMode(CHMDBG_ERR); + }else if(0 == strcasecmp(pEnvVal, "WAN")){ + SetChmDbgMode(CHMDBG_WARN); + }else if(0 == strcasecmp(pEnvVal, "INFO")){ + SetChmDbgMode(CHMDBG_MSG); + }else if(0 == strcasecmp(pEnvVal, "DUMP")){ + SetChmDbgMode(CHMDBG_DUMP); + }else{ + MSG_CHMPRN("%s ENV is not unknown string(%s).", CHMDbgControl::DBGENVNAME, pEnvVal); + return false; + } + return true; +} + +bool CHMDbgControl::LoadEnvFile(void) +{ + char* pEnvVal; + if(NULL == (pEnvVal = getenv(CHMDbgControl::DBGENVFILE))){ + MSG_CHMPRN("%s ENV is not set.", CHMDbgControl::DBGENVFILE); + return true; + } + if(!SetChmDbgFile(pEnvVal)){ + MSG_CHMPRN("%s ENV is unsafe string(%s).", CHMDbgControl::DBGENVFILE, pEnvVal); + return false; + } + return true; +} + +//--------------------------------------------------------- +// Global variable +//--------------------------------------------------------- +ChmDbgMode chm_debug_mode = CHMDBG_SILENT; +FILE* chm_dbg_fp = NULL; + +ChmDbgMode SetChmDbgMode(ChmDbgMode mode) +{ + ChmDbgMode oldmode = chm_debug_mode; + chm_debug_mode = mode; + return oldmode; +} + +ChmDbgMode BumpupChmDbgMode(void) +{ + ChmDbgMode mode = GetChmDbgMode(); + + if(CHMDBG_SILENT == mode){ + mode = CHMDBG_ERR; + }else if(CHMDBG_ERR == mode){ + mode = CHMDBG_WARN; + }else if(CHMDBG_WARN == mode){ + mode = CHMDBG_MSG; + }else if(CHMDBG_MSG == mode){ + mode = CHMDBG_DUMP; + }else{ // CHMDBG_DUMP == mode + mode = CHMDBG_SILENT; + } + return ::SetChmDbgMode(mode); +} + +ChmDbgMode GetChmDbgMode(void) +{ + return chm_debug_mode; +} + +bool LoadChmDbgEnv(void) +{ + return CHMDbgControl::LoadEnv(); +} + +bool SetChmDbgFile(const char* filepath) +{ + if(CHMEMPTYSTR(filepath)){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!UnsetChmDbgFile()){ + return false; + } + FILE* newfp; + if(NULL == (newfp = fopen(filepath, "a+"))){ + ERR_CHMPRN("Could not open debug file(%s). errno = %d", filepath, errno); + return false; + } + chm_dbg_fp = newfp; + return true; +} + +bool UnsetChmDbgFile(void) +{ + if(chm_dbg_fp){ + if(0 != fclose(chm_dbg_fp)){ + ERR_CHMPRN("Could not close debug file. errno = %d", errno); + chm_dbg_fp = NULL; // On this case, chm_dbg_fp is not correct pointer after error... + return false; + } + chm_dbg_fp = NULL; + } + return true; +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmdbg.h b/lib/chmdbg.h new file mode 100644 index 0000000..cffed74 --- /dev/null +++ b/lib/chmdbg.h @@ -0,0 +1,93 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMDBG_H +#define CHMDBG_H + +#include "chmcommon.h" + +DECL_EXTERN_C_START + +//--------------------------------------------------------- +// Debug +//--------------------------------------------------------- +typedef enum chm_dbg_mode{ + CHMDBG_SILENT = 0, + CHMDBG_ERR = 1, + CHMDBG_WARN = 3, + CHMDBG_MSG = 7, + CHMDBG_DUMP = 15 +}ChmDbgMode; + +extern ChmDbgMode chm_debug_mode; // Do not use directly this variable. +extern FILE* chm_dbg_fp; + +ChmDbgMode SetChmDbgMode(ChmDbgMode mode); +ChmDbgMode BumpupChmDbgMode(void); +ChmDbgMode GetChmDbgMode(void); +bool LoadChmDbgEnv(void); +bool SetChmDbgFile(const char* filepath); +bool UnsetChmDbgFile(void); + +//--------------------------------------------------------- +// Debugging Macros +//--------------------------------------------------------- +#define ChmDbgMode_STR(mode) CHMDBG_SILENT == mode ? "SLT" : \ + CHMDBG_ERR == mode ? "ERR" : \ + CHMDBG_WARN == mode ? "WAN" : \ + CHMDBG_MSG == mode ? "MSG" : "" + +#define LOW_CHMPRINT(mode, fmt, ...) \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), "[CHMPX-%s] %s(%d) : " fmt "%s\n", ChmDbgMode_STR(mode), __func__, __LINE__, __VA_ARGS__); + +#define CHMPRINT(mode, ...) \ + if((chm_debug_mode & mode) == mode){ \ + LOW_CHMPRINT(mode, __VA_ARGS__); \ + } + +#define SLT_CHMPRN(...) CHMPRINT(CHMDBG_SILENT, ##__VA_ARGS__, "") // This means nothing... +#define ERR_CHMPRN(...) CHMPRINT(CHMDBG_ERR, ##__VA_ARGS__, "") +#define WAN_CHMPRN(...) CHMPRINT(CHMDBG_WARN, ##__VA_ARGS__, "") +#define MSG_CHMPRN(...) CHMPRINT(CHMDBG_MSG, ##__VA_ARGS__, "") + +// +// If using following macro, need to specify include time.h. +// +#define CLOCKTIME_CHMPRN(phead) \ + if((chm_debug_mode & CHMDBG_DUMP) == CHMDBG_DUMP){ \ + struct timespec reqtime; \ + if(-1 == clock_gettime(CLOCK_REALTIME_COARSE, &reqtime)){ \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), "[CHMPX-TIME-%s] ---s ---ms ---us ---ns\n", (phead ? phead : "DBG")); \ + }else{ \ + fprintf((chm_dbg_fp ? chm_dbg_fp : stderr), "[CHMPX-TIME-%s] %03jds %03ldms %03ldus %03ldns\n", (phead ? phead : "DBG"), static_cast(reqtime.tv_sec), (reqtime.tv_nsec / (1000 * 1000)), ((reqtime.tv_nsec % (1000 * 1000)) / 1000), (reqtime.tv_nsec % 1000)); \ + } \ + } + +DECL_EXTERN_C_END + +#endif // CHMDBG_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmeventbase.cc b/lib/chmeventbase.cc new file mode 100644 index 0000000..f6988dd --- /dev/null +++ b/lib/chmeventbase.cc @@ -0,0 +1,75 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include + +#include "chmcommon.h" +#include "chmeventbase.h" +#include "chmutil.h" +#include "chmdbg.h" + +using namespace std; + +//--------------------------------------------------------- +// ChmEventBase Methods +//--------------------------------------------------------- +ChmEventBase::ChmEventBase(int eventqfd, ChmCntrl* pcntrl) : eqfd(eventqfd), pChmCntrl(pcntrl) +{ +} + +ChmEventBase::~ChmEventBase() +{ + Clean(); +} + +bool ChmEventBase::Clean(void) +{ + pChmCntrl = NULL; + eqfd = CHM_INVALID_HANDLE; + return true; +} + +bool ChmEventBase::UpdateInternalData(void) +{ + return true; +} + +bool ChmEventBase::SetEventQueueFd(int eventqfd) +{ + // do we need check this eqfd? + eqfd = eventqfd; + return true; +} + +bool ChmEventBase::SetChmCntrlObj(ChmCntrl* pcntrl) +{ + pChmCntrl = pcntrl; + return true; +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmeventbase.h b/lib/chmeventbase.h new file mode 100644 index 0000000..10cbbcb --- /dev/null +++ b/lib/chmeventbase.h @@ -0,0 +1,77 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMEVENTBASE_H +#define CHMEVENTBASE_H + +#include + +#include "chmcomstructure.h" + +//--------------------------------------------------------- +// Prototype +//--------------------------------------------------------- +class ChmCntrl; + +//--------------------------------------------------------- +// Typedefs +//--------------------------------------------------------- +typedef std::vector event_fds_t; + +//--------------------------------------------------------- +// ChmEventBase Class +//--------------------------------------------------------- +class ChmEventBase +{ + protected: + int eqfd; // backup + ChmCntrl* pChmCntrl; + + public: + ChmEventBase(int eventqfd = CHM_INVALID_HANDLE, ChmCntrl* pcntrl = NULL); + virtual ~ChmEventBase(); + + bool IsEmpty(void) const { return (!pChmCntrl || CHM_INVALID_HANDLE == eqfd); } + bool SetEventQueueFd(int eventqfd); + bool SetChmCntrlObj(ChmCntrl* pcntrl); + + virtual bool Clean(void); + virtual bool UpdateInternalData(void); + + virtual bool GetEventQueueFds(event_fds_t& fds) = 0; + virtual bool SetEventQueue(void) = 0; + virtual bool UnsetEventQueue(void) = 0; + virtual bool IsEventQueueFd(int fd) = 0; + + virtual bool Send(PCOMPKT pComPkt, const unsigned char* pbody, size_t blength) = 0; + virtual bool Receive(int fd) = 0; + virtual bool NotifyHup(int fd) = 0; + virtual bool Processing(PCOMPKT pComPkt) = 0; +}; + +#endif // CHMEVENTBASE_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmeventmq.cc b/lib/chmeventmq.cc new file mode 100644 index 0000000..9ce143b --- /dev/null +++ b/lib/chmeventmq.cc @@ -0,0 +1,2888 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "chmcommon.h" +#include "chmeventmq.h" +#include "chmcntrl.h" +#include "chmimdata.h" +#include "chmutil.h" +#include "chmlock.h" +#include "chmdbg.h" +#include "chmstructure.tcc" + +using namespace std; + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +#define PROCFILE_FOR_MQ "/proc/sys/fs/mqueue/msg_max" + +#define PARSER_K2HASH_MSGIDS "-" +#define SUFFIX_K2HASH_HEADKEY ".HEAD" +#define SUFFIX_K2HASH_BODYKEY ".BODY" + +#define CHMEVMQ_PROC_THREAD_NAME "ChmEventMq-Proc" +#define CHMEVMQ_MERGE_THREAD_NAME "ChmEventMq-Merge" + +//--------------------------------------------------------- +// Class valiable +//--------------------------------------------------------- +const int ChmEventMq::DEFAULT_RETRYCOUNT; +const long ChmEventMq::DEFAULT_TIMEOUT_US; +const int ChmEventMq::DEFAULT_MQ_THREAD_CNT; +const long ChmEventMq::DEFAULT_GETLTS_SLEEP; +const long ChmEventMq::DEFAULT_GETLTS_LOOP; +const long ChmEventMq::DEFAULT_MERGE_TIMEOUT; + +//--------------------------------------------------------- +// Utility (for performance) +//--------------------------------------------------------- +// [NOTE] +// For convert binary(uint64_t) data to string. +// We can use to_hexstring(in fullock) function, but we need performance. +// So should use this low level function for msgid_t and serial_t. +// BE CAREFUL ABOUT this function does not check input variables. +// (buff MUST be over 17 bytes) +// +inline char* cvt_hexstring(char* buff, uint64_t bin) +{ + sprintf(buff, "%016" PRIx64 , bin); + return buff; +} + +//--------------------------------------------------------- +// Class Methods +//--------------------------------------------------------- +bool ChmEventMq::MakeK2hashKey(msgid_t dept_msgid, msgid_t term_msgid, serial_t serial, string& headkey, string& bodykey) +{ + if(CHM_INVALID_MSGID == dept_msgid || CHM_INVALID_MSGID == term_msgid){ + ERR_CHMPRN("Invalid MSGID."); + return false; + } + char szBuff[24]; // need over 17 bytes + headkey = cvt_hexstring(szBuff, dept_msgid); + headkey += PARSER_K2HASH_MSGIDS; + headkey += cvt_hexstring(szBuff, term_msgid); + headkey += PARSER_K2HASH_MSGIDS; + headkey += cvt_hexstring(szBuff, serial); + + bodykey = headkey; + + headkey += SUFFIX_K2HASH_HEADKEY; + bodykey += SUFFIX_K2HASH_BODYKEY; + return true; +} + +// +// Try to set rlimit(if user is root) for request msg size. +// And if not root user, check current msg size. +// +bool ChmEventMq::InitializeMaxMqSystemSize(long maxmsg) +{ + if(0 == geteuid()){ + // root + struct rlimit mylimit; + memset(&mylimit, 0, sizeof(struct rlimit)); + + if(-1 == getrlimit(RLIMIT_MSGQUEUE, &mylimit)){ + ERR_CHMPRN("Could not get rlimit. errno=%d", errno); + return false; + } + MSG_CHMPRN("NOW: hard limit = %jd, soft limit = %jd", static_cast(mylimit.rlim_max), static_cast(mylimit.rlim_cur)); + + // check current value + if(mylimit.rlim_cur < (maxmsg * (sizeof(struct msg_msg*) + 8/*mqattr.mq_msgsize*/))){ + // set new current value + mylimit.rlim_cur = maxmsg * (sizeof(struct msg_msg*) + 8/*mqattr.mq_msgsize*/); + if(-1 == setrlimit(RLIMIT_MSGQUEUE, &mylimit)){ + ERR_CHMPRN("Could not set rlimit. errno=%d", errno); + return false; + } + // check to read + if(-1 == getrlimit(RLIMIT_MSGQUEUE, &mylimit)){ + ERR_CHMPRN("Could not get rlimit. errno=%d", errno); + return false; + } + MSG_CHMPRN("NEW: hard limit = %zd, soft limit = %zd", mylimit.rlim_max, mylimit.rlim_cur); + + // check new value + if(mylimit.rlim_cur < (maxmsg * (sizeof(struct msg_msg*) + 8/*mqattr.mq_msgsize*/))){ + ERR_CHMPRN("Could not set rlimit for request."); + return false; + } + } + }else{ + // not root + int fd; + if(CHM_INVALID_HANDLE == (fd = open(PROCFILE_FOR_MQ, O_RDONLY))){ + ERR_CHMPRN("Could not open file %s. errno=%d", PROCFILE_FOR_MQ, errno); + return false; + } + // read current limit + char* pbuff; + size_t length = 0L; + if(NULL == (pbuff = reinterpret_cast(chm_read(fd, &length)))){ + ERR_CHMPRN("Could not read file %s", PROCFILE_FOR_MQ); + CHM_CLOSE(fd); + return false; + } + long current = static_cast(atoi(pbuff)); + CHM_Free(pbuff); + CHM_CLOSE(fd); + + if(current < maxmsg){ + ERR_CHMPRN("Current msg %ld is too small for specified msg %ld(MAXCLIENT in GLOBAL section).", current, maxmsg); + ERR_CHMPRN("\n\nYou should set current value(%ld) to MAXCLIENT, or MAXCLIENT(%ld) to %s(ex. # echo %ld > %s ).", current, maxmsg, PROCFILE_FOR_MQ, maxmsg, PROCFILE_FOR_MQ); + return false; + } + } + return true; +} + +//--------------------------------------------------------- +// Class Methods - Processing +//--------------------------------------------------------- +bool ChmEventMq::MergeWorkerFunc(void* common_param, chmthparam_t wp_param) +{ + if(!common_param){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + ChmEventMq* pThis = reinterpret_cast(common_param); + + if(!pThis->mgetfunc){ + ERR_CHMPRN("Why, does not set mgetfunc."); + return false; + } + MSG_CHMPRN("Start merge thread in client(MQ)."); + + // Loop + chmpx_h handle = reinterpret_cast(pThis->pChmCntrl); + PCHM_UPDATA_PARAM pUpdateDataParam; + bool result; + time_t start_time; + while(pThis->notify_merge_update){ + // get perameter + while(!fullock::flck_trylock_noshared_mutex(&(pThis->mparam_list_lockval))); // LOCK + pUpdateDataParam = NULL; + if(0 < pThis->merge_param_list.size()){ + pUpdateDataParam = pThis->merge_param_list.front(); + pThis->merge_param_list.erase(pThis->merge_param_list.begin()); + } + fullock::flck_unlock_noshared_mutex(&(pThis->mparam_list_lockval)); // UNLOCK + + if(!pUpdateDataParam){ + break; + } + // copy param + CHM_MERGE_GETPARAM getparam; + getparam.starthash = 0; // must be start posision 0 + getparam.startts.tv_nsec = pUpdateDataParam->startts.tv_nsec; + getparam.startts.tv_sec = pUpdateDataParam->startts.tv_sec; + getparam.endts.tv_nsec = pUpdateDataParam->endts.tv_nsec; + getparam.endts.tv_sec = pUpdateDataParam->endts.tv_sec; + getparam.target_hash = pUpdateDataParam->pending_hash; + getparam.target_max_hash = pUpdateDataParam->max_pending_hash; + getparam.old_hash = pUpdateDataParam->base_hash; + getparam.old_max_hash = pUpdateDataParam->max_base_hash; + getparam.target_hash_range = pUpdateDataParam->replica_count; + getparam.is_expire_check = pUpdateDataParam->is_expire_check; + + // Loop for each hash + chmhash_t nexthash = 0; + PCHMBIN pdatas; + size_t datacnt; + long loop_count = 0; + start_time = time(NULL); + result = true; + while(pThis->notify_merge_update){ + // make start hash + getparam.starthash = nexthash; + + // get target hash datas + nexthash= 0; + pdatas = NULL; + datacnt = 0; + if(!pThis->mgetfunc(handle, &getparam, &nexthash, &pdatas, &datacnt)){ + WAN_CHMPRN("Failed to get hash datas, probabry no more data."); + break; + }else if(0 == datacnt){ + MSG_CHMPRN("Finish to get datas."); + break; + } + + // loop for sending got datas + for(size_t cnt = 0; cnt < datacnt; ++cnt){ + // transfer + if(!pThis->PxCltSendResponseUpdateData(pUpdateDataParam->chmpxid, &pdatas[cnt], &(getparam.endts))){ + ERR_CHMPRN("Failed to send PXCLT_RES_UPDATEDATA, but continue..."); + result = false; + } + } + // clean + CHM_FREE_CHMBINS(pdatas, datacnt); + + // [FIXME] + // This logic is not good... + // + if(0 == ((++loop_count) % 10000) && ChmEventMq::DEFAULT_MERGE_TIMEOUT != pThis->timeout_merge){ + if((start_time + pThis->timeout_merge) < time(NULL)){ + ERR_CHMPRN("Timeouted(%zu) merging.", pThis->timeout_merge); + result = false; + break; + } + } + + // check reaching end + if(0 == nexthash){ + MSG_CHMPRN("Finish to get datas by reach end."); + break; + } + } + + // Finish to update data, so send result + if(!pThis->PxCltSendResultUpdateData(pUpdateDataParam->chmpxid, (pThis->notify_merge_update && result))){ + ERR_CHMPRN("Failed to send CHMPX_CLT_RESULT_UPDATEDATA, but continue..."); + } + + // clean + CHM_Delete(pUpdateDataParam); + } + MSG_CHMPRN("Finish merge thread in client(MQ)."); + return true; +} + +bool ChmEventMq::ReceiveWorkerProc(void* common_param, chmthparam_t wp_param) +{ + ChmEventMq* pMqObj = reinterpret_cast(common_param); + mqd_t mqfd = static_cast(wp_param); + if(!pMqObj || CHM_INVALID_HANDLE == mqfd){ + ERR_CHMPRN("Paraemters are wrong."); + return true; // sleep thread + } + + // get composed msgid + COMPOSEDMSGID composed; + while(pMqObj->ReceiveComposedMsgid(mqfd, composed)){ + // Processing + if(false == pMqObj->RawReceive(mqfd, composed)){ + WAN_CHMPRN("Failed receiving and to processing for msgid(0x%016" PRIx64 ") at MQ(%d) in worker thread, probably nothing to receive.", composed.msgid, mqfd); + } + } + //MSG_CHMPRN("There is no read composed msgid from MQ(%d).", pProcParam->mqfd); + + return true; // always return true for continue to work. +} + +//--------------------------------------------------------- +// Class Methods - Lock map +//--------------------------------------------------------- +bool ChmEventMq::RcvfdIdMapCallback(mq_msgid_map_t::iterator& iter, void* pparam) +{ + ChmEventMq* pMqObj = reinterpret_cast(pparam); + if(!pMqObj){ + ERR_CHMPRN("Parameter is wrong."); + return true; // do not stop loop. + } + mqd_t mqfd = iter->first; + msgid_t msgid = iter->second; + + // close MQ & delete epoll event & msgid free + if(!pMqObj->FreeMsgIds(msgid, mqfd)){ + WAN_CHMPRN("Failed some phase to close Failed MQ fd(%d)/msgid(0x%016" PRIx64 ").", mqfd, msgid); + } + + // remove recv_idfd_map mapping, because this callback is for recv_fdid_map. + pMqObj->recv_idfd_map.erase(msgid); + + return true; +} + +bool ChmEventMq::DestfdIdMapCallback(mq_msgid_map_t::iterator& iter, void* pparam) +{ + ChmEventMq* pMqObj = reinterpret_cast(pparam); + if(!pMqObj){ + ERR_CHMPRN("Parameter is wrong."); + return true; // do not stop loop. + } + mqd_t mqfd = iter->first; + msgid_t msgid = iter->second; + + // close MQ + if(-1 == mq_close(mqfd)){ + ERR_CHMPRN("MQ fd(%d) for msgid(0x%016" PRIx64 ") could not be closed: errno=%d.", mqfd, msgid, errno); + } + + // remove dest_idfd_map mapping, because this callback is for dest_fdid_map. + pMqObj->dest_idfd_map.erase(msgid); + + return true; +} + +//--------------------------------------------------------- +// Methods +//--------------------------------------------------------- +ChmEventMq::ChmEventMq(int eventqfd, ChmCntrl* pcntrl, chm_merge_get_cb mgetfp, chm_merge_set_cb msetfp, chm_merge_lastts_cb mlastfp) : + ChmEventBase(eventqfd, pcntrl), + recv_idfd_map(CHM_INVALID_HANDLE), + recv_fdid_map(CHM_INVALID_MSGID,ChmEventMq::RcvfdIdMapCallback, this), + reserve_idfd_map(CHM_INVALID_HANDLE), + dest_idfd_map(CHM_INVALID_HANDLE), + dest_fdid_map(CHM_INVALID_MSGID, ChmEventMq::DestfdIdMapCallback, this), + serial_num(MIN_MQ_SERIAL_NUMBER), actmq_lockval(FLCK_NOSHARED_MUTEX_VAL_UNLOCKED), + procthreads(CHMEVMQ_PROC_THREAD_NAME), mergethread(CHMEVMQ_MERGE_THREAD_NAME), mgetfunc(mgetfp), msetfunc(msetfp), mlastfunc(mlastfp), + notify_merge_get(true), pres_lasttime(NULL), mparam_list_lockval(FLCK_NOSHARED_MUTEX_VAL_UNLOCKED), notify_merge_update(false), + retry_count(ChmEventMq::DEFAULT_RETRYCOUNT), timeout_us(ChmEventMq::DEFAULT_TIMEOUT_US), timeout_merge(ChmEventMq::DEFAULT_MERGE_TIMEOUT), + is_server_mode(false), use_mq_ack(true) +{ + assert(pChmCntrl); + + // make a seed for serial number + struct timespec tmpts; + if(-1 == clock_gettime(CLOCK_BOOTTIME, &tmpts)){ + WAN_CHMPRN("Failed to make seed to serial number, but use time() instead of it."); + serial_num = static_cast(time(NULL) & MASK64_LOWBIT); + }else{ + serial_num = static_cast(tmpts.tv_nsec & MASK64_LOWBIT); + } + + // initialize cache value and threads + // + // [NOTE] + // ChmEventMq must be initialized after initialize ChmIMData + // + if(!UpdateInternalData()){ + ERR_CHMPRN("Could not initialize cache data for ImData, but continue..."); + } +} + +ChmEventMq::~ChmEventMq() +{ + Clean(); +} + +//--------------------------------------------------------- +// Methods for Serial number +//--------------------------------------------------------- +// +// [NOTE] +// Do not need to lock for serial_num, because NUM will not be allocated to +// the same MQ at same time. +// +serial_t ChmEventMq::GetSerialNumber(void) +{ + serial_t number = serial_num; + if(MAX_MQ_SERIAL_NUMBER < ++serial_num){ + serial_num = MIN_MQ_SERIAL_NUMBER; + } + return number; +} + +msgid_t ChmEventMq::ComposeSerialMsgid(const COMPOSEDMSGID& composed) +{ + return ComposeSerialMsgid(composed.msgid, composed.number, composed.is_ack, composed.is_ack_success); +} + +msgid_t ChmEventMq::ComposeSerialMsgid(const msgid_t msgid, const serial_t serial, bool is_ack, bool ack_success) +{ + if(IsEmpty()){ + return CHM_INVALID_MSGID; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + return COMPOSE64(static_cast(serial | (is_ack ? (ack_success ? MQ_ACK_SUCCESS : MQ_ACK_FAILURE) : 0L)), (msgid - pImData->GetBaseMsgId())); +} + +bool ChmEventMq::DecomposeSerialMsgid(const msgid_t msgid, COMPOSEDMSGID& composed) +{ + if(IsEmpty()){ + return false; + } + if(CHM_INVALID_MSGID == msgid){ + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + serial_t tmp = (static_cast(msgid) & MASK64_HIBIT) >> 32; + composed.msgid = pImData->GetBaseMsgId() + (msgid & MASK64_LOWBIT); + composed.number = GET_MQ_SERIAL_NUMBER(tmp); + + if(IS_MQ_ACK(tmp)){ + composed.is_ack = true; + composed.is_ack_success = IS_MQ_ACK_SUCCESS(tmp); + }else{ + composed.is_ack = false; + composed.is_ack_success = false; + } + return true; +} + +//--------------------------------------------------------- +// Methods for msgids +//--------------------------------------------------------- +bool ChmEventMq::Clean(void) +{ + if(IsEmpty()){ + return true; + } + // exit merge thread assap + notify_merge_update = false; + if(!mergethread.ExitAllThreads()){ + ERR_CHMPRN("Failed to exit thread for merging."); + } + // If waiting to receive command, exit assap + notify_merge_get = true; + + bool result = true; + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_WRITE); // Lock + + // get all mqfd + msgidlist_t freed_msgids; + recv_idfd_map.get_keys(freed_msgids); + + // close all MQ/msgid for receiving. + recv_fdid_map.clear(); // remove all with recv_idfd_map + + // remove all reserve map + reserve_idfd_map.clear(); + + // Send PX(C) close notify msgids which are disactivated. + if(0 < freed_msgids.size()){ + if(!PxCltSendCloseNotify(freed_msgids)){ + ERR_CHMPRN("Failed to send close notify, but continue..."); + result = false; + } + } + + // close all cache mqfd for destination. + dest_fdid_map.clear(); // remove with dest_idfd_map + + AutoLock.UnLock(); // Unlock + + if(!ChmEventBase::Clean()){ + result = false; + } + return result; +} + +// +// initialize/reinitialize cache value and threads +// +bool ChmEventMq::UpdateInternalData(void) +{ + ChmIMData* pImData; + if(!pChmCntrl || NULL == (pImData = pChmCntrl->GetImDataObj())){ + ERR_CHMPRN("Object is not pre-initialized."); + return false; + } + + // processing thread + int conf_thread_cnt = (pImData->IsChmpxProcess() ? pImData->GetMQThreadCount() : DEFAULT_MQ_THREAD_CNT); // we do not need to cache this value. + int now_thread_cnt = procthreads.GetThreadCount(); + if(conf_thread_cnt < now_thread_cnt){ + if(DEFAULT_MQ_THREAD_CNT == conf_thread_cnt){ + // stop all + if(!procthreads.ExitAllThreads()){ + ERR_CHMPRN("Failed to exit thread for MQ processing."); + }else{ + MSG_CHMPRN("stop all MQ processing thread(%d).", now_thread_cnt); + } + }else{ + // need to stop some thread + if(!procthreads.ExitThreads(now_thread_cnt - conf_thread_cnt)){ + ERR_CHMPRN("Failed to exit thread for MQ processing."); + }else{ + MSG_CHMPRN("stop MQ processing thread(%d - %d = %d).", now_thread_cnt, conf_thread_cnt, now_thread_cnt - conf_thread_cnt); + } + } + }else if(now_thread_cnt < conf_thread_cnt){ + // need to run new threads + // + // - parameter is NULL(because thread is sleep at start) + // - sleep at starting + // - not at onece(not one shot) + // - sleep after every working + // - not keep event count + // + if(!procthreads.CreateThreads(conf_thread_cnt - now_thread_cnt, ChmEventMq::ReceiveWorkerProc, NULL, this, 0, true, false, false, false)){ + ERR_CHMPRN("Failed to create thread for MQ processing, but continue..."); + }else{ + MSG_CHMPRN("start to run MQ processing thread(%d + %d = %d).", now_thread_cnt, conf_thread_cnt - now_thread_cnt, conf_thread_cnt); + } + }else{ + // nothing to do because of same thread count + } + + // others + int tmp_retry_count = pImData->GetMQRetryCnt(); + long tmp_timeout_us = pImData->GetMQTimeout(); + time_t tmp_timeout_merge = pImData->GetMergeTimeout(); + retry_count = (CHMEVENTMQ_RETRY_DEFAULT == tmp_retry_count ? ChmEventMq::DEFAULT_RETRYCOUNT : tmp_retry_count); + timeout_us = (CHMEVENTMQ_TIMEOUT_DEFAULT == tmp_timeout_us ? ChmEventMq::DEFAULT_TIMEOUT_US : tmp_timeout_us); + timeout_merge = (CHMEVENTMQ_TIMEOUT_DEFAULT == tmp_timeout_merge ? ChmEventMq::DEFAULT_MERGE_TIMEOUT : tmp_timeout_merge); + is_server_mode = pImData->IsServerMode(); + use_mq_ack = pImData->IsAckMQ(); + + return true; +} + +int ChmEventMq::GetEventQueueFd(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return CHM_INVALID_HANDLE; + } + // get all MQ fds + mqfd_list_t mqfds; + recv_fdid_map.get_keys(mqfds); + + if(1 < mqfds.size()){ + MSG_CHMPRN("This object has many MQ fd."); + } + return (mqfds.empty() ? CHM_INVALID_HANDLE : mqfds.front()); +} + +bool ChmEventMq::GetEventQueueFds(event_fds_t& fds) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + fds.clear(); + + // get all MQ fds + mqfd_list_t mqfds; + recv_fdid_map.get_keys(mqfds); + + for(mqfd_list_t::const_iterator iter = mqfds.begin(); iter != mqfds.end(); ++iter){ + fds.push_back(*iter); + } + return true; +} + +// +// This method assigned one(default) MQ for receiving. +// Assigned MQ count by each calling SetEventQueue() is changed by MQPERATTACH +// configuration(which get by ChmImData::GetMQPerAttach() method). +// Assigned MQ's msgid is stored in CHMSHM and set status ASSIGNED. +// If this method called by CHMPX process, assigned one MQ with +// large queue size. If called by client process, with 1 length queue. +// +bool ChmEventMq::SetEventQueue(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + long mqcnt_per_attach= pChmCntrl->IsChmpxType() ? pImData->GetChmpxMQCount() : pImData->GetMQPerAttach(); + + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_WRITE); // Lock + + // Loop by mqcnt_per_attach + for(int cnt = 0; cnt < mqcnt_per_attach; cnt++){ + // Get new msghead & msgid + // + // For chmpx process, assigned msgid is activated. + // The server side process, assigned msgid is activated. + // The client side process, assigned msgid is DISactivated. + // + msgid_t newmsgid; + if(pChmCntrl->IsChmpxType()){ + newmsgid = pImData->AssignMsgOnChmpx(); + }else if(pChmCntrl->IsClientOnSvrType()){ + newmsgid = pImData->AssignMsgOnServer(); + }else{ // pChmCntrl->IsClientOnSlvType() + newmsgid = pImData->AssignMsgOnSlave(false); // disactivated + } + if(CHM_INVALID_MSGID == newmsgid){ + ERR_CHMPRN("Could not get new msgid area."); + return false; + } + + // Make MQ path + string mqpath; + if(!MakeMqPath(newmsgid, mqpath)){ + ERR_CHMPRN("Failed MQ path from 0x%016" PRIx64 ".", newmsgid); + pImData->FreeMsg(newmsgid); // msgid status is set NOTASSIGNED + return false; + } + + // set umask + mode_t old_umask = umask(0); + + // Open(Create) MQ + mqd_t mqfd; + struct mq_attr mqattr; + mqattr.mq_flags = O_RDONLY | O_NONBLOCK | O_CREAT | O_EXCL; + mqattr.mq_maxmsg = pChmCntrl->IsChmpxType() ? pImData->GetMaxQueuePerChmpxMQ() : pImData->GetMaxQueuePerClientMQ(); + mqattr.mq_msgsize = sizeof(msgid_t); // mq's data is msgid_t + mqattr.mq_curmsgs = 0; + + if(CHM_INVALID_HANDLE == (mqfd = mq_open(mqpath.c_str(), O_RDONLY | O_NONBLOCK | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, &mqattr))){ + if(EEXIST == errno){ + WAN_CHMPRN("Failed to open MQ(%s) by errno=EEXIST, so retry after removing it.", mqpath.c_str()); + + if(-1 == mq_unlink(mqpath.c_str())){ + ERR_CHMPRN("Failed to unlink MQ(%s) by errno(%d), but retry to open...", mqpath.c_str(), errno); + } + // retry + mqfd = mq_open(mqpath.c_str(), O_RDONLY | O_NONBLOCK | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, &mqattr); + + }else if(ENOSPC == errno){ + ERR_CHMPRN("Failed to open MQ(%s) by errno=ENOSPC, maybe there is not enough \"queues_max\".", mqpath.c_str()); + ERR_CHMPRN("You can change \"queues_max\" by following example command."); + ERR_CHMPRN(" # echo 1024 > /proc/sys/fs/mqueue/queues_max"); + + }else if(EINVAL == errno){ + ERR_CHMPRN("Failed to open MQ(%s) by errno=EINVAL, maybe there is not enough \"msg_max\"(require mq_maxmsg is %ld).", mqpath.c_str(), mqattr.mq_maxmsg); + ERR_CHMPRN("You can change \"msg_max\" by following example command."); + ERR_CHMPRN(" # echo %ld > /proc/sys/fs/mqueue/msg_max", mqattr.mq_maxmsg); + + }else{ + ERR_CHMPRN("Failed to open MQ(%s) by errno=%d.", mqpath.c_str(), errno); + } + if(CHM_INVALID_HANDLE == mqfd){ + pImData->FreeMsg(newmsgid); // msgid status is set NOTASSIGNED + umask(old_umask); + return false; + } + } + + // For debug + memset(&mqattr, 0, sizeof(struct mq_attr)); + if(-1 == mq_getattr(mqfd, &mqattr)){ + WAN_CHMPRN("Failed to get mqattr for debugging: errno=%d, but continue.... ", errno); + }else{ + MSG_CHMPRN("mqattr { flag=%ld, maxmsg=%ld, msgsize=%ld, curmsgs=%ld }", mqattr.mq_flags, mqattr.mq_maxmsg, mqattr.mq_msgsize, mqattr.mq_curmsgs); + } + + umask(old_umask); + + // Add event fd + struct epoll_event eqevent; + memset(&eqevent, 0, sizeof(struct epoll_event)); + eqevent.data.fd = mqfd; + eqevent.events = EPOLLIN | EPOLLET | EPOLLRDHUP; // EPOLLRDHUP is set + if(-1 == epoll_ctl(eqfd, EPOLL_CTL_ADD, mqfd, &eqevent)){ + ERR_CHMPRN("Failed to add mqfd(%s: %d) into epoll event(%d), errno=%d", mqpath.c_str(), mqfd, eqfd, errno); + mq_close(mqfd); + mq_unlink(mqpath.c_str()); + pImData->FreeMsg(newmsgid); // msgid status is set NOTASSIGNED + return false; + } + + // Set internal mapping. + recv_idfd_map.set(newmsgid, mqfd, true); + recv_fdid_map.set(mqfd, newmsgid, true); + + if(pChmCntrl->IsClientOnSlvType()){ + // disactivated MQ list + reserve_idfd_map.set(newmsgid, mqfd); + } + } + return true; +} + +bool ChmEventMq::UnsetEventQueue(void) +{ + return Clean(); +} + +bool ChmEventMq::IsEventQueueFd(int fd) +{ + if(CHM_INVALID_HANDLE == fd){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + return recv_fdid_map.find(fd); +} + +msgid_t ChmEventMq::GetAssignedMsgId(int fd) +{ + if(CHM_INVALID_HANDLE == fd){ + ERR_CHMPRN("Parameter is wrong."); + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return CHM_INVALID_MSGID; + } + return recv_fdid_map.get(fd); // if there is not fd, get() returns default value which is specified constructor. +} + +bool ChmEventMq::MakeMqPath(msgid_t msgid, string& path) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + if(CHM_INVALID_MSGID == msgid){ + ERR_CHMPRN("Invalid MSGID."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + string group; + if(!pImData->GetGroup(group)){ + ERR_CHMPRN("Could not get Group name."); + return false; + } + char szBuff[24]; // need over 17 bytes + path = "/"; + path += group; + path += "."; + path += cvt_hexstring(szBuff, msgid); + + return true; +} + +//--------------------------------------------------------- +// Methods for Managing MQ status +//--------------------------------------------------------- +msgid_t ChmEventMq::ActivatedMsgId(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return CHM_INVALID_MSGID; + } + if(!pChmCntrl->IsClientOnSlvType()){ + ERR_CHMPRN("This process is not a process joining slave chmpx."); + return CHM_INVALID_MSGID; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + while(!fullock::flck_trylock_noshared_mutex(&actmq_lockval)); // LOCK + + // does have reserve MQ? + if(0 >= reserve_idfd_map.count()){ + // get reserve MQ + if(!SetEventQueue() || 0 >= reserve_idfd_map.count()){ + ERR_CHMPRN("Failed to increase reserve MQ."); + fullock::flck_unlock_noshared_mutex(&actmq_lockval); // UNLOCK + return CHM_INVALID_MSGID; + } + } + + // get msgid from reserve MQ + msgidlist_t msgids; + reserve_idfd_map.get_keys(msgids); + if(msgids.empty() || CHM_INVALID_MSGID == msgids.front()){ + ERR_CHMPRN("There is no reserve MQ."); + fullock::flck_unlock_noshared_mutex(&actmq_lockval); // UNLOCK + return CHM_INVALID_MSGID; + } + msgid_t msgid = msgids.front(); + + // set activated flag. + if(!pImData->ActivateMsg(msgid)){ + ERR_CHMPRN("Could not set activated status to msgid(0x%016" PRIx64 ").", msgid); + fullock::flck_unlock_noshared_mutex(&actmq_lockval); // UNLOCK + return CHM_INVALID_MSGID; + } + + // remove msgid from reserve list. + reserve_idfd_map.erase(msgid); + fullock::flck_unlock_noshared_mutex(&actmq_lockval); // UNLOCK + + return msgid; +} + +bool ChmEventMq::DisactivatedMsgId(msgid_t msgid) +{ + if(CHM_INVALID_MSGID == msgid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + if(!pChmCntrl->IsClientOnSlvType()){ + ERR_CHMPRN("This process is not a process joining slave chmpx."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_WRITE); // Lock + + // get MQ fd + mqd_t mqfd = recv_idfd_map.find(msgid); + if(CHM_INVALID_HANDLE == mqfd){ + ERR_CHMPRN("There is no msgid(0x%016" PRIx64 ") in MQ list.", msgid); + return false; + } + + // set disactivated flag. + if(!pImData->DisactivateMsg(msgid)){ + ERR_CHMPRN("Could not set disactivated status to msgid(0x%016" PRIx64 ").", msgid); + return false; + } + + // set msgid to reserve MQ list. + if(reserve_idfd_map.find(msgid)){ + WAN_CHMPRN("Already has msgid(0x%016" PRIx64 ") in reserve MQ list, but over write it.", msgid); + } + reserve_idfd_map.set(msgid, mqfd, true); // allow over write + + // check & free msgid, if over reserve MQ limit. + if(!FreeReserveMsgIds()){ + WAN_CHMPRN("Something error occured to unassign MQ, maybe leaking MQ, but continue..."); + } + return true; +} + +bool ChmEventMq::FreeReserveMsgIds(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + if(!pChmCntrl->IsClientOnSlvType()){ + ERR_CHMPRN("This process is not a process joining slave chmpx."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + long mqcnt_per_attach= pImData->GetMQPerAttach(); + if(mqcnt_per_attach <= 0){ + mqcnt_per_attach = 1L; // why? + } + + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_WRITE); // Lock + + // make count for closing mq + long to_close_cnt; + { + long activated_cnt = static_cast(recv_idfd_map.count() - reserve_idfd_map.count()); + long lest_cnt = ((activated_cnt / mqcnt_per_attach) * mqcnt_per_attach) + (0 == activated_cnt % mqcnt_per_attach ? 0 : 1L); + to_close_cnt = static_cast(recv_idfd_map.count()) - lest_cnt; + if(static_cast(reserve_idfd_map.count()) < to_close_cnt){ + to_close_cnt = static_cast(reserve_idfd_map.count()); // why? + } + } + + // do closing (use reserve_idfd_map directly) + msgidlist_t freed_msgids; + while(!fullock::flck_trylock_noshared_mutex(&reserve_idfd_map.lockval)); // LOCK + for(msgid_mq_map_t::iterator iter = reserve_idfd_map.basemap.begin(); iter != reserve_idfd_map.basemap.end() && 0 < to_close_cnt; reserve_idfd_map.basemap.erase(iter++), --to_close_cnt){ + // close MQ/msgid. + recv_fdid_map.erase(iter->second); // remove all with recv_idfd_map + + // backup freed msgid. + freed_msgids.push_back(iter->first); + } + fullock::flck_unlock_noshared_mutex(&reserve_idfd_map.lockval); // UNLOCK + + // Send PX(C) close notify msgids which are disactivated. + bool result = true; + if(0 < freed_msgids.size()){ + if(!PxCltSendCloseNotify(freed_msgids)){ + ERR_CHMPRN("Failed to send close notify."); + result = false; + } + } + return result; +} + +bool ChmEventMq::FreeMsgIds(msgid_t msgid, mqd_t mqfd) +{ + if(CHM_INVALID_MSGID == msgid || CHM_INVALID_HANDLE == mqfd){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + bool result = true; + + // remove mqfd from eq + if(-1 == epoll_ctl(eqfd, EPOLL_CTL_DEL, mqfd, NULL)){ + ERR_CHMPRN("MQ fd(%d) could not be removed from eventfd(%d): errno=%d, but continue...", mqfd, eqfd, errno); + result = false; + } + + // Close MQ + if(-1 == mq_close(mqfd)){ + ERR_CHMPRN("MQ fd(%d) could not be closed: errno=%d, but continue...", mqfd, errno); + result = false; + } + + // Remove MQ path + string mqpath; + if(!MakeMqPath(msgid, mqpath)){ + ERR_CHMPRN("Failed MQ path from 0x%016" PRIx64 ", but continue...", msgid); + result = false; + }else{ + if(-1 == mq_unlink(mqpath.c_str())){ + ERR_CHMPRN("Failed to unlink MQ(%s) by errno(%d), but continue...", mqpath.c_str(), errno); + } + } + + // msgid is freed + if(!pImData->FreeMsg(msgid)){ // msgid status is set NOTASSIGNED + ERR_CHMPRN("Failed to remove msgid(0x%016" PRIx64 ") from assigned list.", msgid); + result = false; + } + return result; +} + +mqd_t ChmEventMq::OpenDestMQ(msgid_t msgid) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return CHM_INVALID_HANDLE; + } + + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_WRITE); // Write Lock + + // get MQ fd from cache + mqd_t mqfd = dest_idfd_map.get(msgid); + if(CHM_INVALID_HANDLE != mqfd){ + return mqfd; + } + + // make MQ path + string mq_path; + if(!MakeMqPath(msgid, mq_path)){ + ERR_CHMPRN("Failed to make mqueue path for msgid(0x%016" PRIx64 ")", msgid); + return CHM_INVALID_HANDLE; + } + + // open + if(CHM_INVALID_HANDLE == (mqfd = mq_open(mq_path.c_str(), O_WRONLY | O_NONBLOCK))){ + ERR_CHMPRN("Failed to open mqueue for %s(error=%d)", mq_path.c_str(), errno); + return CHM_INVALID_HANDLE; + } + + // set cache + dest_idfd_map.set(msgid, mqfd); + dest_fdid_map.set(mqfd, msgid); + + return mqfd; +} + +bool ChmEventMq::CloseDestMQ(msgid_t msgid) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_WRITE); // Write Lock + + // search msgid in cache + mqd_t mqfd = dest_idfd_map.get(msgid); + if(CHM_INVALID_HANDLE == mqfd){ + MSG_CHMPRN("Could not found MQ for msgid(0x%016" PRIx64 "), maybe already closed it.", msgid); + return true; + } + + // close cache mqfd for destination. + dest_fdid_map.erase(mqfd); // remove with dest_idfd_map + + return true; +} + +//--------------------------------------------------------- +// Methods for COMHEAD +//--------------------------------------------------------- +// +// Make COMPKT for replying from all client. +// +bool ChmEventMq::BuildC2CResponseHead(PCOMHEAD pComHead, PCOMHEAD pReqComHead) +{ + return BuildC2CHeadEx(pComHead, pReqComHead, CHM_INVALID_CHMPXID, 0L, COM_C2C_NORMAL, CHM_INVALID_MSGID); +} + +// +// Make COMPKT for sending from all client. +// +bool ChmEventMq::BuildC2CSendHead(PCOMHEAD pComHead, chmpxid_t tochmpxid, chmhash_t hash, c2ctype_t c2ctype, msgid_t frommsgid) +{ + return BuildC2CHeadEx(pComHead, NULL, tochmpxid, hash, c2ctype, frommsgid); +} + +bool ChmEventMq::BuildC2CHeadEx(PCOMHEAD pComHead, PCOMHEAD pReqComHead, chmpxid_t tochmpxid, chmhash_t hash, c2ctype_t c2ctype, msgid_t frommsgid) +{ + if(!pComHead){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + if(pChmCntrl->IsChmpxType()){ + // This method for client processes. + ERR_CHMPRN("Starting to send/reply C2C message from CHMPX process."); + return false; + } + + if(!pReqComHead){ + // Send New C2C Message type(deperture is this) + if(pChmCntrl->IsChmpxType()){ + // must start client, peer end chmpx + ERR_CHMPRN("Something wrong, because starting C2C message from CHMPX process."); + return false; + } + if(CHM_INVALID_MSGID == frommsgid){ + if(pChmCntrl->IsClientOnSlvType()){ + // client on slave chmpx proc, must set frommsgid. + ERR_CHMPRN("Invalid msgid for starting to send C2C."); + return false; + } + if(CHM_INVALID_MSGID == (frommsgid = pImData->GetRandomClientMsgId())){ + ERR_CHMPRN("Could not get new msgid for starting to send C2C."); + return false; + } + }else{ + if(!pImData->IsMsgidActivated(frommsgid)){ + ERR_CHMPRN("Deperture msgid(0x%016" PRIx64 ") is not activated.", frommsgid); + return false; + } + } + + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + msgid_t term_msgid = CHM_INVALID_MSGID; // C2C cannot decide msgid. + msgid_t peer_term_msgid = pImData->GetRandomChmpxMsgId(); // Get msgid in chmpx process + + if(CHM_INVALID_CHMPXID == selfchmpxid || CHM_INVALID_MSGID == peer_term_msgid){ + ERR_CHMPRN("Could not get self chmpxid(0x%016" PRIx64 ") or peer msgid(0x%016" PRIx64 ")", selfchmpxid, peer_term_msgid); + return false; + } + pComHead->version = COM_VERSION_1; + pComHead->type = COM_C2C; + pComHead->c2ctype = c2ctype; + pComHead->dept_ids.chmpxid = selfchmpxid; + pComHead->dept_ids.msgid = frommsgid; + pComHead->term_ids.chmpxid = tochmpxid; // allow tochmpxid is CHM_INVALID_CHMPXID on not server mode. + pComHead->term_ids.msgid = term_msgid; + pComHead->peer_dept_msgid = frommsgid; + pComHead->peer_term_msgid = peer_term_msgid; + pComHead->mq_serial_num = GetSerialNumber(); + pComHead->hash = hash; + + }else{ + // COMHEAD is response for pReqComHead + if(COM_C2C != pReqComHead->type){ + ERR_CHMPRN("For response, the request type(%s) is not COM_C2C.", STRCOMTYPE(pReqComHead->type)); + return false; + } + + // make peer ternimate/deperture msgid. + msgid_t peer_dept_msgid = pReqComHead->term_ids.msgid; + msgid_t peer_term_msgid = pImData->GetRandomChmpxMsgId(); // one of chmpx msgid(because start response msg) + if(CHM_INVALID_MSGID == peer_term_msgid){ + ERR_CHMPRN("Could not get peer terminal msgid."); + return false; + } + pComHead->version = COM_VERSION_1; + pComHead->type = COM_C2C; + pComHead->c2ctype = c2ctype; + pComHead->dept_ids.chmpxid = pReqComHead->term_ids.chmpxid; + pComHead->dept_ids.msgid = pReqComHead->term_ids.msgid; + pComHead->term_ids.chmpxid = pReqComHead->dept_ids.chmpxid; + pComHead->term_ids.msgid = pReqComHead->dept_ids.msgid; + pComHead->peer_dept_msgid = peer_dept_msgid; + pComHead->peer_term_msgid = peer_term_msgid; + pComHead->mq_serial_num = GetSerialNumber(); + pComHead->hash = pReqComHead->hash; + } + if(-1 == clock_gettime(CLOCK_REALTIME_COARSE, &(pComHead->reqtime))){ + WAN_CHMPRN("Could not get timespec(errno=%d), but continue...", errno); + INIT_TIMESPEC(&(pComHead->reqtime)); + } + + return true; +} + +// +// If ext_length is not PXCLTPKT_AUTO_LENGTH, allocate memory length as +// COMPKT + type + ext_length size. +// +PCOMPKT ChmEventMq::AllocatePxCltPacket(pxclttype_t type, ssize_t ext_length) +{ + ssize_t length = sizeof(COMPKT) + (PXCLTPKT_AUTO_LENGTH == ext_length ? 0L : ext_length); + size_t typelength = SIZEOF_CHMPX_CLT(type); + if(0 == typelength){ + WAN_CHMPRN("ComPacket type is %s(%" PRIu64 ") which is unknown. so does not convert contents.", STRPXCLTTYPE(type), type); + } + length += typelength; + + PCOMPKT rtnptr; + if(NULL == (rtnptr = reinterpret_cast(malloc(length)))){ + ERR_CHMPRN("Could not allocate memory as %zd length for %s(%" PRIu64 ").", length, STRPXCLTTYPE(type), type); + } + return rtnptr; +} + +//--------------------------------------------------------- +// Methods for Communication +//--------------------------------------------------------- +bool ChmEventMq::NotifyHup(int fd) +{ + MSG_CHMPRN("Event for MQ, not set EPOLLRDHUP type. So this function do nothing."); + return true; +} + +// +// This method is checking COM_C2C type, if is not same it, processing it. +// (for client on chmpx slave) +// +bool ChmEventMq::CheckProcessing(PCOMPKT pComPkt) +{ + if(!pComPkt){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + if(COM_C2C == pComPkt->head.type){ + // do not nothing here. + return false; + } + + if(!Processing(pComPkt)){ + ERR_CHMPRN("Failed processing COM_XXX."); + // So doing processing, return true. + } + return true; +} + +bool ChmEventMq::Processing(PCOMPKT pComPkt) +{ + if(!pComPkt){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + DUMPCOM_COMPKT("MQ::Processing", pComPkt); + + if(COM_C2C == pComPkt->head.type){ + // This case is received MQ data to client. + // (The other case is error.) + // + if(pComPkt->head.term_ids.chmpxid != pImData->GetSelfChmpxId()){ + ERR_CHMPRN("Why does not same chmpxid? COMPKT is received from MQ, it should do processing socket event object."); + return false; + } + + // [NOTICE] + // Come here, following patturn: + // + // 1) socket -> chmps(slave) -> MQ + // 2) socket -> chmps(server) -> MQ + // + // Case 1) Must be set end point of msgid + // Case 2) Set or not set end point of msgid + // If not set that, decide that here. + // + + // set deliver head in COMPKT + if(CHM_INVALID_MSGID == pComPkt->head.term_ids.msgid){ + if(!is_server_mode){ + WAN_CHMPRN("On slave chmpx, do not send COM_C2C to any msgid, but continue..."); + } + // Random + pComPkt->head.term_ids.msgid = pImData->GetRandomClientMsgId(); + } + if(CHM_INVALID_MSGID == pComPkt->head.term_ids.msgid){ + ERR_CHMPRN("Could not transfer pComPkt because msgid is INVALID."); + return false; + } + pComPkt->head.peer_term_msgid = pComPkt->head.term_ids.msgid; + + // set deperture msgid is self waiting msgid + if(CHM_INVALID_MSGID != pComPkt->head.peer_dept_msgid){ + MSG_CHMPRN("Here is the message is after receiving from socket, but it has peer_dept_msgid. so reset it by myself."); + } + pComPkt->head.peer_dept_msgid = pImData->GetRandomChmpxMsgId(); + + // set serial number + pComPkt->head.mq_serial_num = GetSerialNumber(); + + // make packet for MQ + COMPKT SendComPkt; + COPY_COMPKT(&SendComPkt, pComPkt); + SendComPkt.length = sizeof(COMPKT); + SendComPkt.offset = static_cast(sizeof(COMPKT)); + + unsigned char* pbody = reinterpret_cast(pComPkt) + pComPkt->offset; + size_t blength = pComPkt->length - sizeof(COMPKT); + + // for trace + struct timespec start_time; + bool is_trace = pImData->IsTraceEnable(); + if(is_trace){ + STR_MATE_TIMESPEC(&start_time); + } + + // Send + if(!Send(&SendComPkt, pbody, blength)){ + ERR_CHMPRN("Sending ComPkt type(%" PRIu64 ":%s) against ComPkt type(%" PRIu64 ":%s), Something error occured.", SendComPkt.head.type, STRCOMTYPE(SendComPkt.head.type), pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + return false; + } + + // set trace + if(is_trace){ + struct timespec fin_time; + STR_MATE_TIMESPEC(&fin_time); + if(!pImData->AddTrace(CHMLOG_TYPE_OUT_MQ, blength, start_time, fin_time)){ + ERR_CHMPRN("Failed to add trace log."); + } + } + + }else if(COM_PX2C == pComPkt->head.type || COM_C2PX == pComPkt->head.type){ + // This case is that data received from MQ, this data is notify to MQ client. + // + PPXCLT_ALL pCltAll = CVT_CLT_ALL_PTR_PXCOMPKT(pComPkt); + + if(CHMPX_CLT_CLOSE_NOTIFY == pCltAll->val_head.type){ + // received close notify + // + if(!PxCltReceiveCloseNotify(&(pComPkt->head), pCltAll)){ + ERR_CHMPRN("Received PXCLT type(%" PRIu64 ":%s). Something error occured.", pCltAll->val_head.type, STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + + }else if(CHMPX_CLT_MERGE_GETLASTTS == pCltAll->val_head.type){ + // received get lastest update time + // + if(!PxCltReceiveMergeGetLastTime(&(pComPkt->head), pCltAll)){ + ERR_CHMPRN("Received PXCLT type(%" PRIu64 ":%s). Something error occured.", pCltAll->val_head.type, STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + + }else if(CHMPX_CLT_MERGE_RESLASTTS == pCltAll->val_head.type){ + // received response lastest update time + // + if(!PxCltReceiveMergeResponseLastTime(&(pComPkt->head), pCltAll)){ + ERR_CHMPRN("Received PXCLT type(%" PRIu64 ":%s). Something error occured.", pCltAll->val_head.type, STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + + }else if(CHMPX_CLT_REQ_UPDATEDATA == pCltAll->val_head.type){ + // received request update data lastest update time + // + if(!PxCltReceiveRequestUpdateData(&(pComPkt->head), pCltAll)){ + ERR_CHMPRN("Received PXCLT type(%" PRIu64 ":%s). Something error occured.", pCltAll->val_head.type, STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + + }else if(CHMPX_CLT_RES_UPDATEDATA == pCltAll->val_head.type){ + // received response update data lastest update time + // + if(!PxCltReceiveResponseUpdateData(&(pComPkt->head), pCltAll)){ + ERR_CHMPRN("Received PXCLT type(%" PRIu64 ":%s). Something error occured.", pCltAll->val_head.type, STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + + }else if(CHMPX_CLT_SET_UPDATEDATA == pCltAll->val_head.type){ + // received set update data lastest update time + // + if(!PxCltReceiveSetUpdateData(&(pComPkt->head), pCltAll)){ + ERR_CHMPRN("Received PXCLT type(%" PRIu64 ":%s). Something error occured.", pCltAll->val_head.type, STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + + }else if(CHMPX_CLT_RESULT_UPDATEDATA == pCltAll->val_head.type){ + // received result of update data + // + if(!PxCltReceiveResultUpdateData(&(pComPkt->head), pCltAll)){ + ERR_CHMPRN("Received PXCLT type(%" PRIu64 ":%s). Something error occured.", pCltAll->val_head.type, STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + + }else if(CHMPX_CLT_ABORT_UPDATEDATA == pCltAll->val_head.type){ + // received abort update data + // + if(!PxCltReceiveAbortUpdateData(&(pComPkt->head), pCltAll)){ + ERR_CHMPRN("Received PXCLT type(%" PRIu64 ":%s). Something error occured.", pCltAll->val_head.type, STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + + }else{ + ERR_CHMPRN("Could not handle PXCLT type(%" PRIu64 ":%s) in ComPkt type(%" PRIu64 ":%s).", pCltAll->val_head.type, STRPXCLTTYPE(pCltAll->val_head.type), pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + return false; + } + + }else{ + ERR_CHMPRN("Could not handle ComPkt type(%" PRIu64 ":%s).", pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + return false; + } + return true; +} + +bool ChmEventMq::Send(PCOMPKT pComPkt, const unsigned char* pbody, size_t blength) +{ + if(!pComPkt){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(COM_C2C == pComPkt->head.type){ + if(pComPkt->length != sizeof(COMPKT) || pComPkt->offset != sizeof(COMPKT)){ + ERR_CHMPRN("pComPkt(%p) is %s type, but its length or offset is wrong value.", pComPkt, STRCOMTYPE(pComPkt->head.type)); + return false; + } + }else if(COM_C2PX == pComPkt->head.type || COM_PX2C == pComPkt->head.type){ + if(pComPkt->length < sizeof(COMPKT) || pComPkt->offset != sizeof(COMPKT) || pbody || 0 < blength){ + ERR_CHMPRN("pComPkt(%p) is %s type, but its length or offset or body(not null) or body length(not 0) is wrong value.", pComPkt, STRCOMTYPE(pComPkt->head.type)); + return false; + } + }else{ + ERR_CHMPRN("pComPkt(%p) is unsupported type(%s)", pComPkt, STRCOMTYPE(pComPkt->head.type)); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + DUMPCOM_COMPKT("MQ::Send", pComPkt); + DUMPCOM_PXCLT("MQ::Send", CVT_CLT_ALL_PTR_PXCOMPKT(pComPkt)); + + // Make priority + unsigned int priority = COM_C2C == pComPkt->head.type ? MQ_PRIORITY_MSG : MQ_PRIORITY_NOTICE; + + // Get k2hash & key names + K2HShm* pK2hash = pImData->GetK2hashObj(); + if(!pK2hash){ + ERR_CHMPRN("K2hash object is NULL."); + return false; + } + string headkey; + string bodykey; + msgid_t composed= ComposeSerialMsgid(pComPkt->head.peer_dept_msgid, pComPkt->head.mq_serial_num); + if(!ChmEventMq::MakeK2hashKey(pComPkt->head.peer_dept_msgid, pComPkt->head.peer_term_msgid, pComPkt->head.mq_serial_num, headkey, bodykey)){ + ERR_CHMPRN("Failed to make k2hash key name for msgid(0x%016" PRIx64 "-0x%016" PRIx64 "-0x%016" PRIx64 ").", pComPkt->head.peer_dept_msgid, pComPkt->head.peer_term_msgid, pComPkt->head.mq_serial_num); + return false; + } + + // make head & body data in k2hash + unsigned char* phead = reinterpret_cast(pComPkt); + size_t hlength = pComPkt->length; + if( !pK2hash->Set(reinterpret_cast(headkey.c_str()), headkey.length() + 1, phead, hlength, NULL, false) || + (pbody && 0 < blength && !pK2hash->Set(reinterpret_cast(bodykey.c_str()), bodykey.length() + 1, pbody, blength, NULL, false)) ) + { + ERR_CHMPRN("Failed to write head/body data to k2hash for msgid(0x%016" PRIx64 "-0x%016" PRIx64 "-0x%016" PRIx64 ").", pComPkt->head.peer_dept_msgid, pComPkt->head.peer_term_msgid, pComPkt->head.mq_serial_num); + + // remove keys + pK2hash->Remove(reinterpret_cast(headkey.c_str()), headkey.length() + 1); + if(pbody && 0 < blength){ + pK2hash->Remove(reinterpret_cast(bodykey.c_str()), bodykey.length() + 1); + } + return false; + } + + // send signal to receiver mqueue + mqd_t term_mqfd; + if(CHM_INVALID_HANDLE == (term_mqfd = OpenDestMQ(pComPkt->head.peer_term_msgid))){ + ERR_CHMPRN("Failed to open mqueue for msgid(0x%016" PRIx64 "), error=%d", pComPkt->head.peer_term_msgid, errno); + + // remove keys + pK2hash->Remove(reinterpret_cast(headkey.c_str()), headkey.length() + 1); + if(pbody && 0 < blength){ + pK2hash->Remove(reinterpret_cast(bodykey.c_str()), bodykey.length() + 1); + } + return false; + } + struct timespec sleeptime; + SET_TIMESPEC(&sleeptime, (static_cast(timeout_us / (1000 * 1000))), ((timeout_us % (1000 * 1000)) * 1000)); + + int cnt; + for(cnt = 0; cnt < retry_count; cnt++){ + if(-1 == mq_send(term_mqfd, reinterpret_cast(&composed), sizeof(msgid_t), priority)){ + if(errno != EAGAIN){ + ERR_CHMPRN("Failed to send mqueue for msgid(0x%016" PRIx64 "), error=%d", pComPkt->head.peer_term_msgid, errno); + + // remove k2hash + pK2hash->Remove(reinterpret_cast(headkey.c_str()), headkey.length() + 1); + if(pbody && 0 < blength){ + pK2hash->Remove(reinterpret_cast(bodykey.c_str()), bodykey.length() + 1); + } + return false; + } + nanosleep(&sleeptime, NULL); + }else{ + break; + } + } + if(cnt >= retry_count){ + ERR_CHMPRN("Failed to send mqueue for msgid(0x%016" PRIx64 ") by timeouted(%ldus * %d)", pComPkt->head.peer_term_msgid, timeout_us, cnt); + + // remove k2hash + pK2hash->Remove(reinterpret_cast(headkey.c_str()), headkey.length() + 1); + if(pbody && 0 < blength){ + pK2hash->Remove(reinterpret_cast(bodykey.c_str()), bodykey.length() + 1); + } + return false; + } + + // wait for ack, only client on slave(server) + if(COM_C2C == pComPkt->head.type && use_mq_ack && !pChmCntrl->IsChmpxType()){ + if(!ReceiveAck(pComPkt->head.peer_dept_msgid)){ + ERR_CHMPRN("Could not receive ACK from msgid(0x%016" PRIx64 ") and mqfd(%d)", pComPkt->head.peer_term_msgid, term_mqfd); + return false; + } + } + return true; +} + +bool ChmEventMq::SendAck(const COMPOSEDMSGID& composed, bool is_success) +{ + if(CHM_INVALID_MSGID == composed.msgid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + mqd_t mqfd; + if(CHM_INVALID_HANDLE == (mqfd = OpenDestMQ(composed.msgid))){ + ERR_CHMPRN("Not found mqfd as msgid(0x%016" PRIx64 ") for sending ack.", composed.msgid); + return false; + } + + // make response ack data + msgid_t composed_msgid = ComposeSerialMsgid(composed.msgid, composed.number, true, is_success); + unsigned int priority = MQ_PRIORITY_MSG; // make priority + struct timespec sleeptime; + SET_TIMESPEC(&sleeptime, (static_cast(timeout_us / (1000 * 1000))), ((timeout_us % (1000 * 1000)) * 1000)); + + // send loop + int cnt; + for(cnt = 0; cnt < retry_count; cnt++){ + if(-1 == mq_send(mqfd, reinterpret_cast(&composed_msgid), sizeof(msgid_t), priority)){ + if(errno != EAGAIN){ + ERR_CHMPRN("Failed to renponse ack to composed msgid(0x%016" PRIx64 "), error=%d", composed_msgid, errno); + return false; + } + nanosleep(&sleeptime, NULL); + }else{ + return true; + } + } + WAN_CHMPRN("Failed to send mqueue for response ack to composed msgid(0x%016" PRIx64 ") by timeouted(%ldus * %d)", composed_msgid, timeout_us, cnt); + return false; +} + +bool ChmEventMq::ReceiveAck(msgid_t msgid) +{ + if(CHM_INVALID_MSGID == msgid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + mqd_t mqfd = recv_idfd_map.get(msgid); + if(CHM_INVALID_HANDLE == mqfd){ + ERR_CHMPRN("There is no mqfd in mapping for msgid(0x%016" PRIx64 ").", msgid); + return false; + } + + COMPOSEDMSGID composed; + bool CanContinueWait; + struct timespec sleeptime; + SET_TIMESPEC(&sleeptime, (static_cast(timeout_us / (1000 * 1000))), ((timeout_us % (1000 * 1000)) * 1000)); + + // receive ack loop + for(int cnt = 0; cnt < retry_count; cnt++){ + CanContinueWait = false; + if(!ReceiveComposedMsgid(mqfd, composed, &CanContinueWait)){ + if(!CanContinueWait){ + WAN_CHMPRN("Failed to receive ACK from MQ(%d).", mqfd); + return false; + } + nanosleep(&sleeptime, NULL); + continue; + } + if(!composed.is_ack){ + WAN_CHMPRN("Received another request during the ACK waiting from MQ(%d), so continue to wait after processing this interrupt request.", mqfd); + if(false == RawReceive(mqfd, composed)){ + ERR_CHMPRN("Failed receiving and to processing for MQ(%d) directly.", mqfd); + } + // continue to wait + if(0 < cnt){ + --cnt; + } + }else{ + return composed.is_ack_success; + } + } + WAN_CHMPRN("Timeouted for receiving ACK from MQ(%d).", mqfd); + return false; +} + +// +// After receiving, this method is processing own work. And if it needs to dispatch +// processing event after own processing, do it by calling control object. +// +// [NOTE] +// Receive method read full information(COMPKT + PXCLT_COM) from MQ, +// If you need to read head & body by each, you should call ReceiveComposedMsgid() + ReceiveHead() and ReceiveBody() methods. +// +bool ChmEventMq::Receive(int fd) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + // Processing... + bool result = false; + if(procthreads.HasThread()){ + // Has processing threads, so run(do work) processing thread. + chmthparam_t wp_param = static_cast(fd); + + if(false == (result = procthreads.DoWorkThread(wp_param))){ + ERR_CHMPRN("Failed to wake up thread for processing(receiving) for MQ(%d).", fd); + } + }else{ + // Does not have processing thread, so call directly. + + // get composed msgid + COMPOSEDMSGID composed; + if(!ReceiveComposedMsgid(fd, composed)){ + MSG_CHMPRN("Failed to read composed msgid from MQ(%d), probably nothing to receive data.", fd); + return false; + } + if(false == (result = RawReceive(fd, composed))){ + ERR_CHMPRN("Failed receiving and to processing for MQ(%d) directly.", fd); + } + } + return result; +} + +bool ChmEventMq::RawReceive(mqd_t mqfd, const COMPOSEDMSGID& composed) +{ + if(CHM_INVALID_HANDLE == mqfd || CHM_INVALID_MSGID == composed.msgid){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + + // read head + PCOMPKT pComPkt = NULL; + if(!ReceiveHead(mqfd, composed, &pComPkt) || !pComPkt){ + MSG_CHMPRN("Failed to read msg head data from msgid(0x%016" PRIx64 ") received MQ(%d), probably nothing to receive data.", composed.msgid, mqfd); + return false; + } + // read body + PCOMPKT pTmp; + if(NULL == (pTmp = ReceiveBody(pComPkt, true))){ + ERR_CHMPRN("Failed to read msg body data from k2hash for MQ(%d).", mqfd); + CHM_Free(pComPkt); + return false; + } + pComPkt = pTmp; + + // Dispatching + if(pComPkt){ + bool is_need_ack = (COM_C2C == pComPkt->head.type && use_mq_ack && pChmCntrl->IsChmpxType()); + + if(pChmCntrl && !pChmCntrl->Processing(pComPkt, ChmCntrl::EVOBJ_TYPE_EVMQ)){ + // send failure ack + if(is_need_ack && !SendAck(composed, false)){ + ERR_CHMPRN("Failed sending failure ACK to to MQ(%d).", mqfd); + } + ERR_CHMPRN("Failed processing after receiving from MQ(%d).", mqfd); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + // send success ack + if(is_need_ack && !SendAck(composed, true)){ + ERR_CHMPRN("Failed sending success ACK to to MQ(%d).", mqfd); + return false; + } + } + return true; +} + +bool ChmEventMq::ReceiveHeadByMsgId(msgid_t msgid, PCOMPKT* ppComPkt, bool* pCanContinueWait) +{ + mqd_t mqfd = recv_idfd_map.get(msgid); + if(CHM_INVALID_HANDLE == mqfd){ + ERR_CHMPRN("msgid(0x%016" PRIx64 ") is not this client msgid on slave.", msgid); + return false; + } + // get composed msgid + COMPOSEDMSGID composed; + if(!ReceiveComposedMsgid(mqfd, composed, pCanContinueWait)){ + MSG_CHMPRN("Failed to read composed msgid from MQ(%d), probably nothing to receive data.", mqfd); + return false; + } + + // read head + return ReceiveHead(mqfd, composed, ppComPkt); +} + +bool ChmEventMq::ReceiveComposedMsgid(int fd, COMPOSEDMSGID& composed, bool* pCanContinueWait) +{ + if(pCanContinueWait){ + *pCanContinueWait = false; + } + if(!IsEventQueueFd(fd)){ + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + // receive from mq + while(true){ + unsigned int priority= 0; + msgid_t msgid = CHM_INVALID_MSGID; + ssize_t length = mq_receive(fd, reinterpret_cast(&msgid), sizeof(msgid_t), &priority); // MQ is set O_NON_BLOCK + if(-1 == length){ + if(EINTR == errno){ + MSG_CHMPRN("Break receive mqfd(%d) by signal, so retry.", fd); + }else if(EAGAIN == errno || ETIMEDOUT == errno){ + //MSG_CHMPRN("Timeouted to receive mqfd(%d), errno=%d", fd, errno); + if(pCanContinueWait){ + *pCanContinueWait = true; + } + break; + }else{ + ERR_CHMPRN("Failed to receive mqfd(%d), errno=%d", fd, errno); + break; + } + + }else if(sizeof(msgid_t) != static_cast(length)){ + ERR_CHMPRN("Received unknown data(length=%zd) from mqfd(%d)", length, fd); + break; + }else{ + // success + if(!DecomposeSerialMsgid(msgid, composed)){ + ERR_CHMPRN("Received composed msgid(0x%016" PRIx64 ") from mqfd(%d) is invalid.", msgid, fd); + break; + } + return true; + } + } + return false; +} + +bool ChmEventMq::ReceiveHead(int fd, const COMPOSEDMSGID& composed, PCOMPKT* ppComPkt) +{ + if(!ppComPkt || CHM_INVALID_MSGID == composed.msgid){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + *ppComPkt = NULL; + + // check + msgid_t pdept_msgid = composed.msgid; + serial_t serial = composed.number; + if(CHM_INVALID_MSGID == pdept_msgid){ + ERR_CHMPRN("received msgid(0x%016" PRIx64 ") is invalid.", pdept_msgid); + return false; + } + + msgid_t pterm_msgid = GetAssignedMsgId(fd); // get self msgid from fd + if(CHM_INVALID_MSGID == pterm_msgid){ + ERR_CHMPRN("specified fd(%d) is not self msgid(0x%016" PRIx64 ").", fd, pterm_msgid); + return false; + } + + // Build k2hash key names + K2HShm* pK2hash = pImData->GetK2hashObj(); + if(!pK2hash){ + ERR_CHMPRN("K2hash object is NULL."); + return false; + } + string headkey; + string bodykey; // not used + if(!ChmEventMq::MakeK2hashKey(pdept_msgid, pterm_msgid, serial, headkey, bodykey)){ + ERR_CHMPRN("Failed to make k2hash key name for msgid(0x%016" PRIx64 "-0x%016" PRIx64 "-0x%016" PRIx64 ").", pdept_msgid, pterm_msgid, serial); + return false; + } + + // Read msg head from k2hash + ssize_t hlength; + unsigned char* phead = NULL; + if(-1 == (hlength = pK2hash->Get(reinterpret_cast(headkey.c_str()), headkey.length() + 1, &phead)) || !phead){ + ERR_CHMPRN("Failed to read msg head(%s) data.", headkey.c_str()); + return false; + } + *ppComPkt = reinterpret_cast(phead); + + // always remove head key + if(!pK2hash->Remove(reinterpret_cast(headkey.c_str()), headkey.length() + 1)){ + ERR_CHMPRN("Failed to remove msg head(%s) data in k2hash, but continue...", headkey.c_str()); + } + + if(COM_C2C == (*ppComPkt)->head.type){ + if((*ppComPkt)->length != sizeof(COMPKT) || (*ppComPkt)->offset != sizeof(COMPKT)){ + ERR_CHMPRN("read msg head(%s) length(%zd) should be %zu.", headkey.c_str(), hlength, sizeof(COMPKT)); + CHM_Free(phead); + *ppComPkt = NULL; + return false; + } + }else if(COM_C2PX == (*ppComPkt)->head.type || COM_PX2C == (*ppComPkt)->head.type){ + if((*ppComPkt)->length < sizeof(COMPKT) || (*ppComPkt)->offset != sizeof(COMPKT)){ + ERR_CHMPRN("*ppComPkt(%p) is %s type, but its length or offset is wrong value.", *ppComPkt, STRCOMTYPE((*ppComPkt)->head.type)); + CHM_Free(phead); + *ppComPkt = NULL; + return false; + } + }else{ + ERR_CHMPRN("*ppComPkt(%p) is unsupported type(%s)", *ppComPkt, STRCOMTYPE((*ppComPkt)->head.type)); + CHM_Free(phead); + *ppComPkt = NULL; + return false; + } + DUMPCOM_COMPKT("MQ::ReceiveHead", *ppComPkt); + DUMPCOM_PXCLT("MQ::ReceiveHead", CVT_CLT_ALL_PTR_PXCOMPKT(*ppComPkt)); + + return true; +} + +// +// This method is only reading body and return it. +// +bool ChmEventMq::ReceiveBody(PCOMPKT pComPkt, unsigned char** ppbody, size_t& blength, bool is_remove_body) +{ + if(!pComPkt || !ppbody){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + if(COM_C2C != pComPkt->head.type){ + //MSG_CHMPRN("pComPkt(%p) is %s type, this type does not have body.", pComPkt, STRCOMTYPE(pComPkt->head.type)); + *ppbody = NULL; + blength = 0L; + return true; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + *ppbody = NULL; + blength = 0L; + + // for trace + struct timespec start_time; + bool is_trace = (pChmCntrl->IsChmpxType() && pImData->IsTraceEnable()); + if(is_trace){ + STR_MATE_TIMESPEC(&start_time); + } + + // Build k2hash key names + K2HShm* pK2hash = pImData->GetK2hashObj(); + if(!pK2hash){ + ERR_CHMPRN("K2hash object is NULL."); + return false; + } + string headkey; + string bodykey; + if(!ChmEventMq::MakeK2hashKey(pComPkt->head.peer_dept_msgid, pComPkt->head.peer_term_msgid, pComPkt->head.mq_serial_num, headkey, bodykey)){ + ERR_CHMPRN("Failed to make k2hash key name for msgid(0x%016" PRIx64 "-0x%016" PRIx64 "-0x%016" PRIx64 ").", pComPkt->head.peer_dept_msgid, pComPkt->head.peer_term_msgid, pComPkt->head.mq_serial_num); + return false; + } + + // Read msg body from k2hash + ssize_t bodylength; + if(-1 == (bodylength = pK2hash->Get(reinterpret_cast(bodykey.c_str()), bodykey.length() + 1, ppbody)) || !(*ppbody)){ + ERR_CHMPRN("Failed to read msg body(%s) data.", bodykey.c_str()); + return false; + } + blength = static_cast(bodylength); + + // set trace + if(is_trace){ + struct timespec fin_time; + STR_MATE_TIMESPEC(&fin_time); + if(!pImData->AddTrace(CHMLOG_TYPE_IN_MQ, blength, start_time, fin_time)){ + ERR_CHMPRN("Failed to add trace log."); + } + } + + // Remove k2hash + if(is_remove_body){ + if(!RemoveReceivedBody(pComPkt)){ + WAN_CHMPRN("Failed to ACKED for head & body msgid(0x%016" PRIx64 "-0x%016" PRIx64 "-0x%016" PRIx64 "), but continue...", pComPkt->head.peer_dept_msgid, pComPkt->head.peer_term_msgid, pComPkt->head.mq_serial_num); + } + } + return true; +} + +// +// Read mesasge body by specified PCOMPKT head, and makes, returns full PCOMPKT. +// +// [NOTICE] +// Returned PCOMPKT is needed to free, but must not free pComPkt. +// So pComPkt is reallocate in this method or return it. +// +PCOMPKT ChmEventMq::ReceiveBody(PCOMPKT pComPkt, bool is_remove_body) +{ + if(!pComPkt){ + ERR_CHMPRN("Parameter is wrong."); + return NULL; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return NULL; + } + + // read body + unsigned char* pbody = NULL; + size_t blength = 0L; + if(!ReceiveBody(pComPkt, &pbody, blength, is_remove_body)){ + // failed, but continue... + WAN_CHMPRN("Failed to read msg body, so set null data."); + // set length & offset + pComPkt->length = sizeof(COMPKT); + pComPkt->offset = static_cast(sizeof(COMPKT)); + }else{ + if(pbody && 0 < blength){ + // reallocate + PCOMPKT pTmp; + if(NULL == (pTmp = reinterpret_cast(realloc(pComPkt, sizeof(COMPKT) + blength)))){ + WAN_CHMPRN("Could not allocate memory."); + return NULL; + } + // set length & offset + pTmp->length = sizeof(COMPKT) + blength; + pTmp->offset = static_cast(sizeof(COMPKT)); + + // copy data + memcpy(reinterpret_cast(pTmp) + pTmp->offset, pbody, blength); + K2H_Free(pbody); + + pComPkt = pTmp; + }else{ + // there is no body, probably type is COM_PX2C or COM_C2PX + } + } + return pComPkt; +} + +// +// Only remove body key in k2hash. +// The head key is removed after reading it automatically, then this method does not care for the head key. +// +bool ChmEventMq::RemoveReceivedBody(PCOMPKT pComPkt) +{ + if(!pComPkt){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(COM_C2C != pComPkt->head.type){ + //MSG_CHMPRN("ComPkt does not COM_C2C type, that does not have body key in k2hash, so return true."); + return true; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + bool result = true; + + // Remove head & body data in k2hash + K2HShm* pK2hash = pImData->GetK2hashObj(); + if(!pK2hash){ + ERR_CHMPRN("K2hash object is NULL."); + result = false; + }else{ + string headkey; + string bodykey; + if(!ChmEventMq::MakeK2hashKey(pComPkt->head.peer_dept_msgid, pComPkt->head.peer_term_msgid, pComPkt->head.mq_serial_num, headkey, bodykey)){ + ERR_CHMPRN("Failed to make k2hash key name for msgid(0x%016" PRIx64 "-0x%016" PRIx64 "-0x%016" PRIx64 ").", pComPkt->head.peer_dept_msgid, pComPkt->head.peer_term_msgid, pComPkt->head.mq_serial_num); + result = false; + }else{ + // remove body + if(!pK2hash->Remove(reinterpret_cast(bodykey.c_str()), bodykey.length() + 1)){ + ERR_CHMPRN("Failed to remove msg body(%s) data in k2hash.", bodykey.c_str()); + result = false; + } + } + } + return result; +} + +//--------------------------------------------------------- +// Methods for Sending/Receiving command on Merge logic +//--------------------------------------------------------- +// Get lastest update time before merging +// +// chmpx(server node) ---- [ PXCLT_MERGE_GETLASTTS ] ---> client chmpx library ---> call chm_merge_get_cb function +// chmpx(server node) <--- [ PXCLT_MERGE_RESLASTTS ] ---- client chmpx library <--- call chm_merge_get_cb function +// +bool ChmEventMq::PxCltSendMergeGetLastTime(struct timespec& lastts) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + if(!pChmCntrl->IsChmpxType()){ + ERR_CHMPRN("PXCLT_MERGE_GETLASTTS command MUST be sent from chmpx server node."); + return false; + } + if(!notify_merge_get){ + ERR_CHMPRN("Already run getting last upate time logic."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Allocation + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventMq::AllocatePxCltPacket(CHMPX_CLT_MERGE_GETLASTTS))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // msgid + msgid_t dept_msgid = pImData->GetRandomChmpxMsgId(); // Get msgid in chmpx process + if(CHM_INVALID_MSGID == dept_msgid){ + ERR_CHMPRN("Could not get own dept msgid(chmpx assigned)."); + CHM_Free(pComPkt); + return false; + } + msgid_t term_msgid = pImData->GetRandomClientMsgId(); // Get client assigned msgid + if(CHM_INVALID_MSGID == term_msgid){ + ERR_CHMPRN("Could not get own terminal msgid(client assigned)."); + CHM_Free(pComPkt); + return false; + } + + // set common datas + PPXCLT_ALL pCltAll = CVT_CLT_ALL_PTR_PXCOMPKT(pComPkt); + PPXCLT_MERGE_GETLASTTS pMergeGetLTS= CVT_CLTPTR_MERGE_GETLASTTS(pCltAll); + SET_PXCLTPKT(pComPkt, true, CHMPX_CLT_MERGE_GETLASTTS, dept_msgid, term_msgid, GetSerialNumber(), 0); + + pMergeGetLTS->head.type = CHMPX_CLT_MERGE_GETLASTTS; + pMergeGetLTS->head.length = SIZEOF_CHMPX_CLT(CHMPX_CLT_MERGE_GETLASTTS); + + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_READ); // Lock + + // send + // + // [NOTICE] + // Send to one of client's msgid + // + if(!Send(pComPkt, NULL, 0)){ + ERR_CHMPRN("Could not send CHMPX_CLT_MERGE_GETLASTTS to msgid(0x%016" PRIx64 ").", term_msgid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + AutoLock.UnLock(); // Unlock + + // [LOOP] + // + // wait for receving result + // + notify_merge_get = false; // force + pres_lasttime = &lastts; + struct timespec sleeptime = {0L, ChmEventMq::DEFAULT_GETLTS_SLEEP}; // 1us + for(long lcnt = 0; !notify_merge_get && lcnt < ChmEventMq::DEFAULT_GETLTS_LOOP; ++lcnt){ + nanosleep(&sleeptime, NULL); + } + if(!notify_merge_get){ + ERR_CHMPRN("No response from client, so getting lastest update time is TIMEOUT(1s)."); + notify_merge_get= true; // reset + pres_lasttime = NULL; + return false; + } + notify_merge_get= true; // reset + pres_lasttime = NULL; + + return true; +} + +bool ChmEventMq::PxCltReceiveMergeGetLastTime(PCOMHEAD pComHead, PPXCLT_ALL pCltAll) +{ + if(!pComHead || !pCltAll){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(COM_PX2C != pComHead->type){ + ERR_CHMPRN("COMHEAD type(%s) is wrong.", STRCOMTYPE(pComHead->type)); + return false; + } + if(CHMPX_CLT_MERGE_GETLASTTS != pCltAll->val_head.type){ + ERR_CHMPRN("PXCLT_HEAD type(%s) is wrong.", STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + // get lastest update time + struct timespec updatets = {0, 0}; + if(mlastfunc){ + chmpx_h handle = reinterpret_cast(pChmCntrl); + if(!mlastfunc(handle, &updatets)){ + ERR_CHMPRN("Failed to get lastest update time, but continue with zero value for merging."); + INIT_TIMESPEC(&updatets); + } + } + + // response the result + return PxCltSendMergeResponseLastTime(updatets); +} + +bool ChmEventMq::PxCltSendMergeResponseLastTime(const struct timespec& lastts) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + if(pChmCntrl->IsChmpxType()){ + ERR_CHMPRN("PXCLT_MERGE_RESLASTTS command MUST be sent from client on chmpx server node."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Allocation + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventMq::AllocatePxCltPacket(CHMPX_CLT_MERGE_RESLASTTS))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // msgid + msgid_t dept_msgid = pImData->GetRandomClientMsgId(); // Get client assigned msgid + if(CHM_INVALID_MSGID == dept_msgid){ + ERR_CHMPRN("Could not get own dept msgid(chmpx assigned)."); + CHM_Free(pComPkt); + return false; + } + msgid_t term_msgid = pImData->GetRandomChmpxMsgId(); // Get msgid in chmpx process + if(CHM_INVALID_MSGID == term_msgid){ + ERR_CHMPRN("Could not get own terminal msgid(client assigned)."); + CHM_Free(pComPkt); + return false; + } + + // set common datas + PPXCLT_ALL pCltAll = CVT_CLT_ALL_PTR_PXCOMPKT(pComPkt); + PPXCLT_MERGE_RESLASTTS pMergeResLTS= CVT_CLTPTR_MERGE_RESLASTTS(pCltAll); + SET_PXCLTPKT(pComPkt, false, CHMPX_CLT_MERGE_RESLASTTS, dept_msgid, term_msgid, GetSerialNumber(), 0); + + pMergeResLTS->head.type = CHMPX_CLT_MERGE_RESLASTTS; + pMergeResLTS->head.length = SIZEOF_CHMPX_CLT(CHMPX_CLT_MERGE_RESLASTTS); + pMergeResLTS->lastupdatets.tv_nsec = lastts.tv_nsec; + pMergeResLTS->lastupdatets.tv_sec = lastts.tv_sec; + + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_READ); // Lock + + // send + // + // [NOTICE] + // Send to one of client's msgid + // + if(!Send(pComPkt, NULL, 0)){ + ERR_CHMPRN("Could not send CHMPX_CLT_MERGE_RESLASTTS to msgid(0x%016" PRIx64 ").", term_msgid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventMq::PxCltReceiveMergeResponseLastTime(PCOMHEAD pComHead, PPXCLT_ALL pCltAll) +{ + if(!pComHead || !pCltAll){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(COM_C2PX != pComHead->type){ + ERR_CHMPRN("COMHEAD type(%s) is wrong.", STRCOMTYPE(pComHead->type)); + return false; + } + if(CHMPX_CLT_MERGE_RESLASTTS != pCltAll->val_head.type){ + ERR_CHMPRN("PXCLT_HEAD type(%s) is wrong.", STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + // get data + PPXCLT_MERGE_RESLASTTS pMergeResLTS = CVT_CLTPTR_MERGE_RESLASTTS(pCltAll); + + // set data + // + // [NOTICE] + // we do not lock this value, because already blocking another threads. + // + if(pres_lasttime){ + pres_lasttime->tv_nsec = pMergeResLTS->lastupdatets.tv_nsec; + pres_lasttime->tv_sec = pMergeResLTS->lastupdatets.tv_sec; + } + notify_merge_get = true; + + return true; +} + +bool ChmEventMq::PxCltSendRequestUpdateData(const PPXCOMMON_MERGE_PARAM pmerge_param) +{ + if(!pmerge_param){ + ERR_CHMPRN("Parameter is wrong."); + return false; + + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + if(!pChmCntrl->IsChmpxType()){ + ERR_CHMPRN("PXCLT_REQ_UPDATEDATA command MUST be sent from chmpx server node."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Allocation + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventMq::AllocatePxCltPacket(CHMPX_CLT_REQ_UPDATEDATA))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // msgid + msgid_t dept_msgid = pImData->GetRandomChmpxMsgId(); // Get msgid in chmpx process + if(CHM_INVALID_MSGID == dept_msgid){ + ERR_CHMPRN("Could not get own dept msgid(chmpx assigned)."); + CHM_Free(pComPkt); + return false; + } + msgid_t term_msgid = pImData->GetRandomClientMsgId(); // Get client assigned msgid + if(CHM_INVALID_MSGID == term_msgid){ + ERR_CHMPRN("Could not get own terminal msgid(client assigned)."); + CHM_Free(pComPkt); + return false; + } + + // set common datas + PPXCLT_ALL pCltAll = CVT_CLT_ALL_PTR_PXCOMPKT(pComPkt); + PPXCLT_REQ_UPDATEDATA pReqUpdateData = CVT_CLTPTR_REQ_UPDATEDATA(pCltAll); + SET_PXCLTPKT(pComPkt, true, CHMPX_CLT_REQ_UPDATEDATA, dept_msgid, term_msgid, GetSerialNumber(), 0); + + pReqUpdateData->head.type = CHMPX_CLT_REQ_UPDATEDATA; + pReqUpdateData->head.length = SIZEOF_CHMPX_CLT(CHMPX_CLT_REQ_UPDATEDATA); + COPY_COMMON_MERGE_PARAM(&(pReqUpdateData->merge_param), pmerge_param); + + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_READ); // Lock + + // send + // + // [NOTICE] + // Send to one of client's msgid + // + if(!Send(pComPkt, NULL, 0)){ + ERR_CHMPRN("Could not send CHMPX_CLT_REQ_UPDATEDATA to msgid(0x%016" PRIx64 ").", term_msgid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventMq::PxCltReceiveRequestUpdateData(PCOMHEAD pComHead, PPXCLT_ALL pCltAll) +{ + if(!pComHead || !pCltAll){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(COM_PX2C != pComHead->type){ + ERR_CHMPRN("COMHEAD type(%s) is wrong.", STRCOMTYPE(pComHead->type)); + return false; + } + if(CHMPX_CLT_REQ_UPDATEDATA != pCltAll->val_head.type){ + ERR_CHMPRN("PXCLT_HEAD type(%s) is wrong.", STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + // request data + PPXCLT_REQ_UPDATEDATA pReqUpdateData = CVT_CLTPTR_REQ_UPDATEDATA(pCltAll); + + if(!mgetfunc){ + // not regist merge get function. + MSG_CHMPRN("mgetfunc is not set, so nothing to do for merge."); + + // send finish to update data + if(!PxCltSendResultUpdateData(pReqUpdateData->merge_param.chmpxid, true)){ // result = true + ERR_CHMPRN("Failed to send CHMPX_CLT_RESULT_UPDATEDATA, but continue..."); + } + return true; + } + + // set merge parameter + PCHM_UPDATA_PARAM pUpdateDataParam= new CHM_UPDATA_PARAM; + pUpdateDataParam->chmpxid = pReqUpdateData->merge_param.chmpxid; + pUpdateDataParam->startts.tv_sec = pReqUpdateData->merge_param.startts.tv_sec; + pUpdateDataParam->startts.tv_nsec = pReqUpdateData->merge_param.startts.tv_nsec; + pUpdateDataParam->max_pending_hash = pReqUpdateData->merge_param.max_pending_hash; + pUpdateDataParam->pending_hash = pReqUpdateData->merge_param.pending_hash; + pUpdateDataParam->max_base_hash = pReqUpdateData->merge_param.max_base_hash; + pUpdateDataParam->base_hash = pReqUpdateData->merge_param.base_hash; + pUpdateDataParam->replica_count = pReqUpdateData->merge_param.replica_count; + pUpdateDataParam->is_expire_check = pReqUpdateData->merge_param.is_expire_check; + + if(!RT_TIMESPEC(&(pUpdateDataParam->endts))){ + pUpdateDataParam->endts.tv_sec = time(NULL); + pUpdateDataParam->endts.tv_nsec = 0; + } + + // stack merge parameter + while(!fullock::flck_trylock_noshared_mutex(&mparam_list_lockval)); // LOCK + merge_param_list.push_back(pUpdateDataParam); + if(!notify_merge_update){ + notify_merge_update = true; + } + fullock::flck_unlock_noshared_mutex(&mparam_list_lockval); // UNLOCK + + // run thread + if(!mergethread.IsThreadRun()){ + // run merge thread + // + // - parameter is this object + // - not sleep at starting + // - at onece(one shot) (*1) + // - sleep after every working do not care this value because (*1) + // - not keep event count do not care this value because (*1) + // + if(!mergethread.CreateThreads(1, ChmEventMq::MergeWorkerFunc, NULL, this, 0, false, true, false, false)){ + ERR_CHMPRN("Could not start merge thread in MQ."); + CHM_Delete(pUpdateDataParam); + return false; + } + }else{ + // already run thread, so nothing to do. + } + return true; +} + +bool ChmEventMq::PxCltSendResponseUpdateData(chmpxid_t requester_chmpxid, const PCHMBIN pbindata, const struct timespec* pts) +{ + if(CHM_INVALID_CHMPXID == requester_chmpxid || !pbindata || !pbindata->byptr || 0 == pbindata->length || !pts){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + if(pChmCntrl->IsChmpxType()){ + ERR_CHMPRN("PXCLT_RES_UPDATEDATA command MUST be sent from client on chmpx server node."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Allocation + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventMq::AllocatePxCltPacket(CHMPX_CLT_RES_UPDATEDATA, pbindata->length))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // msgid + msgid_t dept_msgid = pImData->GetRandomClientMsgId(); // Get client assigned msgid + if(CHM_INVALID_MSGID == dept_msgid){ + ERR_CHMPRN("Could not get own dept msgid(chmpx assigned)."); + CHM_Free(pComPkt); + return false; + } + msgid_t term_msgid = pImData->GetRandomChmpxMsgId(); // Get msgid in chmpx process + if(CHM_INVALID_MSGID == term_msgid){ + ERR_CHMPRN("Could not get own terminal msgid(client assigned)."); + CHM_Free(pComPkt); + return false; + } + + // set common datas + PPXCLT_ALL pCltAll = CVT_CLT_ALL_PTR_PXCOMPKT(pComPkt); + PPXCLT_RES_UPDATEDATA pResUpdateData = CVT_CLTPTR_RES_UPDATEDATA(pCltAll); + SET_PXCLTPKT(pComPkt, false, CHMPX_CLT_RES_UPDATEDATA, dept_msgid, term_msgid, GetSerialNumber(), pbindata->length); + + pResUpdateData->head.type = CHMPX_CLT_RES_UPDATEDATA; + pResUpdateData->head.length = SIZEOF_CHMPX_CLT(CHMPX_CLT_RES_UPDATEDATA) + pbindata->length; + pResUpdateData->chmpxid = requester_chmpxid; + pResUpdateData->ts.tv_sec = pts->tv_sec; + pResUpdateData->ts.tv_nsec = pts->tv_nsec; + pResUpdateData->length = pbindata->length; + pResUpdateData->byptr = reinterpret_cast(SIZEOF_CHMPX_CLT(CHMPX_CLT_RES_UPDATEDATA)); + + unsigned char* psetbase = CHM_OFFSET(pResUpdateData, SIZEOF_CHMPX_CLT(CHMPX_CLT_RES_UPDATEDATA), unsigned char*); + memcpy(psetbase, pbindata->byptr, pbindata->length); + + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_READ); // Lock + + // send + // + // [NOTICE] + // Send to one of client's msgid + // + if(!Send(pComPkt, NULL, 0)){ + ERR_CHMPRN("Could not send CHMPX_CLT_RES_UPDATEDATA to msgid(0x%016" PRIx64 ").", term_msgid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventMq::PxCltReceiveResponseUpdateData(PCOMHEAD pComHead, PPXCLT_ALL pCltAll) +{ + if(!pComHead || !pCltAll){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(COM_C2PX != pComHead->type){ + ERR_CHMPRN("COMHEAD type(%s) is wrong.", STRCOMTYPE(pComHead->type)); + return false; + } + if(CHMPX_CLT_RES_UPDATEDATA != pCltAll->val_head.type){ + ERR_CHMPRN("PXCLT_HEAD type(%s) is wrong.", STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + // check data + PPXCLT_RES_UPDATEDATA pResUpdateData = CVT_CLTPTR_RES_UPDATEDATA(pCltAll); + if(CHM_INVALID_CHMPXID == pResUpdateData->chmpxid || 0 == pResUpdateData->length || NULL == pResUpdateData->byptr){ + ERR_CHMPRN("Received CHMPX_CLT_RES_UPDATEDATA is wrong."); + return false; + } + unsigned char* pbindata = CHM_ABS(pResUpdateData, pResUpdateData->byptr, unsigned char*); + + // transfer + return pChmCntrl->MergeResponseUpdateData(pResUpdateData->chmpxid, pResUpdateData->length, pbindata, &(pResUpdateData->ts)); +} + +bool ChmEventMq::PxCltSendSetUpdateData(size_t length, const unsigned char* pdata, const struct timespec* pts) +{ + if(0 == length || !pdata || !pts){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + if(!pChmCntrl->IsChmpxType()){ + ERR_CHMPRN("PXCLT_SET_UPDATEDATA command MUST be sent from chmpx server node."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Allocation + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventMq::AllocatePxCltPacket(CHMPX_CLT_SET_UPDATEDATA, length))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // msgid + msgid_t dept_msgid = pImData->GetRandomChmpxMsgId(); // Get msgid in chmpx process + if(CHM_INVALID_MSGID == dept_msgid){ + ERR_CHMPRN("Could not get own dept msgid(chmpx assigned)."); + CHM_Free(pComPkt); + return false; + } + msgid_t term_msgid = pImData->GetRandomClientMsgId(); // Get client assigned msgid + if(CHM_INVALID_MSGID == term_msgid){ + ERR_CHMPRN("Could not get own terminal msgid(client assigned)."); + CHM_Free(pComPkt); + return false; + } + + // set common datas + PPXCLT_ALL pCltAll = CVT_CLT_ALL_PTR_PXCOMPKT(pComPkt); + PPXCLT_SET_UPDATEDATA pSetUpdateData = CVT_CLTPTR_SET_UPDATEDATA(pCltAll); + SET_PXCLTPKT(pComPkt, true, CHMPX_CLT_SET_UPDATEDATA, dept_msgid, term_msgid, GetSerialNumber(), length); + + pSetUpdateData->head.type = CHMPX_CLT_SET_UPDATEDATA; + pSetUpdateData->head.length = SIZEOF_CHMPX_CLT(CHMPX_CLT_SET_UPDATEDATA) + length; + pSetUpdateData->ts.tv_sec = pts->tv_sec; + pSetUpdateData->ts.tv_nsec = pts->tv_nsec; + pSetUpdateData->length = length; + pSetUpdateData->byptr = reinterpret_cast(SIZEOF_CHMPX_CLT(CHMPX_CLT_SET_UPDATEDATA)); + + unsigned char* psetbase = CHM_OFFSET(pSetUpdateData, SIZEOF_CHMPX_CLT(CHMPX_CLT_SET_UPDATEDATA), unsigned char*); + memcpy(psetbase, pdata, length); + + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_READ); // Lock + + // send + // + // [NOTICE] + // Send to one of client's msgid + // + if(!Send(pComPkt, NULL, 0)){ + ERR_CHMPRN("Could not send CHMPX_CLT_SET_UPDATEDATA to msgid(0x%016" PRIx64 ").", term_msgid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventMq::PxCltReceiveSetUpdateData(PCOMHEAD pComHead, PPXCLT_ALL pCltAll) +{ + if(!pComHead || !pCltAll){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(COM_PX2C != pComHead->type){ + ERR_CHMPRN("COMHEAD type(%s) is wrong.", STRCOMTYPE(pComHead->type)); + return false; + } + if(CHMPX_CLT_SET_UPDATEDATA != pCltAll->val_head.type){ + ERR_CHMPRN("PXCLT_HEAD type(%s) is wrong.", STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + if(!msetfunc){ + ERR_CHMPRN("This object does not regist merge set update data function."); + return true; + } + + // check data + PPXCLT_SET_UPDATEDATA pSetUpdateData = CVT_CLTPTR_SET_UPDATEDATA(pCltAll); + if(0 == pSetUpdateData->length || NULL == pSetUpdateData->byptr){ + ERR_CHMPRN("Received CHMPX_CLT_SET_UPDATEDATA is wrong."); + return false; + } + unsigned char* pbindata = CHM_ABS(pSetUpdateData, pSetUpdateData->byptr, unsigned char*); + + // run callback function + return msetfunc(reinterpret_cast(pChmCntrl), pSetUpdateData->length, pbindata, &(pSetUpdateData->ts)); +} + +bool ChmEventMq::PxCltSendResultUpdateData(chmpxid_t chmpxid, bool result) +{ + if(CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + if(pChmCntrl->IsChmpxType()){ + ERR_CHMPRN("PXCLT_RESULT_UPDATEDATA command MUST be sent from client on chmpx server node."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Allocation + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventMq::AllocatePxCltPacket(CHMPX_CLT_RESULT_UPDATEDATA))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // msgid + msgid_t dept_msgid = pImData->GetRandomClientMsgId(); // Get client assigned msgid + if(CHM_INVALID_MSGID == dept_msgid){ + ERR_CHMPRN("Could not get own dept msgid(chmpx assigned)."); + CHM_Free(pComPkt); + return false; + } + msgid_t term_msgid = pImData->GetRandomChmpxMsgId(); // Get msgid in chmpx process + if(CHM_INVALID_MSGID == term_msgid){ + ERR_CHMPRN("Could not get own terminal msgid(client assigned)."); + CHM_Free(pComPkt); + return false; + } + + // set common datas + PPXCLT_ALL pCltAll = CVT_CLT_ALL_PTR_PXCOMPKT(pComPkt); + PPXCLT_RESULT_UPDATEDATA pResultUpdateData = CVT_CLTPTR_RESULT_UPDATEDATA(pCltAll); + SET_PXCLTPKT(pComPkt, false, CHMPX_CLT_RESULT_UPDATEDATA, dept_msgid, term_msgid, GetSerialNumber(), 0); + + pResultUpdateData->head.type = CHMPX_CLT_RESULT_UPDATEDATA; + pResultUpdateData->head.length = SIZEOF_CHMPX_CLT(CHMPX_CLT_RESULT_UPDATEDATA); + pResultUpdateData->chmpxid = chmpxid; + pResultUpdateData->result = (result ? CHMPX_COM_REQ_UPDATE_SUCCESS : CHMPX_COM_REQ_UPDATE_FAIL); + + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_READ); // Lock + + // send + // + // [NOTICE] + // Send to one of client's msgid + // + if(!Send(pComPkt, NULL, 0)){ + ERR_CHMPRN("Could not send CHMPX_CLT_RESULT_UPDATEDATA to msgid(0x%016" PRIx64 ").", term_msgid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventMq::PxCltReceiveResultUpdateData(PCOMHEAD pComHead, PPXCLT_ALL pCltAll) +{ + if(!pComHead || !pCltAll){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(COM_C2PX != pComHead->type){ + ERR_CHMPRN("COMHEAD type(%s) is wrong.", STRCOMTYPE(pComHead->type)); + return false; + } + if(CHMPX_CLT_RESULT_UPDATEDATA != pCltAll->val_head.type){ + ERR_CHMPRN("PXCLT_HEAD type(%s) is wrong.", STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + PPXCLT_RESULT_UPDATEDATA pResultUpdateData = CVT_CLTPTR_RESULT_UPDATEDATA(pCltAll); + + // check + if(CHM_INVALID_CHMPXID == pResultUpdateData->chmpxid){ + ERR_CHMPRN("Received PXCLT_RESULT_UPDATEDATA command is something wrong."); + return false; + } + + // transfer + return pChmCntrl->MergeResultUpdateData(pResultUpdateData->chmpxid, pResultUpdateData->result); +} + +bool ChmEventMq::PxCltSendAbortUpdateData(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + if(!pChmCntrl->IsChmpxType()){ + ERR_CHMPRN("PXCLT_ABORT_UPDATEDATA command MUST be sent from chmpx server node."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Allocation + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventMq::AllocatePxCltPacket(CHMPX_CLT_ABORT_UPDATEDATA))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // msgid + msgid_t dept_msgid = pImData->GetRandomChmpxMsgId(); // Get msgid in chmpx process + if(CHM_INVALID_MSGID == dept_msgid){ + ERR_CHMPRN("Could not get own dept msgid(chmpx assigned)."); + CHM_Free(pComPkt); + return false; + } + msgidlist_t term_msgids; + if(!pImData->GetMsgidListByUniqPid(term_msgids)){ // Get msgids for each client process + ERR_CHMPRN("Could not get own terminal activated msgids(by each client)."); + CHM_Free(pComPkt); + return false; + } + if(0 == term_msgids.size()){ + MSG_CHMPRN("There is no terminal activated msgids(by each client)."); + CHM_Free(pComPkt); + return true; + } + + // set common datas + PPXCLT_ALL pCltAll = CVT_CLT_ALL_PTR_PXCOMPKT(pComPkt); + PPXCLT_ABORT_UPDATEDATA pAbortUpdateData= CVT_CLTPTR_ABORT_UPDATEDATA(pCltAll); + SET_PXCLTPKT(pComPkt, true, CHMPX_CLT_ABORT_UPDATEDATA, dept_msgid, term_msgids.front(), GetSerialNumber(), 0); // terminal msgid it temporary + + pAbortUpdateData->head.type = CHMPX_CLT_ABORT_UPDATEDATA; + pAbortUpdateData->head.length = SIZEOF_CHMPX_CLT(CHMPX_CLT_ABORT_UPDATEDATA); + + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_READ); // Lock + + // send loop + for(msgidlist_t::const_iterator iter = term_msgids.begin(); iter != term_msgids.end(); ++iter){ + // set terminate msgid( uses same serial number because of terminated msgid is different ) + pComPkt->head.term_ids.msgid = *iter; + pComPkt->head.peer_term_msgid = *iter; + + // send + if(!Send(pComPkt, NULL, 0)){ + WAN_CHMPRN("Could not send CHMPX_CLT_CLOSE_NOTIFY to msgid(0x%016" PRIx64 "), but continue....", *iter); + } + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventMq::PxCltReceiveAbortUpdateData(PCOMHEAD pComHead, PPXCLT_ALL pCltAll) +{ + if(!pComHead || !pCltAll){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(COM_PX2C != pComHead->type){ + ERR_CHMPRN("COMHEAD type(%s) is wrong.", STRCOMTYPE(pComHead->type)); + return false; + } + if(CHMPX_CLT_ABORT_UPDATEDATA != pCltAll->val_head.type){ + ERR_CHMPRN("PXCLT_HEAD type(%s) is wrong.", STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + // check data -> Nothing to do + //PPXCLT_ABORT_UPDATEDATA pAbortUpdateData = CVT_CLTPTR_ABORT_UPDATEDATA(pCltAll); + + // remove all stacked parameters. + while(!fullock::flck_trylock_noshared_mutex(&mparam_list_lockval)); // LOCK + for(mqmparamlist_t::iterator iter = merge_param_list.begin(); iter != merge_param_list.end(); iter = merge_param_list.erase(iter)){ + PCHM_UPDATA_PARAM pUpdateDataParam = *iter; + CHM_Delete(pUpdateDataParam); + } + fullock::flck_unlock_noshared_mutex(&mparam_list_lockval); // UNLOCK + + // break merge loop in thread + notify_merge_update = false; + + return true; +} + +//--------------------------------------------------------- +// Methods for Sending/Receiving command +//--------------------------------------------------------- +bool ChmEventMq::PxCltSendCloseNotify(msgidlist_t& msgids) +{ + if(0 == msgids.size()){ + ERR_CHMPRN("There is no msgid in closed msgid list."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + // Allocation + PCOMPKT pComPkt; + ssize_t ext_length = static_cast(msgids.size() * sizeof(msgid_t)); + if(NULL == (pComPkt = ChmEventMq::AllocatePxCltPacket(CHMPX_CLT_CLOSE_NOTIFY, ext_length))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // set common datas + PPXCLT_ALL pCltAll = CVT_CLT_ALL_PTR_PXCOMPKT(pComPkt); + PPXCLT_CLOSE_NOTIFY pCloseNotify= CVT_CLTPTR_CLOSE_NOTIFY(pCltAll); + SET_PXCLTPKT(pComPkt, pChmCntrl->IsChmpxType(), CHMPX_CLT_CLOSE_NOTIFY, msgids.front(), CHM_INVALID_MSGID, GetSerialNumber(), ext_length); // deperture msgid is one of msgids + + pCloseNotify->head.type = CHMPX_CLT_CLOSE_NOTIFY; + pCloseNotify->head.length = SIZEOF_CHMPX_CLT(CHMPX_CLT_CLOSE_NOTIFY) + ext_length; + pCloseNotify->count = static_cast(msgids.size()); + pCloseNotify->pmsgids = reinterpret_cast(SIZEOF_CHMPX_CLT(CHMPX_CLT_CLOSE_NOTIFY)); + + long pos = 0; + msgid_t* psetbase = CHM_OFFSET(pCloseNotify, SIZEOF_CHMPX_CLT(CHMPX_CLT_CLOSE_NOTIFY), msgid_t*); + for(msgidlist_t::const_iterator iter = msgids.begin(); iter != msgids.end(); ++iter, ++pos){ + psetbase[pos] = *iter; + } + + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_READ); // Lock + + // get all msgids + msgidlist_t dest_msgids; + dest_idfd_map.get_keys(dest_msgids); + + // send loop + for(msgidlist_t::const_iterator iter = dest_msgids.begin(); iter != dest_msgids.end(); ++iter){ + // set terminate msgid( uses same serial number because of terminated msgid is different ) + pComPkt->head.term_ids.msgid = *iter; + pComPkt->head.peer_term_msgid = *iter; + + // send + if(!Send(pComPkt, NULL, 0)){ + WAN_CHMPRN("Could not send CHMPX_CLT_CLOSE_NOTIFY to msgid(0x%016" PRIx64 "), but continue....", *iter); + }else{ + // [NOTE] + // If the client on slave, enough to sending onece. + if(pChmCntrl->IsClientOnSlvType()){ + break; + } + } + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventMq::PxCltReceiveCloseNotify(PCOMHEAD pComHead, PPXCLT_ALL pCltAll) +{ + if(!pComHead || !pCltAll){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(COM_C2PX != pComHead->type && COM_PX2C != pComHead->type){ + ERR_CHMPRN("COMHEAD type(%s) is wrong.", STRCOMTYPE(pComHead->type)); + return false; + } + if(CHMPX_CLT_CLOSE_NOTIFY != pCltAll->val_head.type){ + ERR_CHMPRN("PXCLT_HEAD type(%s) is wrong.", STRPXCLTTYPE(pCltAll->val_head.type)); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + // check data + PPXCLT_CLOSE_NOTIFY pCloseNotify = CVT_CLTPTR_CLOSE_NOTIFY(pCltAll); + if(pCloseNotify->count <= 0 || !pCloseNotify->pmsgids){ + ERR_CHMPRN("msgids pointer or count is empty."); + return false; + } + + // if msgid in cache, so close and remove it from cache. + msgid_t* pmsgids = CHM_ABS(pCloseNotify, pCloseNotify->pmsgids, msgid_t*); + for(long cnt = 0; cnt < pCloseNotify->count; cnt++){ + if(!CloseDestMQ(pmsgids[cnt])){ + ERR_CHMPRN("Failed to close and remove cache for msgid(0x%016" PRIx64 ").", pmsgids[cnt]); + } + } + return true; +} + +bool ChmEventMq::PxCltSendJoinNotify(pid_t pid) +{ + if(CHM_INVALID_PID == pid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + if(pChmCntrl->IsChmpxType()){ + ERR_CHMPRN("Could not send CHMPX_CLT_JOIN_NOTIFY from CHMPX process."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Allocation + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventMq::AllocatePxCltPacket(CHMPX_CLT_JOIN_NOTIFY))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // terminal msgid + msgid_t term_msgid = pImData->GetRandomChmpxMsgId(); // Get msgid in chmpx process + if(CHM_INVALID_MSGID == term_msgid){ + ERR_CHMPRN("Could not get own msgid(terminal)."); + CHM_Free(pComPkt); + return false; + } + + // deperture msgid + msgid_t dept_msgid = CHM_INVALID_MSGID; + if(pChmCntrl->IsClientOnSlvType()){ + dept_msgid = ActivatedMsgId(); + }else{ + dept_msgid = pImData->GetRandomClientMsgId(); + } + if(CHM_INVALID_MSGID == dept_msgid){ + ERR_CHMPRN("Could not get own msgid(deperture)."); + CHM_Free(pComPkt); + return false; + } + + // set common datas + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + PPXCLT_ALL pCltAll = CVT_CLT_ALL_PTR_PXCOMPKT(pComPkt); + PPXCLT_JOIN_NOTIFY pJoinNotify = CVT_CLTPTR_JOIN_NOTIFY(pCltAll); + SET_PXCLTPKT(pComPkt, pChmCntrl->IsChmpxType(), CHMPX_CLT_JOIN_NOTIFY, dept_msgid, term_msgid, GetSerialNumber(), 0); + + pComPkt->head.dept_ids.chmpxid = selfchmpxid; // do not case for this value. + pComPkt->head.term_ids.chmpxid = selfchmpxid; + pJoinNotify->head.type = CHMPX_CLT_JOIN_NOTIFY; + pJoinNotify->head.length = SIZEOF_CHMPX_CLT(CHMPX_CLT_JOIN_NOTIFY); + pJoinNotify->pid = pid; + + ChmLock AutoLock(CHMLT_MQOBJ, CHMLT_READ); // Lock + + // send + if(!Send(pComPkt, NULL, 0)){ + WAN_CHMPRN("Could not send CHMPX_CLT_JOIN_NOTIFY to msgid(0x%016" PRIx64 ").", dept_msgid); + } + + if(pChmCntrl->IsClientOnSlvType()){ + DisactivatedMsgId(dept_msgid); // Do not need to wait the response. + } + CHM_Free(pComPkt); + + return true; +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmeventmq.h b/lib/chmeventmq.h new file mode 100644 index 0000000..ce84544 --- /dev/null +++ b/lib/chmeventmq.h @@ -0,0 +1,237 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMEVENTMQ_H +#define CHMEVENTMQ_H + +#include +#include + +#include "chmeventbase.h" +#include "chmconf.h" +#include "chmthread.h" +#include "chmpx.h" +#include "chmstructure.tcc" +#include "chmlockmap.tcc" + +//--------------------------------------------------------- +// ABOUT MQUEUE MESSAGING +// +// Sending/Receiving messages on mqueue is following rule. +// * Sending data on mqueue is only sender msgid. +// * Message body is stored on k2hash by keys which are +// "-.XXXX". +// * Message body on k2hash are two type, one is message +// head(COMPKT), the other is real message body. +// +// Messaging on MQ is synchronous communication. This is +// very important. +// And create/destroy client side MQ by each communication. +// It is probably not good performance, but it is not large +// deteriorated. +// +// CHMPX side: +// * Create one MQ as first msgid in base msgid in CHMSHM. +// And add and wait event from epoll. +// +// Client side: +// * Create one MQ as msgid which is not assigned yet. +// It is doing by each attach request on client. +// * When sending, set a message with header in K2HASH and +// send msgid which means client MQ id to CHMPX MQ. +// After that, wait response through client MQ from CHMPX. +// After receiving, close and remove own MQ. +// + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +#define PXCLTPKT_AUTO_LENGTH (-1) +#define CHMEVENTMQ_RETRY_DEFAULT (-1) +#define CHMEVENTMQ_TIMEOUT_DEFAULT (0) + +//--------------------------------------------------------- +// Structure +//--------------------------------------------------------- +typedef struct chm_composed_msgid{ + msgid_t msgid; + serial_t number; + bool is_ack; + bool is_ack_success; + + chm_composed_msgid(void) : msgid(CHM_INVALID_MSGID), number(MIN_MQ_SERIAL_NUMBER), is_ack(false), is_ack_success(false) {} + +}COMPOSEDMSGID, *PCOMPOSEDMSGID; + +typedef struct chm_update_data_param{ + chmpxid_t chmpxid; // requester chmpx + struct timespec startts; // start time for update datas + struct timespec endts; // end time for update datas + chmhash_t max_pending_hash; // max panding hash value + chmhash_t pending_hash; // new(pending) hash value + chmhash_t max_base_hash; // max base hash value + chmhash_t base_hash; // base hash value + long replica_count; // replica count + bool is_expire_check; // whether checking expire time +}CHM_UPDATA_PARAM, *PCHM_UPDATA_PARAM; + +typedef std::vector mqmparamlist_t; + +//--------------------------------------------------------- +// Typedefs +//--------------------------------------------------------- +typedef chm_lock_map msgid_mq_map_t; +typedef chm_lock_map mq_msgid_map_t; +typedef std::vector mqfd_list_t; + +//--------------------------------------------------------- +// ChmEventMq Class +//--------------------------------------------------------- +class ChmEventMq : public ChmEventBase +{ + public: + static const int DEFAULT_RETRYCOUNT = 3; + static const long DEFAULT_TIMEOUT_US = 10 * 1000; // 10ms + static const int DEFAULT_MQ_THREAD_CNT = 0; // MQ processing thread count + static const long DEFAULT_GETLTS_SLEEP = 1000; // 1us sleep time for checking the result of getting last update time + static const long DEFAULT_GETLTS_LOOP = 1000 * 1000; // loop count for checking the result of getting last update time + static const time_t DEFAULT_MERGE_TIMEOUT = 0; // timeout value for merging(default is no timeout) + + protected: + msgid_mq_map_t recv_idfd_map; // map all MQ for receiveing: msgid -> fd + mq_msgid_map_t recv_fdid_map; // map all MQ for receiveing: fd -> msgid + msgid_mq_map_t reserve_idfd_map; // map disactivated MQ for receiveing: msgid -> fd + msgid_mq_map_t dest_idfd_map; // map cache MQ for sending: msgid -> fd + mq_msgid_map_t dest_fdid_map; // map cache MQ for sending: fd -> msgid + serial_t serial_num; // serial number for each mq message(using autolock) + volatile int actmq_lockval; // lock variable for ActivateMQ + ChmThread procthreads; // processing threads + ChmThread mergethread; // merge thread + chm_merge_get_cb mgetfunc; // merge callback for getting each data + chm_merge_set_cb msetfunc; // merge callback for setting each data + chm_merge_lastts_cb mlastfunc; // merge callback for getting lastupdate time + volatile bool notify_merge_get; // flag for receiving lastest update time + struct timespec* pres_lasttime; // temporary buffer for receiving lastest update time + volatile int mparam_list_lockval; // lock variable for merge_param_list + mqmparamlist_t merge_param_list; // update data(merge) parameter list + volatile bool notify_merge_update; // flag for running thread + int retry_count; // cache value for ChmIMData::GetMQRetryCnt() + long timeout_us; // cahce value for ChmIMData::GetMQTimeout() + time_t timeout_merge; // cache value for ChmIMData::GetMergeTimeout() + bool is_server_mode; // cache value for ChmIMData::IsServerMode() + bool use_mq_ack; // cache value for ChmIMData::IsAckMQ() + + protected: + static bool MakeK2hashKey(msgid_t dept_msgid, msgid_t term_msgid, serial_t serial, std::string& headkey, std::string& bodykey); + + // Merge + static bool MergeWorkerFunc(void* common_param, chmthparam_t wp_param); + + // callback functions for thread class + static bool ReceiveWorkerProc(void* common_param, chmthparam_t wp_param); + + // callback for mapping + static bool RcvfdIdMapCallback(mq_msgid_map_t::iterator& iter, void* pparam); + static bool DestfdIdMapCallback(mq_msgid_map_t::iterator& iter, void* pparam); + + bool RawReceive(mqd_t mqfd, const COMPOSEDMSGID& composed); + + serial_t GetSerialNumber(void); + msgid_t ComposeSerialMsgid(const COMPOSEDMSGID& composed); + msgid_t ComposeSerialMsgid(const msgid_t msgid, const serial_t serial, bool is_ack = false, bool ack_success = false); + bool DecomposeSerialMsgid(const msgid_t msgid, COMPOSEDMSGID& composed); + + bool BuildC2CHeadEx(PCOMHEAD pComHead, PCOMHEAD pReqComHead, chmpxid_t tochmpxid, chmhash_t hash, c2ctype_t c2ctype, msgid_t frommsgid); + PCOMPKT AllocatePxCltPacket(pxclttype_t type, ssize_t ext_length = PXCLTPKT_AUTO_LENGTH); + bool MakeMqPath(msgid_t msgid, std::string& path); + msgid_t GetAssignedMsgId(int fd = CHM_INVALID_HANDLE); + bool FreeReserveMsgIds(void); + bool FreeMsgIds(msgid_t msgid, mqd_t mqfd); + mqd_t OpenDestMQ(msgid_t msgid); + + bool PxCltReceiveMergeGetLastTime(PCOMHEAD pComHead, PPXCLT_ALL pCltAll); + bool PxCltSendMergeResponseLastTime(const struct timespec& lastts); + bool PxCltReceiveMergeResponseLastTime(PCOMHEAD pComHead, PPXCLT_ALL pCltAll); + bool PxCltReceiveRequestUpdateData(PCOMHEAD pComHead, PPXCLT_ALL pCltAll); + bool PxCltSendResponseUpdateData(chmpxid_t requester_chmpxid, const PCHMBIN pbindata, const struct timespec* pts); + bool PxCltReceiveResponseUpdateData(PCOMHEAD pComHead, PPXCLT_ALL pCltAll); + bool PxCltReceiveSetUpdateData(PCOMHEAD pComHead, PPXCLT_ALL pCltAll); + bool PxCltSendResultUpdateData(chmpxid_t chmpxid, bool result); + bool PxCltReceiveResultUpdateData(PCOMHEAD pComHead, PPXCLT_ALL pCltAll); + bool PxCltReceiveAbortUpdateData(PCOMHEAD pComHead, PPXCLT_ALL pCltAll); + bool PxCltSendCloseNotify(msgidlist_t& msgids); + bool PxCltReceiveCloseNotify(PCOMHEAD pComHead, PPXCLT_ALL pCltAll); + + public: + static bool InitializeMaxMqSystemSize(long maxmsg); + + ChmEventMq(int eventqfd = CHM_INVALID_HANDLE, ChmCntrl* pcntrl = NULL, chm_merge_get_cb mgetfp = NULL, chm_merge_set_cb msetfp = NULL, chm_merge_lastts_cb mlastfp = NULL); + virtual ~ChmEventMq(); + + virtual bool Clean(void); + virtual bool UpdateInternalData(void); + + int GetEventQueueFd(void); + virtual bool GetEventQueueFds(event_fds_t& fds); + virtual bool SetEventQueue(void); // Make one MQ for reciver side and add it to event queue fd. + virtual bool UnsetEventQueue(void); // Unset mqfd from event queue fd, and destry it. + virtual bool IsEventQueueFd(int fd); + + virtual bool Send(PCOMPKT pComPkt, const unsigned char* pbody, size_t blength); + virtual bool Receive(int fd); + virtual bool NotifyHup(int fd); + virtual bool Processing(PCOMPKT pComPkt); + + bool CheckProcessing(PCOMPKT pComPkt); // for client on slave chmpx to process other than COM_C2C + msgid_t ActivatedMsgId(void); // for client on slave chmpx + bool DisactivatedMsgId(msgid_t msgid); // for client on slave chmpx + + // ack + bool SendAck(const COMPOSEDMSGID& composed, bool is_success); + bool ReceiveAck(msgid_t msgid); + + bool ReceiveComposedMsgid(int fd, COMPOSEDMSGID& composed, bool* pCanContinueWait = NULL); + bool ReceiveHead(int fd, const COMPOSEDMSGID& composed, PCOMPKT* ppComPkt); + bool ReceiveHeadByMsgId(msgid_t msgid, PCOMPKT* ppComPkt, bool* pCanContinueWait = NULL); + bool ReceiveBody(PCOMPKT pComPkt, unsigned char** ppbody, size_t& blength, bool is_remove_body = true); + PCOMPKT ReceiveBody(PCOMPKT pComPkt, bool is_remove_body); + bool RemoveReceivedBody(PCOMPKT pComPkt); + + bool BuildC2CResponseHead(PCOMHEAD pComHead, PCOMHEAD pReqComHead); + bool BuildC2CSendHead(PCOMHEAD pComHead, chmpxid_t tochmpxid, chmhash_t hash, c2ctype_t c2ctype = COM_C2C_ROUTING, msgid_t frommsgid = CHM_INVALID_MSGID); + + bool CloseDestMQ(msgid_t msgid); + + bool PxCltSendMergeGetLastTime(struct timespec& lastts); + bool PxCltSendRequestUpdateData(const PPXCOMMON_MERGE_PARAM pmerge_param); + bool PxCltSendSetUpdateData(size_t length, const unsigned char* pdata, const struct timespec* pts); + bool PxCltSendAbortUpdateData(void); + bool PxCltSendJoinNotify(pid_t pid); +}; + +#endif // CHMEVENTMQ_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmeventshm.cc b/lib/chmeventshm.cc new file mode 100644 index 0000000..f0700ef --- /dev/null +++ b/lib/chmeventshm.cc @@ -0,0 +1,404 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "chmcommon.h" +#include "chmcntrl.h" +#include "chmimdata.h" +#include "chmeventmq.h" +#include "chmutil.h" +#include "chmdbg.h" + +using namespace std; + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +#define PROCFS_PATH "/proc/" +#define CHMEVSHM_THREAD_NAME "ChmEventShm" + +//--------------------------------------------------------- +// Class Methods +//--------------------------------------------------------- +// [NOTICE] +// This class method is run on another worker thread. +// +bool ChmEventShm::CheckProcessRunning(void* common_param, chmthparam_t wp_param) +{ + if(!common_param){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + ChmEventShm* pThis = reinterpret_cast(common_param); + if(!pThis){ + ERR_CHMPRN("Internal error: pThis is NULL."); + return false; + } + ChmIMData* pImData = pThis->pChmCntrl->GetImDataObj(); + ChmEventMq* pMqObj = pThis->pChmCntrl->GetEventMqObj(); + if(!pImData || !pMqObj){ + ERR_CHMPRN("Internal error: ChmImData or ChmEventMQ is NULL."); + return false; + } + + // get all pids + pidlist_t pidlist; + if(!pImData->GetAllPids(pidlist)){ + WAN_CHMPRN("Failed to get pid list."); + return true; // finish. + } + if(0 == pidlist.size()){ + // why?(nothing to do) + return true; + } + + // get down client process. + // + pidlist_t down_pids; + for(int cnt = 0; true; ++cnt){ + for(pidlist_t::const_iterator iter = pidlist.begin(); iter != pidlist.end(); ++iter){ + if(CHM_INVALID_PID != *iter && !ChmEventShm::IsProcessRunning(*iter)){ + down_pids.push_back(*iter); + } + } + if(0 != down_pids.size()){ + break; + } + // no down process + if(1 < cnt){ // = 300ms + return true; + } + // [NOTE] + // For a case of that the process is downing before removing /proc/PID path. + // So we wait a while. + // + struct timespec sleeptime = {0, 100 * 1000 * 1000}; // 100ms + nanosleep(&sleeptime, NULL); + } + + // free msgids by pids. + // + msgidlist_t freedmsgids; + if(!pImData->FreeMsgs(down_pids, freedmsgids)){ + WAN_CHMPRN("Failed to free msgs, but continue for check process ids..."); + } + if(0 == freedmsgids.size()){ + MSG_CHMPRN("There is no to free msgs, maybe already free it or failed FreeMsgs func. but continue for check process ids..."); + } + + // close MQ in MQ event object + // + for(msgidlist_t::const_iterator iter = freedmsgids.begin(); iter != freedmsgids.end(); ++iter){ + if(!pMqObj->CloseDestMQ(*iter)){ + ERR_CHMPRN("Failed to close internal destination MQ(0x%016" PRIx64 "), but continue...", *iter); + } + } + + // remove pids in shm. + for(pidlist_t::const_iterator iter = down_pids.begin(); iter != down_pids.end(); ++iter){ + if(CHM_INVALID_PID != *iter && !pImData->RetriveClientPid(*iter)){ + ERR_CHMPRN("Failed to retrive pid(%d) from shm, but continue...", *iter); + } + } + + // check clients + if(!pImData->IsClientPids()){ + // There is no client process, so send notification. + pThis->pChmCntrl->is_close_notify = true; + } + return true; +} + +bool ChmEventShm::IsProcessRunning(pid_t pid) +{ + if(CHM_INVALID_PID == pid){ + ERR_CHMPRN("Parameter is wrong"); + return false; + } + string procfile = PROCFS_PATH; + procfile += to_string(pid); + + return is_file_exist(procfile.c_str()); +} + +//--------------------------------------------------------- +// ChmEventShm Class +//--------------------------------------------------------- +ChmEventShm::ChmEventShm(int eventqfd, ChmCntrl* pcntrl, const char* file) : ChmEventBase(eventqfd, pcntrl), inotifyfd(CHM_INVALID_HANDLE), watchfd(CHM_INVALID_HANDLE), checkthread(CHMEVSHM_THREAD_NAME) +{ + if(!CHMEMPTYSTR(file)){ + chmshmfile = file; + } + // run check thread + // - sleep at starting + // - not at onece(not one shot) + // - sleep after every working + // - keep event count + // + if(!checkthread.CreateThreads(1, ChmEventShm::CheckProcessRunning, NULL, this, 0, true, false, false, true)){ + ERR_CHMPRN("Failed to create thread for checking, but continue..."); + } +} + +ChmEventShm::~ChmEventShm() +{ + // exit thread + if(!checkthread.ExitAllThreads()){ + ERR_CHMPRN("Failed to exit thread for checking."); + } + Clean(); +} + +bool ChmEventShm::Clean(void) +{ + if(IsWatching()){ + MSG_CHMPRN("Should call RemoveNotify function before calling this destructor."); + UnsetEventQueue(); + } + return ChmEventBase::Clean(); +} + +bool ChmEventShm::GetEventQueueFds(event_fds_t& fds) +{ + if(!IsWatching()){ + MSG_CHMPRN("There is no watching file."); + return false; + } + fds.clear(); + fds.push_back(inotifyfd); + return true; +} + +bool ChmEventShm::SetEventQueue(void) +{ + if(0 == chmshmfile.c_str()){ + ERR_CHMPRN("This object does not have chmshm file path."); + return false; + } + if(CHM_INVALID_HANDLE == eqfd){ + ERR_CHMPRN("event fd is invalid."); + return false; + } + if(IsWatching()){ + if(!UnsetEventQueue()){ + return false; + } + } + + // create inotify + if(-1 == (inotifyfd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC))){ + ERR_CHMPRN("Failed to create inotify, error %d", errno); + return false; + } + + // add inotify + if(CHM_INVALID_HANDLE == (watchfd = inotify_add_watch(inotifyfd, chmshmfile.c_str(), IN_CLOSE))){ + ERR_CHMPRN("Could not watch file %s (errno=%d)", chmshmfile.c_str(), errno); + CHM_CLOSE(inotifyfd); + return false; + } + + // add event + struct epoll_event epoolev; + memset(&epoolev, 0, sizeof(struct epoll_event)); + epoolev.data.fd = inotifyfd; + epoolev.events = EPOLLIN | EPOLLET | EPOLLRDHUP; // EPOLLRDHUP is set + if(-1 == epoll_ctl(eqfd, EPOLL_CTL_ADD, inotifyfd, &epoolev)){ + ERR_CHMPRN("Failed to add inotifyfd(%d)-watchfd(%d) to event fd(%d), error=%d", inotifyfd, watchfd, eqfd, errno); + inotify_rm_watch(inotifyfd, watchfd); + watchfd = CHM_INVALID_HANDLE; + CHM_CLOSE(inotifyfd); + return false; + } + return true; +} + +bool ChmEventShm::UnsetEventQueue(void) +{ + if(!IsWatching()){ + MSG_CHMPRN("There is no watching file."); + return false; + } + bool result = true; + + if(CHM_INVALID_HANDLE == inotifyfd){ + WAN_CHMPRN("inotifyfd is invalid."); + }else{ + epoll_ctl(eqfd, EPOLL_CTL_DEL, inotifyfd, NULL); + + if(CHM_INVALID_HANDLE == inotify_rm_watch(inotifyfd, watchfd)){ + if(EINVAL == errno){ + WAN_CHMPRN("Failed to remove watching fd(%d) from inotify fd(%d), because watchfd is invalid. It maybe removed file, so continue...", watchfd, inotifyfd); + }else{ + ERR_CHMPRN("Could not remove watching fd(%d) from inotify fd(%d), errno=%d", watchfd, inotifyfd, errno); + result = false; + } + } + CHM_CLOSE(inotifyfd); + } + inotifyfd = CHM_INVALID_HANDLE; + watchfd = CHM_INVALID_HANDLE; + + return result; +} + + +bool ChmEventShm::IsEventQueueFd(int fd) +{ + if(CHM_INVALID_HANDLE == fd){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!IsWatching()){ + MSG_CHMPRN("There is no watching file."); + return false; + } + // check + if(inotifyfd != fd){ + return false; + } + return true; +} + +bool ChmEventShm::ResetEventQueue(void) +{ + if(IsWatching()){ + if(!UnsetEventQueue()){ + return false; + } + } + return SetEventQueue(); +} + +// +// If the event is IN_CLOSE which is for this obejct, this method returns true. +// +bool ChmEventShm::CheckNotifyEvent(void) const +{ + if(!IsWatching()){ + MSG_CHMPRN("There is no watching file."); + return false; + } + + // read from inotify event + unsigned char* pevent; + size_t bytes; + if(NULL == (pevent = chm_read(inotifyfd, &bytes))){ + WAN_CHMPRN("read no inotify event, no more inotify event data."); + return false; + } + + // analize event types + struct inotify_event* in_event = NULL; + bool result = false; + for(unsigned char* ptr = pevent; (ptr + sizeof(struct inotify_event)) <= (pevent + bytes); ptr += sizeof(struct inotify_event) + in_event->len){ + in_event = reinterpret_cast(ptr); + + if(watchfd != in_event->wd){ + continue; + } + if(in_event->mask & IN_CLOSE_WRITE){ + MSG_CHMPRN("chmshm file %s is wrote(%d).", chmshmfile.c_str(), in_event->mask); + result = true; + }else if(in_event->mask & IN_CLOSE_NOWRITE){ + MSG_CHMPRN("chmshm file %s is read(%d).", chmshmfile.c_str(), in_event->mask); + result = true; + }else{ + WAN_CHMPRN("inotify event type(%u) is not handled because of waiting another event after it.", in_event->mask); + } + } + CHM_Free(pevent); + return result; +} + +bool ChmEventShm::Receive(int fd) +{ + if(!IsWatching()){ + ERR_CHMPRN("There is no watching file."); + return false; + } + if(!IsEventQueueFd(fd)){ + ERR_CHMPRN("fd(%d) is not this object event fd.", fd); + return false; + } + if(!CheckNotifyEvent()){ + MSG_CHMPRN("There is no event for checking processes running by this object."); + return false; + } + + // check client process running. + // + // Checking client processes running by worker thread instead of COM_C2PX communication, + // becasue working in main thread is not good for performance. + // + if(!checkthread.DoWorkThread()){ + ERR_CHMPRN("Failed to wake up thread for checking."); + return false; + } + + // Nothing to dispatch event in this class. + + return true; +} + +bool ChmEventShm::Send(PCOMPKT pComPkt, const unsigned char* pbody, size_t blength) +{ + MSG_CHMPRN("Nothing to do in this object for this event.(Not implement thie event in this class)"); + return true; +} + +bool ChmEventShm::NotifyHup(int fd) +{ + if(!IsWatching()){ + MSG_CHMPRN("There is no watching file."); + return false; + } + if(!IsEventQueueFd(fd)){ + ERR_CHMPRN("fd(%d) is not this object event fd.", fd); + return false; + } + return UnsetEventQueue(); +} + +bool ChmEventShm::Processing(PCOMPKT pComPkt) +{ + MSG_CHMPRN("Nothing to do in this object for this event.(Not implement thie event in this class)"); + return true; +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmeventshm.h b/lib/chmeventshm.h new file mode 100644 index 0000000..b3c996c --- /dev/null +++ b/lib/chmeventshm.h @@ -0,0 +1,79 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMEVENTSHM_H +#define CHMEVENTSHM_H + +#include + +#include "chmcommon.h" +#include "chmutil.h" +#include "chmeventbase.h" +#include "chmthread.h" + +//--------------------------------------------------------- +// Class ChmEventShm +//--------------------------------------------------------- +// This class is to catch client process down realtime for +// cleaning MQ management data in CHMSHM. +// +class ChmEventShm : public ChmEventBase +{ + protected: + std::string chmshmfile; + int inotifyfd; + int watchfd; + ChmThread checkthread; + + protected: + static bool IsProcessRunning(pid_t pid); + + bool IsWatching(void) const { return (CHM_INVALID_HANDLE != watchfd); } + bool CheckNotifyEvent(void) const; + bool ResetEventQueue(void); + + public: + static bool CheckProcessRunning(void* common_param, chmthparam_t wp_param); + + ChmEventShm(int eventqfd = CHM_INVALID_HANDLE, ChmCntrl* pcntrl = NULL, const char* file = NULL); + virtual ~ChmEventShm(); + + virtual bool Clean(void); + + virtual bool GetEventQueueFds(event_fds_t& fds); + virtual bool SetEventQueue(void); + virtual bool UnsetEventQueue(void); + virtual bool IsEventQueueFd(int fd); + + virtual bool Send(PCOMPKT pComPkt, const unsigned char* pbody, size_t blength); + virtual bool Receive(int fd); + virtual bool NotifyHup(int fd); + virtual bool Processing(PCOMPKT pComPkt); +}; + +#endif // CHMEVENTSHM_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmeventsock.cc b/lib/chmeventsock.cc new file mode 100644 index 0000000..0b25375 --- /dev/null +++ b/lib/chmeventsock.cc @@ -0,0 +1,8613 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "chmcommon.h" +#include "chmeventsock.h" +#include "chmcomstructure.h" +#include "chmstructure.tcc" +#include "chmcntrl.h" +#include "chmsigcntrl.h" +#include "chmnetdb.h" +#include "chmlock.h" +#include "chmutil.h" +#include "chmdbg.h" + +using namespace std; + +//--------------------------------------------------------- +// Utility Macros +//--------------------------------------------------------- +#define CVT_ESTR_NULL(pstr) (CHMEMPTYSTR(pstr) ? NULL : pstr) + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +#define CHMEVSOCK_MERGE_THREAD_NAME "ChmEventSock-Merge" +#define CHMEVSOCK_SOCK_THREAD_NAME "ChmEventSock-Socket" + +// Control Commands +#define CTL_COMMAND_MAX_LENGTH 8192 +#define CTL_RECEIVE_MAX_LENGTH (CTL_COMMAND_MAX_LENGTH * 2) + +#define CTL_COMMAND_DUMP_IMDATA "DUMP" +#define CTL_COMMAND_START_MERGE "MERGE" +#define CTL_COMMAND_STOP_MERGE "ABORTMERGE" +#define CTL_COMMAND_COMPLETE_MERGE "COMPMERGE" +#define CTL_COMMAND_SERVICE_IN "SERVICEIN" +#define CTL_COMMAND_SERVICE_OUT "SERVICEOUT" +#define CTL_COMMAND_SELF_STATUS "SELFSTATUS" +#define CTL_COMMAND_ALLSVR_STATUS "ALLSTATUS" +#define CTL_COMMAND_TRACE_SET "TRACE" +#define CTL_COMMAND_TRACE_VIEW "TRACEVIEW" + +#define CTL_COMMAND_TRACE_SET_ENABLE1 "ENABLE" +#define CTL_COMMAND_TRACE_SET_ENABLE2 "YES" +#define CTL_COMMAND_TRACE_SET_ENABLE3 "ON" +#define CTL_COMMAND_TRACE_SET_DISABLE1 "DISABLE" +#define CTL_COMMAND_TRACE_SET_DISABLE2 "NO" +#define CTL_COMMAND_TRACE_SET_DISABLE3 "OFF" +#define CTL_COMMAND_TRACE_VIEW_DIR "DIR=" +#define CTL_COMMAND_TRACE_VIEW_DEV "DEV=" +#define CTL_COMMAND_TRACE_VIEW_ALL "ALL" +#define CTL_COMMAND_TRACE_VIEW_IN "IN" +#define CTL_COMMAND_TRACE_VIEW_OUT "OUT" +#define CTL_COMMAND_TRACE_VIEW_SOCK "SOCK" +#define CTL_COMMAND_TRACE_VIEW_MQ "MQ" + +#define CTL_RES_SUCCESS "SUCCEED\n" +#define CTL_RES_SUCCESS_NOSERVER "SUCCEED: There no server on RING.\n" +#define CTL_RES_SUCCESS_STATUS_NOTICE "SUCCEES: Send status notice to no server on RING.\n" +#define CTL_RES_ERROR "ERROR: Something error is occured.\n" +#define CTL_RES_ERROR_PARAMETER "ERROR: Parameters are wrong.\n" +#define CTL_RES_ERROR_COMMUNICATION "ERROR: Something error is occured in sending/receiveing data on RING.\n" +#define CTL_RES_ERROR_SERVICE_OUT_PARAM "ERROR: SERVICEOUT command must have parameter as server/ctlport.(ex: \"SERVICEOUT servername.fqdn:ctlport\")\n" +#define CTL_RES_ERROR_MERGE_START "ERROR: Failed to start merging.\n" +#define CTL_RES_ERROR_MERGE_ABORT "ERROR: Failed to stop merging.\n" +#define CTL_RES_ERROR_MERGE_COMPLETE "ERROR: Failed to complete merging.\n" +#define CTL_RES_ERROR_MERGE_AUTO "ERROR: Failed to start merging or complete merging.\n" +#define CTL_RES_ERROR_NOT_FOUND_SVR "ERROR: There is no such as server.\n" +#define CTL_RES_ERROR_NOT_SERVERMODE "ERROR: Command must run on server mode.\n" +#define CTL_RES_ERROR_NOT_SRVIN "ERROR: Server is not \"service in\" on RING.\n" +#define CTL_RES_ERROR_OPERATING "ERROR: Could not change status, because server is operating now.\n" +#define CTL_RES_ERROR_STATUS_NOT_ALLOWED "ERROR: Could not change status, because status is not allowed.\n" +#define CTL_RES_ERROR_STATUS_HAS_SUSPEND "ERROR: Could not change status, because status has SUSPEND.\n" +#define CTL_RES_ERROR_SOME_SERVER_SUSPEND "ERROR: Could not change status, because some servers status is SUSPEND.\n" +#define CTL_RES_ERROR_COULD_NOT_SERVICE_OUT "ERROR: Server could not set SERVICE OUT on RING.\n" +#define CTL_RES_ERROR_NOT_CHANGE_STATUS "ERROR: Server can not be changed status.\n" +#define CTL_RES_ERROR_CHANGE_STATUS "ERROR: Failed to change server status.\n" +#define CTL_RES_ERROR_TRANSFER "ERROR: Failed to transfer command to other target server, something error occured on target server.\n" +#define CTL_RES_ERROR_STATUS_NOTICE "ERROR: Failed to send status notice to other servers.\n" +#define CTL_RES_ERROR_GET_CHMPXSVR "ERROR: Failed to get all information.\n" +#define CTL_RES_ERROR_GET_STAT "ERROR: Failed to get all stat.\n" +#define CTL_RES_ERROR_TRACE_SET_PARAM "ERROR: TRACE command must have parameter as enable/disable.(ex: \"TRACE enable\")\n" +#define CTL_RES_ERROR_TRACE_SET_ALREADY "ERROR: TRACE is already enabled/disabled.\n" +#define CTL_RES_ERROR_TRACE_SET_FAILED "ERROR: Failed to set TRACE enable/disable.\n" +#define CTL_RES_ERROR_TRACE_VIEW_PARAM "ERROR: TRACEVIEW command parameter is \"TRACEVIEW [DIR=IN/OUT/ALL] [DEV=SOCK/MQ/ALL] [COUNT]\"\n" +#define CTL_RES_ERROR_TRACE_VIEW_NOTRACE "ERROR: Now trace count is 0.\n" +#define CTL_RES_ERROR_TRACE_VIEW_NODATA "ERROR: There is no matched trace log now.\n" +#define CTL_RES_ERROR_TRACE_VIEW_INTERR "ERROR: Internal error is occured for TRACEVIEW.\n" +#define CTL_RES_INT_ERROR "INTERNAL ERROR: Something error is occured.\n" +#define CTL_RES_INT_ERROR_NOTGETCHMPX "INTERNAL ERROR: Could not get chmpx servers information.\n" + +// SSL +#define CHMEVENTSOCK_SSL_VP_DEPTH 3 +#define CHMEVENTSOCK_VCB_INDEX_STR "verify_cb_data_index" + +// CheckResultSSL function type +#define CHKRESULTSSL_TYPE_CON 0 // SSL_accept / SSL_connect +#define CHKRESULTSSL_TYPE_RW 1 // SSL_read / SSL_write +#define CHKRESULTSSL_TYPE_SD 2 // SSL_shutdown +#define IS_SAFE_CHKRESULTSSL_TYPE(type) (CHKRESULTSSL_TYPE_CON == type || CHKRESULTSSL_TYPE_RW == type || CHKRESULTSSL_TYPE_SD == type) + +//--------------------------------------------------------- +// Class valiable +//--------------------------------------------------------- +const int ChmEventSock::DEFAULT_SOCK_THREAD_CNT; +const int ChmEventSock::DEFAULT_MAX_SOCK_POOL; +const time_t ChmEventSock::DEFAULT_SOCK_POOL_TIMEOUT; +const time_t ChmEventSock::NO_SOCK_POOL_TIMEOUT; +const int ChmEventSock::DEFAULT_KEEPIDLE; +const int ChmEventSock::DEFAULT_KEEPINTERVAL; +const int ChmEventSock::DEFAULT_KEEPCOUNT; +const int ChmEventSock::DEFAULT_LISTEN_BACKLOG; +const int ChmEventSock::DEFAULT_RETRYCNT; +const int ChmEventSock::DEFAULT_RETRYCNT_CONNECT; +const suseconds_t ChmEventSock::DEFAULT_WAIT_SOCKET; +const suseconds_t ChmEventSock::DEFAULT_WAIT_CONNECT; +int ChmEventSock::CHM_SSL_VERIFY_DEPTH = CHMEVENTSOCK_SSL_VP_DEPTH; +const char* ChmEventSock::strVerifyCBDataIndex = CHMEVENTSOCK_VCB_INDEX_STR; +int ChmEventSock::verify_cb_data_index = -1; +int ChmEventSock::ssl_session_id = static_cast(getpid()); +bool ChmEventSock::is_self_sigined = false; + +//------------------------------------------------------ +// Class Method for SSL +//------------------------------------------------------ +int ChmEventSock::VerifyCallBackSSL(int preverify_ok, X509_STORE_CTX* store_ctx) +{ + char strerr[256]; + X509* err_cert = X509_STORE_CTX_get_current_cert(store_ctx); + int err_code = X509_STORE_CTX_get_error(store_ctx); + int depth = X509_STORE_CTX_get_error_depth(store_ctx); + SSL* ssl = reinterpret_cast(X509_STORE_CTX_get_ex_data(store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx())); + SSL_CTX* ssl_ctx = SSL_get_SSL_CTX(ssl); + const int* pchm_depth = reinterpret_cast(SSL_CTX_get_ex_data(ssl_ctx, ChmEventSock::verify_cb_data_index)); + + // error string + X509_NAME_oneline(X509_get_subject_name(err_cert), strerr, sizeof(strerr)); + + // depth + if(!pchm_depth || *pchm_depth < depth){ + // Force changing error code. + preverify_ok= 0; + err_code = X509_V_ERR_CERT_CHAIN_TOO_LONG; + X509_STORE_CTX_set_error(store_ctx, err_code); + } + + // Message + if(!preverify_ok){ + ERR_CHMPRN("VERIFY ERROR: depth=%d, errnum=%d(%s), string=%s", depth, err_code, X509_verify_cert_error_string(err_code), strerr); + }else{ + MSG_CHMPRN("VERIFY OK: depth=%d, string=%s", depth, strerr); + } + + // check error code + switch(err_code){ + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + X509_NAME_oneline(X509_get_issuer_name(store_ctx->current_cert), strerr, 256); + ERR_CHMPRN("DETAIL: X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: issuer=%s", strerr); + preverify_ok = 0; + break; + + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + if(ChmEventSock::is_self_sigined){ + // For DEBUG + WAN_CHMPRN("SKIP ERROR(DEBUG): X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: self signed certificate is ERROR."); + preverify_ok = 1; + }else{ + ERR_CHMPRN("DETAIL: X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: self signed certificate is ERROR."); + preverify_ok = 0; + } + break; + + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + X509_STORE_CTX_set_error(store_ctx, X509_V_OK); + WAN_CHMPRN("SKIP ERROR: X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: Verified with no error in self signed certificate chain."); + preverify_ok = 1; + break; + + case X509_V_ERR_CERT_NOT_YET_VALID: + ERR_CHMPRN("DETAIL: X509_V_ERR_CERT_NOT_YET_VALID: certificate is not yet valid(date is after the current time)."); + preverify_ok = 0; + break; + + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + ERR_CHMPRN("DETAIL: X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: certificate not before field contains an invalid time."); + preverify_ok = 0; + break; + + case X509_V_ERR_CERT_HAS_EXPIRED: + ERR_CHMPRN("DETAIL: X509_V_ERR_CERT_HAS_EXPIRED: certificate has expired."); + preverify_ok = 0; + break; + + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + ERR_CHMPRN("DETAIL: X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: certificate not after field contains an invalid time."); + preverify_ok = 0; + break; + + default: + break; + } + return preverify_ok; +} + +bool ChmEventSock::CheckResultSSL(int sock, SSL* ssl, long action_result, int type, bool& is_retry, bool& is_close, int retrycnt, suseconds_t waittime) +{ + if(CHM_INVALID_SOCK == sock || !ssl || !IS_SAFE_CHKRESULTSSL_TYPE(type)){ + ERR_CHMPRN("Parameters are wrong."); + is_retry = false; + return false; + } + if(CHMEVENTSOCK_RETRY_DEFAULT == retrycnt){ + retrycnt = ChmEventSock::DEFAULT_RETRYCNT; + waittime = ChmEventSock::DEFAULT_WAIT_SOCKET; + } + is_retry = true; + is_close = false; + + bool result = true; + int werr; + if(action_result <= 0){ + int ssl_result = SSL_get_error(ssl, action_result); + switch(ssl_result){ + case SSL_ERROR_NONE: + // Succeed. + MSG_CHMPRN("SSL action result(%ld): ssl result(%d: %s), succeed.", action_result, ssl_result, ERR_error_string(ssl_result, NULL)); + break; + + case SSL_ERROR_SSL: + if(CHKRESULTSSL_TYPE_SD == type){ + WAN_CHMPRN("SSL action result(%ld): ssl result(%d: %s). so retry to shutdown.", action_result, ssl_result, ERR_error_string(ssl_result, NULL)); + result = false; + }else{ + ERR_CHMPRN("SSL action result(%ld): ssl result(%d: %s), so something error occured(errno=%d).", action_result, ssl_result, ERR_error_string(ssl_result, NULL), errno); + is_retry= false; + is_close= true; + result = false; + } + break; + + case SSL_ERROR_WANT_WRITE: + // Wait for up + if(0 != (werr = ChmEventSock::WaitForReady(sock, WAIT_WRITE_FD, retrycnt, false, waittime))){ // not check SO_ERROR + ERR_CHMPRN("SSL action result(%ld): ssl result(%d: %s), and Failed to wait write.", action_result, ssl_result, ERR_error_string(ssl_result, NULL)); + is_retry = false; + if(ETIMEDOUT != werr){ + is_close= true; + } + }else{ + WAN_CHMPRN("SSL action result(%ld): ssl result(%d: %s), and Succeed to wait write.", action_result, ssl_result, ERR_error_string(ssl_result, NULL)); + } + result = false; + break; + + case SSL_ERROR_WANT_READ: + // Wait for up + if(0 != (werr = ChmEventSock::WaitForReady(sock, WAIT_READ_FD, retrycnt, false, waittime))){ // not check SO_ERROR + ERR_CHMPRN("SSL action result(%ld): ssl result(%d: %s), and Failed to wait read.", action_result, ssl_result, ERR_error_string(ssl_result, NULL)); + is_retry = false; + if(ETIMEDOUT != werr){ + is_close= true; + } + }else{ + WAN_CHMPRN("SSL action result(%ld): ssl result(%d: %s), and Succeed to wait read.", action_result, ssl_result, ERR_error_string(ssl_result, NULL)); + } + result = false; + break; + + case SSL_ERROR_SYSCALL: + if(action_result < 0){ + ERR_CHMPRN("SSL action result(%ld): ssl result(%d: %s), errno(%d).", action_result, ssl_result, ERR_error_string(ssl_result, NULL), errno); + is_retry= false; + result = false; + }else{ // action_result == 0 + if(CHKRESULTSSL_TYPE_CON == type){ + MSG_CHMPRN("SSL action result(%ld): ssl result(%d: %s), so this case is received illigal EOF after calling connect/accept, but no error(no=%d).", action_result, ssl_result, ERR_error_string(ssl_result, NULL), errno); + result = true; + }else if(CHKRESULTSSL_TYPE_SD == type){ + WAN_CHMPRN("SSL action result(%ld): ssl result(%d: %s). so retry to shutdown.", action_result, ssl_result, ERR_error_string(ssl_result, NULL)); + result = false; + }else{ + ERR_CHMPRN("SSL action result(%ld): ssl result(%d: %s).", action_result, ssl_result, ERR_error_string(ssl_result, NULL)); + is_retry= false; + is_close= true; + result = false; + } + } + break; + + case SSL_ERROR_ZERO_RETURN: + if(CHKRESULTSSL_TYPE_SD == type){ + WAN_CHMPRN("SSL action result(%ld): ssl result(%d: %s). so retry to shutdown.", action_result, ssl_result, ERR_error_string(ssl_result, NULL)); + result = false; + }else{ + ERR_CHMPRN("SSL action result(%ld): ssl result(%d: %s), so the peer is closed.", action_result, ssl_result, ERR_error_string(ssl_result, NULL)); + is_retry= false; + is_close= true; + result = false; + } + break; + + default: + if(CHKRESULTSSL_TYPE_SD == type){ + WAN_CHMPRN("SSL action result(%ld): ssl result(%d: %s). so retry to shutdown.", action_result, ssl_result, ERR_error_string(ssl_result, NULL)); + result = false; + }else{ + ERR_CHMPRN("SSL action result(%ld): ssl result(%d: %s).", action_result, ssl_result, ERR_error_string(ssl_result, NULL)); + is_retry= false; + result = false; + } + break; + } + }else{ + // Result is Success! + } + return result; +} + +SSL_CTX* ChmEventSock::MakeSSLContext(const char* CApath, const char* CAfile, const char* server_cert, const char* server_prikey, const char* slave_cert, const char* slave_prikey, bool is_verify_peer) +{ + if(!CHMEMPTYSTR(CApath) && !CHMEMPTYSTR(CAfile)){ + ERR_CHMPRN("Parameters are wrong."); + return NULL; + } + if((CHMEMPTYSTR(server_cert) || CHMEMPTYSTR(server_prikey)) && (CHMEMPTYSTR(slave_cert) || CHMEMPTYSTR(slave_prikey))){ + ERR_CHMPRN("Parameters are wrong."); + return NULL; + } + + // Make context data index + if(-1 == ChmEventSock::verify_cb_data_index){ + // make new index + ChmEventSock::verify_cb_data_index = SSL_CTX_get_ex_new_index(0, const_cast(ChmEventSock::strVerifyCBDataIndex), NULL, NULL, NULL); + } + + SSL_CTX* ctx; + + // Make context(without SSLv2) + if(NULL == (ctx = SSL_CTX_new(SSLv23_method()))){ + ERR_CHMPRN("Could not make SSL Context."); + return NULL; + } + SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2); + + // Options/Modes + SSL_CTX_set_options(ctx, SSL_OP_ALL); + SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); // Non blocking + SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); // Non blocking + + // Load CA cert + if((CHMEMPTYSTR(CAfile) && CHMEMPTYSTR(CApath)) || 1 != SSL_CTX_load_verify_locations(ctx, CAfile, CApath)){ + if(!CHMEMPTYSTR(CAfile) || !CHMEMPTYSTR(CApath)){ + // Failed loading -> try to default CA + WAN_CHMPRN("Failed to load CA certs, CApath=%s, CAfile=%s", CApath, CAfile); + } + // Load default CA + if(1 != SSL_CTX_set_default_verify_paths(ctx)){ + ERR_CHMPRN("Failed to load default certs."); + SSL_CTX_free(ctx); + return NULL; + } + } + + // Set cert + if(!CHMEMPTYSTR(server_cert) && !CHMEMPTYSTR(server_prikey)){ + // Set server cert + if(1 != SSL_CTX_use_certificate_chain_file(ctx, server_cert)){ + // Failed loading server cert + ERR_CHMPRN("Failed to set server cert(%s)", server_cert); + SSL_CTX_free(ctx); + return NULL; + } + // Set server private keys(no pass) + if(1 != SSL_CTX_use_PrivateKey_file(ctx, server_prikey, SSL_FILETYPE_PEM)){ // **** Not use following functions for passwd and RSA **** + // Failed loading server private key + ERR_CHMPRN("Failed to load private key file(%s)", server_prikey); + SSL_CTX_free(ctx); + return NULL; + } + // Verify cert + if(1 != SSL_CTX_check_private_key(ctx)){ + // Not success to verify private key. + ERR_CHMPRN("Failed to verify server cert(%s) & server private key(%s)", server_cert, server_prikey); + SSL_CTX_free(ctx); + return NULL; + } + + // Set session id to context + SSL_CTX_set_session_id_context(ctx, reinterpret_cast(&ChmEventSock::ssl_session_id), sizeof(ChmEventSock::ssl_session_id)); + + // Set CA list for client(slave) + if(!CHMEMPTYSTR(CAfile)){ + STACK_OF(X509_NAME)* cert_names; + if(NULL != (cert_names = SSL_load_client_CA_file(CAfile))){ + SSL_CTX_set_client_CA_list(ctx, cert_names); + }else{ + WAN_CHMPRN("Failed to load client(slave) CA certs(%s)", CAfile); + } + } + + }else if(!CHMEMPTYSTR(slave_cert) && !CHMEMPTYSTR(slave_prikey)){ + // Set slave cert + if(1 != SSL_CTX_use_certificate_chain_file(ctx, slave_cert)){ + // Failed loading slave cert + ERR_CHMPRN("Failed to set slave cert(%s)", slave_cert); + SSL_CTX_free(ctx); + return NULL; + } + // Set slave private keys(no pass) + if(1 != SSL_CTX_use_PrivateKey_file(ctx, slave_prikey, SSL_FILETYPE_PEM)){ // **** Not use following functions for passwd and RSA **** + // Failed loading slave private key + ERR_CHMPRN("Failed to load private key file(%s)", slave_prikey); + SSL_CTX_free(ctx); + return NULL; + } + // Verify cert + if(1 != SSL_CTX_check_private_key(ctx)){ + // Not success to verify private key. + ERR_CHMPRN("Failed to verify slave cert(%s) & slave private key(%s)", slave_cert, slave_prikey); + SSL_CTX_free(ctx); + return NULL; + } + + // slave SSL context does not need verify flag. + is_verify_peer = false; + } + + // Make verify peer mode + int verify_mode = is_verify_peer ? (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE) : SSL_VERIFY_NONE; + + // Set callback + SSL_CTX_set_verify(ctx, verify_mode, ChmEventSock::VerifyCallBackSSL); // Set verify callback + SSL_CTX_set_verify_depth(ctx, ChmEventSock::CHM_SSL_VERIFY_DEPTH + 1); // Verify depth + SSL_CTX_set_ex_data(ctx, ChmEventSock::verify_cb_data_index, &ChmEventSock::CHM_SSL_VERIFY_DEPTH); // Set external data + + return ctx; +} + +SSL* ChmEventSock::HandshakeSSL(SSL_CTX* ctx, int sock, bool is_accept, int con_retrycnt, suseconds_t con_waittime) +{ + if(!ctx || CHM_INVALID_SOCK == sock){ + ERR_CHMPRN("Parameters are wrong."); + return NULL; + } + if(CHMEVENTSOCK_RETRY_DEFAULT == con_retrycnt){ + con_retrycnt = ChmEventSock::DEFAULT_RETRYCNT_CONNECT; + con_waittime = ChmEventSock::DEFAULT_WAIT_CONNECT; + } + + // make SSL object + SSL* ssl = SSL_new(ctx); + if(!ssl){ + ERR_CHMPRN("Could not make SSL object from context."); + return NULL; + } + if(is_accept){ + SSL_set_accept_state(ssl); + }else{ + SSL_set_connect_state(ssl); + } + SSL_set_fd(ssl, sock); + + // accept/connect + bool is_retry; + bool is_close = false; // Not used this value. + long action_result; + for(int tmp_retrycnt = con_retrycnt; 0 < tmp_retrycnt; --tmp_retrycnt){ + // accept + if(is_accept){ + action_result = SSL_accept(ssl); + }else{ + action_result = SSL_connect(ssl); + } + + is_retry = true; + if(ChmEventSock::CheckResultSSL(sock, ssl, action_result, CHKRESULTSSL_TYPE_CON, is_retry, is_close, con_retrycnt, con_waittime)){ + // success + return ssl; + } + if(!is_retry){ + break; + } + } + ERR_CHMPRN("Failed to %s SSL.", (is_accept ? "accept" : "connect")); + + // shutdown SSL + if(!ChmEventSock::ShutdownSSL(sock, ssl, con_retrycnt, con_waittime)){ + ERR_CHMPRN("Failed to shutdown SSL, but continue..."); + } + SSL_free(ssl); + + return NULL; +} + +bool ChmEventSock::ShutdownSSL(int sock, SSL* ssl, int con_retrycnt, suseconds_t con_waittime) +{ + if(CHM_INVALID_SOCK == sock || !ssl){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(CHMEVENTSOCK_RETRY_DEFAULT == con_retrycnt){ + con_retrycnt = ChmEventSock::DEFAULT_RETRYCNT_CONNECT; + con_waittime = ChmEventSock::DEFAULT_WAIT_CONNECT; + } + + bool is_retry; + long action_result; + bool is_close = false; // Not used this value. + for(int tmp_retrycnt = con_retrycnt; 0 < tmp_retrycnt; --tmp_retrycnt){ + // accept + action_result = SSL_shutdown(ssl); + is_retry = true; + + if(ChmEventSock::CheckResultSSL(sock, ssl, action_result, CHKRESULTSSL_TYPE_SD, is_retry, is_close, con_retrycnt, con_waittime)){ + // success + return true; + } + if(!is_retry){ + break; + } + } + ERR_CHMPRN("Failed to shutdown SSL for sock(%d).", sock); + + return false; +} + +//--------------------------------------------------------- +// Class Methods +//--------------------------------------------------------- +bool ChmEventSock::SetNonblocking(int sock) +{ + if(CHM_INVALID_SOCK == sock){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + int flags = fcntl(sock, F_GETFL, 0); + if(0 != (flags & O_NONBLOCK)){ + //MSG_CHMPRN("sock(%d) already set nonblocking.", sock); + return true; + } + if(-1 == fcntl(sock, F_SETFL, flags | O_NONBLOCK)){ + ERR_CHMPRN("Could not set nonblocking flag to sock(%d), errno=%d.", sock, errno); + return false; + } + return true; +} + +int ChmEventSock::WaitForReady(int sock, int type, int retrycnt, bool is_check_so_error, suseconds_t waittime) +{ + // [NOTE] For perforamnce + // signal mask does not change after launching processes. + // (because it is static member in ChmSignalCntrl) + // So, we duplicate it's value as static value in this method. + // And we do not lock at initializing this value. + // + static sigset_t sigset; + static bool is_sigset_init = false; + if(!is_sigset_init){ + ChmSigCntrl sigcntrl; + if(!sigcntrl.GetSignalMask(sigset)){ + ERR_CHMPRN("Could not get signal mask value."); + return EPERM; + } + is_sigset_init = true; + } + + if(CHM_INVALID_SOCK == sock || !ISSAFE_WAIT_FD(type) || (CHMEVENTSOCK_RETRY_DEFAULT != retrycnt && retrycnt < 0)){ + ERR_CHMPRN("Parameters are wrong."); + return EPERM; + } + + if(CHMEVENTSOCK_RETRY_DEFAULT == retrycnt){ + retrycnt = ChmEventSock::DEFAULT_RETRYCNT; + waittime = ChmEventSock::DEFAULT_WAIT_SOCKET; + } + + // setup fds for ppoll + struct pollfd fds; + { + fds.fd = sock; + fds.events = (IS_WAIT_READ_FD(type) ? (POLLIN | POLLPRI | POLLRDHUP) : 0) | (IS_WAIT_WRITE_FD(type) ? (POLLOUT | POLLRDHUP) : 0); + fds.revents = 0; + } + struct timespec ts; + int cnt; + + for(cnt = 0; cnt < (retrycnt + 1); cnt++){ + SET_TIMESPEC(&ts, 0, (waittime * 1000)); // if waittime is default(CHMEVENTSOCK_TIMEOUT_DEFAULT), no sleeping. + + // check ready + int rtcode = ppoll(&fds, 1, &ts, &sigset); + if(-1 == rtcode){ + if(EINTR == errno){ + //MSG_CHMPRN("Waiting connected sock(%d) result is -1(and EINTR), try again.", sock); + continue; + } + // something error + MSG_CHMPRN("Failed to wait fd up for sock=%d by errno=%d.", sock, errno); + return errno; + + }else if(0 != rtcode){ + if(is_check_so_error){ + int conerr = 0; + socklen_t length = sizeof(int); + if(0 > getsockopt(sock, SOL_SOCKET, SO_ERROR, reinterpret_cast(&conerr), &length)){ + MSG_CHMPRN("Failed to getsockopt for sock(%d), errno=%d.", sock, errno); + return errno; + } + if(EINPROGRESS == conerr || EINTR == conerr){ + MSG_CHMPRN("Waiting connected sock(%d) result is errno=%d, but this error needs to wait again.", sock, conerr); + continue; + }else if(0 != conerr){ + MSG_CHMPRN("Waiting connected sock(%d) result is errno=%d", sock, conerr); + return conerr; + } + // OK + } + + // [NOTE] + // we do not check POLLERR, POLLNVAL and POLLHUP status. + // These are not an error since it is one of the factors for canceling the wait state. + // And if socket status is somthing wrong, probabry it can be caught after return this + // method. + // + + //MSG_CHMPRN("Succeed to wait up for connected sock=%d by rtcode=%d.", sock, rtcode); + return 0; + } + // timeout -> retry + //MSG_CHMPRN("Waiting connected sock(%d) result is ETIMEOUT(%ldus), try again.", sock, waittime); + } + //MSG_CHMPRN("Waiting connected sock(%d) result is ETIMEOUT(%ldus * %d).", sock, waittime, retrycnt); + + return ETIMEDOUT; +} + +int ChmEventSock::Listen(const char* hostname, short port) +{ + if(CHM_INVALID_PORT == port){ + ERR_CHMPRN("Parameters are wrong."); + return CHM_INVALID_SOCK; + } + + int sockfd = CHM_INVALID_SOCK; + if(CHMEMPTYSTR(hostname)){ + struct addrinfo* paddrinfo = NULL; + // First, test for INET6 + if(!ChmNetDb::GetAnyAddrInfo(port, &paddrinfo, true)){ + WAN_CHMPRN("Failed to get IN6ADDR_ANY_INIT addrinfo, but continue for INADDR_ANY."); + }else{ + sockfd = ChmEventSock::RawListen(paddrinfo); + freeaddrinfo(paddrinfo); + } + // check + if(CHM_INVALID_SOCK == sockfd){ + // failed to bind/listen by INET6, so try INET4 + if(!ChmNetDb::GetAnyAddrInfo(port, &paddrinfo, false)){ + ERR_CHMPRN("Failed to get INADDR_ANY addrinfo, so both IN6ADDR_ANY_INIT and INADDR_ANY are failed."); + return CHM_INVALID_SOCK; + } + sockfd = ChmEventSock::RawListen(paddrinfo); + freeaddrinfo(paddrinfo); + } + + }else{ + struct addrinfo* paddrinfo = NULL; + if(!ChmNetDb::Get()->GetAddrInfo(hostname, port, &paddrinfo, true)){ // if "localhost", convert fqdn. + ERR_CHMPRN("Failed to get addrinfo for %s:%d.", hostname, port); + return CHM_INVALID_SOCK; + } + sockfd = ChmEventSock::RawListen(paddrinfo); + freeaddrinfo(paddrinfo); + } + + if(CHM_INVALID_SOCK == sockfd){ + ERR_CHMPRN("Could not make socket and listen on %s:%d.", CHMEMPTYSTR(hostname) ? "ADDR_ANY" : hostname, port); + return CHM_INVALID_SOCK; + } + return sockfd; +} + +int ChmEventSock::RawListen(struct addrinfo* paddrinfo) +{ + const int opt_yes = 1; + + if(!paddrinfo){ + ERR_CHMPRN("Parameter is wrong."); + return CHM_INVALID_SOCK; + } + + // make socket, bind, listen + int sockfd = CHM_INVALID_SOCK; + for(struct addrinfo* ptmpaddrinfo = paddrinfo; ptmpaddrinfo && CHM_INVALID_SOCK == sockfd; ptmpaddrinfo = ptmpaddrinfo->ai_next){ + if(IPPROTO_TCP != ptmpaddrinfo->ai_protocol){ + MSG_CHMPRN("protocol in addrinfo does not TCP, so check next addrinfo..."); + continue; + } + // socket + if(-1 == (sockfd = socket(ptmpaddrinfo->ai_family, ptmpaddrinfo->ai_socktype, ptmpaddrinfo->ai_protocol))){ + ERR_CHMPRN("Failed to make socket by errno=%d, but continue to make next addrinfo...", errno); + // sockfd = CHM_INVALID_SOCK; + continue; + } + // sockopt(keepalive, etc) + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&opt_yes), sizeof(int)); +#ifdef SO_REUSEPORT + setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast(&opt_yes), sizeof(int)); +#endif + setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast(&opt_yes), sizeof(int)); + setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&opt_yes), sizeof(int)); + setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, reinterpret_cast(&ChmEventSock::DEFAULT_KEEPIDLE), sizeof(int)); + setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, reinterpret_cast(&ChmEventSock::DEFAULT_KEEPINTERVAL), sizeof(int)); + setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, reinterpret_cast(&ChmEventSock::DEFAULT_KEEPCOUNT), sizeof(int)); + ChmEventSock::SetNonblocking(sockfd); // NONBLOCKING + + // bind + if(-1 == bind(sockfd, ptmpaddrinfo->ai_addr, ptmpaddrinfo->ai_addrlen)){ + ERR_CHMPRN("Failed to bind by errno=%d, but continue to make next addrinfo...", errno); + CHM_CLOSESOCK(sockfd); + continue; + } + + // listen + if(-1 == listen(sockfd, ChmEventSock::DEFAULT_LISTEN_BACKLOG)){ + ERR_CHMPRN("Failed to listen by errno=%d, but continue to make next addrinfo...", errno); + CHM_CLOSESOCK(sockfd); + continue; + } + } + + if(CHM_INVALID_SOCK == sockfd){ + ERR_CHMPRN("Could not make socket and listen."); + return CHM_INVALID_SOCK; + } + return sockfd; +} + +int ChmEventSock::Connect(const char* hostname, short port, bool is_blocking, int con_retrycnt, suseconds_t con_waittime) +{ + const int opt_yes = 1; + + if(CHMEMPTYSTR(hostname) || CHM_INVALID_PORT == port){ + ERR_CHMPRN("Parameters are wrong."); + return CHM_INVALID_SOCK; + } + + // Get addrinfo + struct addrinfo* paddrinfo = NULL; + if(!ChmNetDb::Get()->GetAddrInfo(hostname, port, &paddrinfo, true)){ // if "localhost", convert fqdn. + ERR_CHMPRN("Failed to get addrinfo for %s:%d.", hostname, port); + return CHM_INVALID_SOCK; + } + + // make socket, bind, listen + int sockfd = CHM_INVALID_SOCK; + for(struct addrinfo* ptmpaddrinfo = paddrinfo; ptmpaddrinfo && CHM_INVALID_SOCK == sockfd; ptmpaddrinfo = ptmpaddrinfo->ai_next){ + if(IPPROTO_TCP != ptmpaddrinfo->ai_protocol){ + MSG_CHMPRN("protocol in addrinfo which is made from %s:%d does not TCP, so check next addrinfo...", hostname, port); + continue; + } + // socket + if(-1 == (sockfd = socket(ptmpaddrinfo->ai_family, ptmpaddrinfo->ai_socktype, ptmpaddrinfo->ai_protocol))){ + ERR_CHMPRN("Failed to make socket for %s:%d by errno=%d, but continue to make next addrinfo...", hostname, port, errno); + // sockfd = CHM_INVALID_SOCK; + continue; + } + + // options + setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast(&opt_yes), sizeof(int)); + setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&opt_yes), sizeof(int)); + setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, reinterpret_cast(&ChmEventSock::DEFAULT_KEEPIDLE), sizeof(int)); + setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, reinterpret_cast(&ChmEventSock::DEFAULT_KEEPINTERVAL), sizeof(int)); + setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, reinterpret_cast(&ChmEventSock::DEFAULT_KEEPCOUNT), sizeof(int)); + if(!is_blocking){ + ChmEventSock::SetNonblocking(sockfd); // NONBLOCKING + } + + // connect + if(-1 == connect(sockfd, ptmpaddrinfo->ai_addr, ptmpaddrinfo->ai_addrlen)){ + if(is_blocking || EINPROGRESS != errno){ + ERR_CHMPRN("Failed to connect for %s:%d by errno=%d, but continue to make next addrinfo...", hostname, port, errno); + CHM_CLOSESOCK(sockfd); + continue; + }else{ + // wait connected...(non blocking & EINPROGRESS) + int werr = ChmEventSock::WaitForReady(sockfd, WAIT_WRITE_FD, con_retrycnt, true, con_waittime); // check SO_ERROR + if(0 != werr){ + MSG_CHMPRN("Failed to connect for %s:%d by errno=%d.", hostname, port, werr); + CHM_CLOSESOCK(sockfd); + continue; + } + } + } + if(CHM_INVALID_SOCK != sockfd){ + break; + } + } + freeaddrinfo(paddrinfo); + + if(CHM_INVALID_SOCK == sockfd){ + MSG_CHMPRN("Could not make socket and connect %s:%d.", hostname, port); + } + return sockfd; +} + +// [TODO] +// In most case, pComPkt is allocated memory. But in case of large data it should +// be able to allocate memory by shared memory. +// +bool ChmEventSock::RawSend(int sock, SSL* ssl, PCOMPKT pComPkt, bool& is_closed, bool is_blocking, int retrycnt, suseconds_t waittime) +{ + if(CHM_INVALID_SOCK == sock || !pComPkt){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + DUMPCOM_COMPKT_TYPE("Sock::RawSend", pComPkt); + DUMPCOM_COMPKT("Sock::RawSend", pComPkt); + + // convert hton + size_t length = 0L; + if(!ChmEventSock::hton(pComPkt, length)){ + ERR_CHMPRN("Failed to convert packet by hton."); + return false; + } + //DUMPCOM_COMPKT("Sock::RawSend", pComPkt); + + if(length <= 0L){ + ERR_CHMPRN("Length(%zu) in PCOMPKT is wrong.", length); + return false; + } + unsigned char* pbydata = reinterpret_cast(pComPkt); + + // send + return ChmEventSock::RawSend(sock, ssl, pbydata, length, is_closed, is_blocking, retrycnt, waittime); +} + +bool ChmEventSock::RawSend(int sock, SSL* ssl, const unsigned char* pbydata, size_t length, bool& is_closed, bool is_blocking, int retrycnt, suseconds_t waittime) +{ + if(CHM_INVALID_SOCK == sock || !pbydata || length <= 0L){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + + // send + ssize_t onesent = 0; + size_t totalsent; + bool is_retry = true; + for(totalsent = 0; totalsent < length; totalsent += static_cast(onesent)){ + if(ssl){ + // SSL + onesent = 0; + is_retry = true; + long write_result= SSL_write(ssl, &pbydata[totalsent], length - totalsent); + if(ChmEventSock::CheckResultSSL(sock, ssl, write_result, CHKRESULTSSL_TYPE_RW, is_retry, is_closed, retrycnt, waittime)){ + // success + if(0 < write_result){ + onesent = static_cast(write_result); + } + }else{ + if(!is_retry){ + // If the socket is closed, it occures notification. so nothing to do here. + ERR_CHMPRN("Failed to write from SSL on sock(%d), and the socket is %s.", sock, (is_closed ? "closed" : "not closed")); + return false; + } + } + }else{ + // Not SSL + if(!is_blocking){ + int werr = ChmEventSock::WaitForReady(sock, WAIT_WRITE_FD, retrycnt, false, waittime); // not check SO_ERROR + if(0 != werr){ + ERR_CHMPRN("Failed to send PCOMPKT(length:%zu), because failed to wait ready for sending on sock(%d) by errno=%d.", length, sock, werr); + if(ETIMEDOUT != werr){ + is_closed = true; + } + return false; + } + } + + if(-1 == (onesent = send(sock, &pbydata[totalsent], length - totalsent, is_blocking ? 0 : MSG_DONTWAIT))){ + if(EINTR == errno){ + // retry assap + MSG_CHMPRN("Interapted signal during sending to sock(%d), errno=%d(EINTR).", sock, errno); + + }else if(EAGAIN == errno || EWOULDBLOCK == errno){ + // wait(non blocking) + MSG_CHMPRN("sock(%d) does not ready for sending, errno=%d(EAGAIN or EWOULDBLOCK).", sock, errno); + + }else if(EACCES == errno || EBADF == errno || ECONNRESET == errno || ENOTCONN == errno || EDESTADDRREQ == errno || EISCONN == errno || ENOTSOCK == errno){ + // something error to closing + ERR_CHMPRN("sock(%d) does not ready for sending, errno=%d(EACCES or EBADF or ECONNRESET or ENOTCONN or EDESTADDRREQ or EISCONN or ENOTSOCK).", sock, errno); + is_closed = true; + return false; + + }else{ + // failed + ERR_CHMPRN("Failed to send PCOMPKT(length:%zu), errno=%d.", length, errno); + return false; + } + // continue... + onesent = 0; + } + } + } + return true; +} + +// [TODO] +// For large data case, pbuff is shared memory instead of allocated memory. +// +bool ChmEventSock::RawReceiveByte(int sock, SSL* ssl, bool& is_closed, unsigned char* pbuff, size_t length, bool is_blocking, int retrycnt, suseconds_t waittime) +{ + if(CHM_INVALID_SOCK == sock || !pbuff || 0 == length){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + is_closed = false; + + // receive + ssize_t onerecv = 0; + size_t totalrecv; + bool is_retry = true; + for(totalrecv = 0; totalrecv < length; totalrecv += static_cast(onerecv)){ + if(ssl){ + // SSL + onerecv = 0; + is_retry = true; + long read_result = SSL_read(ssl, &pbuff[totalrecv], length - totalrecv); + if(ChmEventSock::CheckResultSSL(sock, ssl, read_result, CHKRESULTSSL_TYPE_RW, is_retry, is_closed, retrycnt, waittime)){ + // success + if(0 < read_result){ + onerecv = static_cast(read_result); + } + }else{ + if(!is_retry){ + // If the socket is closed, it occures notification. so nothing to do here. + ERR_CHMPRN("Failed to receive from SSL on sock(%d), and the socket is %s.", sock, (is_closed ? "closed" : "not closed")); + return false; + } + } + }else{ + // Not SSL + if(!is_blocking){ + int werr = ChmEventSock::WaitForReady(sock, WAIT_READ_FD, retrycnt, false, waittime); // not check SO_ERROR + if(0 != werr){ + ERR_CHMPRN("Failed to receive from sock(%d), because failed to wait ready for receiving, errno=%d.", sock, werr); + if(ETIMEDOUT != werr){ + is_closed = true; + } + return false; + } + } + + if(-1 == (onerecv = recv(sock, &pbuff[totalrecv], length - totalrecv, is_blocking ? 0 : MSG_DONTWAIT))){ + if(EINTR == errno){ + // retry assap + MSG_CHMPRN("Interapted signal during receiving from sock(%d), errno=%d(EINTR).", sock, errno); + + }else if(EAGAIN == errno || EWOULDBLOCK == errno){ + // wait(non blocking) + MSG_CHMPRN("There are no received data on sock(%d), errno=%d(EAGAIN or EWOULDBLOCK).", sock, errno); + + }else if(EBADF == errno || ECONNREFUSED == errno || ENOTCONN == errno || ENOTSOCK == errno){ + // error for closing + ERR_CHMPRN("There are no received data on sock(%d), errno=%d(EBADF or ECONNREFUSED or ENOTCONN or ENOTSOCK).", sock, errno); + is_closed = true; + return false; + + }else{ + // failed + ERR_CHMPRN("Failed to receive from sock(%d), errno=%d.", sock, errno); + return false; + } + // continue... + onerecv = 0; + + }else if(0 == onerecv){ + // close sock + // + // [NOTICE] + // We do not specify EPOLLRDHUP for epoll, then we know the socket closed by receive length = 0. + // Because case of specified EPOLLRDHUP, there is possibility of a problem that the packet just + // before FIN is replaced with FIN. + // + MSG_CHMPRN("Receive 0 byte from sock(%d), it means socket is closed.", sock); + is_closed = true; + return false; + } + } + } + return true; +} + +bool ChmEventSock::RawReceiveAny(int sock, bool& is_closed, unsigned char* pbuff, size_t* plength, bool is_blocking, int retrycnt, suseconds_t waittime) +{ + if(CHM_INVALID_SOCK == sock || !pbuff || !plength){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + is_closed = false; + + // receive + ssize_t onerecv; + while(true){ + if(!is_blocking){ + int werr = ChmEventSock::WaitForReady(sock, WAIT_READ_FD, retrycnt, false, waittime); // not check SO_ERROR + if(0 != werr){ + ERR_CHMPRN("Failed to receive from sock(%d), because failed to wait ready for receiving, errno=%d.", sock, werr); + if(ETIMEDOUT != werr){ + is_closed = true; + } + return false; + } + } + + if(-1 == (onerecv = recv(sock, pbuff, *plength, is_blocking ? 0 : MSG_DONTWAIT))){ + if(EINTR == errno){ + // retry + MSG_CHMPRN("Interapted signal during receiving from sock(%d), errno=%d(EINTR).", sock, errno); + + }else if(EAGAIN == errno || EWOULDBLOCK == errno){ + // no data(return assap) + MSG_CHMPRN("There are no received data on sock(%d), so not wait. errno=%d(EAGAIN or EWOULDBLOCK).", sock, errno); + *plength = 0L; + break; + + }else if(EBADF == errno || ECONNREFUSED == errno || ENOTCONN == errno || ENOTSOCK == errno){ + // error for closing + ERR_CHMPRN("There are no received data on sock(%d), errno=%d(EBADF or ECONNREFUSED or ENOTCONN or ENOTSOCK).", sock, errno); + is_closed = true; + return false; + + }else{ + // failed + ERR_CHMPRN("Failed to receive from sock(%d), errno=%d.", sock, errno); + return false; + } + + }else if(0 == onerecv){ + // close sock + // + // [NOTICE] + // We do not specify EPOLLRDHUP for epoll, then we know the socket closed by receive *plength = 0. + // Because case of specified EPOLLRDHUP, there is possibility of a problem that the packet just + // before FIN is replaced with FIN. + // + ERR_CHMPRN("Receive 0 byte from sock(%d), it means socket is closed.", sock); + is_closed = true; + return false; + + }else{ + // read some bytes. + *plength = static_cast(onerecv); + break; + } + } + return true; +} + +// [NOTICE] +// If *ppComPkt is NULL, this function returns *ppComPkt to allocated memory. +// The other(not NULL), this function set received data into *ppComPkt. Then pktlength +// means *ppComPkt buffer length. +// +bool ChmEventSock::RawReceive(int sock, SSL* ssl, bool& is_closed, PCOMPKT* ppComPkt, size_t pktlength, bool is_blocking, int retrycnt, suseconds_t waittime) +{ + if(CHM_INVALID_SOCK == sock || !ppComPkt){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + is_closed = false; + + // At first, read only COMPKT + COMPKT ComPkt; + unsigned char* pbyComPkt = reinterpret_cast(&ComPkt); + if(!ChmEventSock::RawReceiveByte(sock, ssl, is_closed, pbyComPkt, sizeof(COMPKT), is_blocking, retrycnt, waittime)){ + if(is_closed){ + MSG_CHMPRN("Failed to receive only COMPKT from sock(%d), socket is closed.", sock); + }else{ + ERR_CHMPRN("Failed to receive only COMPKT from sock(%d), socket is not closed.", sock); + } + return false; + } + //DUMPCOM_COMPKT("Sock::RawReceive", &ComPkt); + + // get remaining length + size_t totallength = 0L; + { + // ntoh + NTOH_PCOMPKT(&ComPkt); + if(ComPkt.length < sizeof(COMPKT)){ + ERR_CHMPRN("The packet length(%zu) in resieved COMPKT from sock(%d) is too short, should be %zu byte.", ComPkt.length, sock, sizeof(COMPKT)); + return false; + } + totallength = ComPkt.length; + } + DUMPCOM_COMPKT("Sock::RawReceive", &ComPkt); + + // build buffer & copy COMPKT + unsigned char* pbyall; + bool is_alloc = false; + if(NULL == *ppComPkt){ + if(NULL == (pbyall = reinterpret_cast(malloc(totallength)))){ + ERR_CHMPRN("Coult not allocate memory(size=%zu)", totallength); + return false; + } + *ppComPkt = reinterpret_cast(pbyall); + is_alloc = true; + }else{ + if(pktlength < totallength){ + ERR_CHMPRN("Buffer length(%zu) is too small, receiving length is %zu, so COULD NOT READ REMAINING DATA.", pktlength, totallength); + return false; + } + pbyall = reinterpret_cast(*ppComPkt); + } + memcpy(pbyall, pbyComPkt, sizeof(COMPKT)); + + // Read remaining data + if(!ChmEventSock::RawReceiveByte(sock, ssl, is_closed, &pbyall[sizeof(COMPKT)], totallength - sizeof(COMPKT), is_blocking, retrycnt, waittime)){ + if(is_closed){ + MSG_CHMPRN("Failed to receive after COMPKT from sock(%d), socket is closed.", sock); + }else{ + ERR_CHMPRN("Failed to receive after COMPKT from sock(%d), socket is not closed.", sock); + } + if(is_alloc){ + CHM_Free(pbyall); + *ppComPkt = NULL; + } + return false; + } + + // ntoh + if(!ChmEventSock::ntoh(*ppComPkt, true)){ // Already convert COMPKT + ERR_CHMPRN("Failed to convert packet by ntoh."); + if(is_alloc){ + CHM_Free(pbyall); + *ppComPkt = NULL; + } + return false; + } + DUMPCOM_COMPKT_TYPE("Sock::RawReceive", *ppComPkt); + + return true; +} + +// +// [NOTE] +// This method opens NEW control socket, so we do not need to lock socket for sending/receiving. +// BUT we must lock sockfd_lockval because this opens new socket. +// +bool ChmEventSock::RawSendCtlPort(const char* hostname, short ctlport, const unsigned char* pbydata, size_t length, string& strResult, volatile int& sockfd_lockval, int retrycnt, suseconds_t waittime, int con_retrycnt, suseconds_t con_waittime) +{ + strResult = ""; + + if(CHMEMPTYSTR(hostname) || CHM_INVALID_PORT == ctlport || !pbydata || 0L == length){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + // try to connect to ctlport + int ctlsock; + while(!fullock::flck_trylock_noshared_mutex(&sockfd_lockval)); // LOCK + if(CHM_INVALID_SOCK == (ctlsock = ChmEventSock::Connect(hostname, ctlport, false, con_retrycnt, con_waittime))){ + // could not connect other server ctlport + ERR_CHMPRN("Could not connect to %s:%d.", hostname, ctlport); + fullock::flck_unlock_noshared_mutex(&sockfd_lockval); // UNLOCK + return false; + } + MSG_CHMPRN("Connected to %s:%d, then transfer command.", hostname, ctlport); + fullock::flck_unlock_noshared_mutex(&sockfd_lockval); // UNLOCK + + // send(does not lock for control socket) + bool is_closed = false; + if(!ChmEventSock::RawSend(ctlsock, NULL, pbydata, length, is_closed, false, retrycnt, waittime)){ + ERR_CHMPRN("Could not send to %s:%d(sock:%d).", hostname, ctlport, ctlsock); + if(!is_closed){ + CHM_CLOSESOCK(ctlsock); + } + return false; + } + + // receive + char szReceive[CTL_RECEIVE_MAX_LENGTH]; + size_t RecLength = CTL_RECEIVE_MAX_LENGTH - 1; + memset(szReceive, 0, CTL_RECEIVE_MAX_LENGTH); + + if(!ChmEventSock::RawReceiveAny(ctlsock, is_closed, reinterpret_cast(szReceive), &RecLength, false, retrycnt * 10, waittime)){ // wait 10 times by normal + ERR_CHMPRN("Failed to receive data from ctlport ctlsock(%d), ctlsock is %s.", ctlsock, is_closed ? "closed" : "not closed"); + if(!is_closed){ + CHM_CLOSESOCK(ctlsock); + } + return false; + } + CHM_CLOSESOCK(ctlsock); + + strResult = szReceive; + return true; +} + +bool ChmEventSock::hton(PCOMPKT pComPkt, size_t& length) +{ + if(!pComPkt){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + // backup + comtype_t type = pComPkt->head.type; + off_t offset = pComPkt->offset; + length = pComPkt->length; + + // hton + HTON_PCOMPKT(pComPkt); + + if(COM_PX2PX == type){ + // following datas + PPXCOM_ALL pComPacket = CHM_OFFSET(pComPkt, offset, PPXCOM_ALL); + + // backup + pxcomtype_t comtype = pComPacket->val_head.type; + //size_t comlength = pComPacket->val_head.length; // unused now + + // hton by type + if(CHMPX_COM_STATUS_REQ == comtype){ + PPXCOM_STATUS_REQ pComContents = CVT_COMPTR_STATUS_REQ(pComPacket); + HTON_PPXCOM_STATUS_REQ(pComContents); + + }else if(CHMPX_COM_STATUS_RES == comtype){ + PPXCOM_STATUS_RES pComContents = CVT_COMPTR_STATUS_RES(pComPacket); + // hton each chmpxsvr + PCHMPXSVR pChmpxsvr = CHM_OFFSET(pComContents, pComContents->pchmpxsvr_offset, PCHMPXSVR); + for(long cnt = 0; cnt < pComContents->count; cnt++){ + HTON_PCHMPXSVR(&pChmpxsvr[cnt]); + } + HTON_PPXCOM_STATUS_RES(pComContents); + + }else if(CHMPX_COM_CONINIT_REQ == comtype){ + PPXCOM_CONINIT_REQ pComContents = CVT_COMPTR_CONINIT_REQ(pComPacket); + HTON_PPXCOM_CONINIT_REQ(pComContents); + + }else if(CHMPX_COM_CONINIT_RES == comtype){ + PPXCOM_CONINIT_RES pComContents = CVT_COMPTR_CONINIT_RES(pComPacket); + HTON_PPXCOM_CONINIT_RES(pComContents); + + }else if(CHMPX_COM_JOIN_RING == comtype){ + PPXCOM_JOIN_RING pComContents = CVT_COMPTR_JOIN_RING(pComPacket); + HTON_PPXCOM_JOIN_RING(pComContents); + + }else if(CHMPX_COM_STATUS_UPDATE == comtype){ + PPXCOM_STATUS_UPDATE pComContents = CVT_COMPTR_STATUS_UPDATE(pComPacket); + // hton each chmpxsvr + PCHMPXSVR pChmpxsvr = CHM_OFFSET(pComContents, pComContents->pchmpxsvr_offset, PCHMPXSVR); + for(long cnt = 0; cnt < pComContents->count; cnt++){ + HTON_PCHMPXSVR(&pChmpxsvr[cnt]); + } + HTON_PPXCOM_STATUS_UPDATE(pComContents); + + }else if(CHMPX_COM_STATUS_CONFIRM == comtype){ + PPXCOM_STATUS_CONFIRM pComContents = CVT_COMPTR_STATUS_CONFIRM(pComPacket); + // hton each chmpxsvr + PCHMPXSVR pChmpxsvr = CHM_OFFSET(pComContents, pComContents->pchmpxsvr_offset, PCHMPXSVR); + for(long cnt = 0; cnt < pComContents->count; cnt++){ + HTON_PCHMPXSVR(&pChmpxsvr[cnt]); + } + HTON_PPXCOM_STATUS_CONFIRM(pComContents); + + }else if(CHMPX_COM_STATUS_CHANGE == comtype){ + PPXCOM_STATUS_CHANGE pComContents = CVT_COMPTR_STATUS_CHANGE(pComPacket); + HTON_PPXCOM_STATUS_CHANGE(pComContents); + + }else if(CHMPX_COM_MERGE_START == comtype){ + PPXCOM_MERGE_START pComContents = CVT_COMPTR_MERGE_START(pComPacket); + HTON_PPXCOM_MERGE_START(pComContents); + + }else if(CHMPX_COM_MERGE_ABORT == comtype){ + PPXCOM_MERGE_ABORT pComContents = CVT_COMPTR_MERGE_ABORT(pComPacket); + HTON_PPXCOM_MERGE_ABORT(pComContents); + + }else if(CHMPX_COM_MERGE_COMPLETE == comtype){ + PPXCOM_MERGE_COMPLETE pComContents = CVT_COMPTR_MERGE_COMPLETE(pComPacket); + HTON_PPXCOM_MERGE_COMPLETE(pComContents); + + }else if(CHMPX_COM_SERVER_DOWN == comtype){ + PPXCOM_SERVER_DOWN pComContents = CVT_COMPTR_SERVER_DOWN(pComPacket); + HTON_PPXCOM_SERVER_DOWN(pComContents); + + }else if(CHMPX_COM_REQ_UPDATEDATA == comtype){ + PPXCOM_REQ_UPDATEDATA pComContents = CVT_COMPTR_REQ_UPDATEDATA(pComPacket); + // hton each map + PPXCOM_REQ_IDMAP pIdMap = CHM_OFFSET(pComContents, pComContents->plist_offset, PPXCOM_REQ_IDMAP); + for(long cnt = 0; cnt < pComContents->count; cnt++){ + HTON_PPXCOM_REQ_IDMAP(&pIdMap[cnt]); + } + HTON_PPXCOM_REQ_UPDATEDATA(pComContents); + + }else if(CHMPX_COM_RES_UPDATEDATA == comtype){ + PPXCOM_RES_UPDATEDATA pComContents = CVT_COMPTR_RES_UPDATEDATA(pComPacket); + HTON_PPXCOM_RES_UPDATEDATA(pComContents); + + }else if(CHMPX_COM_RESULT_UPDATEDATA == comtype){ + PPXCOM_RESULT_UPDATEDATA pComContents = CVT_COMPTR_RESULT_UPDATEDATA(pComPacket); + HTON_PPXCOM_RESULT_UPDATEDATA(pComContents); + + }else{ + WAN_CHMPRN("ComPacket type is %s(%" PRIu64 ") which is unknown. so does not convert contents.", STRPXCOMTYPE(comtype), comtype); + } + }else if(COM_C2C == type){ + // + // Not implement + // + + }else{ + WAN_CHMPRN("Packet type is %s(%" PRIu64 "), why does send to other chmpx? so does not convert contents.", STRCOMTYPE(type), type); + } + return true; +} + +bool ChmEventSock::ntoh(PCOMPKT pComPkt, bool is_except_compkt) +{ + if(!pComPkt){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + + // ntoh + if(!is_except_compkt){ + NTOH_PCOMPKT(pComPkt); + } + + if(COM_PX2PX == pComPkt->head.type){ + // following datas + PPXCOM_ALL pComPacket = CHM_OFFSET(pComPkt, pComPkt->offset, PPXCOM_ALL); + pxcomtype_t type = be64toh(pComPacket->val_head.type); + + // ntoh by type + if(CHMPX_COM_STATUS_REQ == type){ + PPXCOM_STATUS_REQ pComContents = CVT_COMPTR_STATUS_REQ(pComPacket); + NTOH_PPXCOM_STATUS_REQ(pComContents); + + }else if(CHMPX_COM_STATUS_RES == type){ + PPXCOM_STATUS_RES pComContents = CVT_COMPTR_STATUS_RES(pComPacket); + NTOH_PPXCOM_STATUS_RES(pComContents); + // ntoh each chmpx + PCHMPXSVR pChmpxsvr = CHM_OFFSET(pComContents, pComContents->pchmpxsvr_offset, PCHMPXSVR); + for(long cnt = 0; cnt < pComContents->count; cnt++){ + NTOH_PCHMPXSVR(&pChmpxsvr[cnt]); + } + + }else if(CHMPX_COM_CONINIT_REQ == type){ + PPXCOM_CONINIT_REQ pComContents = CVT_COMPTR_CONINIT_REQ(pComPacket); + NTOH_PPXCOM_CONINIT_REQ(pComContents); + + }else if(CHMPX_COM_CONINIT_RES == type){ + PPXCOM_CONINIT_RES pComContents = CVT_COMPTR_CONINIT_RES(pComPacket); + NTOH_PPXCOM_CONINIT_RES(pComContents); + + }else if(CHMPX_COM_JOIN_RING == type){ + PPXCOM_JOIN_RING pComContents = CVT_COMPTR_JOIN_RING(pComPacket); + NTOH_PPXCOM_JOIN_RING(pComContents); + + }else if(CHMPX_COM_STATUS_UPDATE == type){ + PPXCOM_STATUS_UPDATE pComContents = CVT_COMPTR_STATUS_UPDATE(pComPacket); + NTOH_PPXCOM_STATUS_UPDATE(pComContents); + // ntoh each chmpx + PCHMPXSVR pChmpxsvr = CHM_OFFSET(pComContents, pComContents->pchmpxsvr_offset, PCHMPXSVR); + for(long cnt = 0; cnt < pComContents->count; cnt++){ + NTOH_PCHMPXSVR(&pChmpxsvr[cnt]); + } + + }else if(CHMPX_COM_STATUS_CONFIRM == type){ + PPXCOM_STATUS_CONFIRM pComContents = CVT_COMPTR_STATUS_CONFIRM(pComPacket); + NTOH_PPXCOM_STATUS_CONFIRM(pComContents); + // ntoh each chmpx + PCHMPXSVR pChmpxsvr = CHM_OFFSET(pComContents, pComContents->pchmpxsvr_offset, PCHMPXSVR); + for(long cnt = 0; cnt < pComContents->count; cnt++){ + NTOH_PCHMPXSVR(&pChmpxsvr[cnt]); + } + + }else if(CHMPX_COM_STATUS_CHANGE == type){ + PPXCOM_STATUS_CHANGE pComContents = CVT_COMPTR_STATUS_CHANGE(pComPacket); + NTOH_PPXCOM_STATUS_CHANGE(pComContents); + + }else if(CHMPX_COM_MERGE_START == type){ + PPXCOM_MERGE_START pComContents = CVT_COMPTR_MERGE_START(pComPacket); + NTOH_PPXCOM_MERGE_START(pComContents); + + }else if(CHMPX_COM_MERGE_ABORT == type){ + PPXCOM_MERGE_ABORT pComContents = CVT_COMPTR_MERGE_ABORT(pComPacket); + NTOH_PPXCOM_MERGE_ABORT(pComContents); + + }else if(CHMPX_COM_MERGE_COMPLETE == type){ + PPXCOM_MERGE_COMPLETE pComContents = CVT_COMPTR_MERGE_COMPLETE(pComPacket); + NTOH_PPXCOM_MERGE_COMPLETE(pComContents); + + }else if(CHMPX_COM_SERVER_DOWN == type){ + PPXCOM_SERVER_DOWN pComContents = CVT_COMPTR_SERVER_DOWN(pComPacket); + NTOH_PPXCOM_SERVER_DOWN(pComContents); + + }else if(CHMPX_COM_REQ_UPDATEDATA == type){ + PPXCOM_REQ_UPDATEDATA pComContents = CVT_COMPTR_REQ_UPDATEDATA(pComPacket); + NTOH_PPXCOM_REQ_UPDATEDATA(pComContents); + // ntoh each chmpx + PPXCOM_REQ_IDMAP pIdMap = CHM_OFFSET(pComContents, pComContents->plist_offset, PPXCOM_REQ_IDMAP); + for(long cnt = 0; cnt < pComContents->count; cnt++){ + NTOH_PPXCOM_REQ_IDMAP(&pIdMap[cnt]); + } + + }else if(CHMPX_COM_RES_UPDATEDATA == type){ + PPXCOM_RES_UPDATEDATA pComContents = CVT_COMPTR_RES_UPDATEDATA(pComPacket); + NTOH_PPXCOM_RES_UPDATEDATA(pComContents); + + }else if(CHMPX_COM_RESULT_UPDATEDATA == type){ + PPXCOM_RESULT_UPDATEDATA pComContents = CVT_COMPTR_RESULT_UPDATEDATA(pComPacket); + NTOH_PPXCOM_RESULT_UPDATEDATA(pComContents); + + }else{ + WAN_CHMPRN("ComPacket type is %s(%" PRIu64 ") which is unknown. so does not convert contents.", STRPXCOMTYPE(type), type); + } + }else if(COM_C2C == pComPkt->head.type){ + // + // Not implement + // + + }else{ + WAN_CHMPRN("Packet type is %s(%" PRIu64 "), why does receive from other chmpx? so does not convert contents.", STRCOMTYPE(pComPkt->head.type), pComPkt->head.type); + } + return true; +} + +// +// If ext_length is not PXCOMPKT_AUTO_LENGTH, allocate memory length as +// COMPKT + type + ext_length size. +// +PCOMPKT ChmEventSock::AllocatePxComPacket(pxcomtype_t type, ssize_t ext_length) +{ + ssize_t length = sizeof(COMPKT) + (PXCOMPKT_AUTO_LENGTH == ext_length ? 0L : ext_length); + + if(CHMPX_COM_STATUS_REQ == type){ + length += sizeof(PXCOM_STATUS_REQ); + + }else if(CHMPX_COM_STATUS_RES == type){ + length += sizeof(PXCOM_STATUS_RES); + + }else if(CHMPX_COM_CONINIT_REQ == type){ + length += sizeof(PXCOM_CONINIT_REQ); + + }else if(CHMPX_COM_CONINIT_RES == type){ + length += sizeof(PXCOM_CONINIT_RES); + + }else if(CHMPX_COM_JOIN_RING == type){ + length += sizeof(PXCOM_JOIN_RING); + + }else if(CHMPX_COM_STATUS_UPDATE == type){ + length += sizeof(PXCOM_STATUS_UPDATE); + + }else if(CHMPX_COM_STATUS_CONFIRM == type){ + length += sizeof(PXCOM_STATUS_CONFIRM); + + }else if(CHMPX_COM_STATUS_CHANGE == type){ + length += sizeof(PXCOM_STATUS_CHANGE); + + }else if(CHMPX_COM_MERGE_START == type){ + length += sizeof(PXCOM_MERGE_START); + + }else if(CHMPX_COM_MERGE_ABORT == type){ + length += sizeof(PXCOM_MERGE_ABORT); + + }else if(CHMPX_COM_MERGE_COMPLETE == type){ + length += sizeof(PXCOM_MERGE_COMPLETE); + + }else if(CHMPX_COM_SERVER_DOWN == type){ + length += sizeof(PXCOM_SERVER_DOWN); + + }else if(CHMPX_COM_REQ_UPDATEDATA == type){ + length += sizeof(PXCOM_REQ_UPDATEDATA); + + }else if(CHMPX_COM_RES_UPDATEDATA == type){ + length += sizeof(PXCOM_RES_UPDATEDATA); + + }else if(CHMPX_COM_RESULT_UPDATEDATA == type){ + length += sizeof(PXCOM_RESULT_UPDATEDATA); + + }else{ + WAN_CHMPRN("ComPacket type is %s(%" PRIu64 ") which is unknown. so does not convert contents.", STRPXCOMTYPE(type), type); + } + + PCOMPKT rtnptr; + if(NULL == (rtnptr = reinterpret_cast(malloc(length)))){ + ERR_CHMPRN("Could not allocate memory as %zd length for %s(%" PRIu64 ").", length, STRPXCOMTYPE(type), type); + } + return rtnptr; +} + +PCOMPKT ChmEventSock::DuplicateComPkt(PCOMPKT pComPkt) +{ + if(!pComPkt){ + ERR_CHMPRN("Parameter is wrong."); + return NULL; + } + + // allocation + PCOMPKT pDupPkt; + if(NULL == (pDupPkt = reinterpret_cast(malloc(pComPkt->length)))){ + ERR_CHMPRN("Could not allocate memory as %zu length.", pComPkt->length); + return NULL; + } + + // copy compkt + COPY_COMPKT(pDupPkt, pComPkt); + + // copy after compkt + if(sizeof(COMPKT) < pComPkt->length){ + unsigned char* psrc = reinterpret_cast(pComPkt) + sizeof(COMPKT); + unsigned char* pdest = reinterpret_cast(pDupPkt) + sizeof(COMPKT); + memcpy(pdest, psrc, (pComPkt->length - sizeof(COMPKT))); + } + return pDupPkt; +} + +//--------------------------------------------------------- +// Class Methods - Processing +//--------------------------------------------------------- +bool ChmEventSock::ReceiveWorkerProc(void* common_param, chmthparam_t wp_param) +{ + ChmEventSock* pSockObj = reinterpret_cast(common_param); + int sock = static_cast(wp_param); + if(!pSockObj || CHM_INVALID_SOCK == sock){ + ERR_CHMPRN("Paraemtera are wrong."); + return true; // sleep thread + } + // [NOTE] + // We use edge triggerd epoll, in this type the event is accumulated and not sent when the data to + // the socket is left. Thus we must read more until EAGAIN. Otherwise we lost data. + // So we checked socket for rest data here. + // + bool is_closed; + suseconds_t waittime = pSockObj->sock_wait_time; + int werr; + while(0 == (werr = ChmEventSock::WaitForReady(sock, WAIT_READ_FD, 0, false, waittime))){ // check rest data & return assap + // Processing + is_closed = false; + if(false == pSockObj->RawReceive(sock, is_closed)){ + if(!is_closed){ + ERR_CHMPRN("Failed to receive and to process for sock(%d) by event socket object.", sock); + }else{ + MSG_CHMPRN("sock(%d) is closed while processing thread.", sock); + } + break; + } + } + if((0 != werr && ETIMEDOUT != werr) || is_closed){ + if(!pSockObj->RawNotifyHup(sock)){ + ERR_CHMPRN("Failed to closing socket(%d), but continue...", sock); + } + } + return true; // always return true for continue to work. +} + +//--------------------------------------------------------- +// Class Methods - Merge +//--------------------------------------------------------- +// Merge logic +// +// This merge thread(chmpx server node) +// --- [MQ] -----> get lastest update time(to client on this server node through MQ) +// <-------------- +// +// --- [SOCK] ---> push all update datas to this chmpx(to all other chmpx) +// Other chmpx server node(status is up/servicein/nosuspend) +// --- [MQ] ---> send request of pushing update datas(to client on server node) +// client chmpx library +// --- [CB] ---> loop: get update datas/send it to original chmpx +// <-- [MQ] ---- finish +// Other chmpx server node(status is NOT up/servicein/nosuspend) +// nothing to do +// <-- [SOCK] ---- finish +// +// Wait for all server node finished. +// +// [NOTICE] +// This method is run on another worker thread. +// If need to stop this method, change "is_run_merge" flag to false. +// So that this method exits and return worker thread proc. +// After exiting this method, the worker proc is SLEEP. +// +// "is_run_merge" flag is changed only in main thread, so do not lock it. +// +bool ChmEventSock::MergeWorkerFunc(void* common_param, chmthparam_t wp_param) +{ + if(!common_param){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + ChmEventSock* pThis = reinterpret_cast(common_param); + + if(!pThis->is_do_merge){ + WAN_CHMPRN("Why does this method call when is_do_merge flag is false."); + pThis->is_run_merge = false; + return false; + } + + //--------------------------------- + // Make communication datas + //--------------------------------- + // check target chmpxid map + if(0 == pThis->mergeidmap.size()){ + ERR_CHMPRN("There is no merge chmpxids."); + pThis->is_run_merge = false; + return true; // keep running thread for next request + } + // next chmpxid + chmpxid_t to_chmpxid = pThis->GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == to_chmpxid){ + ERR_CHMPRN("There is no next chmpx server."); + pThis->is_run_merge = false; + return true; // keep running thread for next request + } + // make merge param + PXCOMMON_MERGE_PARAM merge_param; + struct timespec lastts; + // get lastest update time. + if(!pThis->pChmCntrl->MergeGetLastTime(lastts)){ + ERR_CHMPRN("Something error occurred during getting lastest update time."); + pThis->is_run_merge = false; + return true; // keep running thread for next request + } + merge_param.startts.tv_sec = lastts.tv_sec; + merge_param.startts.tv_nsec = lastts.tv_nsec; + + // hash values + ChmIMData* pImData = pThis->pChmCntrl->GetImDataObj(); + chmhash_t tmp_hashval = static_cast(-1); + chmhash_t tmp_max_hashval = static_cast(-1); + merge_param.chmpxid = pImData->GetSelfChmpxId(); + merge_param.replica_count = pImData->GetReplicaCount() + 1; // ImData::Replica count means copy count + merge_param.is_expire_check = true; // always check expire time + + if(!pImData->GetSelfPendingHash(tmp_hashval) || !pImData->GetMaxPendingHashCount(tmp_max_hashval)){ + ERR_CHMPRN("Could not get own hash and pending hash values."); + pThis->is_run_merge = false; + return true; // keep running thread for next request + }else{ + // + // *** modify start hash value and replica count *** + // + // [NOTE] + // Main hash value's data is had the servers which are assigned after main hash value with replica count. + // Conversely, Main hash value's server has another servers data which is pointed by forwarding replica + // count to hash value. Thus we modify start hash value(but do not change replica count(range)) at here. + // For example) + // replica = 2, new hash value = 5, max hash value = 10 ---> start hash = 3, range = 2 + // replica = 2, new hash value = 1, max hash value = 10 ---> start hash = 9, range = 2 + // + if(static_cast(merge_param.replica_count) <= (tmp_hashval + 1)){ + tmp_hashval = (tmp_hashval + 1) - static_cast(merge_param.replica_count); + }else{ + tmp_hashval = (tmp_hashval + tmp_max_hashval + 1) - static_cast(merge_param.replica_count); + } + if((tmp_max_hashval + 1) < static_cast(merge_param.replica_count)){ + // why, but set maximum + merge_param.replica_count = static_cast(tmp_max_hashval + 1); + } + } + merge_param.pending_hash = tmp_hashval; + merge_param.max_pending_hash= tmp_max_hashval; + + // + // [NOTE] + // If this chmpx join at first time to ring, probabry base hash value is ignored. + // (but max base hash is not ignored.) + // + tmp_hashval = static_cast(-1); + tmp_max_hashval = static_cast(-1); + if(!pImData->GetSelfBaseHash(tmp_hashval) || !pImData->GetMaxBaseHashCount(tmp_max_hashval) || tmp_hashval == static_cast(-1) || tmp_max_hashval == static_cast(-1)){ + // base hash value is ignored, maybe this process run and first join to ring. + MSG_CHMPRN("Could not own base hash(or max base hash count), on this case we need to check all hash."); + merge_param.base_hash = static_cast(-1); + merge_param.max_base_hash = static_cast(-1); + }else{ + if(static_cast(merge_param.replica_count) <= (tmp_hashval + 1)){ + tmp_hashval = (tmp_hashval + 1) - static_cast(merge_param.replica_count); + }else{ + tmp_hashval = (tmp_hashval + tmp_max_hashval + 1) - static_cast(merge_param.replica_count); + } + if((tmp_max_hashval + 1) < static_cast(merge_param.replica_count)){ + // why, but set maximum + merge_param.replica_count = static_cast(tmp_max_hashval + 1); + } + merge_param.base_hash = tmp_hashval; + merge_param.max_base_hash = tmp_max_hashval; + } + + //--------------------------------- + // send request to other server node + //--------------------------------- + if(!pThis->PxComSendReqUpdateData(to_chmpxid, &merge_param)){ + ERR_CHMPRN("Failed to send request update datas command."); + pThis->is_run_merge = false; + return true; // keep running thread for next request + } + + //--------------------------------- + // Loop: wait for all chmpx + //--------------------------------- + struct timespec sleeptime = {0, 100 * 1000 * 1000}; // 100ms + bool is_all_finish = false; + while(pThis->is_run_merge && !is_all_finish){ + // check exited + is_all_finish = true; + + while(!fullock::flck_trylock_noshared_mutex(&(pThis->mergeidmap_lockval))); // LOCK + for(mergeidmap_t::const_iterator iter = pThis->mergeidmap.begin(); iter != pThis->mergeidmap.end(); ++iter){ + if(!IS_PXCOM_REQ_UPDATE_RESULT(iter->second)){ + is_all_finish = false; + break; + } + } + fullock::flck_unlock_noshared_mutex(&(pThis->mergeidmap_lockval)); // UNLOCK + + if(pThis->is_run_merge && !is_all_finish){ + // sleep for wait + nanosleep(&sleeptime, NULL); + } + } + if(!pThis->is_run_merge){ + // If stop merging, exit this method immediately. + // Do not change status, changing status is done by main thread which calls abort merging. + return true; + } + + // Done + // change status. + // + if(pThis->is_run_merge){ + if(!pThis->MergeDone()){ + ERR_CHMPRN("Failed to change status \"DONE\"."); + pThis->is_run_merge = false; + return true; // keep running thread for next request + } + } + pThis->is_run_merge = false; + + return true; +} + +//--------------------------------------------------------- +// Class Methods - Lock map +//--------------------------------------------------------- +bool ChmEventSock::ServerSockMapCallback(sock_ids_map_t::iterator& iter, void* psockobj) +{ + ChmEventSock* pSockObj = reinterpret_cast(psockobj); + if(!pSockObj){ + ERR_CHMPRN("Parameter is wrong."); + return true; // do not stop loop. + } + int sock = iter->first; + if(CHM_INVALID_SOCK != sock){ + // close + pSockObj->UnlockSendSock(sock); // UNLOCK SOCK(For safety) + pSockObj->CloseSocketWithEpoll(sock); // not check result + } + + ChmIMData* pImData = pSockObj->pChmCntrl->GetImDataObj(); + if(pImData){ + chmpxid_t chmpxid = pImData->GetChmpxIdBySock(sock, CLOSETG_SERVERS); + if(CHM_INVALID_CHMPXID != chmpxid){ + if(chmpxid != iter->second){ + ERR_CHMPRN("Not same slave chmpxid(0x%016" PRIx64 " : 0x%016" PRIx64 "), by socket in imdata and by parameter", chmpxid, iter->second); + } + // set invalid sock in server list + if(CHM_INVALID_SOCK != sock && !pImData->RemoveServerSock(chmpxid, sock)){ + ERR_CHMPRN("Could not set sock(INVALID) to chmpxid(0x%016" PRIx64 "), but continue...", chmpxid); + } + } + } + return true; +} + +bool ChmEventSock::SlaveSockMapCallback(sock_ids_map_t::iterator& iter, void* psockobj) +{ + ChmEventSock* pSockObj = reinterpret_cast(psockobj); + if(!pSockObj){ + ERR_CHMPRN("Parameter is wrong."); + return true; // do not stop loop. + } + + int sock = iter->first; + if(CHM_INVALID_SOCK != sock){ + // close + pSockObj->UnlockSendSock(sock); // UNLOCK SOCK(For safety) + pSockObj->CloseSocketWithEpoll(sock); // not check result + } + + ChmIMData* pImData = pSockObj->pChmCntrl->GetImDataObj(); + if(pImData){ + chmpxid_t chmpxid = pImData->GetChmpxIdBySock(sock, CLOSETG_SLAVES); + if(CHM_INVALID_CHMPXID != chmpxid && chmpxid == iter->second){ + if(CHM_INVALID_SOCK != sock && !pImData->RemoveSlaveSock(chmpxid, sock)){ + ERR_CHMPRN("Failed to remove slave(0x%016" PRIx64 ") information, but continue...", iter->second); + } + }else{ + ERR_CHMPRN("Not same slave chmpxid(0x%016" PRIx64 " : 0x%016" PRIx64 "), by socket in imdata and by parameter", chmpxid, iter->second); + } + } + return true; +} + +bool ChmEventSock::AcceptMapCallback(sock_pending_map_t::iterator& iter, void* psockobj) +{ + ChmEventSock* pSockObj = reinterpret_cast(psockobj); + if(!pSockObj){ + ERR_CHMPRN("Parameter is wrong."); + return true; // do not stop loop. + } + // close + pSockObj->UnlockSendSock(iter->first); // UNLOCK SOCK(For safety) + pSockObj->CloseSocketWithEpoll(iter->first); // not check result + + return true; +} + +bool ChmEventSock::ControlSockMapCallback(sock_ids_map_t::iterator& iter, void* psockobj) +{ + ChmEventSock* pSockObj = reinterpret_cast(psockobj); + if(!pSockObj){ + ERR_CHMPRN("Parameter is wrong."); + return true; // do not stop loop. + } + // close + pSockObj->UnlockSendSock(iter->first); // UNLOCK SOCK(For safety) + pSockObj->CloseSocketWithEpoll(iter->first); // not check result + + return true; +} + +bool ChmEventSock::SslSockMapCallback(sock_ssl_map_t::iterator& iter, void* psockobj) +{ + ChmEventSock* pSockObj = reinterpret_cast(psockobj); + if(!pSockObj){ + ERR_CHMPRN("Parameter is wrong."); + return true; // do not stop loop. + } + int sock = iter->first; + SSL* pssl = iter->second; + + pSockObj->UnlockSendSock(sock); // UNLOCK SOCK + + if(pssl){ + ChmEventSock::ShutdownSSL(sock, pssl, pSockObj->con_retry_count, pSockObj->con_wait_time); + SSL_free(pssl); + } + return true; +} + +bool ChmEventSock::SendLockMapCallback(sendlockmap_t::iterator& iter, void* psockobj) +{ + ChmEventSock* pSockObj = reinterpret_cast(psockobj); + if(!pSockObj){ + ERR_CHMPRN("Parameter is wrong."); + return true; // do not stop loop. + } + //int sock = iter->first; + PCHMSSSTAT pssstat = iter->second; + CHM_Delete(pssstat); + return true; +} + +//--------------------------------------------------------- +// Methods +//--------------------------------------------------------- +ChmEventSock::ChmEventSock(int eventqfd, ChmCntrl* pcntrl) : + ChmEventBase(eventqfd, pcntrl), + seversockmap(CHM_INVALID_CHMPXID, ChmEventSock::ServerSockMapCallback, this), + slavesockmap(CHM_INVALID_CHMPXID, ChmEventSock::SlaveSockMapCallback, this), + acceptingmap(string(""), ChmEventSock::AcceptMapCallback, this), + ctlsockmap(CHM_INVALID_CHMPXID, ChmEventSock::ControlSockMapCallback, this), + sslmap(NULL, ChmEventSock::SslSockMapCallback, this), + sendlockmap(NULL, ChmEventSock::SendLockMapCallback, this), + svr_sslctx(NULL), slv_sslctx(NULL), is_do_merge(false), is_auto_merge(false), is_run_merge(false), + procthreads(CHMEVSOCK_SOCK_THREAD_NAME), mergethread(CHMEVSOCK_MERGE_THREAD_NAME), + sockfd_lockval(FLCK_NOSHARED_MUTEX_VAL_UNLOCKED), last_check_time(time(NULL)), dyna_sockfd_lockval(FLCK_NOSHARED_MUTEX_VAL_UNLOCKED), + mergeidmap_lockval(FLCK_NOSHARED_MUTEX_VAL_UNLOCKED), is_server_mode(false), max_sock_pool(ChmEventSock::DEFAULT_MAX_SOCK_POOL), + sock_pool_timeout(ChmEventSock::DEFAULT_SOCK_POOL_TIMEOUT), sock_retry_count(ChmEventSock::DEFAULT_RETRYCNT), + con_retry_count(ChmEventSock::DEFAULT_RETRYCNT_CONNECT), sock_wait_time(ChmEventSock::DEFAULT_WAIT_SOCKET), + con_wait_time(ChmEventSock::DEFAULT_WAIT_CONNECT) +{ + assert(pChmCntrl); + + // first initialize cache value and threads + // + // [NOTE] + // ChmEventSock must be initialized after initialize ChmIMData + // + if(!UpdateInternalData()){ + ERR_CHMPRN("Could not initialize cache data for ImData, but continue..."); + } +} + +ChmEventSock::~ChmEventSock() +{ + // clear all + Clean(); +} + +bool ChmEventSock::Clean(void) +{ + if(IsEmpty()){ + return true; + } + // exit merge thread + is_run_merge = false; + if(!mergethread.ExitAllThreads()){ + ERR_CHMPRN("Failed to exit thread for merging."); + } + // exit processing thread + if(!procthreads.ExitAllThreads()){ + ERR_CHMPRN("Failed to exit thread for processing."); + } + // close sockets + if(!CloseSelfSocks()){ + ERR_CHMPRN("Failed to close self sock and ctlsock, but continue..."); + } + if(!CloseFromSlavesSocks()){ + ERR_CHMPRN("Failed to close connection from slaves, but continue..."); + } + if(!CloseToServersSocks()){ + ERR_CHMPRN("Failed to close connection to other servers, but continue..."); + } + if(!CloseCtlportClientSocks()){ + ERR_CHMPRN("Failed to close control port connection from clients, but continue..."); + } + if(!CloseFromSlavesAcceptingSocks()){ + ERR_CHMPRN("Failed to close accepting connection from slaves, but continue..."); + } + // Here, there are no SSL connection, but check it. + CloseAllSSL(); + + // SSL contexts + if(svr_sslctx){ + SSL_CTX_free(svr_sslctx); + svr_sslctx = NULL; + } + if(slv_sslctx){ + SSL_CTX_free(slv_sslctx); + slv_sslctx = NULL; + } + + // all socket lock are freed + sendlockmap.clear(); // sendlockmap does not have default cb function & not call cb here --> do nothing + + return ChmEventBase::Clean(); +} + +// +// initialize/reinitialize cache value and threads +// +bool ChmEventSock::UpdateInternalData(void) +{ + ChmIMData* pImData; + if(!pChmCntrl || NULL == (pImData = pChmCntrl->GetImDataObj())){ + ERR_CHMPRN("Object is not pre-initialized."); + return false; + } + + // + // If random mode, set not do merging and automatically merge start. + // If hash(not random) mode, set do merge and manually merge start. + // + // [NOTE] BE CAREFUL + // In configuration the auto merge flag at random mode MUST be false, + // but this class needs the flag is true at random mode. + // So convert flag here! + // + bool tmp_is_auto_merge = pImData->IsRandomDeliver() ? true : pImData->IsAutoMergeConf(); + bool tmp_is_do_merge = pImData->IsRandomDeliver() ? false : pImData->IsDoMergeConf(); + + // merge thread + if(!tmp_is_do_merge && is_run_merge){ + // now work to merge, but we do do not stop it. + WAN_CHMPRN("Re-initialize by configration. new do_merge mode is false, but mergeing now. be careful about merging."); + } + if(tmp_is_do_merge && !mergethread.HasThread()){ + // do_merge false to true, we need to run merge thread. + // + // - parameter is this object + // - sleep at starting + // - not at onece(not one shot) + // - sleep after every working + // - keep event count + // + if(!mergethread.CreateThreads(1, ChmEventSock::MergeWorkerFunc, NULL, this, 0, true, false, false, true)){ + ERR_CHMPRN("Failed to create thread for merging on socket, but continue..."); + }else{ + MSG_CHMPRN("start to run merge thread on socket."); + } + }else{ + // [NOTE] + // if new do_merge flag is false and already run thread, but we do not stop it. + } + is_auto_merge = tmp_is_auto_merge; + is_do_merge = tmp_is_do_merge; + + // processing thread + int conf_thread_cnt = pImData->GetSocketThreadCount(); // we do not need to cache this value. + int now_thread_cnt = procthreads.GetThreadCount(); + if(conf_thread_cnt < now_thread_cnt){ + if(DEFAULT_SOCK_THREAD_CNT == conf_thread_cnt){ + // stop all + if(!procthreads.ExitAllThreads()){ + ERR_CHMPRN("Failed to exit thread for sock processing."); + }else{ + MSG_CHMPRN("stop all sock processing thread(%d).", now_thread_cnt); + } + }else{ + // need to stop some thread + if(!procthreads.ExitThreads(now_thread_cnt - conf_thread_cnt)){ + ERR_CHMPRN("Failed to exit thread for sock processing."); + }else{ + MSG_CHMPRN("stop sock processing thread(%d - %d = %d).", now_thread_cnt, conf_thread_cnt, now_thread_cnt - conf_thread_cnt); + } + } + }else if(now_thread_cnt < conf_thread_cnt){ + // need to run new threads + // + // - parameter is NULL(because thread is sleep at start) + // - sleep at starting + // - not at onece(not one shot) + // - sleep after every working + // - not keep event count + // + if(!procthreads.CreateThreads(conf_thread_cnt - now_thread_cnt, ChmEventSock::ReceiveWorkerProc, NULL, this, 0, true, false, false, false)){ + ERR_CHMPRN("Failed to create thread for sock processing, but continue..."); + }else{ + MSG_CHMPRN("start to run sock processing thread(%d + %d = %d).", now_thread_cnt, conf_thread_cnt - now_thread_cnt, conf_thread_cnt); + } + }else{ + // nothing to do because of same thread count + } + + // others + int tmp_sock_retry_count= pImData->GetSockRetryCnt(); + suseconds_t tmp_sock_wait_time = pImData->GetSockTimeout(); + suseconds_t tmp_con_wait_time = pImData->GetConnectTimeout(); + + sock_retry_count = (CHMEVENTSOCK_RETRY_DEFAULT == tmp_sock_retry_count ? ChmEventSock::DEFAULT_RETRYCNT : tmp_sock_retry_count); + sock_wait_time = (CHMEVENTSOCK_TIMEOUT_DEFAULT == tmp_sock_wait_time ? ChmEventSock::DEFAULT_WAIT_SOCKET : tmp_sock_wait_time); + con_wait_time = (CHMEVENTSOCK_TIMEOUT_DEFAULT == tmp_con_wait_time ? ChmEventSock::DEFAULT_WAIT_CONNECT : tmp_con_wait_time); + con_retry_count = sock_retry_count; + is_server_mode = pImData->IsServerMode(); + max_sock_pool = pImData->GetMaxSockPool(); + sock_pool_timeout = pImData->GetSockPoolTimeout(); + + return true; +} + +bool ChmEventSock::GetEventQueueFds(event_fds_t& fds) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + fds.clear(); + + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + int sock = CHM_INVALID_SOCK; + int ctlsock = CHM_INVALID_SOCK; + if(!pImData->GetSelfSocks(sock, ctlsock)){ + ERR_CHMPRN("Could not get self sock and ctlsock."); + return false; + } + if(CHM_INVALID_SOCK != sock){ + fds.push_back(sock); + } + if(CHM_INVALID_SOCK != ctlsock){ + fds.push_back(ctlsock); + } + seversockmap.get_keys(fds); + slavesockmap.get_keys(fds); + ctlsockmap.get_keys(fds); + acceptingmap.get_keys(fds); + return true; +} + +bool ChmEventSock::SetEventQueue(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // first check for SSL + if(!IsSafeParamsForSSL()){ + ERR_CHMPRN("Something wrong about SSL parameters, thus could not make SSL context."); + return false; + } + + // all status update. + if(!InitialAllServerStatus()){ + ERR_CHMPRN("Could not update all server status."); + return false; + } + + // Get self port + short port = CHM_INVALID_PORT; + short ctlport = CHM_INVALID_PORT; + if(!pImData->GetSelfPorts(port, ctlport) || CHM_INVALID_PORT == ctlport || (is_server_mode && CHM_INVALID_PORT == port)){ + ERR_CHMPRN("Could not get self port and ctlport."); + return false; + } + + struct epoll_event eqevent; + int sock = CHM_INVALID_SOCK; + int ctlsock = CHM_INVALID_SOCK; + + // on only server mode, listen port + if(is_server_mode){ + // listen port + if(CHM_INVALID_SOCK == (sock = ChmEventSock::Listen(NULL, port))){ + ERR_CHMPRN("Could not listen port(%d).", port); + return false; + } + // add event fd + memset(&eqevent, 0, sizeof(struct epoll_event)); + eqevent.data.fd = sock; + eqevent.events = EPOLLIN | EPOLLET | EPOLLRDHUP; // EPOLLRDHUP is set + if(-1 == epoll_ctl(eqfd, EPOLL_CTL_ADD, sock, &eqevent)){ + ERR_CHMPRN("Failed to add sock(port %d: sock %d) into epoll event(%d), errno=%d", port, sock, eqfd, errno); + CHM_CLOSESOCK(sock); + return false; + } + } + + // listen ctlport + if(CHM_INVALID_SOCK == (ctlsock = ChmEventSock::Listen(NULL, ctlport))){ + ERR_CHMPRN("Could not listen ctlport(%d).", ctlport); + if(CHM_INVALID_SOCK != sock){ + epoll_ctl(eqfd, EPOLL_CTL_DEL, sock, NULL); + } + CHM_CLOSESOCK(sock); + return false; + } + + // add event fd + memset(&eqevent, 0, sizeof(struct epoll_event)); + eqevent.data.fd = ctlsock; + eqevent.events = EPOLLIN | EPOLLET | EPOLLRDHUP; // EPOLLRDHUP is set. + if(-1 == epoll_ctl(eqfd, EPOLL_CTL_ADD, ctlsock, &eqevent)){ + ERR_CHMPRN("Failed to add sock(port %d: sock %d) into epoll event(%d), errno=%d", ctlport, ctlsock, eqfd, errno); + if(CHM_INVALID_SOCK != sock){ + epoll_ctl(eqfd, EPOLL_CTL_DEL, sock, NULL); + } + CHM_CLOSESOCK(sock); + CHM_CLOSESOCK(ctlsock); + return false; + } + + // set chmshm + if(!pImData->SetSelfSocks(sock, ctlsock)){ + ERR_CHMPRN("Could not set self sock(%d) and ctlsock(%d) into chmshm.", sock, ctlsock); + if(CHM_INVALID_SOCK != sock){ + epoll_ctl(eqfd, EPOLL_CTL_DEL, sock, NULL); + } + if(CHM_INVALID_SOCK != ctlsock){ + epoll_ctl(eqfd, EPOLL_CTL_DEL, ctlsock, NULL); + } + CHM_CLOSESOCK(sock); + CHM_CLOSESOCK(ctlsock); + return false; + } + + // check self status + chmpxsts_t status = pImData->GetSelfStatus(); + if(is_server_mode){ + if(CHMPXSTS_SRVOUT_DOWN_NORMAL == status){ + // normally default come here. + MSG_CHMPRN("initial chmpx status(0x%016" PRIx64 ":%s) is wrong, so force to set CHMPXSTS_SRVOUT_UP_NORMAL.", status, STR_CHMPXSTS_FULL(status).c_str()); + status = CHMPXSTS_SRVOUT_UP_NORMAL; + + }else if(CHMPXSTS_SRVIN_DOWN_NORMAL == status){ + WAN_CHMPRN("initial chmpx status(0x%016" PRIx64 ":%s) is wrong, so force to set CHMPXSTS_SRVIN_UP_MERGING.", status, STR_CHMPXSTS_FULL(status).c_str()); + status = CHMPXSTS_SRVIN_UP_MERGING; + + }else if(CHMPXSTS_SRVIN_DOWN_DELPENDING == status){ + WAN_CHMPRN("initial chmpx status(0x%016" PRIx64 ":%s) is wrong, so force to set CHMPXSTS_SRVIN_UP_MERGING.", status, STR_CHMPXSTS_FULL(status).c_str()); + status = CHMPXSTS_SRVIN_UP_MERGING; // should be CHMPXSTS_SRVIN_UP_NORMAL ? + + }else if(CHMPXSTS_SRVIN_DOWN_DELETED == status){ + WAN_CHMPRN("initial chmpx status(0x%016" PRIx64 ":%s) is wrong, so force to set CHMPXSTS_SRVOUT_UP_NORMAL.", status, STR_CHMPXSTS_FULL(status).c_str()); + status = CHMPXSTS_SRVIN_UP_DELETED; + + }else{ + // nothing to change status + } + + }else{ + WAN_CHMPRN("initial chmpx status(0x%016" PRIx64 ":%s) is slave, so force to set CHMPXSTS_SRVOUT_UP_NORMAL.", status, STR_CHMPXSTS_FULL(status).c_str()); + status = CHMPXSTS_SLAVE_UP_NORMAL; + } + + // re-set self status(set suspend in this method) + if(!pImData->SetSelfStatus(status)){ + CloseSelfSocks(); + return false; + } + + // Connect(join ring/connect servers) + if(is_server_mode){ + // server mode + if(!ConnectRing()){ + ERR_CHMPRN("Failed to join RING(mode is server as SERVICE OUT/IN)."); + return false; + } + }else{ + // slave mode + if(!ConnectServers()){ + ERR_CHMPRN("Failed to connect servers on RING(mode is SLAVE)."); + return false; + } + } + return true; +} + +bool ChmEventSock::UnsetEventQueue(void) +{ + return Clean(); +} + +bool ChmEventSock::IsEventQueueFd(int fd) +{ + if(CHM_INVALID_HANDLE == fd){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + int sock = CHM_INVALID_SOCK; + int ctlsock = CHM_INVALID_SOCK; + if(!pImData->GetSelfSocks(sock, ctlsock)){ + ERR_CHMPRN("Could not get self sock and ctlsock, but continue..."); + }else{ + if(fd == sock || fd == ctlsock){ + return true; + } + } + if(seversockmap.find(fd) || slavesockmap.find(fd) || ctlsockmap.find(fd) || acceptingmap.find(fd)){ + return true; + } + return false; +} + +//------------------------------------------------------ +// SSL context +//------------------------------------------------------ +SSL_CTX* ChmEventSock::GetSSLContext(const char* CApath, const char* CAfile, const char* server_cert, const char* server_prikey, const char* slave_cert, const char* slave_prikey, bool is_verify_peer) +{ + if(!CHMEMPTYSTR(CApath) && !CHMEMPTYSTR(CAfile)){ + ERR_CHMPRN("Parameter is wrong."); + return NULL; + } + + // Which ctx is needed? + SSL_CTX** ppctx = NULL; + if(!CHMEMPTYSTR(server_cert) && !CHMEMPTYSTR(server_prikey)){ + ppctx = &svr_sslctx; + }else{ + ppctx = &slv_sslctx; + } + + if(!*ppctx){ + // Make context + if(NULL == (*ppctx = ChmEventSock::MakeSSLContext(CVT_ESTR_NULL(CApath), CVT_ESTR_NULL(CAfile), CVT_ESTR_NULL(server_cert), CVT_ESTR_NULL(server_prikey), CVT_ESTR_NULL(slave_cert), CVT_ESTR_NULL(slave_prikey), is_verify_peer))){ + ERR_CHMPRN("Failed to make SSL context."); + } + }else{ + // already has ctx. + } + return *ppctx; +} + +SSL* ChmEventSock::GetSSL(int sock) +{ + if(IsEmpty()){ + return NULL; + } + if(!sslmap.find(sock)){ + return NULL; + } + return sslmap[sock]; +} + +// [NOTE] +// This method is checking whichever the parameters for SSL(CA, cert, keys, etc) is no problem. +// So that, this method makes SSL context when SSL mode. +// If could not make SSL context, it means that those parameter are wrong. +// +bool ChmEventSock::IsSafeParamsForSSL(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + CHMPXSSL ssldata; + if(!pImData->GetSelfSsl(ssldata)){ + ERR_CHMPRN("Failed to get SSL structure from self chmpx."); + return false; + } + if(!CHMEMPTYSTR(ssldata.server_cert)){ + if(NULL == ChmEventSock::GetSSLContext((!ssldata.is_ca_file ? ssldata.capath : NULL), (ssldata.is_ca_file ? ssldata.capath : NULL), ssldata.server_cert, ssldata.server_prikey, ssldata.slave_cert, ssldata.slave_prikey, ssldata.verify_peer)){ + ERR_CHMPRN("Failed to make self SSL context for server socket."); + return false; + } + } + if(!CHMEMPTYSTR(ssldata.slave_cert)){ + if(NULL == ChmEventSock::GetSSLContext((!ssldata.is_ca_file ? ssldata.capath : NULL), (ssldata.is_ca_file ? ssldata.capath : NULL), NULL, NULL, ssldata.slave_cert, ssldata.slave_prikey, ssldata.verify_peer)){ + ERR_CHMPRN("Failed to make self SSL context for slave socket."); + return false; + } + } + return true; +} + +//--------------------------------------------------------- +// Lock for socket(for server/slave node) +//--------------------------------------------------------- +// [NOTE] +// This method uses and manages socket lock mapping. +// Because this library supports multi-thread for sending/recieving, this class needs to +// lock socket before sending. (but the control socket does not be needed to lock.) +// +// [NOTICE] +// For locking, we use sendlockmap_t class(template), and use directly lock variable in +// sendlockmap_t here. +// +bool ChmEventSock::RawLockSendSock(int sock, bool is_lock, bool is_onece) +{ + if(CHM_INVALID_SOCK == sock){ + return false; + } + pid_t tid = gettid(); + bool result = false; + for(bool is_loop = true; !result && is_loop; is_loop = !is_onece){ + // get trylock + if(!fullock::flck_trylock_noshared_mutex(&sendlockmap.lockval)){ + // [NOTE] + // no wait and not sched_yield + continue; + } + + sendlockmap_t::iterator iter = sendlockmap.basemap.find(sock); + if(sendlockmap.basemap.end() == iter || NULL == iter->second){ + // there is no sock in map(or invalid pointer), so set new status data to map + PCHMSSSTAT pssstat = new CHMSSSTAT; + pssstat->tid = is_lock ? tid : CHM_INVALID_TID; + pssstat->last_time = time(NULL); + sendlockmap.basemap[sock] = pssstat; + result = true; + }else{ + // found sock status in map + PCHMSSSTAT pssstat = iter->second; + if(is_lock){ + if(CHM_INVALID_TID == pssstat->tid){ + // sock is unlocked. + pssstat->tid = tid; + result = true; + }else if(tid == pssstat->tid){ + // same thread locked. + result = true; + }else{ + // other thread is locked, so looping + } + }else{ + if(CHM_INVALID_TID == pssstat->tid){ + // already unlocked + }else{ + if(tid != pssstat->tid){ + MSG_CHMPRN("thread(%d) unlocked sock(%d) locked by thread(%d).", tid, sock, pssstat->tid); + } + pssstat->tid = CHM_INVALID_TID; + pssstat->last_time = time(NULL); // last_time is updated only at closing! + } + result = true; + } + } + fullock::flck_unlock_noshared_mutex(&sendlockmap.lockval); // UNLOCK + } + return result; +} + +//--------------------------------------------------------- +// Methods for Communication base +//--------------------------------------------------------- +// +// Lapped RawSend method with LockSendSock +// +bool ChmEventSock::LockedSend(int sock, SSL* ssl, PCOMPKT pComPkt, bool is_blocking) +{ + LockSendSock(sock); // LOCK SOCKET + + bool is_closed = false; + if(!ChmEventSock::RawSend(sock, ssl, pComPkt, is_closed, is_blocking, sock_retry_count, sock_wait_time)){ + ERR_CHMPRN("Failed to send COMPKT to sock(%d).", sock); + UnlockSendSock(sock); // UNLOCK SOCKET + if(!is_closed){ + CloseSSL(sock); // close socket + } + return false; + } + UnlockSendSock(sock); // UNLOCK SOCKET + + return true; +} + +// +// Lapped RawSend method with LockSendSock +// +bool ChmEventSock::LockedSend(int sock, SSL* ssl, const unsigned char* pbydata, size_t length, bool is_blocking) +{ + LockSendSock(sock); // LOCK SOCKET + + bool is_closed = false; + if(!ChmEventSock::RawSend(sock, ssl, pbydata, length, is_closed, is_blocking, sock_retry_count, sock_wait_time)){ + ERR_CHMPRN("Failed to send binary data to sock(%d).", sock); + UnlockSendSock(sock); // UNLOCK SOCKET + if(!is_closed){ + CloseSSL(sock); // close socket + } + return false; + } + UnlockSendSock(sock); // UNLOCK SOCKET + + return true; +} + +// +// [NOTE] +// We check socket count in each server here, and if it is over socket pool count, +// this method tries to close it. +// This logic is valid only when server socket. +// +// When this method opens/closes a socket, this locks dyna_sockfd_lockval every time. +// If dyna_sockfd_lockval is already locked, this does not check over max pool count +// and not open new socket, and continue to loop. +// +bool ChmEventSock::GetLockedSendSock(chmpxid_t chmpxid, int& sock, bool is_check_slave) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + bool is_server = pImData->IsServerChmpxId(chmpxid); + + if(!is_server && !is_check_slave){ + WAN_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ") sock in slave list.", chmpxid); + return false; + } + socklist_t socklist; + + // check max socket pool (if over max sock pool count, close idle sockets which is over time limit) + if(is_server && (last_check_time + sock_pool_timeout) < time(NULL)){ + // get socket list to server + if(pImData->GetServerSock(chmpxid, socklist)){ + // check max socket pool (if over max sock pool count, close idle sockets which is over time limit) + if(max_sock_pool < static_cast(socklist.size())){ + if(fullock::flck_trylock_noshared_mutex(&dyna_sockfd_lockval)){ // LOCK(dynamic) + for(socklist_t::reverse_iterator riter = socklist.rbegin(); riter != socklist.rend(); ){ + if(TryLockSendSock(*riter)){ // try to lock socket + // Succeed to lock socket, so close this. + seversockmap.erase(*riter); // close socket + sendlockmap.erase(*riter); // remove socket from send lock map(with unlock it) + + // remove socket from socklist + if((++riter) != socklist.rend()){ + socklist.erase((riter).base()); + }else{ + socklist.erase(socklist.begin()); + break; // over the start position. + } + + // check over max pool count + if(static_cast(socklist.size()) <= max_sock_pool){ + break; + } + }else{ + ++riter; + } + } + fullock::flck_unlock_noshared_mutex(&dyna_sockfd_lockval); // UNLOCK(dynamic) + + last_check_time = time(NULL); + } + } + } + } + + // loop + size_t slv_sockcnt = 0; + for(sock = CHM_INVALID_SOCK; CHM_INVALID_SOCK == sock; ){ + slv_sockcnt = 0; + + // at first, server socket + if(is_server){ + // get socket list to server + socklist.clear(); + if(!pImData->GetServerSock(chmpxid, socklist)){ + socklist.clear(); + } + + // try to lock existed socket + for(socklist_t::const_iterator iter = socklist.begin(); iter != socklist.end(); ++iter){ + if(TryLockSendSock(*iter)){ // try to lock socket + // Succeed to lock socket! + sock = *iter; + break; + } + } + + // check for closing another idle socket which is over time limit. + // + // [NOTE] + // interval for checking is 5 times by sock_pool_timeout. + // already locked one socket(sock), so minimum socket count is guaranteed) + // + if(CHM_INVALID_SOCK != sock && ChmEventSock::NO_SOCK_POOL_TIMEOUT < sock_pool_timeout && (last_check_time + (sock_pool_timeout * 5) < time(NULL))){ + if(fullock::flck_trylock_noshared_mutex(&dyna_sockfd_lockval)){ // LOCK(dynamic) + time_t nowtime = time(NULL); + + for(socklist_t::const_iterator iter = socklist.begin(); iter != socklist.end(); ++iter){ + if(sock == *iter){ + continue; // skip + } + if(TryLockSendSock(*iter)){ // try to lock socket + // get locked sock status. + const PCHMSSSTAT pssstat = sendlockmap.get(*iter); + if(pssstat && (pssstat->last_time + sock_pool_timeout) < nowtime){ + // timeouted socket. + seversockmap.erase(*iter); // close socket + sendlockmap.erase(*iter); // remove socket from send lock map(with unlock it) + + // [NOTE] + // we do not remove socket(iter) from socklist, because this loop will be broken soon. + // + }else{ + UnlockSendSock(*iter); // unlock socket + } + } + } + fullock::flck_unlock_noshared_mutex(&dyna_sockfd_lockval); // UNLOCK(dynamic) + } + } + } + + // search socket in list of slave ( for a case of other server connect to server as slave ) + if(CHM_INVALID_SOCK == sock && is_check_slave){ + socklist.clear(); + if(!pImData->GetSlaveSock(chmpxid, socklist) || socklist.empty()){ + slv_sockcnt = 0; + if(!is_server){ + // there is no server, so no more checking. + WAN_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ") sock in slave list.", chmpxid); + return false; + } + }else{ + slv_sockcnt = socklist.size(); + + // slave has socket list. next, search unlocked socket. + bool need_wait_unlock = false; + for(socklist_t::const_iterator iter = socklist.begin(); iter != socklist.end(); ++iter){ + int tmpsock = *iter; + if(CHM_INVALID_SOCK != tmpsock){ + need_wait_unlock = true; + if(TryLockSendSock(tmpsock)){ // try to lock socket + // Succeed to lock socket! + sock = tmpsock; + break; + } + } + } + if(!is_server && !need_wait_unlock){ + // there is no server, so no more checking. + WAN_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ") sock in slave list.", chmpxid); + return false; + } + } + } + + // try to connect new socket ( there is no (idle) socket ) + if(CHM_INVALID_SOCK == sock && is_server){ + // reget socket list, and check socket pool count + socklist.clear(); + if(pImData->GetServerSock(chmpxid, socklist) && static_cast(socklist.size()) < max_sock_pool){ + if(fullock::flck_trylock_noshared_mutex(&dyna_sockfd_lockval)){ // LOCK(dynamic) + // can connect new sock + if(!RawConnectServer(chmpxid, sock, false, true) && CHM_INVALID_SOCK == sock){ + + if(0 == socklist.size() && 0 == slv_sockcnt){ + // no server socket and could not connect, and no slave socket, so no more try. + WAN_CHMPRN("Could not connect to server chmpxid(0x%016" PRIx64 ").", chmpxid); + fullock::flck_unlock_noshared_mutex(&dyna_sockfd_lockval); // UNLOCK(dynamic) + return false; + } + MSG_CHMPRN("Could not connect to server chmpxid(0x%016" PRIx64 "), but continue...", chmpxid); + }else{ + // Succeed to connect new sock and lock it. + } + fullock::flck_unlock_noshared_mutex(&dyna_sockfd_lockval); // UNLOCK(dynamic) + } + } + } + } + return true; +} + +// +// If you make all COMPKT before calling this method, you can set NULL +// to pbody. If pbody is not NULL, this method builds COMPKT with body data. +// +bool ChmEventSock::Send(PCOMPKT pComPkt, const unsigned char* pbody, size_t blength) +{ + if(!pComPkt){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // for stat + bool is_measure = (COM_C2C == pComPkt->head.type); + chmpxid_t term_chmpxid= pComPkt->head.term_ids.chmpxid; + struct timespec start_time; + if(is_measure){ + STR_MATE_TIMESPEC(&start_time); + } + + // check & build packet + PCOMPKT pPacked = pComPkt; + bool is_pack = false; + if(pbody && 0L < blength){ + if(NULL == (pPacked = reinterpret_cast(malloc(sizeof(COMPKT) + blength)))){ + ERR_CHMPRN("Could not allocate memory as %zd length.", sizeof(COMPKT) + blength); + return false; + } + COPY_COMHEAD(&(pPacked->head), &(pComPkt->head)); + pPacked->length = sizeof(COMPKT) + blength; + pPacked->offset = sizeof(COMPKT); + + unsigned char* pdest = reinterpret_cast(pPacked) + sizeof(COMPKT); + memcpy(pdest, pbody, blength); + + is_pack = true; + } + // for stat + size_t stat_length = pPacked->length; + + // get socket + int sock = CHM_INVALID_SOCK; + if(!GetLockedSendSock(pComPkt->head.term_ids.chmpxid, sock, true) || CHM_INVALID_SOCK == sock){ // LOCK SOCKET + WAN_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ") sock.", pComPkt->head.term_ids.chmpxid); + if(is_pack){ + CHM_Free(pPacked); + } + return false; + } + + // send + bool is_closed = false; + if(!ChmEventSock::RawSend(sock, GetSSL(sock), pPacked, is_closed, false, sock_retry_count, sock_wait_time)){ + ERR_CHMPRN("Failed to send COMPKT to sock(%d).", sock); + UnlockSendSock(sock); // UNLOCK SOCKET + + if(!is_closed){ + CloseSSL(sock); // close socket + } + if(is_pack){ + CHM_Free(pPacked); + } + return false; + } + UnlockSendSock(sock); // UNLOCK SOCKET + + // stat + if(is_measure){ + struct timespec fin_time; + struct timespec elapsed_time; + FIN_MATE_TIMESPEC2(&start_time, &fin_time, &elapsed_time); + + if(!pImData->AddStat(term_chmpxid, true, stat_length, elapsed_time)){ + ERR_CHMPRN("Failed to update stat(send)."); + } + if(pImData->IsTraceEnable() && !pImData->AddTrace(CHMLOG_TYPE_OUT_SOCKET, stat_length, start_time, fin_time)){ + ERR_CHMPRN("Failed to add trace log."); + } + } + + if(is_pack){ + CHM_Free(pPacked); + } + return true; +} + +bool ChmEventSock::Receive(int fd) +{ + if(CHM_INVALID_HANDLE == fd){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + bool result = false; + if(procthreads.HasThread()){ + // Has processing threads, so run(do work) processing thread. + chmthparam_t wp_param = static_cast(fd); + + if(false == (result = procthreads.DoWorkThread(wp_param))){ + ERR_CHMPRN("Failed to wake up thread for processing(receiving)."); + } + }else{ + // Does not have processing thread, so call directly. + // + // [NOTE] + // We use edge triggerd epoll, in this type the event is accumulated and not sent when the data to + // the socket is left. Thus we must read more until EAGAIN. Otherwise we lost data. + // So we checked socket for rest data here. + // + bool is_closed; + int rtcode; + suseconds_t waittime = sock_wait_time; + do{ + is_closed = false; + if(false == (result = RawReceive(fd, is_closed))){ + if(!is_closed){ + ERR_CHMPRN("Failed to receive processing directly."); + }else{ + MSG_CHMPRN("sock(%d) is closed while processing directly.", fd); + result = true; + } + break; + } + }while(0 == (rtcode = ChmEventSock::WaitForReady(fd, WAIT_READ_FD, 0, false, waittime))); // check rest data & return assap + + if(0 != rtcode && ETIMEDOUT != rtcode){ + if(RawNotifyHup(fd)){ + ERR_CHMPRN("Failed to closing socket(%d), but continue...", fd); + } + result = true; + } + } + return result; +} + +// +// If fd is sock, it means connecting from other servers. +// After receiving this method is processing own work. And if it needs to dispatch +// processing event after own processing, do it by calling control object. +// +// [NOTE] +// This method calls RawReceiveByte and RawReceiveAny methods, these methods read only packet size +// from socket. We use edge triggerd epoll, thus we must read more until EAGAIN. Otherwise we +// lost data. So we checked rest data in caller method. +// +bool ChmEventSock::RawReceive(int fd, bool& is_closed) +{ + is_closed = false; + + if(CHM_INVALID_HANDLE == fd){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + + PCOMPKT pComPkt = NULL; + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + int sock = CHM_INVALID_SOCK; + int ctlsock = CHM_INVALID_SOCK; + if(!pImData->GetSelfSocks(sock, ctlsock)){ + ERR_CHMPRN("Could not get self sock and ctlsock, but continue..."); + } + + // Processing + if(fd == sock){ + // self port -> new connection + if(!Accept(sock)){ + ERR_CHMPRN("Could not accept new connection."); + return false; + } + + }else if(fd == ctlsock){ + // self ctlport + if(!AcceptCtlport(ctlsock)){ + ERR_CHMPRN("Could not accept new control port connection."); + return false; + } + + }else if(seversockmap.find(fd)){ + // server chmpx + + // for stat + struct timespec start_time; + STR_MATE_TIMESPEC(&start_time); + + if(!ChmEventSock::RawReceive(fd, GetSSL(fd), is_closed, &pComPkt, 0L, false, sock_retry_count, sock_wait_time) || !pComPkt){ + CHM_Free(pComPkt); + + if(!is_closed){ + ERR_CHMPRN("Failed to receive ComPkt from sock(%d), sock is not closed.", fd); + }else{ + MSG_CHMPRN("Failed to receive ComPkt from sock(%d), sock is closed.", fd); + // close socket + chmpxid_t chmpxid = seversockmap[fd]; + if(!RawNotifyHup(fd)){ + ERR_CHMPRN("Failed to closing \"to server socket\" for chmpxid(0x%016" PRIx64 "), but continue...", chmpxid); + } + } + return false; + } + // stat + if(COM_C2C == pComPkt->head.type){ + struct timespec fin_time; + struct timespec elapsed_time; + FIN_MATE_TIMESPEC2(&start_time, &fin_time, &elapsed_time); + + if(!pImData->AddStat(pComPkt->head.dept_ids.chmpxid, false, pComPkt->length, elapsed_time)){ + ERR_CHMPRN("Failed to update stat(receive)."); + } + if(pImData->IsTraceEnable() && !pImData->AddTrace(CHMLOG_TYPE_IN_SOCKET, pComPkt->length, start_time, fin_time)){ + ERR_CHMPRN("Failed to add trace log."); + } + } + + }else if(slavesockmap.find(fd)){ + // slave chmpx + + // for stat + struct timespec start_time; + STR_MATE_TIMESPEC(&start_time); + + if(!ChmEventSock::RawReceive(fd, GetSSL(fd), is_closed, &pComPkt, 0L, false, sock_retry_count, sock_wait_time) || !pComPkt){ + CHM_Free(pComPkt); + + if(!is_closed){ + ERR_CHMPRN("Failed to receive ComPkt from sock(%d), sock is not closed.", fd); + }else{ + MSG_CHMPRN("Failed to receive ComPkt from sock(%d), sock is closed.", fd); + // close socket + chmpxid_t chmpxid = slavesockmap[fd]; + if(!RawNotifyHup(fd)){ + ERR_CHMPRN("Failed to closing \"from slave socket\" for chmpxid(0x%016" PRIx64 "), but continue...", chmpxid); + } + } + return false; + } + // stat + if(COM_C2C == pComPkt->head.type){ + struct timespec fin_time; + struct timespec elapsed_time; + FIN_MATE_TIMESPEC2(&start_time, &fin_time, &elapsed_time); + + if(!pImData->AddStat(pComPkt->head.dept_ids.chmpxid, false, pComPkt->length, elapsed_time)){ + ERR_CHMPRN("Failed to update stat(receive)."); + } + if(pImData->IsTraceEnable() && !pImData->AddTrace(CHMLOG_TYPE_IN_SOCKET, pComPkt->length, start_time, fin_time)){ + ERR_CHMPRN("Failed to add trace log."); + } + } + + }else if(acceptingmap.find(fd)){ + // slave socket before accepting + string achname = acceptingmap[fd]; + + if(!ChmEventSock::RawReceive(fd, GetSSL(fd), is_closed, &pComPkt, 0L, false, sock_retry_count, sock_wait_time) || !pComPkt){ + CHM_Free(pComPkt); + + if(!is_closed){ + ERR_CHMPRN("Failed to receive ComPkt from accepting sock(%d), sock is not closed.", fd); + }else{ + // remove mapping + MSG_CHMPRN("Failed to receive ComPkt from accepting sock(%d), sock is closed.", fd); + if(!RawNotifyHup(fd)){ + ERR_CHMPRN("Failed to closing \"accepting socket\" for %s, but continue...", achname.c_str()); + } + } + return false; + } + + // + // Accepting logic needs socket, so we processing here. + // + + // check COMPKT(length, type, offset) + if(COM_PX2PX != pComPkt->head.type || (sizeof(COMPKT) + sizeof(PXCOM_CONINIT_REQ)) != pComPkt->length || sizeof(COMPKT) != pComPkt->offset){ + ERR_CHMPRN("ComPkt is wrong for accepting, type(%" PRIu64 ":%s), length(%zu), offset(%jd).", pComPkt->head.type, STRCOMTYPE(pComPkt->head.type), pComPkt->length, static_cast(pComPkt->offset)); + CHM_Free(pComPkt); + return false; + } + // check CONINIT_REQ + PPXCOM_ALL pComAll = CHM_OFFSET(pComPkt, pComPkt->offset, PPXCOM_ALL); + if(CHMPX_COM_CONINIT_REQ != pComAll->val_head.type){ + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s), but allow only CHMPX_COM_CONINIT_REQ.", pComAll->val_head.type, STRPXCOMTYPE(pComAll->val_head.type)); + CHM_Free(pComPkt); + return false; + } + + // Parse + PCOMPKT pResComPkt = NULL; // dummy + chmpxid_t slvchmpxid = CHM_INVALID_CHMPXID; + short ctlport = CHM_INVALID_PORT; + PCHMPXSSL pssl = NULL; + if(!PxComReceiveConinitReq(&(pComPkt->head), pComAll, &pResComPkt, slvchmpxid, ctlport)){ + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pComAll->val_head.type, STRPXCOMTYPE(pComAll->val_head.type)); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + // check ACL(with port) + if(!pImData->IsAllowHostname(achname.c_str(), &ctlport, &pssl)){ + // Not allow accessing from slave. + ERR_CHMPRN("Denied %s(sock:%d):%d by not allowed.", achname.c_str(), fd, ctlport); + PxComSendConinitRes(fd, slvchmpxid, CHMPX_COM_RES_ERROR); + + // close + if(!RawNotifyHup(fd)){ + ERR_CHMPRN("Failed to closing \"accepting socket\" for %s, but continue...", achname.c_str()); + } + is_closed = true; + return false; + } + + // Set nonblock + if(!ChmEventSock::SetNonblocking(fd)){ + WAN_CHMPRN("Failed to nonblock socket(sock:%d), but continue...", fd); + } + // update slave list in CHMSHM + // + // [NOTICE] + // All server status which connects to this server is "SLAVE". + // If other server connects this server for chain of RING, then this server sets that server status as "SLAVE" in slaves list. + // See: comment in chmstructure.h + // + if(!pImData->SetSlaveAll(slvchmpxid, achname.c_str(), ctlport, pssl, fd, CHMPXSTS_SLAVE_UP_NORMAL)){ + ERR_CHMPRN("Failed to add/update slave list, chmpid=0x%016" PRIx64 ", hostname=%s, ctlport=%d, sock=%d", slvchmpxid, achname.c_str(), ctlport, fd); + PxComSendConinitRes(fd, slvchmpxid, CHMPX_COM_RES_ERROR); + + // close + if(!RawNotifyHup(fd)){ + ERR_CHMPRN("Failed to closing \"accepting socket\" for %s, but continue...", achname.c_str()); + } + is_closed = true; + return false; + } + + // add internal mapping + slavesockmap.set(fd, slvchmpxid); + // remove mapping + acceptingmap.erase(fd, NULL, NULL); // do not call callback because this socket is used by slave map. + + // Send CHMPX_COM_CONINIT_RES(success) + if(!PxComSendConinitRes(fd, slvchmpxid, CHMPX_COM_RES_SUCCESS)){ + ERR_CHMPRN("Failed to send CHMPX_COM_CONINIT_RES, but do(could) not recover for automatic closing."); + return false; + } + + }else if(ctlsockmap.find(fd)){ + // control port + char szReceive[CTL_COMMAND_MAX_LENGTH]; + size_t RecLength = CTL_COMMAND_MAX_LENGTH - 1; + chmpxid_t chmpxid = ctlsockmap[fd]; + + // receive command + memset(szReceive, 0, CTL_COMMAND_MAX_LENGTH); + if(!ChmEventSock::RawReceiveAny(fd, is_closed, reinterpret_cast(szReceive), &RecLength, false, sock_retry_count, sock_wait_time)){ + if(!is_closed){ + ERR_CHMPRN("Failed to receive data from ctlport ctlsock(%d), ctlsock is not closed.", fd); + }else{ + MSG_CHMPRN("Failed to receive data from ctlport ctlsock(%d), ctlsock is closed.", fd); + if(!RawNotifyHup(fd)){ + ERR_CHMPRN("Failed to closing \"from control socket\" for chmpxid(0x%016" PRIx64 "), but continue...", chmpxid); + } + } + return false; + } + if(RecLength <= 0L){ + MSG_CHMPRN("Receive null command on control port from ctlsock(%d)", fd); + if(!RawNotifyHup(fd)){ + ERR_CHMPRN("Failed to closing \"from control socket\" for chmpxid(0x%016" PRIx64 "), but continue...", chmpxid); + } + return true; + } + szReceive[RecLength] = '\0'; // terminate(not need this) + + // processing command. + if(!Processing(fd, szReceive)){ + ERR_CHMPRN("Failed to do command(%s) from ctlsock(%d)", szReceive, fd); + if(!RawNotifyHup(fd)){ + ERR_CHMPRN("Failed to closing \"from control socket\" for chmpxid(0x%016" PRIx64 "), but continue...", chmpxid); + } + return false; + } + + // close ctlport + if(!RawNotifyHup(fd)){ + ERR_CHMPRN("Failed to closing \"from control socket\" for chmpxid(0x%016" PRIx64 "), but continue...", chmpxid); + } + return true; + + }else{ + // from other chmpx + WAN_CHMPRN("Received from unknown sock(%d).", fd); + + if(!ChmEventSock::RawReceive(fd, GetSSL(fd), is_closed, &pComPkt, 0L, false, sock_retry_count, sock_wait_time) || !pComPkt){ + CHM_Free(pComPkt); + + if(!is_closed){ + ERR_CHMPRN("Failed to receive ComPkt from sock(%d), sock is not closed.", fd); + }else{ + MSG_CHMPRN("Failed to receive ComPkt from sock(%d), sock is closed.", fd); + if(!RawNotifyHup(fd)){ + ERR_CHMPRN("Failed to closing server(sock:%d), but continue...", fd); + } + } + return false; + } + } + + // Dispatching + if(pComPkt){ + if(pChmCntrl && !pChmCntrl->Processing(pComPkt, ChmCntrl::EVOBJ_TYPE_EVSOCK)){ + ERR_CHMPRN("Failed processing after receiving from socket."); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + } + return true; +} + +// +// [NOTICE] +// We set EPOLLRDHUP for socket event, then we will catch EPOLLRDHUP at here. +// EPOLLRDHUP has possibility of a problem that the packet just before FIN is replaced with FIN. +// So we need to receive rest data and 0 byte(EOF) in Receive() for closing. +// +bool ChmEventSock::NotifyHup(int fd) +{ + if(!Receive(fd)){ + ERR_CHMPRN("Failed to receive data from event fd(%d).", fd); + return false; + } + return true; +} + +// +// The connection which this server connect to other servers for joining RING +// when the mode is both server and slave is set EPOLLRDHUP for epoll. +// Then the connection occurres EPOLLRDHUP event by epoll, thus this method is +// called. +// This method closes the socket and reconnect next server on RING. +// +// [NOTE] +// This method is certainly close socket and remove epoll event. +// +bool ChmEventSock::RawNotifyHup(int fd) +{ + if(CHM_INVALID_HANDLE == fd){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + while(!fullock::flck_trylock_noshared_mutex(&sockfd_lockval)); // LOCK + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + // try to close for safety + CloseSocketWithEpoll(fd); // not check result + fullock::flck_unlock_noshared_mutex(&sockfd_lockval); // UNLOCK + return false; + } + + // [NOTE] + // following method call is with locking sockfd_lockval. + // so should not connect to new servers while locking. + // + bool result = true; + bool is_close= false; + chmpxid_t chmpxid = CHM_INVALID_CHMPXID; + if(!ServerSockNotifyHup(fd, is_close, chmpxid)){ + ERR_CHMPRN("Something error occured in closing server socket(%d) by HUP.", fd); + result = false; + } + if(!is_close && !SlaveSockNotifyHup(fd, is_close)){ + ERR_CHMPRN("Something error occured in closing slave socket(%d) by HUP.", fd); + result = false; + } + if(!is_close && !AcceptingSockNotifyHup(fd, is_close)){ + ERR_CHMPRN("Something error occured in closing accepting socket(%d) by HUP.", fd); + result = false; + } + if(!is_close && !ControlSockNotifyHup(fd, is_close)){ + ERR_CHMPRN("Something error occured in closing control socket(%d) by HUP.", fd); + result = false; + } + if(!is_close){ + // try to close for safety + CloseSocketWithEpoll(fd); // not check result + } + + // Clear worker thread queue + // + // [NOTE] + // Must clear before close socket. + // + if(procthreads.HasThread()){ + int sock = static_cast(procthreads.GetSelfWorkerParam()); + if(sock == fd){ + // fd is worker thread's socket, so clear worker loop count(queued work count). + if(!procthreads.ClearSelfWorkerStatus()){ + ERR_CHMPRN("Something error occured in clearing worker thread loop count for sock(%d), but continue...", fd); + } + } + } + fullock::flck_unlock_noshared_mutex(&sockfd_lockval); // UNLOCK + + // do rechain RING when closed fd is for servers. + if(CHM_INVALID_CHMPXID != chmpxid){ + if(!ServerDownNotifyHup(chmpxid)){ + ERR_CHMPRN("Something error occured in setting chmpxid(0x%016" PRIx64 ") - fd(%d) to down status.", chmpxid, fd); + result = false; + } + } + + return result; +} + +bool ChmEventSock::ServerDownNotifyHup(chmpxid_t chmpxid) +{ + if(CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // backup operation mode. + bool is_operating = pImData->IsOperating(); + + // set status + chmpxsts_t status = pImData->GetServerStatus(chmpxid); + + CHANGE_CHMPXSTS_TO_DOWN(status); + if(!pImData->SetServerStatus(chmpxid, status)){ + ERR_CHMPRN("Could not set status(0x%016" PRIx64 ":%s) to chmpxid(0x%016" PRIx64 "), but continue...", status, STR_CHMPXSTS_FULL(status).c_str(), chmpxid); + } + + if(!is_server_mode){ + // do nothing no more for slave mode. + return true; + } + + // get next chmpxid on RING + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID != nextchmpxid){ + // rechain + bool is_rechain = false; + if(!CheckRechainRing(nextchmpxid, is_rechain)){ + ERR_CHMPRN("Something error occured in rehcaining RING, but continue..."); + }else{ + if(is_rechain){ + MSG_CHMPRN("Rechained RING to chmpxid(0x%016" PRIx64 ") for down chmpxid(0x%016" PRIx64 ").", nextchmpxid, chmpxid); + }else{ + MSG_CHMPRN("Not rechained RING to chmpxid(0x%016" PRIx64 ") for down chmpxid(0x%016" PRIx64 ").", nextchmpxid, chmpxid); + } + } + } + + // [NOTICE] + // If merging, at first stop merging before sending "SERVER_DOWN". + // Then do not care about status and pengins hash when receiving "SERVER_DOWN". + // + if(CHM_INVALID_CHMPXID == nextchmpxid){ + // there is no server on RING. + if(is_operating){ + if(!MergeAbort()){ + ERR_CHMPRN("Got server down notice during merging, but failed to abort merge."); + return false; + } + } + }else{ + if(is_operating){ + if(!PxComSendMergeAbort(nextchmpxid)){ + ERR_CHMPRN("Got server down notice during merging, but failed to abort merge. but continue..."); + } + } + // send SERVER_DOWN + if(!PxComSendServerDown(nextchmpxid, chmpxid)){ + ERR_CHMPRN("Failed to send down chmpx server information to chmpxid(0x%016" PRIx64 ").", nextchmpxid); + return false; + } + } + return true; +} + +// [NOTE] +// The method which calls this method locks sockfd_lockval. +// So this method returns assap without doing any. +// +bool ChmEventSock::ServerSockNotifyHup(int fd, bool& is_close, chmpxid_t& chmpxid) +{ + is_close= false; + chmpxid = CHM_INVALID_CHMPXID; + + // [NOTE] + // we not check fd and object empty because NotifyHup() already checks it. + // + if(!seversockmap.find(fd)){ + return true; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // get sock to chmpxid + if(CHM_INVALID_CHMPXID == (chmpxid = pImData->GetChmpxIdBySock(fd, CLOSETG_SERVERS))){ + ERR_CHMPRN("Could not find sock(%d) in servers list, but continue...", fd); + } + + // close socket at first.(check both server and slave) + seversockmap.erase(fd); // call default cb function + is_close = true; + + return true; +} + +// [NOTE] +// The method which calls this method locks sockfd_lockval. +// So this method returns assap without doing any. +// +bool ChmEventSock::SlaveSockNotifyHup(int fd, bool& is_close) +{ + is_close = false; + + // [NOTE] + // we not check fd and object empty because NotifyHup() already checks it. + // + if(!slavesockmap.find(fd)){ + return true; + } + // close socket + slavesockmap.erase(fd); + is_close = true; + + return true; +} + +// [NOTE] +// The method which calls this method locks sockfd_lockval. +// So this method returns assap without doing any. +// +bool ChmEventSock::AcceptingSockNotifyHup(int fd, bool& is_close) +{ + is_close = false; + + // [NOTE] + // we not check fd and object empty because NotifyHup() already checks it. + // + if(!acceptingmap.find(fd)){ + return true; + } + // close socket + acceptingmap.erase(fd); + is_close = true; + + return true; +} + +// [NOTE] +// The method which calls this method locks sockfd_lockval. +// So this method returns assap without doing any. +// +bool ChmEventSock::ControlSockNotifyHup(int fd, bool& is_close) +{ + is_close = false; + + // [NOTE] + // we not check fd and object empty because NotifyHup() already checks it. + // + if(!ctlsockmap.find(fd)){ + return true; + } + // close socket + ctlsockmap.erase(fd); + is_close = true; + + return true; +} + +//--------------------------------------------------------- +// Methods for Communication controls +//--------------------------------------------------------- +bool ChmEventSock::CloseSelfSocks(void) +{ + if(IsEmpty()){ + return true; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // close own liten sockets + int sock = CHM_INVALID_SOCK; + int ctlsock = CHM_INVALID_SOCK; + if(!pImData->GetSelfSocks(sock, ctlsock)){ + ERR_CHMPRN("Could not get self sock and ctlsock."); + return false; + } + if(CHM_INVALID_SOCK != sock){ + epoll_ctl(eqfd, EPOLL_CTL_DEL, sock, NULL); + } + if(CHM_INVALID_SOCK != ctlsock){ + epoll_ctl(eqfd, EPOLL_CTL_DEL, ctlsock, NULL); + } + CloseSSL(sock); + CloseSSL(ctlsock); + + // update sock in chmshm + bool result = true; + if(!pImData->SetSelfSocks(CHM_INVALID_SOCK, CHM_INVALID_SOCK)){ + ERR_CHMPRN("Could not clean self sock and ctlsock, but continue..."); + result = false; + } + + // update status in chmshm + chmpxsts_t status = pImData->GetSelfStatus(); + + CHANGE_CHMPXSTS_TO_DOWN(status); + if(!pImData->SetSelfStatus(status)){ + ERR_CHMPRN("Could not set status(0x%016" PRIx64 ":%s) for self.", status, STR_CHMPXSTS_FULL(status).c_str()); + result = false; + } + + return result; +} + +bool ChmEventSock::CloseFromSlavesSocks(void) +{ + if(IsEmpty()){ + return true; + } + // close sockets connecting from clients + slavesockmap.clear(); + + return true; +} + +bool ChmEventSock::CloseToServersSocks(void) +{ + if(IsEmpty()){ + return true; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // close all server ssl + seversockmap.clear(); // call default cb function + + // close sockets connecting to other servers. + if(!pImData->CloseSocks(CLOSETG_SERVERS)){ + ERR_CHMPRN("Failed to close connection to other servers."); + return false; + } + return true; +} + +bool ChmEventSock::CloseCtlportClientSocks(void) +{ + if(IsEmpty()){ + return true; + } + ctlsockmap.clear(); + return true; +} + +bool ChmEventSock::CloseFromSlavesAcceptingSocks(void) +{ + if(IsEmpty()){ + return true; + } + // close accepting sockets connecting from clients + acceptingmap.clear(); + return true; +} + +bool ChmEventSock::CloseSSL(int sock, bool with_sock) +{ + if(IsEmpty()){ + return true; + } + sslmap.erase(sock); + + if(with_sock){ + CHM_CLOSESOCK(sock); + } + return true; +} + +bool ChmEventSock::CloseAllSSL(void) +{ + if(IsEmpty()){ + return true; + } + sslmap.clear(); + return true; +} + +// +// [NOTE] +// Must lock sockfd_lockval outside. +// +bool ChmEventSock::CloseSocketWithEpoll(int sock) +{ + if(CHM_INVALID_SOCK == sock){ + return false; + } + if(0 != epoll_ctl(eqfd, EPOLL_CTL_DEL, sock, NULL)){ + // [NOTE] + // Sometimes epoll_ctl retuns error, because socket is not add epoll yet. + // + MSG_CHMPRN("Failed to delete socket(%d) from epoll event, probably already remove it.", sock); + } + CloseSSL(sock); + + return true; +} + +// +// [NOTICE] This method blocks receiving data, means waits connecting. +// (See. RawConnectServer method) +// +bool ChmEventSock::ConnectServer(chmpxid_t chmpxid, int& sock, bool without_set_imdata) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // [NOTE] + // This method checks only existing socket to chmpxid. Do not check lock + // status for socket and count of socket. + // + socklist_t socklist; + if(pImData->GetServerSock(chmpxid, socklist) && !socklist.empty()){ + // already connected. + sock = socklist.front(); + return true; + } + return RawConnectServer(chmpxid, sock, without_set_imdata, false); // Do not lock socket +} + +// +// [NOTICE] This method blocks receiving data, means waits connecting. +// +// This method try to connect chmpxid server, and send CHMPX_COM_CONINIT_REQ request +// after connecting the server and receive CHMPX_COM_CONINIT_RES. This is flow for +// connecting server. +// So that this method calls RawSend and RawReceive in PxComSendConinitReq(). +// +bool ChmEventSock::RawConnectServer(chmpxid_t chmpxid, int& sock, bool without_set_imdata, bool is_lock) +{ + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // get hostanme/port + string hostname; + short port = CHM_INVALID_PORT; + short ctlport = CHM_INVALID_PORT; + if(!pImData->GetServerBase(chmpxid, hostname, port, ctlport)){ + ERR_CHMPRN("Could not get hostname/port/ctlport by chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + + // try to connect + while(!fullock::flck_trylock_noshared_mutex(&sockfd_lockval)); // LOCK + if(CHM_INVALID_SOCK == (sock = ChmEventSock::Connect(hostname.c_str(), port, false, con_retry_count, con_wait_time))){ + // could not connect + MSG_CHMPRN("Could not connect to %s:%d, set status to DOWN.", hostname.c_str(), port); + fullock::flck_unlock_noshared_mutex(&sockfd_lockval); // UNLOCK + return false; + } + // connected + MSG_CHMPRN("Connected to %s:%d, set sock(%d).", hostname.c_str(), port, sock); + fullock::flck_unlock_noshared_mutex(&sockfd_lockval); // UNLOCK + + // SSL + CHMPXSSL ssldata; + if(!pImData->GetServerBase(chmpxid, ssldata)){ + ERR_CHMPRN("Failed to get SSL structure from chmpxid(0x%" PRIx64 ").", chmpxid); + CHM_CLOSESOCK(sock); + return false; + } + if(ssldata.is_ssl){ // Check whichever the terget server is ssl + // Get own keys + if(!pImData->GetSelfSsl(ssldata)){ + ERR_CHMPRN("Failed to get SSL structure from self chmpx."); + CHM_CLOSESOCK(sock); + return false; + } + // get SSL context + SSL_CTX* ctx = ChmEventSock::GetSSLContext((!ssldata.is_ca_file ? ssldata.capath : NULL), (ssldata.is_ca_file ? ssldata.capath : NULL), NULL, NULL, ssldata.slave_cert, ssldata.slave_prikey, ssldata.verify_peer); + if(!ctx){ + ERR_CHMPRN("Failed to get SSL context."); + CHM_CLOSESOCK(sock); + return false; + } + + // Accept SSL + SSL* ssl = ChmEventSock::ConnectSSL(ctx, sock, con_retry_count, con_wait_time); + if(!ssl){ + ERR_CHMPRN("Failed to connect SSL."); + CHM_CLOSESOCK(sock); + return false; + } + + // Set SSL mapping + sslmap.set(sock, ssl, true); // over write whtere exitsts or does not. + } + + // Send coninit_req + if(!PxComSendConinitReq(sock, chmpxid)){ + ERR_CHMPRN("Failed to send PCCOM_CONINIT_REQ to sock(%d).", sock); + CloseSSL(sock); + return false; + } + + // Receive resnponse + // + // [NOTICE] + // retry count is sock_retry_count + // + bool is_closed = false; + PCOMPKT pComPkt = NULL; + if(!ChmEventSock::RawReceive(sock, GetSSL(sock), is_closed, &pComPkt, 0L, false, sock_retry_count, sock_wait_time) || !pComPkt){ + ERR_CHMPRN("Failed to receive ComPkt from sock(%d), sock is %s.", sock, is_closed ? "closed" : "not closed"); + CloseSSL(sock); + CHM_Free(pComPkt); + return false; + } + + // Check response type + if(COM_PX2PX != pComPkt->head.type){ + ERR_CHMPRN("Received data is %s(%" PRIu64 ") type, which does not COM_PX2PX type from sock(%d).", STRCOMTYPE(pComPkt->head.type), pComPkt->head.type, sock); + CloseSSL(sock); + CHM_Free(pComPkt); + return false; + } + PPXCOM_ALL pChmpxCom = CHM_OFFSET(pComPkt, pComPkt->offset, PPXCOM_ALL); + if(CHMPX_COM_CONINIT_RES != pChmpxCom->val_head.type){ + ERR_CHMPRN("Received data is %s(%" PRIu64 ") type, which does not CHMPX_COM_CONINIT_RES type from sock(%d).", STRPXCOMTYPE(pChmpxCom->val_head.type), pChmpxCom->val_head.type, sock); + CloseSSL(sock); + CHM_Free(pComPkt); + return false; + } + + // Check response + PCOMPKT pResComPkt = NULL; // tmp + pxcomres_t coninit_result = CHMPX_COM_RES_ERROR; + if(!PxComReceiveConinitRes(&(pComPkt->head), pChmpxCom, &pResComPkt, coninit_result)){ + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + CloseSSL(sock); + CHM_Free(pComPkt); + CHM_Free(pResComPkt); + return false; + } + CHM_Free(pComPkt); + CHM_Free(pResComPkt); + + if(CHMPX_COM_RES_SUCCESS != coninit_result){ + ERR_CHMPRN("Connected to %s:%d, but could not allow from this server.", hostname.c_str(), port); + CloseSSL(sock); + return false; + } + + // Lock + if(is_lock){ + LockSendSock(sock); // LOCK SOCKET + } + + // Set IM data + if(!without_set_imdata){ + // set mapping + seversockmap.set(sock, chmpxid); + + // add event fd + struct epoll_event eqevent; + memset(&eqevent, 0, sizeof(struct epoll_event)); + eqevent.data.fd = sock; + eqevent.events = EPOLLIN | EPOLLET | EPOLLRDHUP; // EPOLLRDHUP is set, connecting to server socket is needed to know server side down. + if(-1 == epoll_ctl(eqfd, EPOLL_CTL_ADD, sock, &eqevent)){ + ERR_CHMPRN("Failed to add sock(port %d: sock %d) into epoll event(%d), errno=%d", port, sock, eqfd, errno); + seversockmap.erase(sock); + return false; + } + // set sock in server list and update status + if(!pImData->SetServerSocks(chmpxid, sock, CHM_INVALID_SOCK, SOCKTG_SOCK)){ + MSG_CHMPRN("Could not set sock(%d) to chmpxid(0x%016" PRIx64 ").", sock, chmpxid); + seversockmap.erase(sock); + return false; + } + } + return true; +} + +// +// This method try to connect all server, and updates chmshm status for each server +// when connecting servers. +// +bool ChmEventSock::ConnectServers(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + if(is_server_mode){ + ERR_CHMPRN("Chmpx mode does not slave mode."); + return false; + } + + for(chmpxpos_t pos = pImData->GetNextServerPos(CHM_INVALID_CHMPXLISTPOS, CHM_INVALID_CHMPXLISTPOS, false, false); CHM_INVALID_CHMPXLISTPOS != pos; pos = pImData->GetNextServerPos(CHM_INVALID_CHMPXLISTPOS, pos, false, false)){ + // get base information + string name; + socklist_t socklist; + short port; + short ctlport; + chmpxid_t chmpxid = CHM_INVALID_CHMPXID; + if(!pImData->GetServerBase(pos, name, chmpxid, port, ctlport) || CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Could not get serer name/chmpxid/port/ctlport for pos(%" PRIu64 ").", pos); + continue; + } + + // status check + chmpxsts_t status = pImData->GetServerStatus(chmpxid); + if(!IS_CHMPXSTS_SERVER(status) || !IS_CHMPXSTS_UP(status)){ + MSG_CHMPRN("chmpid(0x%016" PRIx64 ") which is pos(%" PRIu64 ") is status(0x%016" PRIx64 ":%s), not enough status to connecting.", chmpxid, pos, status, STR_CHMPXSTS_FULL(status).c_str()); + continue; + } + + // check socket to server and try to connect if not exist. + int sock = CHM_INVALID_SOCK; + if(!ConnectServer(chmpxid, sock, false) || CHM_INVALID_SOCK == sock){ // connect & set epoll & chmshm + // could not connect + ERR_CHMPRN("Could not connect to %s:%d, set status to DOWN.", name.c_str(), port); + + // [NOTICE] + // Other server status is updated, because DOWN status can not be sent by that server. + // + CHANGE_CHMPXSTS_TO_DOWN(status); + if(!pImData->SetServerStatus(chmpxid, status)){ + MSG_CHMPRN("Could not set status(0x%016" PRIx64 ":%s) by down to chmpid(%" PRIu64 ") which is pos(%" PRIu64 ").", status, STR_CHMPXSTS_FULL(status).c_str(), chmpxid, pos); + continue; + } + } + } + return true; +} + +bool ChmEventSock::ConnectRing(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Check self status, must be service out + if(!is_server_mode){ + ERR_CHMPRN("Chmpx mode does not server-mode."); + return false; + } + chmpxsts_t selfstatus = pImData->GetSelfStatus(); + if(!IS_CHMPXSTS_SERVER(selfstatus) || !IS_CHMPXSTS_UP(selfstatus)){ + ERR_CHMPRN("This server is status(0x%016" PRIx64 ":%s), not enough of self status for connecting.", selfstatus, STR_CHMPXSTS_FULL(selfstatus).c_str()); + return false; + } + + // Get self pos + chmpxpos_t selfpos = pImData->GetSelfServerPos(); + if(CHM_INVALID_CHMPXLISTPOS == selfpos){ + ERR_CHMPRN("Not found self chmpx data in list."); + return false; + } + + int sock = CHM_INVALID_SOCK; + chmpxpos_t pos = selfpos; // Start at self pos + for(pos = pImData->GetNextServerPos(selfpos, pos, true, true); CHM_INVALID_CHMPXLISTPOS != pos; pos = pImData->GetNextServerPos(selfpos, pos, true, true)){ + // get server information + string name; + short port; + short ctlport; + chmpxid_t chmpxid = CHM_INVALID_CHMPXID; + if(!pImData->GetServerBase(pos, name, chmpxid, port, ctlport)){ + ERR_CHMPRN("Could not get serer name/chmpxid/port/ctlport for pos(%" PRIu64 ").", pos); + continue; + } + + // status check + chmpxsts_t status = pImData->GetServerStatus(chmpxid); + if(!IS_CHMPXSTS_SERVER(status) || !IS_CHMPXSTS_UP(status)){ + MSG_CHMPRN("chmpid(0x%016" PRIx64 "):%s which is pos(%" PRIu64 ") is status(0x%01lx:%s), not enough status to connecting.", chmpxid, name.c_str(), pos, status, STR_CHMPXSTS_FULL(status).c_str()); + continue; + } + + // try to connect + sock = CHM_INVALID_SOCK; + if(!ConnectServer(chmpxid, sock, false) || CHM_INVALID_SOCK == sock){ // connect & set epoll & chmshm + // could not connect + ERR_CHMPRN("Could not connect to %s:%d, set status to DOWN.", name.c_str(), port); + }else{ + // send CHMPX_COM_JOIN_RING + CHMPXSVR selfchmpxsvr; + if(!pImData->GetSelfChmpxSvr(&selfchmpxsvr)){ + ERR_CHMPRN("Could not get self chmpx information, this error can not recover."); + return false; + } + if(!PxComSendJoinRing(chmpxid, &selfchmpxsvr)){ + // Failed to join. + ERR_CHMPRN("Failed to send CHMPX_COM_JOIN_RING to chmpxid(0x%016" PRIx64 ") on RING, so close connection to servers.", chmpxid); + if(!CloseToServersSocks()){ + ERR_CHMPRN("Failed to close connection to other servers, but continue..."); + } + return false; + } + break; + } + } + if(CHM_INVALID_SOCK == sock){ + // + // This case is there is no server UP on RING. + // It means this server is first UP on RING. + // + ERR_CHMPRN("There is no server UP on RING."); + return true; + } + return true; +} + +bool ChmEventSock::CloseRechainRing(chmpxid_t nowchmpxid) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + chmpxpos_t selfpos = pImData->GetSelfServerPos(); + if(CHM_INVALID_CHMPXLISTPOS == selfpos){ + ERR_CHMPRN("Not found self chmpx data in list."); + return false; + } + + chmpxpos_t pos = selfpos; // Start at self pos + for(pos = pImData->GetNextServerPos(selfpos, pos, true, true); CHM_INVALID_CHMPXLISTPOS != pos; pos = pImData->GetNextServerPos(selfpos, pos, true, true)){ + string name; + socklist_t socklist; + chmpxid_t chmpxid = CHM_INVALID_CHMPXID; + short port = CHM_INVALID_PORT; + short ctlport = CHM_INVALID_PORT; + if(!pImData->GetServerBase(pos, name, chmpxid, port, ctlport) || CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Could not get chmpxid by position(%" PRIu64 ") in server list, but continue...", pos); + continue; + } + if(nowchmpxid == chmpxid){ + continue; + } + + // [NOTE] + // Close all sockets to servers except next server. + // + socklist.clear(); + if(!pImData->GetServerSock(chmpxid, socklist) || socklist.empty()){ + MSG_CHMPRN("Not have connection to chmpxid(0x%016" PRIx64 ").", chmpxid); + continue; + } + // close + for(socklist_t::iterator iter = socklist.begin(); iter != socklist.end(); iter = socklist.erase(iter)){ + seversockmap.erase(*iter); + } + } + return true; +} + +bool ChmEventSock::RechainRing(chmpxid_t newchmpxid) +{ + if(CHM_INVALID_CHMPXID == newchmpxid){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + // connect new server. + int newsock = CHM_INVALID_SOCK; + if(!ConnectServer(newchmpxid, newsock, false) || CHM_INVALID_SOCK == newsock){ // update chmshm + ERR_CHMPRN("Could not connect new server chmpxid=0x%016" PRIx64 ", and could not recover about this error.", newchmpxid); + return false; + } + + if(!CloseRechainRing(newchmpxid)){ + ERR_CHMPRN("Something error occured, close old chain ring except chmpxid(0x%016" PRIx64 ").", newchmpxid); + return false; + } + return true; +} + +bool ChmEventSock::CheckRechainRing(chmpxid_t newchmpxid, bool& is_rechain) +{ + if(CHM_INVALID_CHMPXID == newchmpxid){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + is_rechain = false; + + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + chmpxid_t nowchmpxid = pImData->GetNextRingChmpxId(); + if(nowchmpxid == newchmpxid){ + socklist_t nowsocklist; + if(pImData->GetServerSock(nowchmpxid, nowsocklist) && !nowsocklist.empty()){ + MSG_CHMPRN("New next chmpxid(0x%016" PRIx64 ") is same as now chmpxid(0x%016" PRIx64 "), so do not need rechain RING.", newchmpxid, nowchmpxid); + return true; + } + } + + chmpxpos_t selfpos = pImData->GetSelfServerPos(); + if(CHM_INVALID_CHMPXLISTPOS == selfpos){ + ERR_CHMPRN("Not found self chmpx data in list."); + return false; + } + + chmpxpos_t pos = selfpos; // Start at self pos + string name; + chmpxid_t chmpxid; + short port; + short ctlport; + for(pos = pImData->GetNextServerPos(selfpos, pos, true, true); CHM_INVALID_CHMPXLISTPOS != pos; pos = pImData->GetNextServerPos(selfpos, pos, true, true)){ + chmpxid = CHM_INVALID_CHMPXID; + port = CHM_INVALID_PORT; + ctlport = CHM_INVALID_PORT; + if(!pImData->GetServerBase(pos, name, chmpxid, port, ctlport) || CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Could not get chmpxid by position(%" PRIu64 ") in server list, but continue...", pos); + continue; + } + if(nowchmpxid == chmpxid){ + socklist_t nowsocklist; + if(pImData->GetServerSock(nowchmpxid, nowsocklist) && !nowsocklist.empty()){ + MSG_CHMPRN("Found now next chmpxid(0x%016" PRIx64 ") before new chmpxid(0x%016" PRIx64 "), so do not need rechain RING.", nowchmpxid, newchmpxid); + }else{ + // do rechain + if(!RechainRing(nowchmpxid)){ + ERR_CHMPRN("Failed to rechain RING for chmpxid(0x%016" PRIx64 ").", nowchmpxid); + }else{ + MSG_CHMPRN("Succeed to rechain RING for chmpxid(0x%016" PRIx64 ").", nowchmpxid); + is_rechain = true; + } + } + return true; + + }else if(newchmpxid == chmpxid){ + MSG_CHMPRN("Found new chmpxid(0x%016" PRIx64 ") before next chmpxid(0x%016" PRIx64 "), so need rechain RING.", newchmpxid, nowchmpxid); + + // do rechain + if(!RechainRing(newchmpxid)){ + ERR_CHMPRN("Failed to rechain RING for chmpxid(0x%016" PRIx64 ").", newchmpxid); + }else{ + MSG_CHMPRN("Succeed to rechain RING for chmpxid(0x%016" PRIx64 ").", newchmpxid); + is_rechain = true; + } + return true; + } + } + // why...? + MSG_CHMPRN("There is no server UP on RING(= there is not the server of chmpxid(0x%016" PRIx64 ") in server list), so can not rechain RING.", newchmpxid); + return true; +} + +// +// If the RING from this server is broken, connect to next server in RING for recovering. +// Returns the chmpxid of next connected server in RING when TRUE. +// +chmpxid_t ChmEventSock::GetNextRingChmpxId(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + chmpxid_t chmpxid; + + if(CHM_INVALID_CHMPXID != (chmpxid = pImData->GetNextRingChmpxId())){ + MSG_CHMPRN("Not need to connect rechain RING."); + return chmpxid; + } + + chmpxpos_t selfpos = pImData->GetSelfServerPos(); + if(CHM_INVALID_CHMPXLISTPOS == selfpos){ + ERR_CHMPRN("Not found self chmpx data in list."); + return CHM_INVALID_CHMPXID; + } + + chmpxpos_t pos = selfpos; // Start at self pos + string name; + short port; + short ctlport; + for(pos = pImData->GetNextServerPos(selfpos, pos, true, true); CHM_INVALID_CHMPXLISTPOS != pos; pos = pImData->GetNextServerPos(selfpos, pos, true, true)){ + chmpxid = CHM_INVALID_CHMPXID; + port = CHM_INVALID_PORT; + ctlport = CHM_INVALID_PORT; + + if(!pImData->GetServerBase(pos, name, chmpxid, port, ctlport) || CHM_INVALID_CHMPXID == chmpxid){ + WAN_CHMPRN("Could not get chmpxid by position(%" PRIu64 ") in server list, but continue...", pos); + continue; + } + // status check + chmpxsts_t status = pImData->GetServerStatus(chmpxid); + if(IS_CHMPXSTS_SERVER(status) && IS_CHMPXSTS_UP(status)){ + // try to connect server. + int sock = CHM_INVALID_SOCK; + if(ConnectServer(chmpxid, sock, false) && CHM_INVALID_SOCK != sock){ // update chmshm + MSG_CHMPRN("Connect new server chmpxid(0x%016" PRIx64 ") to sock(%d), and recover RING.", chmpxid, sock); + return chmpxid; + }else{ + ERR_CHMPRN("Could not connect new server chmpxid(0x%016" PRIx64 "), so try to next server.", chmpxid); + } + } + } + WAN_CHMPRN("There is no connect-able server in RING. probably there is no UP server in RING."); + return CHM_INVALID_CHMPXID; +} + +// Check next chmpxid which is over step deperture chmpxid in RING. +// On this case, if send packet to next chmpxid, it includes loop packet in RING. +// So we need to check next and deperture chmpxid before transfer packet. +// +bool ChmEventSock::IsSafeDeptAndNextChmpxId(chmpxid_t dept_chmpxid, chmpxid_t next_chmpxid) +{ + if(CHM_INVALID_CHMPXID == dept_chmpxid || CHM_INVALID_CHMPXID == next_chmpxid){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + if(dept_chmpxid == next_chmpxid){ + return true; + } + + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + chmpxpos_t selfpos = pImData->GetSelfServerPos(); + if(CHM_INVALID_CHMPXLISTPOS == selfpos){ + ERR_CHMPRN("Not found self chmpx data in list."); + return false; + } + + chmpxpos_t pos = selfpos; // Start at self pos + string name; + chmpxid_t chmpxid; + short port; + short ctlport; + for(pos = pImData->GetNextServerPos(selfpos, pos, true, true); CHM_INVALID_CHMPXLISTPOS != pos; pos = pImData->GetNextServerPos(selfpos, pos, true, true)){ + chmpxid = CHM_INVALID_CHMPXID; + port = CHM_INVALID_PORT; + ctlport = CHM_INVALID_PORT; + if(!pImData->GetServerBase(pos, name, chmpxid, port, ctlport) || CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Could not get chmpxid by position(%" PRIu64 ") in server list, but continue...", pos); + continue; + } + if(next_chmpxid == chmpxid){ + MSG_CHMPRN("Found next chmpxid(0x%016" PRIx64 ") before deperture chmpxid(0x%016" PRIx64 "), so can send this pkt.", next_chmpxid, dept_chmpxid); + return true; + } + if(dept_chmpxid == chmpxid){ + MSG_CHMPRN("Found deperture chmpxid(0x%016" PRIx64 ") before next chmpxid(0x%016" PRIx64 "), so can not send this pkt.", dept_chmpxid, next_chmpxid); + return false; + } + } + // why...? + MSG_CHMPRN("There is no server UP on RING(= there is not the server of chmpxid(0x%016" PRIx64 ") in server list), so can not send this pkt.", next_chmpxid); + return false; +} + +bool ChmEventSock::Accept(int sock) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + while(!fullock::flck_trylock_noshared_mutex(&sockfd_lockval)); // LOCK + + // accept + int newsock; + struct sockaddr_storage from; + socklen_t fromlen = static_cast(sizeof(struct sockaddr_storage)); + if(-1 == (newsock = accept(sock, reinterpret_cast(&from), &fromlen))){ + if(EAGAIN == errno || EWOULDBLOCK == errno){ + MSG_CHMPRN("There is no waiting accept request for sock(%d)", sock); + fullock::flck_unlock_noshared_mutex(&sockfd_lockval); // UNLOCK + return true; // return succeed + } + ERR_CHMPRN("Failed to accept from sock(%d), error=%d", sock, errno); + fullock::flck_unlock_noshared_mutex(&sockfd_lockval); // UNLOCK + return false; + } + MSG_CHMPRN("Accept new socket(%d)", newsock); + fullock::flck_unlock_noshared_mutex(&sockfd_lockval); // UNLOCK + + // get hostanme for accessing control + string stripaddress; + string strhostname; + if(!ChmNetDb::CvtAddrInfoToIpAddress(&from, fromlen, stripaddress)){ + ERR_CHMPRN("Failed to convert addrinfo(new sock=%d) to ipaddress.", newsock); + CHM_CLOSESOCK(newsock); + return false; + } + if(!ChmNetDb::Get()->GetHostname(stripaddress.c_str(), strhostname, true)){ + ERR_CHMPRN("Failed to convert FQDN from %s.", stripaddress.c_str()); + CHM_CLOSESOCK(newsock); + return false; + } + + // add tempolary accepting socket mapping.(before adding epoll for multi threading) + acceptingmap.set(newsock, strhostname); + + // check ACL + if(!pImData->IsAllowHostname(strhostname.c_str())){ + // Not allow accessing from slave. + ERR_CHMPRN("Denied %s(sock:%d) by not allowed.", strhostname.c_str(), newsock); + if(!NotifyHup(newsock)){ + ERR_CHMPRN("Failed to closing \"accepting socket\" for %s, but continue...", strhostname.c_str()); + } + return false; + } + + // Set nonblock + if(!ChmEventSock::SetNonblocking(newsock)){ + WAN_CHMPRN("Failed to nonblock socket(sock:%d), but continue...", newsock); + } + + // + // Push accepting socket which is not allowed completely yet. + // + MSG_CHMPRN("%s(sock:%d) is accepting tempolary, but not allowed completely yet..", strhostname.c_str(), newsock); + + // SSL + CHMPXSSL ssldata; + if(!pImData->GetSelfSsl(ssldata)){ + ERR_CHMPRN("Failed to get SSL structure from self chmpx."); + if(!NotifyHup(newsock)){ + ERR_CHMPRN("Failed to closing \"accepting socket\" for %s, but continue...", strhostname.c_str()); + } + return false; + } + if(ssldata.is_ssl){ + // get SSL context + SSL_CTX* ctx = ChmEventSock::GetSSLContext((!ssldata.is_ca_file ? ssldata.capath : NULL), (ssldata.is_ca_file ? ssldata.capath : NULL), ssldata.server_cert, ssldata.server_prikey, ssldata.slave_cert, ssldata.slave_prikey, ssldata.verify_peer); + if(!ctx){ + ERR_CHMPRN("Failed to get SSL context."); + if(!NotifyHup(newsock)){ + ERR_CHMPRN("Failed to closing \"accepting socket\" for %s, but continue...", strhostname.c_str()); + } + return false; + } + + // Accept SSL + SSL* ssl = ChmEventSock::AcceptSSL(ctx, newsock, con_retry_count, con_wait_time); + if(!ssl){ + ERR_CHMPRN("Failed to accept SSL."); + if(!NotifyHup(newsock)){ + ERR_CHMPRN("Failed to closing \"accepting socket\" for %s, but continue...", strhostname.c_str()); + } + return false; + } + + // Set SSL mapping + sslmap.set(newsock, ssl, true); // over write + } + + // add event fd + struct epoll_event eqevent; + memset(&eqevent, 0, sizeof(struct epoll_event)); + eqevent.data.fd = newsock; + eqevent.events = EPOLLIN | EPOLLET | EPOLLRDHUP; // EPOLLRDHUP is set. + if(-1 == epoll_ctl(eqfd, EPOLL_CTL_ADD, newsock, &eqevent)){ + ERR_CHMPRN("Failed to add sock(sock %d) into epoll event(%d), errno=%d", newsock, eqfd, errno); + if(!NotifyHup(newsock)){ + ERR_CHMPRN("Failed to closing \"accepting socket\" for %s, but continue...", strhostname.c_str()); + } + return false; + } + // + // CONINIT_REQ will get in main event loop. + // + return true; +} + +// Accept socket for control port. +// The socket does not have chmpxid and not need to set chmshm. +// The sock value has only internal this class. +// +bool ChmEventSock::AcceptCtlport(int ctlsock) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // accept + int newctlsock; + struct sockaddr_storage from; + socklen_t fromlen = static_cast(sizeof(struct sockaddr_storage)); + if(-1 == (newctlsock = accept(ctlsock, reinterpret_cast(&from), &fromlen))){ + if(EAGAIN == errno || EWOULDBLOCK == errno){ + MSG_CHMPRN("There is no waiting accept request for ctlsock(%d)", ctlsock); + return false; + } + ERR_CHMPRN("Failed to accept from ctlsock(%d), error=%d", ctlsock, errno); + return false; + } + // add internal mapping + ctlsockmap.set(newctlsock, CHM_INVALID_CHMPXID); // control sock does not have chmpxid + + // Convert IPv4-mapped IPv6 addresses to plain IPv4. + if(!ChmNetDb::CvtV4MappedAddrInfo(&from, fromlen)){ + ERR_CHMPRN("Something error occured during converting IPv4-mapped IPv6 addresses to plain IPv4, but continue..."); + } + + // get hostanme for accessing control + string stripaddress; + string strhostname; + if(!ChmNetDb::CvtAddrInfoToIpAddress(&from, fromlen, stripaddress)){ + ERR_CHMPRN("Failed to convert addrinfo(new ctlsock=%d) to ipaddress.", newctlsock); + if(!NotifyHup(newctlsock)){ + ERR_CHMPRN("Failed to closing \"from control socket\" for chmpxid(0x%016" PRIx64 "), but continue...", CHM_INVALID_CHMPXID); + } + return false; + } + if(!ChmNetDb::Get()->GetHostname(stripaddress.c_str(), strhostname, true)){ + ERR_CHMPRN("Failed to convert FQDN from %s.", stripaddress.c_str()); + if(!NotifyHup(newctlsock)){ + ERR_CHMPRN("Failed to closing \"from control socket\" for chmpxid(0x%016" PRIx64 "), but continue...", CHM_INVALID_CHMPXID); + } + return false; + } + + // check ACL + if(!pImData->IsAllowHostname(strhostname.c_str())){ + // Not allow accessing from slave. + ERR_CHMPRN("Denied %s(ctlsock:%d) by not allowed.", strhostname.c_str(), newctlsock); + if(!NotifyHup(newctlsock)){ + ERR_CHMPRN("Failed to closing \"from control socket\" for chmpxid(0x%016" PRIx64 "), but continue...", CHM_INVALID_CHMPXID); + } + return false; + } + + // Set nonblock + if(!ChmEventSock::SetNonblocking(newctlsock)){ + WAN_CHMPRN("Failed to nonblock socket(sock:%d), but continue...", newctlsock); + } + + // add event fd + struct epoll_event eqevent; + memset(&eqevent, 0, sizeof(struct epoll_event)); + eqevent.data.fd = newctlsock; + eqevent.events = EPOLLIN | EPOLLET | EPOLLRDHUP; // EPOLLRDHUP is set. + if(-1 == epoll_ctl(eqfd, EPOLL_CTL_ADD, newctlsock, &eqevent)){ + ERR_CHMPRN("Failed to add socket(ctlsock %d) into epoll event(%d), errno=%d", newctlsock, eqfd, errno); + if(!NotifyHup(newctlsock)){ + ERR_CHMPRN("Failed to closing \"from control socket\" for chmpxid(0x%016" PRIx64 "), but continue...", CHM_INVALID_CHMPXID); + } + return false; + } + + return true; +} + +// [NOTICE] +// This function is updating all server status from a server on RING. +// This method opens the port of one of servers, and sends CHMPX_COM_STATUS_REQ, +// receives CHMPX_COM_STATUS_RES, updates all server status by result. +// This method uses the socket for that port, the socket is connecting very short time. +// After updating all server status from the result, this method closes that socket ASSAP. +// +bool ChmEventSock::InitialAllServerStatus(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Connect to ONE of servers. + int sock = CHM_INVALID_SOCK; + chmpxid_t chmpxid = CHM_INVALID_CHMPXID; + for(chmpxpos_t pos = pImData->GetNextServerPos(CHM_INVALID_CHMPXLISTPOS, CHM_INVALID_CHMPXLISTPOS, true, false); CHM_INVALID_CHMPXLISTPOS != pos; pos = pImData->GetNextServerPos(CHM_INVALID_CHMPXLISTPOS, pos, true, false), sock = CHM_INVALID_SOCK){ + string name; + short port; + short ctlport; + if(!pImData->GetServerBase(pos, name, chmpxid, port, ctlport)){ + ERR_CHMPRN("Could not get serer name/chmpxid/port/ctlport for pos(%" PRIu64 ").", pos); + continue; + } + if(ConnectServer(chmpxid, sock, true)){ // connect & not set epoll & not chmshm + // connected. + break; + } + MSG_CHMPRN("Could not connect to %s:%d, try to connect next server.", name.c_str(), port); + } + if(CHM_INVALID_SOCK == sock){ + MSG_CHMPRN("There is no server to connect port, so it means any server ready up."); + return true; // Succeed to udate + } + + // Send request + // + // [NOTE] + // This method is for initializing chmpx, so do not need to lock socket. + // + if(!PxComSendStatusReq(sock, chmpxid)){ + ERR_CHMPRN("Failed to send PXCOM_STATUS_REQ."); + CloseSSL(sock); + return false; + } + + // Receive resnponse + // + // [NOTICE] + // retry count is sock_retry_count + // + bool is_closed = false; + PCOMPKT pComPkt = NULL; + if(!ChmEventSock::RawReceive(sock, GetSSL(sock), is_closed, &pComPkt, 0L, false, sock_retry_count, sock_wait_time) || !pComPkt){ + ERR_CHMPRN("Failed to receive ComPkt from sock(%d), sock is %s.", sock, is_closed ? "closed" : "not closed"); + CloseSSL(sock); + CHM_Free(pComPkt); + return false; + } + + // Check response type + if(COM_PX2PX != pComPkt->head.type){ + ERR_CHMPRN("Received data is %s(%" PRIu64 ") type, which does not COM_PX2PX type from sock(%d).", STRCOMTYPE(pComPkt->head.type), pComPkt->head.type, sock); + CloseSSL(sock); + CHM_Free(pComPkt); + return false; + } + PPXCOM_ALL pChmpxCom = CHM_OFFSET(pComPkt, pComPkt->offset, PPXCOM_ALL); + if(CHMPX_COM_STATUS_RES != pChmpxCom->val_head.type){ + ERR_CHMPRN("Received data is %s(%" PRIu64 ") type, which does not CHMPX_COM_STATUS_RES type from sock(%d).", STRPXCOMTYPE(pChmpxCom->val_head.type), pChmpxCom->val_head.type, sock); + CloseSSL(sock); + CHM_Free(pComPkt); + return false; + } + + // Check response & Do merge + PCOMPKT pResComPkt = NULL; // tmp + if(!PxComReceiveStatusRes(&(pComPkt->head), pChmpxCom, &pResComPkt, true)){ + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + CloseSSL(sock); + CHM_Free(pComPkt); + CHM_Free(pResComPkt); + return false; + } + CloseSSL(sock); + CHM_Free(pComPkt); + CHM_Free(pResComPkt); + + return true; +} + +bool ChmEventSock::Processing(PCOMPKT pComPkt) +{ + if(!pComPkt){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); // not used + + if(COM_PX2PX == pComPkt->head.type){ + // check length + if(pComPkt->length <= sizeof(COMPKT) || 0 == pComPkt->offset){ + ERR_CHMPRN("ComPkt type(%" PRIu64 ":%s) has invalid values.", pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + return false; + } + // following datas + PPXCOM_ALL pChmpxCom = CHM_OFFSET(pComPkt, pComPkt->offset, PPXCOM_ALL); + + if(CHMPX_COM_STATUS_REQ == pChmpxCom->val_head.type){ + PCOMPKT pResComPkt = NULL; + if(!PxComReceiveStatusReq(&(pComPkt->head), pChmpxCom, &pResComPkt) || !pResComPkt){ + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + CHM_Free(pResComPkt); + return false; + } + // send response + if(!Send(pResComPkt, NULL, 0L)){ + ERR_CHMPRN("Sending response ComPkt type(%" PRIu64 ":%s) against ComPkt type(%" PRIu64 ":%s), Something error occured.", pResComPkt->head.type, STRCOMTYPE(pResComPkt->head.type), pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + CHM_Free(pResComPkt); + return false; + } + CHM_Free(pResComPkt); + + }else if(CHMPX_COM_STATUS_RES == pChmpxCom->val_head.type){ + // processing + PCOMPKT pResComPkt = NULL; + if(!PxComReceiveStatusRes(&(pComPkt->head), pChmpxCom, &pResComPkt, false) || pResComPkt){ // should be pResComPkt=NULL + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + CHM_Free(pResComPkt); + return false; + } + + // If this pkt receiver is this server, check status and merging. + // + if(is_server_mode && pComPkt->head.term_ids.chmpxid == pImData->GetSelfChmpxId()){ + chmpxsts_t status = pImData->GetSelfStatus(); + + // If change self status after processing, so need to update status to all servers. + chmpxid_t to_chmpxid = GetNextRingChmpxId(); + CHMPXSVR selfchmpxsvr; + if(!pImData->GetSelfChmpxSvr(&selfchmpxsvr)){ + ERR_CHMPRN("Could not get self chmpx information,"); + }else{ + if(CHM_INVALID_CHMPXID == to_chmpxid){ + // no server found, finish doing so pending hash updated self. + WAN_CHMPRN("Could not get to chmpxid, probably there is no server without self chmpx on RING. So only sending status update to slaves."); + if(!PxComSendSlavesStatusChange(&selfchmpxsvr)){ + ERR_CHMPRN("Failed to send self status change to slaves, but continue..."); + } + }else{ + if(!PxComSendStatusChange(to_chmpxid, &selfchmpxsvr)){ + ERR_CHMPRN("Failed to send self status change, but continue..."); + } + } + } + + // When this server is up and after joining ring and the status is changed pending, do merge. + // + // [NOTICE] + // MergeChmpxSvrs() in PxComReceiveStatusRes() can change self status from CHMPXSTS_SRVIN_DOWN_NORMAL or + // CHMPXSTS_SRVIN_DOWN_DELPENDING to CHMPXSTS_SRVIN_UP_MERGING, if it is neeed. + // If status is changed, we need to start merging. + // + if(is_auto_merge){ + if(!IS_CHMPXSTS_NOTHING(status)){ + // start merge automatically + if(CHM_INVALID_CHMPXID == to_chmpxid){ + if(!MergeStart()){ + ERR_CHMPRN("Failed to merge or complete merge for \"server up and automatically merging\"."); + return false; + } + }else{ + if(!RequestMergeStart()){ + ERR_CHMPRN("Failed to merge or complete merge for \"server up and automatically merging\"."); + return false; + } + } + } + } + } + CHM_Free(pResComPkt); + + }else if(CHMPX_COM_CONINIT_REQ == pChmpxCom->val_head.type){ + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured, so exit with no processing.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + return false; + + }else if(CHMPX_COM_CONINIT_RES == pChmpxCom->val_head.type){ + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured, so exit with no processing.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + return false; + + }else if(CHMPX_COM_JOIN_RING == pChmpxCom->val_head.type){ + PCOMPKT pResComPkt = NULL; + if(!PxComReceiveJoinRing(&(pComPkt->head), pChmpxCom, &pResComPkt)){ + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + + // [NOTICE] + // Close all socket, it occurres epoll event on the other servers. + // Because other servers send server down notify, this server do + // nothing. + // + if(!CloseSelfSocks()){ + ERR_CHMPRN("Failed to close self sock and ctlsock, but continue..."); + } + if(!CloseFromSlavesSocks()){ + ERR_CHMPRN("Failed to close connection from clients, but continue..."); + } + if(!CloseToServersSocks()){ + ERR_CHMPRN("Failed to close connection to other servers, but continue..."); + } + if(!CloseCtlportClientSocks()){ + ERR_CHMPRN("Failed to close control port connection from clients, but continue..."); + } + return false; + } + if(pResComPkt){ + // send(transfer) compkt + if(!Send(pResComPkt, NULL, 0L)){ + ERR_CHMPRN("Sending ComPkt type(%" PRIu64 ":%s) against ComPkt type(%" PRIu64 ":%s), Something error occured.", pResComPkt->head.type, STRCOMTYPE(pResComPkt->head.type), pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + CHM_Free(pResComPkt); + return false; + } + CHM_Free(pResComPkt); + }else{ + // Success to join on RING, do next step(update pending hash) + MSG_CHMPRN("Succeed to join RING, next step which is updating status and server list runs automatically."); + } + + }else if(CHMPX_COM_STATUS_UPDATE == pChmpxCom->val_head.type){ + PCOMPKT pResComPkt = NULL; + if(!PxComReceiveStatusUpdate(&(pComPkt->head), pChmpxCom, &pResComPkt)){ + // Failed Status Update + // (recoverd status update in receive funcion if possible) + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + return false; + } + if(pResComPkt){ + // Success Status Update, send(transfer) compkt to next server + if(!Send(pResComPkt, NULL, 0L)){ + ERR_CHMPRN("Sending ComPkt type(%" PRIu64 ":%s) against ComPkt type(%" PRIu64 ":%s), Something error occured.", pResComPkt->head.type, STRCOMTYPE(pResComPkt->head.type), pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + CHM_Free(pResComPkt); + return false; + } + CHM_Free(pResComPkt); + }else{ + // Success Status Update, wait to receive merging...(Nothing to do here) + } + + }else if(CHMPX_COM_STATUS_CONFIRM == pChmpxCom->val_head.type){ + PCOMPKT pResComPkt = NULL; + if(!PxComReceiveStatusConfirm(&(pComPkt->head), pChmpxCom, &pResComPkt)){ + // Failed Status Confirm + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + return false; + } + // Success Status Confirm + if(pResComPkt){ + // and send(transfer) compkt to next server + if(!Send(pResComPkt, NULL, 0L)){ + ERR_CHMPRN("Sending ComPkt type(%" PRIu64 ":%s) against ComPkt type(%" PRIu64 ":%s), Something error occured.", pResComPkt->head.type, STRCOMTYPE(pResComPkt->head.type), pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + CHM_Free(pResComPkt); + return false; + } + CHM_Free(pResComPkt); + } + + }else if(CHMPX_COM_STATUS_CHANGE == pChmpxCom->val_head.type){ + PCOMPKT pResComPkt = NULL; + if(!PxComReceiveStatusChange(&(pComPkt->head), pChmpxCom, &pResComPkt)){ + // Failed + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + return false; + } + if(pResComPkt){ + // Success, send(transfer) compkt to next server + if(!Send(pResComPkt, NULL, 0L)){ + ERR_CHMPRN("Sending ComPkt type(%" PRIu64 ":%s) against ComPkt type(%" PRIu64 ":%s), Something error occured.", pResComPkt->head.type, STRCOMTYPE(pResComPkt->head.type), pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + CHM_Free(pResComPkt); + return false; + } + CHM_Free(pResComPkt); + }else{ + // Success + MSG_CHMPRN("Succeed CHMPXCOM type(%" PRIu64 ":%s).", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + } + + // + // If any server chmpx is up, need to check up and connect it on slave mode. + // + if(!is_server_mode){ + if(!ConnectServers()){ + ERR_CHMPRN("Something error occurred during connecting server after status changed on slave mode chmpx, but continue..."); + } + } + + }else if(CHMPX_COM_MERGE_START == pChmpxCom->val_head.type){ + PCOMPKT pResComPkt = NULL; + if(!PxComReceiveMergeStart(&(pComPkt->head), pChmpxCom, &pResComPkt)){ + // Failed + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + return false; + } + if(pResComPkt){ + // Success, at first send(transfer) compkt to next server + if(!Send(pResComPkt, NULL, 0L)){ + ERR_CHMPRN("Sending ComPkt type(%" PRIu64 ":%s) against ComPkt type(%" PRIu64 ":%s), Something error occured.", pResComPkt->head.type, STRCOMTYPE(pResComPkt->head.type), pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + CHM_Free(pResComPkt); + return false; + } + CHM_Free(pResComPkt); + } + // start merging + // + // [NOTICE] + // must do it here after received and transferred "merge start". + // because pending hash value must not change before receiving "merge start". + // (MergeStart method sends "status change" in it) + // + if(!MergeStart()){ + WAN_CHMPRN("Failed to start merge."); + return false; + } + + }else if(CHMPX_COM_MERGE_ABORT == pChmpxCom->val_head.type){ + PCOMPKT pResComPkt = NULL; + if(!PxComReceiveMergeAbort(&(pComPkt->head), pChmpxCom, &pResComPkt)){ + // Failed Status Confirm + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + return false; + } + if(pResComPkt){ + // Success merge complete, at first send(transfer) compkt to next server + if(!Send(pResComPkt, NULL, 0L)){ + ERR_CHMPRN("Sending ComPkt type(%" PRIu64 ":%s) against ComPkt type(%" PRIu64 ":%s), Something error occured.", pResComPkt->head.type, STRCOMTYPE(pResComPkt->head.type), pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + CHM_Free(pResComPkt); + return false; + } + CHM_Free(pResComPkt); + } + + // abort merging + // + // [NOTICE] + // must do it here after received and transferred "merge abort". + // because pending hash value must not change before receiving "merge abort". + // (MergeComplete method sends "status change" in it) + // + if(!MergeAbort()){ + WAN_CHMPRN("Failed to abort merge."); + return false; + } + + // [NOTICE] + // If there are CHMPXSTS_SRVIN_DOWN_DELETED status server, that server can not send + // "change status" by itself, so that if this server is start server of sending "merge abort", + // this server send "status change" instead of down server. + // + if(pComPkt->head.dept_ids.chmpxid == pImData->GetSelfChmpxId()){ + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + chmpxid_t downchmpxid; + + // [NOTICE] + // Any DOWN status server does not have SUSPEND status. + // + while(CHM_INVALID_CHMPXID != (downchmpxid = pImData->GetChmpxIdByStatus(CHMPXSTS_SRVIN_DOWN_DELETED))){ + // status update + if(!pImData->SetServerStatus(downchmpxid, CHMPXSTS_SRVIN_DOWN_DELPENDING)){ + ERR_CHMPRN("Failed to update status(CHMPXSTS_SRVIN_DOWN_DELPENDING) for down server(0x%016" PRIx64 "), but continue...", downchmpxid); + continue; + } + // send status change + CHMPXSVR chmpxsvr; + if(!pImData->GetChmpxSvr(downchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Could not get chmpx information for down server(0x%016" PRIx64 "), but continue...", downchmpxid); + continue; + } + if(CHM_INVALID_CHMPXID == nextchmpxid){ + if(!PxComSendSlavesStatusChange(&chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change to slaves, but continue..."); + } + }else{ + if(!PxComSendStatusChange(nextchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change, but continue..."); + } + } + } + } + + }else if(CHMPX_COM_MERGE_COMPLETE == pChmpxCom->val_head.type){ + PCOMPKT pResComPkt = NULL; + if(!PxComReceiveMergeComplete(&(pComPkt->head), pChmpxCom, &pResComPkt)){ + // Failed + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + return false; + } + if(pResComPkt){ + // Success, at first send(transfer) compkt to next server + if(!Send(pResComPkt, NULL, 0L)){ + ERR_CHMPRN("Sending ComPkt type(%" PRIu64 ":%s) against ComPkt type(%" PRIu64 ":%s), Something error occured.", pResComPkt->head.type, STRCOMTYPE(pResComPkt->head.type), pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + CHM_Free(pResComPkt); + return false; + } + CHM_Free(pResComPkt); + } + + // complete merging + // + // [NOTICE] + // must do it here after received and transferred "merge complete". + // because pending hash value must not change before receiving "merge complete". + // (MergeComplete method sends "status change" in it) + // + if(!MergeComplete()){ + WAN_CHMPRN("Failed to complete merge on this server because of maybe merging now, so wait complete merge after finishing merge"); + return true; + } + + // [NOTICE] + // If there are CHMPXSTS_SRVIN_DOWN_DELETED status server, that server can not send + // "change status" by itself, so that if this server is start server of sending "merge complete", + // this server send "status change" instead of down server. + // + if(pComPkt->head.dept_ids.chmpxid == pImData->GetSelfChmpxId()){ + chmpxid_t nextchmpxid = pImData->GetNextRingChmpxId(); + chmpxid_t downchmpxid; + + // [NOTICE] + // Any DOWN status server does not have SUSPEND status. + // + while(CHM_INVALID_CHMPXID != (downchmpxid = pImData->GetChmpxIdByStatus(CHMPXSTS_SRVIN_DOWN_DELETED))){ + // hash & status update + if(!pImData->SetServerBaseHash(downchmpxid, CHM_INVALID_HASHVAL)){ + ERR_CHMPRN("Failed to update base hash(CHM_INVALID_HASHVAL) for down server(0x%016" PRIx64 "), but continue...", downchmpxid); + continue; + } + if(!pImData->SetServerStatus(downchmpxid, CHMPXSTS_SRVOUT_DOWN_NORMAL)){ + ERR_CHMPRN("Failed to update status(CHMPXSTS_SRVOUT_DOWN_NORMAL) for down server(0x%016" PRIx64 "), but continue...", downchmpxid); + continue; + } + // send status change + CHMPXSVR chmpxsvr; + if(!pImData->GetChmpxSvr(downchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Could not get chmpx information for down server(0x%016" PRIx64 "), but continue...", downchmpxid); + continue; + } + if(CHM_INVALID_CHMPXID == nextchmpxid){ + if(!PxComSendSlavesStatusChange(&chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change to slaves, but continue..."); + } + }else{ + if(!PxComSendStatusChange(nextchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change, but continue..."); + } + } + } + } + + }else if(CHMPX_COM_SERVER_DOWN == pChmpxCom->val_head.type){ + PCOMPKT pResComPkt = NULL; + if(!PxComReceiveServerDown(&(pComPkt->head), pChmpxCom, &pResComPkt)){ + // Failed Server Down(could not recover...) + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + return false; + } + if(pResComPkt){ + // Success Server Down, send(transfer) compkt to next server + if(!Send(pResComPkt, NULL, 0L)){ + ERR_CHMPRN("Sending ComPkt type(%" PRIu64 ":%s) against ComPkt type(%" PRIu64 ":%s), Something error occured.", pResComPkt->head.type, STRCOMTYPE(pResComPkt->head.type), pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + CHM_Free(pResComPkt); + return false; + } + CHM_Free(pResComPkt); + } + + // [NOTICE] + // The status is alreay updated, because probably received "merge abort" before receiving "SERVER_DOWN", + // so status changed at that time. + // Thus do not care about status here. + // + + }else if(CHMPX_COM_REQ_UPDATEDATA == pChmpxCom->val_head.type){ + PCOMPKT pResComPkt = NULL; + if(!PxComReceiveReqUpdateData(&(pComPkt->head), pChmpxCom, &pResComPkt)){ + // Failed request update data + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + return false; + } + if(pResComPkt){ + // Success Server Down, send(transfer) compkt to next server + if(!Send(pResComPkt, NULL, 0L)){ + ERR_CHMPRN("Sending ComPkt type(%" PRIu64 ":%s) against ComPkt type(%" PRIu64 ":%s), Something error occured.", pResComPkt->head.type, STRCOMTYPE(pResComPkt->head.type), pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + CHM_Free(pResComPkt); + return false; + } + CHM_Free(pResComPkt); + } + + }else if(CHMPX_COM_RES_UPDATEDATA == pChmpxCom->val_head.type){ + // only trans + if(!PxComReceiveResUpdateData(&(pComPkt->head), pChmpxCom)){ + // Failed set update data + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + return false; + } + + }else if(CHMPX_COM_RESULT_UPDATEDATA == pChmpxCom->val_head.type){ + // only trans + if(!PxComReceiveResultUpdateData(&(pComPkt->head), pChmpxCom)){ + // Failed receive result of update data + ERR_CHMPRN("Received CHMPXCOM type(%" PRIu64 ":%s). Something error occured.", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type)); + return false; + } + + }else{ + ERR_CHMPRN("Could not handle ChmpxCom type(%" PRIu64 ":%s) in ComPkt type(%" PRIu64 ":%s).", pChmpxCom->val_head.type, STRPXCOMTYPE(pChmpxCom->val_head.type), pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + return false; + } + + }else if(COM_C2C == pComPkt->head.type){ + // This case is received MQ data to trans that to Socket. + // (The other case is error.) + // + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + if(pComPkt->head.dept_ids.chmpxid != selfchmpxid){ + ERR_CHMPRN("Why does not same chmpxid? COMPKT is received from socket, it should do processing MQ event object."); + return false; + } + DUMPCOM_COMPKT("Sock::Processing(COM_C2C)", pComPkt); + + // [NOTICE] + // Come here, following patturn: + // + // 1) client -> MQ -> chmpx(server) -> chmpx(slave) + // 2) client -> MQ -> chmpx(slave) -> chmpx(server) + // + // Case 1) Must be set end point of chmpxid and msgid + // Case 2) Set or not set end point of chmpxid and msgid + // If not set these, decide these here. + // + // And both type end point must be connected. + // + + // set deliver head in COMPKT + // + chmpxid_t org_chmpxid = pComPkt->head.term_ids.chmpxid; // backup + chmpxidlist_t ex_chmpxids; + if(CHM_INVALID_CHMPXID == pComPkt->head.term_ids.chmpxid){ + long ex_chmpxcnt = pImData->GetReceiverChmpxids(pComPkt->head.hash, pComPkt->head.c2ctype, ex_chmpxids); + if(0 > ex_chmpxcnt){ + ERR_CHMPRN("Failed to get target chmpxids by something error occurred."); + return false; + }else if(0 == ex_chmpxcnt){ + WAN_CHMPRN("There are no target chmpxids, but method returns with success."); + return true; + } + }else{ + ex_chmpxids.push_back(pComPkt->head.term_ids.chmpxid); + } + + // Send + chmpxid_t tmpchmpxid; + while(0 < ex_chmpxids.size()){ + tmpchmpxid = ex_chmpxids.front(); + + PCOMPKT pTmpPkt; + bool is_duplicate; + if(1 < ex_chmpxids.size()){ + if(NULL == (pTmpPkt = ChmEventSock::DuplicateComPkt(pComPkt))){ + ERR_CHMPRN("Could not duplicate COMPKT."); + return false; + } + is_duplicate = true; + }else{ + pTmpPkt = pComPkt; + is_duplicate = false; + } + + pTmpPkt->head.mq_serial_num = MIN_MQ_SERIAL_NUMBER; + pTmpPkt->head.peer_dept_msgid = CHM_INVALID_MSGID; + pTmpPkt->head.peer_term_msgid = CHM_INVALID_MSGID; + pTmpPkt->head.term_ids.chmpxid = tmpchmpxid; + + if(CHM_INVALID_CHMPXID == org_chmpxid || org_chmpxid != pTmpPkt->head.term_ids.chmpxid){ + // this case is not replying, so reset terminated msgid. + pTmpPkt->head.term_ids.msgid= CHM_INVALID_MSGID; + } + ex_chmpxids.pop_front(); + + // check connect + if(is_server_mode){ + if(!pImData->IsConnectSlave(pTmpPkt->head.term_ids.chmpxid)){ + // there is no socket as slave connection for term chmpx. + // if term chmpx is slave node, abort this processing. + // (if server node, try to connect to it in Send() method.) + if(!pImData->IsServerChmpxId(pTmpPkt->head.term_ids.chmpxid)){ + ERR_CHMPRN("Could not find slave socket for slave chmpx(0x%016" PRIx64 ").", pTmpPkt->head.term_ids.chmpxid); + if(is_duplicate){ + CHM_Free(pTmpPkt); + } + return false; + } + MSG_CHMPRN("Could not find slave socket for server chmpx(0x%016" PRIx64 "), thus try to connect chmpx(server mode).", pTmpPkt->head.term_ids.chmpxid); + + // [NOTE] + // if server node and deperture and terminate chmpx id are same, + // need to send this packet to MQ directly. + // and if do not send packet on this case, drop it here. + // + if(tmpchmpxid == selfchmpxid){ + if(IS_C2CTYPE_SELF(pComPkt->head.c2ctype)){ + bool selfresult = false; + if(pChmCntrl){ + selfresult = pChmCntrl->Processing(pTmpPkt, ChmCntrl::EVOBJ_TYPE_EVSOCK); + } + if(is_duplicate){ + CHM_Free(pTmpPkt); + } + if(!selfresult){ + ERR_CHMPRN("Failed processing(sending packet to self) to MQ directly."); + return false; + } + } + continue; + } + } + }else{ + if(!pImData->IsConnectServer(pTmpPkt->head.term_ids.chmpxid)){ + // try to connect all servers + ConnectServers(); // not need to check error. + } + } + + if(!Send(pTmpPkt, NULL, 0L)){ + // decode for error message + ChmEventSock::ntoh(pTmpPkt); + ERR_CHMPRN("Sending ComPkt type(%" PRIu64 ":%s), Something error occured.", pTmpPkt->head.type, STRCOMTYPE(pTmpPkt->head.type)); + if(is_duplicate){ + CHM_Free(pTmpPkt); + } + return false; + } + if(is_duplicate){ + CHM_Free(pTmpPkt); + } + } + + }else if(COM_C2PX == pComPkt->head.type){ + // This case is received MQ data to chmpx process(PX) + // (The other case is error.) + // + // check length + if(pComPkt->length <= sizeof(COMPKT) || 0 == pComPkt->offset){ + ERR_CHMPRN("ComPkt type(%" PRIu64 ":%s) has invalid values.", pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + return false; + } + // following datas + PPXCLT_ALL pPxCltCom = CVT_CLT_ALL_PTR_PXCOMPKT(pComPkt); + + if(CHMPX_CLT_JOIN_NOTIFY == pPxCltCom->val_head.type){ + if(!PxCltReceiveJoinNotify(&(pComPkt->head), pPxCltCom)){ + // Failed Join Notify + ERR_CHMPRN("Received PXCLTCOM type(%" PRIu64 ":%s). Something error occured.", pPxCltCom->val_head.type, STRPXCLTTYPE(pPxCltCom->val_head.type)); + return false; + } + }else{ + ERR_CHMPRN("Could not handle PxCltCom type(%" PRIu64 ":%s) in ComPkt type(%" PRIu64 ":%s).", pPxCltCom->val_head.type, STRPXCOMTYPE(pPxCltCom->val_head.type), pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + return false; + } + + }else{ + ERR_CHMPRN("Could not handle ComPkt type(%" PRIu64 ":%s).", pComPkt->head.type, STRCOMTYPE(pComPkt->head.type)); + return false; + } + return true; +} + +bool ChmEventSock::Processing(int sock, const char* pCommand) +{ + if(CHM_INVALID_SOCK == sock || !pCommand){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + strlst_t cmdarray; + if(!str_paeser(pCommand, cmdarray) || 0 == cmdarray.size()){ + ERR_CHMPRN("Something wrong %s command, because could not parse it.", pCommand); + return false; + } + string strCommand = cmdarray.front(); + cmdarray.pop_front(); + + string strResponse; + + if(0 == strcasecmp(strCommand.c_str(), CTL_COMMAND_DUMP_IMDATA)){ + // Dump + stringstream sstream; + pImData->Dump(sstream); + strResponse = sstream.str(); + + }else if(0 == strcasecmp(strCommand.c_str(), CTL_COMMAND_START_MERGE)){ + // Start Merge + // + // The merging flow is sending CHMPX_COM_STATUS_CONFIRM, and sending CHMPX_COM_MERGE_START + // after returning CHMPX_COM_STATUS_CONFIRM result on RING. After getting CHMPX_COM_MERGE_START + // start to merge for self. + // + if(!CtlComMergeStart(strResponse)){ + ERR_CHMPRN("CTL_COMMAND_START_MERGE is failed, so stop merging."); + }else{ + MSG_CHMPRN("CTL_COMMAND_START_MERGE is succeed, so do next step after reciveing result."); + } + + }else if(0 == strcasecmp(strCommand.c_str(), CTL_COMMAND_STOP_MERGE)){ + // Abort Merge + if(!CtlComMergeAbort(strResponse)){ + ERR_CHMPRN("CTL_COMMAND_COMPLETE_MERGE is failed, so stop merging."); + }else{ + MSG_CHMPRN("CTL_COMMAND_COMPLETE_MERGE is succeed, so do next step after reciveing result."); + } + + }else if(0 == strcasecmp(strCommand.c_str(), CTL_COMMAND_COMPLETE_MERGE)){ + // Complete Merge + if(!CtlComMergeComplete(strResponse)){ + ERR_CHMPRN("CTL_COMMAND_COMPLETE_MERGE is failed, so stop merging."); + }else{ + MSG_CHMPRN("CTL_COMMAND_COMPLETE_MERGE is succeed, so do next step after receiving result."); + } + + }else if(0 == strcasecmp(strCommand.c_str(), CTL_COMMAND_SERVICE_IN)){ + // Service IN + // + if(!CtlComServiceIn(strResponse)){ + ERR_CHMPRN("CTL_COMMAND_SERVICE_IN is failed."); + }else{ + MSG_CHMPRN("CTL_COMMAND_SERVICE_IN is succeed."); + } + + }else if(0 == strcasecmp(strCommand.c_str(), CTL_COMMAND_SERVICE_OUT)){ + // Service OUT(status is changed to delete pending) + // + if(0 == cmdarray.size()){ + ERR_CHMPRN("%s command must have parameter for server name/port.", strCommand.c_str()); + strResponse = CTL_RES_ERROR_SERVICE_OUT_PARAM; + }else{ + string strTmp = cmdarray.front(); + strlst_t paramarray; + if(!str_paeser(strTmp.c_str(), paramarray, ":") || 2 != paramarray.size()){ + ERR_CHMPRN("%s command parameter(%s) must be servername:port.", strCommand.c_str(), strTmp.c_str()); + strResponse = CTL_RES_ERROR_SERVICE_OUT_PARAM; + }else{ + // retriving the server which is set SERVICE OUT on RING + string strServer = paramarray.front(); paramarray.pop_front(); + string strCtlPort = paramarray.front(); + short ctlport = static_cast(atoi(strCtlPort.c_str())); + if(!CtlComServiceOut(strServer.c_str(), ctlport, pCommand, strResponse)){ + ERR_CHMPRN("CTL_COMMAND_SERVICE_OUT is failed."); + }else{ + MSG_CHMPRN("CTL_COMMAND_SERVICE_OUT is succeed."); + } + } + } + + }else if(0 == strcasecmp(strCommand.c_str(), CTL_COMMAND_SELF_STATUS)){ + // Print self status + // + if(!CtlComSelfStatus(strResponse)){ + ERR_CHMPRN("CTL_COMMAND_SELF_STATUS is failed."); + }else{ + MSG_CHMPRN("CTL_COMMAND_SELF_STATUS is succeed."); + } + + }else if(0 == strcasecmp(strCommand.c_str(), CTL_COMMAND_ALLSVR_STATUS)){ + // Print all server status + // + if(!CtlComAllServerStatus(strResponse)){ + ERR_CHMPRN("CTL_COMMAND_SELF_STATUS is failed."); + }else{ + MSG_CHMPRN("CTL_COMMAND_SELF_STATUS is succeed."); + } + + }else if(0 == strcasecmp(strCommand.c_str(), CTL_COMMAND_TRACE_SET)){ + // Trace dis/enable + // + if(0 == cmdarray.size()){ + ERR_CHMPRN("%s command must have parameter for enable/disable.", strCommand.c_str()); + strResponse = CTL_RES_ERROR_TRACE_SET_PARAM; + }else{ + string strTmp = cmdarray.front(); + bool enable = false; + if(0 == strcasecmp(strTmp.c_str(), CTL_COMMAND_TRACE_SET_ENABLE1) || 0 == strcasecmp(strTmp.c_str(), CTL_COMMAND_TRACE_SET_ENABLE2) || 0 == strcasecmp(strTmp.c_str(), CTL_COMMAND_TRACE_SET_ENABLE3)){ + enable = true; + }else if(0 == strcasecmp(strTmp.c_str(), CTL_COMMAND_TRACE_SET_DISABLE1) || 0 == strcasecmp(strTmp.c_str(), CTL_COMMAND_TRACE_SET_DISABLE2) || 0 == strcasecmp(strTmp.c_str(), CTL_COMMAND_TRACE_SET_DISABLE3)){ + enable = false; + }else{ + ERR_CHMPRN("%s command parameter(%s) must be enable or disable.", strCommand.c_str(), strTmp.c_str()); + strResponse = CTL_RES_ERROR_TRACE_SET_PARAM; + } + + if(!CtlComAllTraceSet(strResponse, enable)){ + ERR_CHMPRN("CTL_COMMAND_TRACE_SET is failed."); + }else{ + MSG_CHMPRN("CTL_COMMAND_TRACE_SET is succeed."); + } + } + + }else if(0 == strcasecmp(strCommand.c_str(), CTL_COMMAND_TRACE_VIEW)){ + // Print Trace log + // + long view_count = pImData->GetTraceCount(); + + if(0 == view_count){ + ERR_CHMPRN("Now trace count is 0."); + strResponse = CTL_RES_ERROR_TRACE_VIEW_NOTRACE; + }else{ + bool isError = false; + long count = view_count; + logtype_t dirmask = CHMLOG_TYPE_UNKOWN; + logtype_t devmask = CHMLOG_TYPE_UNKOWN; + + if(0 == cmdarray.size()){ + MSG_CHMPRN("%s command does not have parameter, so use default value(dir=all, dev=all, count=all trace count).", strCommand.c_str()); + }else{ + for(string strTmp = ""; 0 < cmdarray.size(); cmdarray.pop_front()){ + strTmp = cmdarray.front(); + + if(0 == strTmp.find(CTL_COMMAND_TRACE_VIEW_DIR)){ + strTmp = strTmp.substr(strlen(CTL_COMMAND_TRACE_VIEW_DIR)); + + if(0 == strcasecmp(strTmp.c_str(), CTL_COMMAND_TRACE_VIEW_IN)){ + dirmask |= CHMLOG_TYPE_INPUT; + }else if(0 == strcasecmp(strTmp.c_str(), CTL_COMMAND_TRACE_VIEW_OUT)){ + dirmask |= CHMLOG_TYPE_OUTPUT; + }else if(0 == strcasecmp(strTmp.c_str(), CTL_COMMAND_TRACE_VIEW_ALL)){ + dirmask |= (CHMLOG_TYPE_INPUT | CHMLOG_TYPE_OUTPUT); + }else{ + ERR_CHMPRN("DIR parameter %s does not defined.", strTmp.c_str()); + isError = true; + break; + } + }else if(0 == strTmp.find(CTL_COMMAND_TRACE_VIEW_DEV)){ + strTmp = strTmp.substr(strlen(CTL_COMMAND_TRACE_VIEW_DEV)); + + if(0 == strcasecmp(strTmp.c_str(), CTL_COMMAND_TRACE_VIEW_SOCK)){ + devmask |= CHMLOG_TYPE_SOCKET; + }else if(0 == strcasecmp(strTmp.c_str(), CTL_COMMAND_TRACE_VIEW_MQ)){ + devmask |= CHMLOG_TYPE_MQ; + }else if(0 == strcasecmp(strTmp.c_str(), CTL_COMMAND_TRACE_VIEW_ALL)){ + devmask |= (CHMLOG_TYPE_SOCKET | CHMLOG_TYPE_MQ); + }else{ + ERR_CHMPRN("DEV parameter %s does not defined.", strTmp.c_str()); + isError = true; + break; + } + }else{ + count = static_cast(atoi(strTmp.c_str())); + if(0 == count || view_count < count){ + ERR_CHMPRN("view count %ld is %s.", count, (0 == count ? "0" : "over trace maximum count")); + isError = true; + break; + } + } + } + } + if(isError){ + strResponse = CTL_RES_ERROR_TRACE_VIEW_PARAM; + }else{ + if(0 == (dirmask & CHMLOG_MASK_DIRECTION)){ + dirmask |= (CHMLOG_TYPE_OUTPUT | CHMLOG_TYPE_INPUT); + } + if(0 == (devmask & CHMLOG_MASK_DEVICE)){ + devmask |= (CHMLOG_TYPE_SOCKET | CHMLOG_TYPE_MQ); + } + + if(!CtlComAllTraceView(strResponse, dirmask, devmask, count)){ + ERR_CHMPRN("CTL_COMMAND_TRACE_VIEW is failed."); + }else{ + MSG_CHMPRN("CTL_COMMAND_TRACE_VIEW is succeed."); + } + } + } + + }else{ + // error + ERR_CHMPRN("Unknown command(%s).", strCommand.c_str()); + strResponse = "Unknown command("; + strResponse += strCommand; + strResponse += ")\n"; + } + + // send + // + // [NOTE] This socket is control port socket. + // + return ChmEventSock::LockedSend(sock, GetSSL(sock), reinterpret_cast(strResponse.c_str()), strResponse.length()); +} + +bool ChmEventSock::ChangeStatusBeforeMergeStart(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Check status + chmpxsts_t status = pImData->GetSelfStatus(); + if(!IS_CHMPXSTS_SRVIN(status)){ + // [NOTE] + // For auto merging + // local status value is changed SERVICEIN/UP/ADD/PENDING/NOSUSPEND here, when server is SERVICEOUT/UP/NOACT(ADD)/NOTHING(ANY)/NOSUSPEND. + // + if(is_auto_merge && IS_CHMPXSTS_SRVOUT(status) && IS_CHMPXSTS_UP(status) && !IS_CHMPXSTS_DELETE(status) && IS_CHMPXSTS_NOSUP(status)){ + SET_CHMPXSTS_SRVIN(status); + SET_CHMPXSTS_ADD(status); + SET_CHMPXSTS_PENDING(status); + }else{ + MSG_CHMPRN("Server status(0x%016" PRIx64 ":%s) is not CHMPXSTS_VAL_SRVIN, so could not change status.", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + } + if(!IS_CHMPXSTS_PENDING(status)){ + if(IS_CHMPXSTS_DOING(status) || IS_CHMPXSTS_DONE(status)){ + return true; + } + if(!IS_CHMPXSTS_NOACT(status)){ + MSG_CHMPRN("Already not pending/doing/done status(0x%016" PRIx64 ":%s), so not change status and nothing to do.", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + + // check whichever this server is needed to merge, it can check by hash values. + chmhash_t base_hash = CHM_INVALID_HASHVAL; + chmhash_t pending_hash = CHM_INVALID_HASHVAL; + chmhash_t max_base_hash = CHM_INVALID_HASHVAL; + chmhash_t max_pending_hash= CHM_INVALID_HASHVAL; + if(!pImData->GetSelfHash(base_hash, pending_hash) || !pImData->GetMaxHashCount(max_base_hash, max_pending_hash)){ + ERR_CHMPRN("Could not get base/pending hash value and those max value."); + return false; + } + if(base_hash == pending_hash && max_base_hash == max_pending_hash){ + MSG_CHMPRN("this server has status(0x%016" PRIx64 ":%s) and base/pending hash(0x%016" PRIx64 ") is same, so nothing to do.", status, STR_CHMPXSTS_FULL(status).c_str(), base_hash); + return false; + } + SET_CHMPXSTS_PENDING(status); + } + if(IS_CHMPXSTS_ADD(status) && IS_CHMPXSTS_SUSPEND(status)){ + ERR_CHMPRN("Server status(0x%016" PRIx64 ":%s) is SUSPEND(on ADD), so could not change status.", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + + // new status + if(IS_CHMPXSTS_NOACT(status)){ + if(!IS_CHMPXSTS_SUSPEND(status)){ + status = CHMPXSTS_SRVIN_UP_MERGING; + }else{ + status = CHMPXSTS_SRVIN_UP_MERGED; + } + }else if(IS_CHMPXSTS_ADD(status)){ + if(!IS_CHMPXSTS_SUSPEND(status)){ + status = CHMPXSTS_SRVIN_UP_ADDING; + }else{ + status = CHMPXSTS_SRVIN_UP_ADDED; + } + }else if(IS_CHMPXSTS_UP(status) && IS_CHMPXSTS_DELETE(status)){ + if(!IS_CHMPXSTS_SUSPEND(status)){ + status = CHMPXSTS_SRVIN_UP_DELETING; + }else{ + status = CHMPXSTS_SRVIN_UP_DELETED; + } + }else if(IS_CHMPXSTS_DOWN(status) && IS_CHMPXSTS_DELETE(status)){ + status = CHMPXSTS_SRVIN_DOWN_DELETED; + }else{ // why? + ERR_CHMPRN("Un-safe status(0x%016" PRIx64 ":%s).", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + + // set new status(set suspend in this method) + if(!pImData->SetSelfStatus(status)){ + ERR_CHMPRN("Failed to change self status to 0x%016" PRIx64 ":%s", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + + // send status update + // + // If error occured in sending status change, but continue to merge. + // + chmpxid_t to_chmpxid = GetNextRingChmpxId(); + CHMPXSVR selfchmpxsvr; + if(!pImData->GetSelfChmpxSvr(&selfchmpxsvr)){ + ERR_CHMPRN("Could not get self chmpx information,"); + return false; + } + if(CHM_INVALID_CHMPXID == to_chmpxid){ + // no server found, finish doing so pending hash updated self. + WAN_CHMPRN("Could not get to chmpxid, probably there is no server without self chmpx on RING. So only sending status update to slaves."); + + if(!PxComSendSlavesStatusChange(&selfchmpxsvr)){ + ERR_CHMPRN("Failed to send self status change to slaves, but continue..."); + } + }else{ + if(!PxComSendStatusChange(to_chmpxid, &selfchmpxsvr)){ + ERR_CHMPRN("Failed to send self status change, but continue..."); + } + } + return true; +} + +// [NOTE] +// If there are down servers, this server changes down server's status and +// send it on behalf of down server before starting merging. +// +bool ChmEventSock::ChangeDownSvrStatusBeforeMerge(CHMDOWNSVRMERGE type) +{ + if(CHM_DOWNSVR_MERGE_START != type && CHM_DOWNSVR_MERGE_COMPLETE != type && CHM_DOWNSVR_MERGE_ABORT != type){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // get all server status + PCHMPXSVR pchmpxsvrs = NULL; + long count = 0L; + if(!pImData->GetChmpxSvrs(&pchmpxsvrs, count)){ + ERR_CHMPRN("Could not get all server status, so stop update status."); + return false; + } + + chmpxid_t to_chmpxid = GetNextRingChmpxId(); + + // loop: check down servers and update it's status. + for(long cnt = 0; cnt < count; ++cnt){ + // check status: merge start -> SERVICEIN / DOWN / DELETE / NOTHING or PENDING + // merge complete -> SERVICEIN / DOWN / DELETE / ANYTHING + // merge abort -> SERVICEIN / DOWN / DELETE / DOING or DONE + if( (CHM_DOWNSVR_MERGE_START == type && IS_CHMPXSTS_SRVIN(pchmpxsvrs[cnt].status) && IS_CHMPXSTS_DOWN(pchmpxsvrs[cnt].status) && IS_CHMPXSTS_DELETE(pchmpxsvrs[cnt].status) && (!IS_CHMPXSTS_DOING(pchmpxsvrs[cnt].status) && !IS_CHMPXSTS_DONE(pchmpxsvrs[cnt].status))) || + (CHM_DOWNSVR_MERGE_COMPLETE == type && IS_CHMPXSTS_SRVIN(pchmpxsvrs[cnt].status) && IS_CHMPXSTS_DOWN(pchmpxsvrs[cnt].status) && IS_CHMPXSTS_DELETE(pchmpxsvrs[cnt].status)) || + (CHM_DOWNSVR_MERGE_ABORT == type && IS_CHMPXSTS_SRVIN(pchmpxsvrs[cnt].status) && IS_CHMPXSTS_DOWN(pchmpxsvrs[cnt].status) && IS_CHMPXSTS_DELETE(pchmpxsvrs[cnt].status) && (IS_CHMPXSTS_DOING(pchmpxsvrs[cnt].status) || IS_CHMPXSTS_DONE(pchmpxsvrs[cnt].status))) ) + { + // set new status: merge start -> SERVICEIN / DOWN / DELETE / DONE + // merge complete -> SERVICEOUT / DOWN / NOACT / NOTHING + // merge abort -> SERVICEIN / DOWN / DELETE / PENDING + chmpxsts_t newstatus = CHM_DOWNSVR_MERGE_START == type ? CHMPXSTS_SRVIN_DOWN_DELETED : CHM_DOWNSVR_MERGE_COMPLETE == type ? CHMPXSTS_SRVOUT_DOWN_NORMAL : CHMPXSTS_SRVIN_DOWN_DELPENDING; + + // set new status and hash + if(!pImData->SetServerStatus(pchmpxsvrs[cnt].chmpxid, newstatus) || (CHM_DOWNSVR_MERGE_COMPLETE == type && !pImData->SetServerBaseHash(pchmpxsvrs[cnt].chmpxid, CHM_INVALID_HASHVAL))){ + ERR_CHMPRN("Could not change server(0x%016" PRIx64 ") status to 0x%016" PRIx64 ":%s.", pchmpxsvrs[cnt].chmpxid, newstatus, STR_CHMPXSTS_FULL(newstatus).c_str()); + + }else{ + // reget status + CHMPXSVR chmpxsvr; + if(!pImData->GetChmpxSvr(pchmpxsvrs[cnt].chmpxid, &chmpxsvr)){ + ERR_CHMPRN("Could not get server(0x%016" PRIx64 ") information.", pchmpxsvrs[cnt].chmpxid); + + }else{ + // send status update + if(CHM_INVALID_CHMPXID == to_chmpxid){ + // no server found, finish doing so pending hash updated self. + WAN_CHMPRN("Could not get to chmpxid, probably there is no server without self chmpx on RING. So only sending status update to slaves."); + if(!PxComSendSlavesStatusChange(&chmpxsvr)){ + ERR_CHMPRN("Failed to send server(0x%016" PRIx64 ") status to slaves, but continue...", pchmpxsvrs[cnt].chmpxid); + } + }else{ + if(!PxComSendStatusChange(to_chmpxid, &chmpxsvr)){ + ERR_CHMPRN("Failed to send server(0x%016" PRIx64 ") status to servers, but continue...", pchmpxsvrs[cnt].chmpxid); + } + } + } + } + } + } + CHM_Free(pchmpxsvrs); + + return true; +} + +// +// CAREFUL +// This method is called from another thread for merging worker. +// +bool ChmEventSock::MergeDone(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Check status + chmpxsts_t status = pImData->GetSelfStatus(); + if(!IS_CHMPXSTS_SRVIN(status)){ + ERR_CHMPRN("Server status(0x%016" PRIx64 ":%s) is not CHMPXSTS_VAL_SRVIN, so could not change status.", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + if(!IS_CHMPXSTS_DOING(status)){ + WAN_CHMPRN("Already status(0x%016" PRIx64 ":%s) is not \"DOING\", so not change status and nothing to do.", status, STR_CHMPXSTS_FULL(status).c_str()); + if(IS_CHMPXSTS_DONE(status)){ + return true; + } + return false; + } + if((IS_CHMPXSTS_NOACT(status) || IS_CHMPXSTS_ADD(status)) && IS_CHMPXSTS_SUSPEND(status)){ + ERR_CHMPRN("Server status(0x%016" PRIx64 ":%s) is SUSPEND(not on DELETE), so could not change status.", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + + // new status + if(IS_CHMPXSTS_NOACT(status)){ + status = CHMPXSTS_SRVIN_UP_MERGED; + }else if(IS_CHMPXSTS_ADD(status)){ + status = CHMPXSTS_SRVIN_UP_ADDED; + }else if(IS_CHMPXSTS_UP(status) && IS_CHMPXSTS_DELETE(status)){ + status = CHMPXSTS_SRVIN_UP_DELETED; + }else if(IS_CHMPXSTS_DOWN(status) && IS_CHMPXSTS_DELETE(status)){ + status = CHMPXSTS_SRVIN_DOWN_DELETED; // No case come here. + }else{ // why? + ERR_CHMPRN("Un-safe status(0x%016" PRIx64 ":%s).", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + + // set new status(set suspend in this method) + if(!pImData->SetSelfStatus(status)){ + ERR_CHMPRN("Failed to change self status to 0x%016" PRIx64 ":%s", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + + // send status change + chmpxid_t to_chmpxid = GetNextRingChmpxId(); + CHMPXSVR selfchmpxsvr; + if(!pImData->GetSelfChmpxSvr(&selfchmpxsvr)){ + WAN_CHMPRN("Could not get self chmpx information, but continue..."); + }else{ + if(CHM_INVALID_CHMPXID == to_chmpxid){ + // no server found, finish doing so pending hash updated self. + WAN_CHMPRN("Could not get to chmpxid, probably there is no server without self chmpx on RING. So only sending status update to slaves."); + + if(!PxComSendSlavesStatusChange(&selfchmpxsvr)){ + WAN_CHMPRN("Failed to send self status change to slaves, but continue..."); + } + }else{ + // Update pending hash. + if(!pImData->UpdatePendingHash()){ + ERR_CHMPRN("Failed to update pending hash for all servers, so stop update status."); + return false; + } + // push status changed + if(!PxComSendStatusChange(to_chmpxid, &selfchmpxsvr)){ + WAN_CHMPRN("Failed to send self status change, but continue..."); + } + } + } + + // if the mode for merge is automatical, do complete here. + if(is_auto_merge){ + if(!RequestMergeComplete()){ + ERR_CHMPRN("Could not change status merge \"COMPLETE\", probabry another server does not change status yet, DO COMPMERGE BY MANUAL!"); + } + } + return true; +} + +bool ChmEventSock::IsSuspendServerInRing(void) const +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // get all server status + PCHMPXSVR pchmpxsvrs = NULL; + long count = 0L; + if(!pImData->GetChmpxSvrs(&pchmpxsvrs, count)){ + ERR_CHMPRN("Could not get all server status, so stop update status."); + return false; + } + + // loop: check wrong status + bool result = true; + for(long cnt = 0; cnt < count; ++cnt){ + if(IS_CHMPXSTS_SRVIN(pchmpxsvrs[cnt].status)){ + if(IS_CHMPXSTS_NOACT(pchmpxsvrs[cnt].status) && IS_CHMPXSTS_NOSUP(pchmpxsvrs[cnt].status)){ + result = false; + }else if(IS_CHMPXSTS_ADD(pchmpxsvrs[cnt].status) && IS_CHMPXSTS_NOSUP(pchmpxsvrs[cnt].status)){ + result = false; + } + // If DELETE action, do not care for suspend status. + } + // If SERVICEOUT, do not care for suspend status. + } + CHM_Free(pchmpxsvrs); + + if(!result){ + MSG_CHMPRN("Found SERVICEIN & SUSPEND server(not have clinet, but join RING), so could not start to merge."); + } + return result; +} + +// +// This method sends "STATUS_COMFIRM", and After receiving "STATUS_COMFIRM", +// sends "MERGE_START" automatically. +// +bool ChmEventSock::RequestMergeStart(string* pstring) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + if(pstring){ + *pstring = CTL_RES_INT_ERROR; + } + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // check SUSPEND status + if(IsSuspendServerInRing()){ + ERR_CHMPRN("Some server in RING have SUSPEND status, so could not change status."); + if(pstring){ + *pstring = CTL_RES_ERROR_SOME_SERVER_SUSPEND; + } + return false; + } + + // get terminated chmpxid + chmpxid_t chmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == chmpxid){ + // no server found, finish doing so pending hash updated self. + WAN_CHMPRN("Could not get to chmpxid, probably there is no server without self chmpx on RING. So stop sending status update."); + + // Start to merge(only self on RING) + // + // If there is no server in RING, do start merge. + // + if(!MergeStart()){ + ERR_CHMPRN("Failed to start merge."); + if(pstring){ + *pstring = CTL_RES_ERROR_MERGE_START; + } + return false; + }else{ + if(pstring){ + *pstring = CTL_RES_SUCCESS_NOSERVER; + } + return true; + } + } + + // get all server status + PCHMPXSVR pchmpxsvrs = NULL; + long count = 0L; + if(!pImData->GetChmpxSvrs(&pchmpxsvrs, count)){ + ERR_CHMPRN("Could not get all server status, so stop update status."); + if(pstring){ + *pstring = CTL_RES_INT_ERROR_NOTGETCHMPX; + } + return false; + } + + // send status_confirm + if(!PxComSendStatusConfirm(chmpxid, pchmpxsvrs, count)){ + ERR_CHMPRN("Failed to send CHMPX_COM_STATUS_CONFIRM."); + if(pstring){ + *pstring = CTL_RES_ERROR_COMMUNICATION; + } + return false; + } + if(pstring){ + *pstring = CTL_RES_SUCCESS; + } + return true; +} + +bool ChmEventSock::MergeStart(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + // change status "DOING" before merging. + if(!ChangeStatusBeforeMergeStart()){ + MSG_CHMPRN("Failed to change status \"DOING\", probabry nothing to do."); + return true; + } + // change down server status before merging. + if(!ChangeDownSvrStatusBeforeMerge(CHM_DOWNSVR_MERGE_START)){ + ERR_CHMPRN("Failed to change down server status, but continue..."); + } + + // start merging. + if(is_do_merge){ + if(is_run_merge){ + ERR_CHMPRN("Already to run merge."); + return false; + } + + if(!mergethread.HasThread()){ + ERR_CHMPRN("The thread for merging is not running, why?"); + return false; + } + + // get chmpxids(targets) which have same basehash or replica basehash. + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + chmhash_t basehash; + if(!pImData->GetSelfBaseHash(basehash)){ + // there is no server, so set status "DONE" here. + if(!MergeDone()){ + ERR_CHMPRN("Failed to change status \"DOING to DONE to COMPLETE\"."); + return false; + } + return true; + } + // we need to collect minimum servers + chmpxidlist_t alllist; + if(pImData->IsPendingExchangeData()){ + // need all servers which are up/noact/nosuspend + pImData->GetServerChmpxIdForMerge(alllist); + }else{ + // this case is suspend to nosuspend or down to up(servicein) + pImData->GetServerChmpxIdByBaseHash(basehash, alllist); + } + + while(!fullock::flck_trylock_noshared_mutex(&mergeidmap_lockval)); // LOCK + + // make chmpxid map for update datas + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + mergeidmap.clear(); + for(chmpxidlist_t::iterator iter = alllist.begin(); iter != alllist.end(); ++iter){ + if(selfchmpxid != *iter){ + mergeidmap[*iter] = CHMPX_COM_REQ_UPDATE_INIVAL; + } + } + if(0 == mergeidmap.size()){ + // there is no server, so set status "DONE" here. + fullock::flck_unlock_noshared_mutex(&mergeidmap_lockval); // UNLOCK + + if(!MergeDone()){ + ERR_CHMPRN("Failed to change status \"DOING to DONE to COMPLETE\"."); + return false; + } + return true; + } + fullock::flck_unlock_noshared_mutex(&mergeidmap_lockval); // UNLOCK + + // set "run" status for merging worker method. + is_run_merge = true; + + // run merging worker thread method. + if(!mergethread.DoWorkThread()){ + ERR_CHMPRN("Failed to wake up thread for merging."); + is_run_merge = false; + return false; + } + }else{ + // Do not run merging, so set status "DONE" here. + // + if(!MergeDone()){ + ERR_CHMPRN("Failed to change status \"DONE\"."); + return false; + } + } + return true; +} + +bool ChmEventSock::MergeAbort(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // status check + chmpxsts_t status = pImData->GetSelfStatus(); + if(!IS_CHMPXSTS_SRVIN(status) || (!IS_CHMPXSTS_DOING(status) && !IS_CHMPXSTS_DONE(status))){ + // [NOTICE] + // CHMPXSTS_SRVIN_UP_MERGING & CHMPXSTS_SRVIN_UP_MERGED could not abort. + // Only doing merge complete or merging on no-suspend + // + MSG_CHMPRN("Now status is status(0x%016" PRIx64 ":%s) which is not \"DOING\" nor \"DONE\", so nothing to do.", status, STR_CHMPXSTS_FULL(status).c_str()); + return true; + } + + if(is_do_merge){ + if(!mergethread.HasThread()){ + ERR_CHMPRN("The thread for merging is not running, why? but continue..."); + }else{ + // set "stop" status for merging worker method. + is_run_merge = false; + } + } + + // send abort update data to all client process + if(!pChmCntrl->MergeAbortUpdateData()){ + ERR_CHMPRN("Failed to send abort update data, but continue..."); + } + + // set original status + CHANGE_CHMPXSTS_TO_MERGESTOP(status); + if(!pImData->SetSelfStatus(status)){ + ERR_CHMPRN("Failed to update(rewind) status(0x%061lx:%s).", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + + // Update pending hash. + if(!pImData->UpdatePendingHash()){ + ERR_CHMPRN("Failed to update pending hash for all servers, so stop update status."); + return false; + } + + // send status change + CHMPXSVR chmpxsvr; + if(!pImData->GetSelfChmpxSvr(&chmpxsvr)){ + ERR_CHMPRN("Could not get self chmpx information."); + return false; + } + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + WAN_CHMPRN("Could not get next chmpxid, probably there is no server without self chmpx on RING."); + + if(!PxComSendSlavesStatusChange(&chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change to slaves."); + return false; + } + }else{ + if(!PxComSendStatusChange(nextchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change."); + return false; + } + } + + // change down server status. + if(!ChangeDownSvrStatusBeforeMerge(CHM_DOWNSVR_MERGE_ABORT)){ + ERR_CHMPRN("Failed to change down server status, but continue..."); + } + return true; +} + +bool ChmEventSock::RequestMergeComplete(string* pstring) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + if(pstring){ + *pstring = CTL_RES_INT_ERROR; + } + return false; + } +// ChmIMData* pImData = pChmCntrl->GetImDataObj(); not used + + // check SUSPEND status + if(IsSuspendServerInRing()){ + MSG_CHMPRN("Some server in RING have SUSPEND status, so could not change status."); + if(pstring){ + *pstring = CTL_RES_ERROR_SOME_SERVER_SUSPEND; + } + return false; + } + + // get terminated chmpxid + chmpxid_t chmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == chmpxid){ + // no server found, finish doing so pending hash updated self. + WAN_CHMPRN("Could not get to chmpxid, probably there is no server without self chmpx on RING. So stop sending status update."); + + // Complete to merge + // + // If there is no server in RING, do complete merge. + // + if(!MergeComplete()){ + ERR_CHMPRN("Failed to complete merge."); + if(pstring){ + *pstring = CTL_RES_ERROR_MERGE_COMPLETE; + } + return false; + } + if(pstring){ + *pstring = CTL_RES_SUCCESS_NOSERVER; + } + return true; + } + + // send complete_merge + if(!PxComSendMergeComplete(chmpxid)){ + ERR_CHMPRN("Failed to send CHMPX_COM_MERGE_COMPLETE."); + if(pstring){ + *pstring = CTL_RES_ERROR_COMMUNICATION; + } + return false; + } + if(pstring){ + *pstring = CTL_RES_SUCCESS; + } + return true; +} + +bool ChmEventSock::RequestServiceIn(string* pstring) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + if(pstring){ + *pstring = CTL_RES_INT_ERROR; + } + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + if(pImData->IsOperating()){ + ERR_CHMPRN("Servers are now operating."); + if(pstring){ + *pstring = CTL_RES_ERROR_OPERATING; + } + return false; + } + + // check self status + chmpxsts_t status = pImData->GetSelfStatus(); + if(!(IS_CHMPXSTS_SRVIN(status) && IS_CHMPXSTS_UP(status) && IS_CHMPXSTS_DELETE(status)) && !(IS_CHMPXSTS_SRVOUT(status) && IS_CHMPXSTS_UP(status) && IS_CHMPXSTS_NOACT(status))){ + ERR_CHMPRN("Server is status(0x%016" PRIx64 ":%s), so could not change status.", status, STR_CHMPXSTS_FULL(status).c_str()); + if(pstring){ + *pstring = CTL_RES_ERROR_STATUS_NOT_ALLOWED; + } + return false; + } + if(IS_CHMPXSTS_SRVOUT(status) && IS_CHMPXSTS_SUSPEND(status)){ + ERR_CHMPRN("Server status(0x%016" PRIx64 ":%s) is SUSPEND, so could not change status.", status, STR_CHMPXSTS_FULL(status).c_str()); + if(pstring){ + *pstring = CTL_RES_ERROR_STATUS_HAS_SUSPEND; + } + return false; + } + + // new status + if(IS_CHMPXSTS_SRVIN(status) && IS_CHMPXSTS_SUSPEND(status)){ // SERVICEIN & UP & DELETE & SUSPEND + status = CHMPXSTS_SRVIN_UP_ADDPENDING; + }else if(IS_CHMPXSTS_SRVIN(status) && IS_CHMPXSTS_NOSUP(status)){ // SERVICEIN & UP & DELETE & NOT SUSPEND + status = CHMPXSTS_SRVIN_UP_NORMAL; + }else if(IS_CHMPXSTS_SRVOUT(status)){ // SERVICEOUT & UP & NOACT & NOT SUSPEND + status = CHMPXSTS_SRVIN_UP_ADDPENDING; + } + + // set status(set suspend in this method) + if(!pImData->SetSelfStatus(status)){ + ERR_CHMPRN("Failed to change server status(0x%016" PRIx64 ").", status); + if(pstring){ + *pstring = CTL_RES_ERROR_CHANGE_STATUS; + } + return false; + } + + // Update pending hash. + if(!pImData->UpdatePendingHash()){ + ERR_CHMPRN("Failed to update pending hash for all servers, so stop update status."); + if(pstring){ + *pstring = CTL_RES_ERROR_CHANGE_STATUS; + } + return false; + } + + // send status update + CHMPXSVR chmpxsvr; + if(!pImData->GetSelfChmpxSvr(&chmpxsvr)){ + ERR_CHMPRN("Could not get self chmpx information."); + if(pstring){ + *pstring = CTL_RES_ERROR_STATUS_NOTICE; + } + return false; + } + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + WAN_CHMPRN("Could not get next chmpxid, probably there is no server without self chmpx on RING."); + if(!PxComSendSlavesStatusChange(&chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change to slaves."); + if(pstring){ + *pstring = CTL_RES_ERROR_STATUS_NOTICE; + } + return false; + } + if(pstring){ + *pstring = CTL_RES_SUCCESS_STATUS_NOTICE; + } + }else{ + if(!PxComSendStatusChange(nextchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change."); + if(pstring){ + *pstring = CTL_RES_ERROR_STATUS_NOTICE; + } + return false; + } + } + return true; +} + +bool ChmEventSock::MergeComplete(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // change down server status before merging. + if(!ChangeDownSvrStatusBeforeMerge(CHM_DOWNSVR_MERGE_COMPLETE)){ + ERR_CHMPRN("Failed to change down server status, but continue..."); + } + + // status check + chmpxsts_t status = pImData->GetSelfStatus(); + if(!IS_CHMPXSTS_SRVIN(status)){ + MSG_CHMPRN("Server status(0x%016" PRIx64 ":%s) is not CHMPXSTS_VAL_SRVIN, so could not change status.", status, STR_CHMPXSTS_FULL(status).c_str()); + return true; + } + if(!IS_CHMPXSTS_DONE(status)){ + if(IS_CHMPXSTS_NOTHING(status)){ + MSG_CHMPRN("status(0x%016" PRIx64 ":%s) is not \"DONE\", but not need to change status and nothing to do.", status, STR_CHMPXSTS_FULL(status).c_str()); + return true; + } + WAN_CHMPRN("status(0x%016" PRIx64 ":%s) is not \"DONE\", so probably now doing thus could not change status.", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + if(IS_CHMPXSTS_ADD(status) && IS_CHMPXSTS_SUSPEND(status)){ + // allow NOACT & SUSPEND + ERR_CHMPRN("Server status(0x%016" PRIx64 ":%s) is SUSPEND(on ADD), so could not change status.", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + + // get pending hash + chmhash_t hash; + if(!pImData->GetSelfPendingHash(hash)){ + ERR_CHMPRN("Failed to get pending hash value."); + return false; + } + + // new status + if(IS_CHMPXSTS_NOACT(status)){ + status = CHMPXSTS_SRVIN_UP_NORMAL; + }else if(IS_CHMPXSTS_ADD(status)){ + status = CHMPXSTS_SRVIN_UP_NORMAL; + }else if(IS_CHMPXSTS_UP(status) && IS_CHMPXSTS_DELETE(status)){ + SET_CHMPXSTS_UP(status); + SET_CHMPXSTS_SRVOUT(status); + SET_CHMPXSTS_NOACT(status); + SET_CHMPXSTS_NOTHING(status); + }else if(IS_CHMPXSTS_DOWN(status) && IS_CHMPXSTS_DELETE(status)){ + status = CHMPXSTS_SRVOUT_DOWN_NORMAL; + hash = CHM_INVALID_HASHVAL; + }else{ // why? + ERR_CHMPRN("Un-safe status(0x%016" PRIx64 ":%s).", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + + // hash update + if(!pImData->SetSelfBaseHash(hash)){ + ERR_CHMPRN("Failed to update base hash(0x%016" PRIx64 ").", hash); + return false; + } + + // set new status(set suspend in this method) + if(!pImData->SetSelfStatus(status)){ + ERR_CHMPRN("Failed to change self status to 0x%016" PRIx64 ":%s", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + + // send status change + CHMPXSVR chmpxsvr; + if(!pImData->GetSelfChmpxSvr(&chmpxsvr)){ + ERR_CHMPRN("Could not get self chmpx information."); + return false; + } + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + WAN_CHMPRN("Could not get next chmpxid, probably there is no server without self chmpx on RING."); + + if(!PxComSendSlavesStatusChange(&chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change to slaves."); + return false; + } + }else{ + if(!PxComSendStatusChange(nextchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change."); + return false; + } + } + return true; +} + +bool ChmEventSock::RequestMergeAbort(string* pstring) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + if(pstring){ + *pstring = CTL_RES_INT_ERROR; + } + return false; + } + //ChmIMData* pImData = pChmCntrl->GetImDataObj(); not used + + // get terminated chmpxid + chmpxid_t chmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == chmpxid){ + // no server found, finish doing so pending hash updated self. + WAN_CHMPRN("Could not get to chmpxid, probably there is no server without self chmpx on RING. So stop sending status update."); + + // abort merge + // + // If there is no server in RING, do abort merge. + // + if(!MergeAbort()){ + MSG_CHMPRN("Failed to abort merge, maybe status does not DOING nor DONE now."); + if(pstring){ + *pstring = CTL_RES_ERROR_MERGE_ABORT; + } + }else{ + if(pstring){ + *pstring = CTL_RES_SUCCESS_NOSERVER; + } + } + return true; + } + + // send merge abort + if(!PxComSendMergeAbort(chmpxid)){ + ERR_CHMPRN("Failed to send CHMPX_COM_MERGE_ABORT."); + if(pstring){ + *pstring = CTL_RES_ERROR_COMMUNICATION; + } + return false; + } + if(pstring){ + *pstring = CTL_RES_SUCCESS; + } + return true; +} + +bool ChmEventSock::DoSuspend(void) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + if(!pImData->IsChmpxProcess()){ + ERR_CHMPRN("This method must be called on Chmpx process."); + return false; + } + + // check status + chmpxsts_t status = pImData->GetSelfStatus(); + if(IS_CHMPXSTS_SUSPEND(status)){ + MSG_CHMPRN("Server is already SUSPEND status(0x%016" PRIx64 ":%s).", status, STR_CHMPXSTS_FULL(status).c_str()); + return true; + } + if(IS_CHMPXSTS_DOWN(status) || IS_CHMPXSTS_SLAVE(status)){ + MSG_CHMPRN("Server is status(0x%016" PRIx64 ":%s), so could not need to add status SUSPEND.", status, STR_CHMPXSTS_FULL(status).c_str()); + return true; + } + + // add suspend status + CHANGE_CHMPXSTS_TO_SUSPEND(status); + if(!pImData->SetSelfStatus(status)){ + ERR_CHMPRN("Failed to change server status(0x%016" PRIx64 ").", status); + return false; + } + + // Update pending hash. + if(!pImData->UpdatePendingHash()){ + ERR_CHMPRN("Failed to update pending hash for all servers, so stop update status."); + return false; + } + + // send status update + CHMPXSVR chmpxsvr; + if(!pImData->GetSelfChmpxSvr(&chmpxsvr)){ + ERR_CHMPRN("Could not get self chmpx information."); + return false; + } + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + WAN_CHMPRN("Could not get next chmpxid, probably there is no server without self chmpx on RING."); + if(!PxComSendSlavesStatusChange(&chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change to slaves."); + return false; + } + }else{ + if(!PxComSendStatusChange(nextchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change."); + return false; + } + } + + // If merging, stop it. + if(!RequestMergeAbort()){ + ERR_CHMPRN("Failed stopping merging."); + return false; + } + return true; +} + +//--------------------------------------------------------- +// Methods for Ctlport Command +//--------------------------------------------------------- +bool ChmEventSock::SendCtlPort(chmpxid_t chmpxid, const unsigned char* pbydata, size_t length, string& strResult) +{ + if(CHM_INVALID_CHMPXID == chmpxid || !pbydata || 0L == length){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + string hostname; + short port = CHM_INVALID_PORT; + short ctlport = CHM_INVALID_PORT; + if(!pImData->GetServerBase(chmpxid, hostname, port, ctlport)){ + ERR_CHMPRN("Could not find server by chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + return ChmEventSock::RawSendCtlPort(hostname.c_str(), ctlport, pbydata, length, strResult, sockfd_lockval, sock_retry_count, sock_wait_time, con_retry_count, con_wait_time); +} + +bool ChmEventSock::CtlComDump(string& strResponse) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + stringstream sstream; + pImData->Dump(sstream); + strResponse = sstream.str(); + + return true; +} + +bool ChmEventSock::CtlComMergeStart(string& strResponse) +{ + return RequestMergeStart(&strResponse); +} + +bool ChmEventSock::CtlComMergeAbort(string& strResponse) +{ + return RequestMergeAbort(&strResponse); +} + +bool ChmEventSock::CtlComMergeComplete(string& strResponse) +{ + return RequestMergeComplete(&strResponse); +} + +bool ChmEventSock::CtlComServiceIn(string& strResponse) +{ + if(!RequestServiceIn(&strResponse)){ + return false; + } + + // If do not merge(almost random deliver mode), do merging, completing it. + if(is_auto_merge){ + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + + // start merge automatically + if(CHM_INVALID_CHMPXID == nextchmpxid){ + if(!MergeStart()){ + ERR_CHMPRN("Failed to merge or complete merge for \"service in\"."); + strResponse += "\n"; + strResponse += CTL_RES_ERROR_MERGE_AUTO; + return false; + } + }else{ + if(!RequestMergeStart(&strResponse)){ + ERR_CHMPRN("Failed to merge or complete merge for \"service in\"."); + strResponse += "\n"; + strResponse += CTL_RES_ERROR_MERGE_AUTO; + return false; + } + } + } + if(0 == strResponse.length()){ + strResponse = CTL_RES_SUCCESS; + } + return true; +} + +bool ChmEventSock::CtlComServiceOut(const char* hostname, short ctlport, const char* pOrgCommand, string& strResponse) +{ + if(CHMEMPTYSTR(hostname) || CHM_INVALID_PORT == ctlport || CHMEMPTYSTR(pOrgCommand)){ + ERR_CHMPRN("Parameters are wrong."); + strResponse = CTL_RES_ERROR_PARAMETER; + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + strResponse = CTL_RES_INT_ERROR; + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + chmpxid_t chmpxid; + if(CHM_INVALID_CHMPXID == (chmpxid = pImData->GetChmpxIdByToServerName(hostname, ctlport))){ + ERR_CHMPRN("Could not find a server as %s:%d.", hostname, ctlport); + strResponse = CTL_RES_ERROR_NOT_FOUND_SVR; + return false; + } + + if(selfchmpxid == chmpxid){ + // set self status + chmpxsts_t status = pImData->GetSelfStatus(); + if(IS_CHMPXSTS_SLAVE(status) || IS_CHMPXSTS_SRVOUT(status) || IS_CHMPXSTS_DOWN(status) || IS_CHMPXSTS_DELETE(status)){ + ERR_CHMPRN("Server is status(0x%016" PRIx64 ":%s), not enough status to deleting.", status, STR_CHMPXSTS_FULL(status).c_str()); + strResponse = CTL_RES_ERROR_STATUS_NOT_ALLOWED; + return false; + } + + // new status + SET_CHMPXSTS_DELETE(status); + SET_CHMPXSTS_PENDING(status); + + // set new status(set suspend in this method) + if(!pImData->SetSelfStatus(status)){ + ERR_CHMPRN("Failed to change server status(0x%016" PRIx64 ":%s).", status, STR_CHMPXSTS_FULL(status).c_str()); + strResponse = CTL_RES_ERROR_CHANGE_STATUS; + return false; + } + + // Update pending hash. + if(!pImData->UpdatePendingHash()){ + ERR_CHMPRN("Failed to update pending hash for all servers, so stop update status."); + strResponse = CTL_RES_ERROR_CHANGE_STATUS; + return false; + } + + // send status update + CHMPXSVR chmpxsvr; + if(!pImData->GetSelfChmpxSvr(&chmpxsvr)){ + ERR_CHMPRN("Could not get self chmpx information."); + strResponse = CTL_RES_ERROR_STATUS_NOTICE; + return false; + } + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + WAN_CHMPRN("Could not get next chmpxid, probably there is no server without self chmpx on RING."); + if(!PxComSendSlavesStatusChange(&chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change to slaves."); + strResponse = CTL_RES_ERROR_STATUS_NOTICE; + return false; + } + strResponse = CTL_RES_SUCCESS_STATUS_NOTICE; + } + if(!PxComSendStatusChange(nextchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change."); + strResponse = CTL_RES_ERROR_STATUS_NOTICE; + return false; + } + + // If do not merge(almost random deliver mode), do merging, completing it. + if(is_auto_merge){ + // start merge automatically + if(CHM_INVALID_CHMPXID == nextchmpxid){ + if(!MergeStart()){ + ERR_CHMPRN("Failed to merge or complete merge for \"service out\"."); + strResponse += "\n"; + strResponse += CTL_RES_ERROR_MERGE_AUTO; + return false; + } + }else{ + if(!RequestMergeStart(&strResponse)){ + ERR_CHMPRN("Failed to merge or complete merge for \"service out\"."); + strResponse += "\n"; + strResponse += CTL_RES_ERROR_MERGE_AUTO; + return false; + } + } + } + + }else{ + // set other server status + chmpxsts_t status = pImData->GetServerStatus(chmpxid); + if(IS_CHMPXSTS_SLAVE(status) || IS_CHMPXSTS_SRVOUT(status) || IS_CHMPXSTS_DELETE(status)){ + ERR_CHMPRN("Server is status(0x%016" PRIx64 ":%s), not enough status to deleting.", status, STR_CHMPXSTS_FULL(status).c_str()); + strResponse = CTL_RES_ERROR_STATUS_NOT_ALLOWED; + return false; + } + + if(!IS_CHMPXSTS_DOWN(status)){ + // can transfer command and do on server. + if(!ChmEventSock::RawSendCtlPort(hostname, ctlport, reinterpret_cast(pOrgCommand), strlen(pOrgCommand) + 1, strResponse, sockfd_lockval, sock_retry_count, sock_wait_time, con_retry_count, con_wait_time)){ + ERR_CHMPRN("Failed to transfer command to %s:%d", hostname, ctlport); + strResponse = CTL_RES_ERROR_TRANSFER; + return false; + } + MSG_CHMPRN("Success to transfer command to %s:%d", hostname, ctlport); + + }else{ + // set status on this server and send status update. + // + if(!IS_CHMPXSTS_NOTHING(status)){ // SERVICEIN & DOWN + ERR_CHMPRN("Server is status(0x%016" PRIx64 ":%s), already deleting or deleted.", status, STR_CHMPXSTS_FULL(status).c_str()); + strResponse = CTL_RES_ERROR_STATUS_NOT_ALLOWED; + return false; + } + + // new status + SET_CHMPXSTS_DELETE(status); + SET_CHMPXSTS_PENDING(status); + + // set new status + if(!pImData->SetServerStatus(chmpxid, status)){ + ERR_CHMPRN("Failed to change server(chmpxid:0x%016" PRIx64 ") status(0x%016" PRIx64 ":%s).", chmpxid, status, STR_CHMPXSTS_FULL(status).c_str()); + strResponse = CTL_RES_ERROR_CHANGE_STATUS; + return false; + } + + // Update pending hash. + if(!pImData->UpdatePendingHash()){ + ERR_CHMPRN("Failed to update pending hash for all servers, so stop update status."); + strResponse = CTL_RES_ERROR_CHANGE_STATUS; + return false; + } + + // send status update + CHMPXSVR chmpxsvr; + if(!pImData->GetChmpxSvr(chmpxid, &chmpxsvr)){ + ERR_CHMPRN("Could not get server(0x%016" PRIx64 ") information.", chmpxid); + strResponse = CTL_RES_ERROR_STATUS_NOTICE; + return false; + } + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + WAN_CHMPRN("Could not get next chmpxid, probably there is no server without self chmpx on RING."); + + if(!PxComSendSlavesStatusChange(&chmpxsvr)){ + ERR_CHMPRN("Failed to send server(0x%016" PRIx64 ") status change to slaves.", chmpxid); + strResponse = CTL_RES_ERROR_STATUS_NOTICE; + return false; + } + strResponse = CTL_RES_SUCCESS_STATUS_NOTICE; + }else{ + if(!PxComSendStatusChange(nextchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Failed to send server(0x%016" PRIx64 ") status change.", chmpxid); + strResponse = CTL_RES_ERROR_STATUS_NOTICE; + return false; + } + } + + // If do not merge(almost random deliver mode), do merging, completing it. + if(is_auto_merge){ + // start merge automatically + if(CHM_INVALID_CHMPXID == nextchmpxid){ + if(!MergeStart()){ + ERR_CHMPRN("Failed to merge or complete merge for \"service out\"."); + strResponse += "\n"; + strResponse += CTL_RES_ERROR_MERGE_AUTO; + return false; + } + }else{ + if(!RequestMergeStart(&strResponse)){ + ERR_CHMPRN("Failed to merge or complete merge for \"service out\"."); + strResponse += "\n"; + strResponse += CTL_RES_ERROR_MERGE_AUTO; + return false; + } + } + } + } + } + + strResponse = CTL_RES_SUCCESS; + return true; +} + +bool ChmEventSock::CtlComSelfStatus(string& strResponse) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + strResponse = CTL_RES_INT_ERROR; + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // get all information for self chmpx + long maxmqcnt = pImData->GetMaxMQCount(); + long maxqppxmq = pImData->GetMaxQueuePerChmpxMQ(); + long maxqpcmq = pImData->GetMaxQueuePerClientMQ(); + bool is_server = pImData->IsServerMode(); + long svrcnt = pImData->GetServerCount(); + long slvcnt = pImData->GetSlaveCount(); + size_t clntcnt = ctlsockmap.count(); + CHMPXSVR chmpxsvr; + string group; + int sock = CHM_INVALID_SOCK; + int ctlsock = CHM_INVALID_SOCK; + CHMSTAT server_stat; + CHMSTAT slave_stat; + if(!pImData->GetGroup(group) || !pImData->GetSelfChmpxSvr(&chmpxsvr) || !pImData->GetSelfSocks(sock, ctlsock)){ + ERR_CHMPRN("Failed to get all information for self chmpx."); + strResponse = CTL_RES_ERROR_GET_CHMPXSVR; + return false; + } + if(!pImData->GetStat(&server_stat, &slave_stat)){ + ERR_CHMPRN("Failed to get all stat data."); + strResponse = CTL_RES_ERROR_GET_STAT; + return false; + } + + // set output buffer + stringstream ss; + ss << "Server Name = " << chmpxsvr.name << endl; + ss << "Internal ID = 0x" << to_hexstring(chmpxsvr.chmpxid) << endl; + ss << "RING Name = " << group << endl; + ss << "Mode = " << (is_server ? "Server" : "Slave") << endl; + ss << "Maximum MQ Count = " << to_string(maxmqcnt) << endl; + ss << "Maximum Queue / Chmpx MQ = " << to_string(maxqppxmq) << endl; + ss << "Maximum Queue / Client MQ = " << to_string(maxqpcmq) << endl; + ss << "Hash" << endl; + ss << " Enable Hash Value = 0x" << to_hexstring(chmpxsvr.base_hash) << endl; + ss << " Pending Hash Value = 0x" << to_hexstring(chmpxsvr.pending_hash) << endl; + ss << "Status" << endl; + ss << " Server Status = 0x" << to_hexstring(chmpxsvr.status) << STR_CHMPXSTS_FULL(chmpxsvr.status) << ")" << endl; + ss << " Last Update = " << to_string(chmpxsvr.last_status_time) << endl; + ss << "Connection" << endl; + ss << " Port(socket) = " << to_string(chmpxsvr.port) << "(" << (CHM_INVALID_SOCK == sock ? "n/a" : to_string(sock)) << ")" << endl; + ss << " Control Port(socket) = " << to_string(chmpxsvr.ctlport) << "(" << (CHM_INVALID_SOCK == ctlsock ? "n/a" : to_string(ctlsock)) << ")" << endl; + ss << " Use SSL = " << (chmpxsvr.ssl.is_ssl ? "yes" : "no") << endl; + ss << " Verify Peer = " << (chmpxsvr.ssl.verify_peer ? "yes" : "no") << endl; + ss << " CA path type = " << (chmpxsvr.ssl.is_ca_file ? "file" : "dir") << endl; + ss << " CA path = " << chmpxsvr.ssl.capath << endl; + ss << " Server Cert = " << chmpxsvr.ssl.server_cert << endl; + ss << " Server Private Key = " << chmpxsvr.ssl.server_prikey << endl; + ss << " Slave Cert = " << chmpxsvr.ssl.slave_cert << endl; + ss << " Slave Private Key = " << chmpxsvr.ssl.slave_prikey << endl; + ss << "Connection Count" << endl; + ss << " To Servers = " << to_string(svrcnt) << endl; + ss << " From Slaves = " << to_string(slvcnt) << endl; + ss << " To Control port = " << to_string(clntcnt) << endl; + ss << "Stats" << endl; + ss << " To(From) Servers" << endl; + ss << " send count = " << to_string(server_stat.total_sent_count) << " count" << endl; + ss << " receive count = " << to_string(server_stat.total_received_count) << " count" << endl; + ss << " total = " << to_string(server_stat.total_body_bytes) << " bytes" << endl; + ss << " minimum = " << to_string(server_stat.min_body_bytes) << " bytes" << endl; + ss << " maximum = " << to_string(server_stat.max_body_bytes) << " bytes" << endl; + ss << " total = " << to_string(server_stat.total_elapsed_time.tv_sec) << "s " << to_string(server_stat.total_elapsed_time.tv_nsec) << "ns" << endl; + ss << " minmum = " << to_string(server_stat.min_elapsed_time.tv_sec) << "s " << to_string(server_stat.min_elapsed_time.tv_nsec) << "ns" << endl; + ss << " maximum = " << to_string(server_stat.max_elapsed_time.tv_sec) << "s " << to_string(server_stat.max_elapsed_time.tv_nsec) << "ns" << endl; + ss << " To(From) Slaves" << endl; + ss << " send count = " << to_string(slave_stat.total_sent_count) << " count" << endl; + ss << " receive count = " << to_string(slave_stat.total_received_count) << " count" << endl; + ss << " total = " << to_string(slave_stat.total_body_bytes) << " bytes" << endl; + ss << " minimum = " << to_string(slave_stat.min_body_bytes) << " bytes" << endl; + ss << " maximum = " << to_string(slave_stat.max_body_bytes) << " bytes" << endl; + ss << " total = " << to_string(slave_stat.total_elapsed_time.tv_sec) << "s " << to_string(slave_stat.total_elapsed_time.tv_nsec) << "ns" << endl; + ss << " minmum = " << to_string(slave_stat.min_elapsed_time.tv_sec) << "s " << to_string(slave_stat.min_elapsed_time.tv_nsec) << "ns" << endl; + ss << " maximum = " << to_string(slave_stat.max_elapsed_time.tv_sec) << "s " << to_string(slave_stat.max_elapsed_time.tv_nsec) << "ns" << endl; + + strResponse = ss.str(); + + return true; +} + +bool ChmEventSock::CtlComAllServerStatus(string& strResponse) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + strResponse = CTL_RES_INT_ERROR; + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // get information for all servers + PCHMPXSVR pchmpxsvrs = NULL; + long count = 0L; + string group; + if(!pImData->GetGroup(group) || !pImData->GetChmpxSvrs(&pchmpxsvrs, count) || !pchmpxsvrs){ + ERR_CHMPRN("Failed to get information for all server."); + strResponse = CTL_RES_ERROR_GET_CHMPXSVR; + return false; + } + + // set output buffer + stringstream ss; + ss << "RING Name = " << group << endl << endl; + + for(long cnt = 0; cnt < count; cnt++){ + ss << "No." << to_string(cnt + 1) << endl; + ss << " Server Name = " << pchmpxsvrs[cnt].name << endl; + ss << " Port = " << to_string(pchmpxsvrs[cnt].port) << endl; + ss << " Control Port = " << to_string(pchmpxsvrs[cnt].ctlport) << endl; + ss << " Use SSL = " << (pchmpxsvrs[cnt].ssl.is_ssl ? "yes" : "no") << endl; + if(pchmpxsvrs[cnt].ssl.is_ssl){ + ss << " Verify Peer = " << (pchmpxsvrs[cnt].ssl.verify_peer ? "yes" : "no") << endl; + ss << " CA path type = " << (pchmpxsvrs[cnt].ssl.is_ca_file ? "file" : "dir")<< endl; + ss << " CA path = " << pchmpxsvrs[cnt].ssl.capath << endl; + ss << " Server Cert = " << pchmpxsvrs[cnt].ssl.server_cert << endl; + ss << " Server Private Key = " << pchmpxsvrs[cnt].ssl.server_prikey << endl; + ss << " Slave Cert = " << pchmpxsvrs[cnt].ssl.slave_cert << endl; + ss << " Slave Private Key = " << pchmpxsvrs[cnt].ssl.slave_prikey << endl; + } + ss << " Server Status = 0x" << to_hexstring(pchmpxsvrs[cnt].status) << STR_CHMPXSTS_FULL(pchmpxsvrs[cnt].status) << ")" << endl; + ss << " Last Update = " << to_string(pchmpxsvrs[cnt].last_status_time) << endl; + ss << " Enable Hash Value = 0x" << to_hexstring(pchmpxsvrs[cnt].base_hash) << endl; + ss << " Pending Hash Value = 0x" << to_hexstring(pchmpxsvrs[cnt].pending_hash) << endl; + } + CHM_Free(pchmpxsvrs); + strResponse = ss.str(); + + return true; +} + +bool ChmEventSock::CtlComAllTraceSet(string& strResponse, bool enable) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + strResponse = CTL_RES_ERROR; + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + bool now_enable = pImData->IsTraceEnable(); + + if(enable == now_enable){ + ERR_CHMPRN("Already trace is %s.", (enable ? "enabled" : "disabled")); + strResponse = CTL_RES_ERROR_TRACE_SET_ALREADY; + return false; + } + + bool result; + if(enable){ + result = pImData->EnableTrace(); + }else{ + result = pImData->DisableTrace(); + } + if(!result){ + ERR_CHMPRN("Something error is occured in setting dis/enable TRACE."); + strResponse = CTL_RES_ERROR_TRACE_SET_FAILED; + return false; + } + + strResponse = CTL_RES_SUCCESS; + return true; +} + +bool ChmEventSock::CtlComAllTraceView(string& strResponse, logtype_t dirmask, logtype_t devmask, long count) +{ + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + strResponse = CTL_RES_ERROR; + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + long trcount = pImData->GetTraceCount(); + + if(!IS_SAFE_CHMLOG_MASK(dirmask) || !IS_SAFE_CHMLOG_MASK(devmask)){ + ERR_CHMPRN("dirmask(0x%016" PRIx64 ") or devmask(0x%016" PRIx64 ") are wrong.", dirmask, devmask); + strResponse = CTL_RES_ERROR_TRACE_VIEW_INTERR; + return false; + } + if(count <= 0 || trcount < count){ + WAN_CHMPRN("TRACEVIEW count is wrong, so set all."); + count = trcount; + } + + // get TRACE log. + PCHMLOGRAW plograwarr; + if(NULL == (plograwarr = reinterpret_cast(malloc(sizeof(CHMLOGRAW) * count)))){ + ERR_CHMPRN("Could not allocate memory."); + strResponse = CTL_RES_ERROR_TRACE_VIEW_INTERR; + return false; + } + if(!pImData->GetTrace(plograwarr, count, dirmask, devmask)){ + ERR_CHMPRN("Failed to get trace log."); + strResponse = CTL_RES_ERROR; + CHM_Free(plograwarr); + return false; + } + + // make result string + if(0 == count){ + MSG_CHMPRN("There is no trace log."); + strResponse = CTL_RES_ERROR_TRACE_VIEW_NODATA; + }else{ + for(long cnt = 0; cnt < count; ++cnt){ + strResponse += STR_CHMLOG_TYPE(plograwarr[cnt].log_type); + strResponse += "\t"; + + strResponse += to_string(plograwarr[cnt].length); + strResponse += " byte\t"; + + strResponse += "START("; + strResponse += to_string(plograwarr[cnt].start_time.tv_sec); + strResponse += "s "; + strResponse += to_string(plograwarr[cnt].start_time.tv_nsec); + strResponse += "ns) - "; + + strResponse += "FINISH("; + strResponse += to_string(plograwarr[cnt].fin_time.tv_sec); + strResponse += "s "; + strResponse += to_string(plograwarr[cnt].fin_time.tv_nsec); + strResponse += "ns)\n"; + } + } + CHM_Free(plograwarr); + return true; +} + +//--------------------------------------------------------- +// Methods for PX2PX Command +//--------------------------------------------------------- +// +// PxComSendStatusReq uses sock directly. +// +// [NOTE] +// If you need to lock the socket, you must lock it before calling this method. +// +bool ChmEventSock::PxComSendStatusReq(int sock, chmpxid_t chmpxid) +{ + if(CHM_INVALID_SOCK == sock || CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Make packet + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_STATUS_REQ))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // compkt + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_STATUS_REQ pStatusReq = CVT_COMPTR_STATUS_REQ(pComAll); + SET_PXCOMPKT(pComPkt, CHMPX_COM_STATUS_REQ, pImData->GetSelfChmpxId(), chmpxid, true, 0L); + + pStatusReq->head.type = CHMPX_COM_STATUS_REQ; + pStatusReq->head.result = CHMPX_COM_RES_SUCCESS; + pStatusReq->head.length = sizeof(PXCOM_STATUS_REQ); + + // Send request + // + // [NOTE] + // Should lock the socket in caller if you need. + // + bool is_closed = false; + if(!ChmEventSock::RawSend(sock, GetSSL(sock), pComPkt, is_closed, false, sock_retry_count, sock_wait_time)){ // as default nonblocking + ERR_CHMPRN("Failed to send CHMPX_COM_STATUS_REQ to sock(%d).", sock); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventSock::PxComReceiveStatusReq(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt) +{ + if(!pComHead || !pComAll || !ppResComPkt){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + //PPXCOM_STATUS_REQ pStatusReq = CVT_COMPTR_STATUS_REQ(pComAll); // unused now + *ppResComPkt = NULL; + + // get all server status + PCHMPXSVR pchmpxsvrs = NULL; + long count = 0L; + pxcomres_t result = CHMPX_COM_RES_SUCCESS; + if(!pImData->GetChmpxSvrs(&pchmpxsvrs, count)){ + ERR_CHMPRN("Could not get all server status, but continue to response a error."); + pchmpxsvrs = NULL; + count = 0L; + result = CHMPX_COM_RES_ERROR; + } + + // make response data + PCOMPKT pResComPkt; + if(NULL == (pResComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_STATUS_RES, sizeof(CHMPXSVR) * count))){ + ERR_CHMPRN("Could not allocation memory."); + CHM_Free(pchmpxsvrs); + return false; + } + + // compkt + PPXCOM_ALL pResComAll = CVT_COM_ALL_PTR_PXCOMPKT(pResComPkt); + PPXCOM_STATUS_RES pStatusRes = CVT_COMPTR_STATUS_RES(pResComAll); + SET_PXCOMPKT(pResComPkt, CHMPX_COM_STATUS_RES, pComHead->term_ids.chmpxid, pComHead->dept_ids.chmpxid, true, (sizeof(CHMPXSVR) * count)); // switch chmpxid + + pStatusRes->head.type = CHMPX_COM_STATUS_RES; + pStatusRes->head.result = result; + pStatusRes->head.length = sizeof(PXCOM_STATUS_RES) + (sizeof(CHMPXSVR) * count); + pStatusRes->count = count; + pStatusRes->pchmpxsvr_offset = sizeof(PXCOM_STATUS_RES); + + unsigned char* pbyres = CHM_OFFSET(pStatusRes, sizeof(PXCOM_STATUS_RES), unsigned char*); + if(pchmpxsvrs && 0 < count){ + memcpy(pbyres, pchmpxsvrs, sizeof(CHMPXSVR) * count); + } + + CHM_Free(pchmpxsvrs); + *ppResComPkt = pResComPkt; + + return true; +} + +bool ChmEventSock::PxComReceiveStatusRes(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt, bool is_init_process) +{ + if(!pComHead || !pComAll || !ppResComPkt){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + PPXCOM_STATUS_RES pStatusRes = CVT_COMPTR_STATUS_RES(pComAll); + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + *ppResComPkt = NULL; + + if(pComHead->term_ids.chmpxid == selfchmpxid){ + // To me + if(CHMPX_COM_RES_SUCCESS != pStatusRes->head.result){ + // Something error occured by status response + ERR_CHMPRN("PXCOM_STATUS_RES is failed."); + return false; + } + + // Succeed status response + PCHMPXSVR pchmpxsvrs = CHM_OFFSET(pStatusRes, pStatusRes->pchmpxsvr_offset, PCHMPXSVR); + if(!pchmpxsvrs){ + ERR_CHMPRN("There is no CHMPXSVR data in received PXCOM_STATUS_RES."); + return false; + } + + // bup status + chmpxsts_t bupstatus = pImData->GetSelfStatus(); + + // Merge all status + if(!pImData->MergeChmpxSvrs(pchmpxsvrs, pStatusRes->count, true, is_init_process, eqfd)){ // remove server chmpx data if there is not in list + ERR_CHMPRN("Failed to merge server CHMPXLIST from CHMPXSVR list."); + return false; + } + + // If server mode, update hash values + if(is_server_mode){ + if(!pImData->UpdatePendingHash()){ + ERR_CHMPRN("Failed to update pending hash for all servers, so stop update status."); + return false; + } + // check self status changing( normal -> merging ) + // + // [NOTICE] + // MergeChmpxSvrs() can change self status from CHMPXSTS_SRVIN_DOWN_NORMAL or CHMPXSTS_SRVIN_DOWN_DELPENDING + // to CHMPXSTS_SRVIN_UP_MERGING, if it is neeed. + // If status is changed, we need to start merging. + // + // BUT we do not start merging here, because the caller funstion do it. + // + chmpxsts_t newstatus = pImData->GetSelfStatus(); + if((IS_CHMPXSTS_NOTHING(bupstatus) || IS_CHMPXSTS_PENDING(bupstatus)) && (IS_CHMPXSTS_DOING(newstatus) || IS_CHMPXSTS_DONE(newstatus))){ + WAN_CHMPRN("self status changed from 0x%016" PRIx64 ":%s to 0x%016" PRIx64 ":%s.", bupstatus, STR_CHMPXSTS_FULL(bupstatus).c_str(), newstatus, STR_CHMPXSTS_FULL(newstatus).c_str()); + } + } + }else{ + // To other chmpxid + ERR_CHMPRN("Received PXCOM_STATUS_RES packet, but terminal chmpxid(0x%016" PRIx64 ") is not self chmpxid(0x%016" PRIx64 ").", pComHead->term_ids.chmpxid, selfchmpxid); + return false; + } + return true; +} + +// +// PxComSendConinitReq uses sock directly. +// +// [NOTE] +// PPXCOM_CONINIT_REQ does not select socket.(Do not use GetLockedSendSock method). +// +bool ChmEventSock::PxComSendConinitReq(int sock, chmpxid_t chmpxid) +{ + if(CHM_INVALID_SOCK == sock || CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // datas + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + short port = CHM_INVALID_PORT; + short ctlport = CHM_INVALID_PORT; + if(!pImData->GetSelfPorts(port, ctlport)){ + ERR_CHMPRN("Could not get self ctlport."); + return false; + } + + // Make packet + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_CONINIT_REQ))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_CONINIT_REQ pConinitReq = CVT_COMPTR_CONINIT_REQ(pComAll); + SET_PXCOMPKT(pComPkt, CHMPX_COM_CONINIT_REQ, selfchmpxid, chmpxid, true, 0L); + + pConinitReq->head.type = CHMPX_COM_CONINIT_REQ; + pConinitReq->head.result = CHMPX_COM_RES_SUCCESS; + pConinitReq->head.length = sizeof(PXCOM_CONINIT_REQ); + pConinitReq->chmpxid = selfchmpxid; + pConinitReq->ctlport = ctlport; + + // Send request + if(!ChmEventSock::LockedSend(sock, GetSSL(sock), pComPkt)){ // as default nonblocking + ERR_CHMPRN("Failed to send CHMPX_COM_CONINIT_REQ to sock(%d).", sock); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventSock::PxComReceiveConinitReq(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt, chmpxid_t& from_chmpxid, short& ctlport) +{ + if(!pComHead || !pComAll || !ppResComPkt){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + PPXCOM_CONINIT_REQ pConinitReq = CVT_COMPTR_CONINIT_REQ(pComAll); + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + *ppResComPkt = NULL; + + if(pComHead->term_ids.chmpxid == selfchmpxid){ + // To me + from_chmpxid= pConinitReq->chmpxid; + ctlport = pConinitReq->ctlport; + + }else{ + // To other chmpxid + ERR_CHMPRN("Received PPXCOM_CONINIT_REQ packet, but terminal chmpxid(0x%016" PRIx64 ") is not self chmpxid(0x%016" PRIx64 ").", pComHead->term_ids.chmpxid, selfchmpxid); + return false; + } + return true; +} + +// +// PxComSendConinitRes uses sock directly. +// +// [NOTE] +// PPXCOM_CONINIT_RES does not select socket.(Do not use GetLockedSendSock method). +// +bool ChmEventSock::PxComSendConinitRes(int sock, chmpxid_t chmpxid, pxcomres_t result) +{ + if(CHM_INVALID_SOCK == sock || CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Make packet + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_CONINIT_RES))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // compkt + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_CONINIT_RES pConinitRes = CVT_COMPTR_CONINIT_RES(pComAll); + SET_PXCOMPKT(pComPkt, CHMPX_COM_CONINIT_RES, pImData->GetSelfChmpxId(), chmpxid, true, 0L); + + pConinitRes->head.type = CHMPX_COM_CONINIT_RES; + pConinitRes->head.result = result; + pConinitRes->head.length = sizeof(PXCOM_CONINIT_RES); + + // Send request + if(!ChmEventSock::LockedSend(sock, GetSSL(sock), pComPkt)){ // as default nonblocking + ERR_CHMPRN("Failed to send CHMPX_COM_CONINIT_RES to sock(%d).", sock); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventSock::PxComReceiveConinitRes(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt, pxcomres_t& result) +{ + if(!pComHead || !pComAll || !ppResComPkt){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + PPXCOM_CONINIT_RES pConinitRes = CVT_COMPTR_CONINIT_RES(pComAll); + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + *ppResComPkt = NULL; + + if(pComHead->term_ids.chmpxid == selfchmpxid){ + // To me + result = pConinitRes->head.result; + + }else{ + // To other chmpxid + ERR_CHMPRN("Received PPXCOM_CONINIT_RES packet, but terminal chmpxid(0x%016" PRIx64 ") is not self chmpxid(0x%016" PRIx64 ").", pComHead->term_ids.chmpxid, selfchmpxid); + return false; + } + return true; +} + +bool ChmEventSock::PxComSendJoinRing(chmpxid_t chmpxid, PCHMPXSVR pserver) +{ + if(CHM_INVALID_CHMPXID == chmpxid || !pserver){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Make packet + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_JOIN_RING))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // compkt + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_JOIN_RING pJoinRing = CVT_COMPTR_JOIN_RING(pComAll); + SET_PXCOMPKT(pComPkt, CHMPX_COM_JOIN_RING, pImData->GetSelfChmpxId(), chmpxid, true, 0L); + + pJoinRing->head.type = CHMPX_COM_JOIN_RING; + pJoinRing->head.result = CHMPX_COM_RES_SUCCESS; + pJoinRing->head.length = sizeof(PXCOM_JOIN_RING); + COPY_PCHMPXSVR(&(pJoinRing->server), pserver); + + // Send request + if(!Send(pComPkt, NULL, 0L)){ + ERR_CHMPRN("Failed to send CHMPX_COM_JOIN_RING to chmpxid(0x%016" PRIx64 ").", chmpxid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventSock::PxComReceiveJoinRing(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt) +{ + if(!pComHead || !pComAll || !ppResComPkt){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + PPXCOM_JOIN_RING pJoinRing = CVT_COMPTR_JOIN_RING(pComAll); + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + *ppResComPkt = NULL; + + if(pComHead->dept_ids.chmpxid == selfchmpxid){ + // around -> next step: update pending hash value + // + if(CHMPX_COM_RES_SUCCESS == pJoinRing->head.result){ + // Succeed adding this server on RING + // + // Next step is update all server status. + // + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + ERR_CHMPRN("Could not get next server chmpxid, maybe there is no server on RING...But WHY?"); + return true; + } + + int nextsock = CHM_INVALID_SOCK; + if(!GetLockedSendSock(nextchmpxid, nextsock, false) || CHM_INVALID_SOCK == nextsock){ // LOCK SOCKET + ERR_CHMPRN("Could not get socket for chmpxid(0x%016" PRIx64 ").", nextchmpxid); + return false; + } + + // Send request + if(!PxComSendStatusReq(nextsock, nextchmpxid)){ + ERR_CHMPRN("Failed to send PXCOM_STATUS_REQ."); + UnlockSendSock(nextsock); // UNLOCK SOCKET + return false; + } + UnlockSendSock(nextsock); // UNLOCK SOCKET + return true; + + }else{ + // Failed adding this server on RING + // + // This function returns false, so caller gets it and close all connection. + // So that, this function do nothing. + // + ERR_CHMPRN("PXCOM_JOIN_RING is failed, hope to recover automatically."); + return false; + } + }else{ + // update own chmshm & transfer packet. + // + + // update chmshm + pxcomres_t ResultCode; + if(!pImData->MergeChmpxSvrs(&(pJoinRing->server), 1, false, false, eqfd)){ // not remove other + // error occured, so transfer packet. + ERR_CHMPRN("Could not update server chmpx information."); + ResultCode = CHMPX_COM_RES_ERROR; + }else{ + // succeed. + ResultCode = CHMPX_COM_RES_SUCCESS; + } + if(CHMPX_COM_RES_SUCCESS != pJoinRing->head.result){ + MSG_CHMPRN("Already error occured before this server."); + ResultCode = CHMPX_COM_RES_ERROR; + } + + // check & rechain + bool is_rechain = false; + if(!CheckRechainRing(pJoinRing->server.chmpxid, is_rechain)){ + ERR_CHMPRN("Something error occured in rehcaining RING, but continue..."); + ResultCode = CHMPX_COM_RES_ERROR; + }else{ + if(is_rechain){ + MSG_CHMPRN("Rechained RING after joining chmpxid(0x%016" PRIx64 ").", pJoinRing->server.chmpxid); + }else{ + MSG_CHMPRN("Not rechained RING after joining chmpxid(0x%016" PRIx64 ").", pJoinRing->server.chmpxid); + } + } + + // next chmpxid(after rechaining) + chmpxid_t nextchmpxid; + if(is_rechain){ + // After rechaining, new server does not in server list. + // So get old next server by GetNextRingChmpxId(), force set new server chmpxid. + // + nextchmpxid = pJoinRing->server.chmpxid; + }else{ + nextchmpxid = GetNextRingChmpxId(); + } + if(CHM_INVALID_CHMPXID == nextchmpxid){ + ERR_CHMPRN("Could not get next server chmpxid."); + return false; + } + + if(!IsSafeDeptAndNextChmpxId(pComHead->dept_ids.chmpxid, nextchmpxid)){ + // Deperture chmpx maybe DOWN! + // + // [NOTICE] + // This case is very small case, deperture server sends this packet but that server could not + // be connected from in RING server.(ex. down after sending, or FQDN is wrong, etc) + // So we do not transfer this packet, because it could not be stopped in RING. + // + ERR_CHMPRN("Deperture chmpxid(0x%016" PRIx64 ") maybe down, so stop transferring this packet.", pComHead->dept_ids.chmpxid); + *ppResComPkt = NULL; + + }else{ + // make response data buffer + PCOMPKT pResComPkt; + if(NULL == (pResComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_JOIN_RING))){ + ERR_CHMPRN("Could not allocation memory."); + return false; + } + + // compkt + PPXCOM_ALL pResComAll = CVT_COM_ALL_PTR_PXCOMPKT(pResComPkt); + PPXCOM_JOIN_RING pResJoinRing= CVT_COMPTR_JOIN_RING(pResComAll); + + SET_PXCOMPKT(pResComPkt, CHMPX_COM_JOIN_RING, pComHead->dept_ids.chmpxid, nextchmpxid, false, 0L); // dept chmpxid is not changed + COPY_TIMESPEC(&(pResComPkt->head.reqtime), &(pComHead->reqtime)); // not change + + // join_ring(copy) + pResJoinRing->head.type = pJoinRing->head.type; + pResJoinRing->head.result = ResultCode; + pResJoinRing->head.length = pJoinRing->head.length; + COPY_PCHMPXSVR(&(pResJoinRing->server), &(pJoinRing->server)); + + *ppResComPkt = pResComPkt; + } + } + return true; +} + +bool ChmEventSock::PxComSendStatusUpdate(chmpxid_t chmpxid, PCHMPXSVR pchmpxsvrs, long count) +{ + if(CHM_INVALID_CHMPXID == chmpxid || !pchmpxsvrs || count <= 0L){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // make data + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_STATUS_UPDATE, sizeof(CHMPXSVR) * count))){ + ERR_CHMPRN("Could not allocation memory."); + return false; + } + + // compkt + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_STATUS_UPDATE pStsUpdate = CVT_COMPTR_STATUS_UPDATE(pComAll); + SET_PXCOMPKT(pComPkt, CHMPX_COM_STATUS_UPDATE, pImData->GetSelfChmpxId(), chmpxid, true, (sizeof(CHMPXSVR) * count)); + + // status_update + pStsUpdate->head.type = CHMPX_COM_STATUS_UPDATE; + pStsUpdate->head.result = CHMPX_COM_RES_SUCCESS; + pStsUpdate->head.length = sizeof(PXCOM_STATUS_UPDATE) + (sizeof(CHMPXSVR) * count); + pStsUpdate->count = count; + pStsUpdate->pchmpxsvr_offset = sizeof(PXCOM_STATUS_UPDATE); + + // extra area + PCHMPXSVR pStsUpChmsvr = CHM_OFFSET(pStsUpdate, sizeof(PXCOM_STATUS_UPDATE), PCHMPXSVR); + for(long cnt = 0; cnt < count; cnt++){ + COPY_PCHMPXSVR(&pStsUpChmsvr[cnt], &pchmpxsvrs[cnt]); + } + + // Send request + if(!Send(pComPkt, NULL, 0L)){ + ERR_CHMPRN("Failed to send CHMPX_COM_STATUS_UPDATE to chmpxid(0x%016" PRIx64 ").", chmpxid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventSock::PxComReceiveStatusUpdate(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt) +{ + if(!pComHead || !pComAll || !ppResComPkt){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + PPXCOM_STATUS_UPDATE pReqStsUpdate = CVT_COMPTR_STATUS_UPDATE(pComAll); + PCHMPXSVR pReqChmsvrs = CHM_OFFSET(pReqStsUpdate, sizeof(PXCOM_STATUS_UPDATE), PCHMPXSVR); + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + *ppResComPkt = NULL; + + if(pComHead->dept_ids.chmpxid == selfchmpxid){ + // around + // + if(CHMPX_COM_RES_SUCCESS == pReqStsUpdate->head.result){ + // Succeed updating status + return true; + }else{ + // Failed updating status, for recovering... + ERR_CHMPRN("PXCOM_STATUS_UPDATE is failed, NEED to check all servers and recover MANUALLY."); + return false; + } + + }else{ + // update own chmshm & transfer packet. + // + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + ERR_CHMPRN("Could not get next server chmpxid."); + return false; + } + + pxcomres_t ResultCode = pReqStsUpdate->head.result; + long ResultCount = pReqStsUpdate->count; + PCHMPXSVR pResultChmsvrs = pReqChmsvrs; + + // Already error occured, skip merging + if(CHMPX_COM_RES_SUCCESS != ResultCode){ + MSG_CHMPRN("Already error occured before this server."); + ResultCode = CHMPX_COM_RES_ERROR; + + }else{ + // update status into chmshm + if(!pImData->MergeChmpxSvrsForStatusUpdate(pResultChmsvrs, ResultCount, eqfd)){ // not remove other + // error occured. + ERR_CHMPRN("Could not update all server status, maybe recovering in function..."); + ResultCode = CHMPX_COM_RES_ERROR; + }else{ + MSG_CHMPRN("Succeed update status."); + ResultCode = CHMPX_COM_RES_SUCCESS; + + // Update pending hash. + if(!pImData->UpdatePendingHash()){ + ERR_CHMPRN("Failed to update pending hash for all servers, but continue...."); + } + } + } + + if(!IsSafeDeptAndNextChmpxId(pComHead->dept_ids.chmpxid, nextchmpxid)){ + // Deperture chmpx maybe DOWN! + // + // [NOTICE] + // This case is very small case, deperture server sends this packet but that server could not + // be connected from in RING server.(ex. down after sending, or FQDN is wrong, etc) + // So we do not transfer this packet, because it could not be stopped in RING. + // + ERR_CHMPRN("Deperture chmpxid(0x%016" PRIx64 ") maybe down, so stop transferring this packet.", pComHead->dept_ids.chmpxid); + *ppResComPkt = NULL; + + }else{ + // make response data buffer + if(NULL == ((*ppResComPkt) = ChmEventSock::AllocatePxComPacket(CHMPX_COM_STATUS_UPDATE, sizeof(CHMPXSVR) * ResultCount))){ + ERR_CHMPRN("Could not allocation memory."); + return false; + } + + // compkt + PPXCOM_ALL pResComAll = CVT_COM_ALL_PTR_PXCOMPKT(*ppResComPkt); + PPXCOM_STATUS_UPDATE pResStsUpdate = CVT_COMPTR_STATUS_UPDATE(pResComAll); + + SET_PXCOMPKT((*ppResComPkt), CHMPX_COM_STATUS_UPDATE, pComHead->dept_ids.chmpxid, nextchmpxid, false, (sizeof(CHMPXSVR) * ResultCount)); // dept chmpxid is not changed. + COPY_TIMESPEC(&((*ppResComPkt)->head.reqtime), &(pComHead->reqtime)); // not change + + // status_update(copy) + pResStsUpdate->head.type = pReqStsUpdate->head.type; + pResStsUpdate->head.result = ResultCode; + pResStsUpdate->head.length = pReqStsUpdate->head.length; + pResStsUpdate->count = ResultCount; + pResStsUpdate->pchmpxsvr_offset = sizeof(PXCOM_STATUS_UPDATE); + + // extra area + PCHMPXSVR pResChmsvrs = CHM_OFFSET(pResStsUpdate, sizeof(PXCOM_STATUS_UPDATE), PCHMPXSVR); + for(long cnt = 0; cnt < ResultCount; cnt++){ + COPY_PCHMPXSVR(&pResChmsvrs[cnt], &pResultChmsvrs[cnt]); + } + } + } + return true; +} + +bool ChmEventSock::PxComSendStatusConfirm(chmpxid_t chmpxid, PCHMPXSVR pchmpxsvrs, long count) +{ + if(CHM_INVALID_CHMPXID == chmpxid || !pchmpxsvrs || count <= 0L){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // make data + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_STATUS_CONFIRM, sizeof(CHMPXSVR) * count))){ + ERR_CHMPRN("Could not allocation memory."); + return false; + } + + // compkt + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_STATUS_CONFIRM pStsConfirm = CVT_COMPTR_STATUS_CONFIRM(pComAll); + SET_PXCOMPKT(pComPkt, CHMPX_COM_STATUS_CONFIRM, pImData->GetSelfChmpxId(), chmpxid, true, (sizeof(CHMPXSVR) * count)); + + // status_update + pStsConfirm->head.type = CHMPX_COM_STATUS_CONFIRM; + pStsConfirm->head.result = CHMPX_COM_RES_SUCCESS; + pStsConfirm->head.length = sizeof(PXCOM_STATUS_CONFIRM) + (sizeof(CHMPXSVR) * count); + pStsConfirm->count = count; + pStsConfirm->pchmpxsvr_offset = sizeof(PXCOM_STATUS_CONFIRM); + + // extra area + PCHMPXSVR pStsChmsvr = CHM_OFFSET(pStsConfirm, sizeof(PXCOM_STATUS_CONFIRM), PCHMPXSVR); + for(long cnt = 0; cnt < count; cnt++){ + COPY_PCHMPXSVR(&pStsChmsvr[cnt], &pchmpxsvrs[cnt]); + } + + // Send request + if(!Send(pComPkt, NULL, 0L)){ + ERR_CHMPRN("Failed to send CHMPX_COM_STATUS_CONFIRM to chmpxid(0x%016" PRIx64 ").", chmpxid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventSock::PxComReceiveStatusConfirm(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt) +{ + if(!pComHead || !pComAll || !ppResComPkt){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + PPXCOM_STATUS_CONFIRM pReqStsConfirm = CVT_COMPTR_STATUS_CONFIRM(pComAll); + PCHMPXSVR pReqChmsvrs = CHM_OFFSET(pReqStsConfirm, sizeof(PXCOM_STATUS_CONFIRM), PCHMPXSVR); + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + *ppResComPkt = NULL; + + if(pComHead->dept_ids.chmpxid == selfchmpxid){ + // around + // + if(CHMPX_COM_RES_SUCCESS == pReqStsConfirm->head.result){ + // Succeed status confirm, do next step + // + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + ERR_CHMPRN("Could not get next server chmpxid."); + return false; + } + + // Check suspending server + if(IsSuspendServerInRing()){ + WAN_CHMPRN("Found suspendind servers, so then could not start to merge."); + return true; + } + + // Send start merge + if(!PxComSendMergeStart(nextchmpxid)){ + ERR_CHMPRN("Failed to send CHMPX_COM_MERGE_START to next chmpxid(0x%016" PRIx64 ").", nextchmpxid); + return false; + } + + }else{ + // Failed status confirm + ERR_CHMPRN("PXCOM_STATUS_CONFIRM is failed, do nothing to recover..."); + return false; + } + + }else{ + // check chmshm & transfer packet. + // + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + ERR_CHMPRN("Could not get next server chmpxid."); + return false; + } + + pxcomres_t ResultCode = pReqStsConfirm->head.result; + + // Already error occured, skip merging + if(CHMPX_COM_RES_SUCCESS != ResultCode){ + MSG_CHMPRN("Already error occured before this server."); + ResultCode = CHMPX_COM_RES_ERROR; + }else{ + // Compare + if(!pImData->CompareChmpxSvrs(pReqChmsvrs, pReqStsConfirm->count)){ + ResultCode = CHMPX_COM_RES_ERROR; + }else{ + ResultCode = CHMPX_COM_RES_SUCCESS; + } + } + + if(!IsSafeDeptAndNextChmpxId(pComHead->dept_ids.chmpxid, nextchmpxid)){ + // Deperture chmpx maybe DOWN! + // + // [NOTICE] + // This case is very small case, deperture server sends this packet but that server could not + // be connected from in RING server.(ex. down after sending, or FQDN is wrong, etc) + // So we do not transfer this packet, because it could not be stopped in RING. + // + ERR_CHMPRN("Deperture chmpxid(0x%016" PRIx64 ") maybe down, so stop transferring this packet.", pComHead->dept_ids.chmpxid); + *ppResComPkt = NULL; + + }else{ + // make response data buffer + if(NULL == ((*ppResComPkt) = ChmEventSock::AllocatePxComPacket(CHMPX_COM_STATUS_CONFIRM, sizeof(CHMPXSVR) * pReqStsConfirm->count))){ + ERR_CHMPRN("Could not allocation memory."); + return false; + } + + // compkt + PPXCOM_ALL pResComAll = CVT_COM_ALL_PTR_PXCOMPKT(*ppResComPkt); + PPXCOM_STATUS_CONFIRM pResStsConfirm = CVT_COMPTR_STATUS_CONFIRM(pResComAll); + + SET_PXCOMPKT((*ppResComPkt), CHMPX_COM_STATUS_CONFIRM, pComHead->dept_ids.chmpxid, nextchmpxid, false, (sizeof(CHMPXSVR) * pReqStsConfirm->count)); // dept chmpxid is not changed. + COPY_TIMESPEC(&((*ppResComPkt)->head.reqtime), &(pComHead->reqtime)); // not change + + // status_update(copy) + pResStsConfirm->head.type = pReqStsConfirm->head.type; + pResStsConfirm->head.result = ResultCode; + pResStsConfirm->head.length = pReqStsConfirm->head.length; + pResStsConfirm->count = pReqStsConfirm->count; + pResStsConfirm->pchmpxsvr_offset = sizeof(PXCOM_STATUS_CONFIRM); + + // extra area + PCHMPXSVR pResChmsvrs = CHM_OFFSET(pResStsConfirm, sizeof(PXCOM_STATUS_CONFIRM), PCHMPXSVR); + for(long cnt = 0; cnt < pReqStsConfirm->count; cnt++){ + COPY_PCHMPXSVR(&pResChmsvrs[cnt], &pReqChmsvrs[cnt]); + } + } + } + return true; +} + +// +// STATUS_CHANGE is only sending packet to slaves. +// +bool ChmEventSock::PxComSendStatusChange(chmpxid_t chmpxid, PCHMPXSVR pserver, bool is_send_slaves) +{ + if(CHM_INVALID_CHMPXID == chmpxid || !pserver){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Make packet + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_STATUS_CHANGE))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // compkt + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_STATUS_CHANGE pStatusChange = CVT_COMPTR_STATUS_CHANGE(pComAll); + SET_PXCOMPKT(pComPkt, CHMPX_COM_STATUS_CHANGE, pImData->GetSelfChmpxId(), chmpxid, true, 0L); + + pStatusChange->head.type = CHMPX_COM_STATUS_CHANGE; + pStatusChange->head.result = CHMPX_COM_RES_SUCCESS; + pStatusChange->head.length = sizeof(PXCOM_STATUS_CHANGE); + COPY_PCHMPXSVR(&(pStatusChange->server), pserver); + + // Send request + if(!Send(pComPkt, NULL, 0L)){ + ERR_CHMPRN("Failed to send CHMPX_COM_STATUS_CHANGE to chmpxid(0x%016" PRIx64 ").", chmpxid); + CHM_Free(pComPkt); + return false; + } + + // send to slaves + if(is_send_slaves){ + if(!PxComSendSlavesStatusChange(pserver)){ + ERR_CHMPRN("Failed to send CHMPX_COM_STATUS_CHANGE to slaves."); + } + } + CHM_Free(pComPkt); + + return true; +} + +// +// Only send slaves +// +bool ChmEventSock::PxComSendSlavesStatusChange(PCHMPXSVR pserver) +{ + if(!pserver){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // get all slave chmpxid. + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + chmpxidlist_t chmpxidlist; + if(0L == pImData->GetSlaveChmpxIds(chmpxidlist)){ + MSG_CHMPRN("There is no slave, so not need to send STATUS_CHANGE."); + return true; + } + + // Make packet + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_STATUS_CHANGE))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // compkt + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_STATUS_CHANGE pStatusChange = CVT_COMPTR_STATUS_CHANGE(pComAll); + + // loop: send to slaves + for(chmpxidlist_t::const_iterator iter = chmpxidlist.begin(); iter != chmpxidlist.end(); ++iter){ + // Slave chmpxid list has server mode chmpxid. + // Because the server connects other server for RING as SLAVE. + // Then if server chmpxid, skip it. + // + if(pImData->IsServerChmpxId(*iter)){ + continue; + } + + // set status change struct data + SET_PXCOMPKT(pComPkt, CHMPX_COM_STATUS_CHANGE, selfchmpxid, (*iter), true, 0L); + + pStatusChange->head.type = CHMPX_COM_STATUS_CHANGE; + pStatusChange->head.result = CHMPX_COM_RES_SUCCESS; + pStatusChange->head.length = sizeof(PXCOM_STATUS_CHANGE); + COPY_PCHMPXSVR(&(pStatusChange->server), pserver); + + // send + if(!Send(pComPkt, NULL, 0L)){ + ERR_CHMPRN("Failed to send CHMPX_COM_STATUS_CHANGE to slave chmpxid(0x%016" PRIx64 "), but continue...", *iter); + } + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventSock::PxComReceiveStatusChange(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt) +{ + if(!pComHead || !pComAll || !ppResComPkt){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + PPXCOM_STATUS_CHANGE pStatusChange = CVT_COMPTR_STATUS_CHANGE(pComAll); + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + *ppResComPkt = NULL; + + if(pComHead->dept_ids.chmpxid == selfchmpxid){ + // around + // + if(CHMPX_COM_RES_SUCCESS == pStatusChange->head.result){ + // Succeed change status + MSG_CHMPRN("PXCOM_STATUS_CHANGE is around the RING with success."); + }else{ + // Failed change status + ERR_CHMPRN("PXCOM_STATUS_CHANGE is failed, hope to recover automatic"); + return false; + } + }else{ + // update own chmshm & transfer packet. + // + + // update chmshm + pxcomres_t ResultCode; + if(!pImData->MergeChmpxSvrs(&(pStatusChange->server), 1, false, false, eqfd)){ // not remove other + // error occured, so transfer packet. + ERR_CHMPRN("Could not update server chmpx information."); + ResultCode = CHMPX_COM_RES_ERROR; + }else{ + // succeed. + ResultCode = CHMPX_COM_RES_SUCCESS; + } + if(CHMPX_COM_RES_SUCCESS != pStatusChange->head.result){ + MSG_CHMPRN("Already error occured before this server."); + ResultCode = CHMPX_COM_RES_ERROR; + } + + // Update pending hash. + if(is_server_mode){ + if(!pImData->UpdatePendingHash()){ + WAN_CHMPRN("Failed to update pending hash for all servers, so stop update status, but continue..."); + } + } + + // [NOTICE] + // This STATUS_CHANGE is only received by Slave and Server. + // If server mode, transfer packet to RING, if slave mode, do not transfer it. + // + if(is_server_mode){ + // always, new updated status to send to slaves + // + if(!PxComSendSlavesStatusChange(&(pStatusChange->server))){ + ERR_CHMPRN("Failed to send CHMPX_COM_STATUS_CHANGE to slaves, but continue..."); + } + + // check & rechain + // + bool is_rechain = false; + if(!CheckRechainRing(pStatusChange->server.chmpxid, is_rechain)){ + ERR_CHMPRN("Something error occured in rehcaining RING, but continue..."); + ResultCode = CHMPX_COM_RES_ERROR; + }else{ + if(is_rechain){ + MSG_CHMPRN("Rechained RING after joining chmpxid(0x%016" PRIx64 ").", pStatusChange->server.chmpxid); + }else{ + MSG_CHMPRN("Not rechained RING after joining chmpxid(0x%016" PRIx64 ").", pStatusChange->server.chmpxid); + } + } + + // next chmpxid(after rechaining) + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + ERR_CHMPRN("Could not get next server chmpxid."); + return false; + } + + if(!IsSafeDeptAndNextChmpxId(pComHead->dept_ids.chmpxid, nextchmpxid)){ + // Deperture chmpx maybe DOWN! + // + // [NOTICE] + // This case is very small case, deperture server sends this packet but that server could not + // be connected from in RING server.(ex. down after sending, or FQDN is wrong, etc) + // So we do not transfer this packet, because it could not be stopped in RING. + // + ERR_CHMPRN("Deperture chmpxid(0x%016" PRIx64 ") maybe down, so stop transferring this packet.", pComHead->dept_ids.chmpxid); + *ppResComPkt = NULL; + + }else{ + // make response data buffer + PCOMPKT pResComPkt; + if(NULL == (pResComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_STATUS_CHANGE))){ + ERR_CHMPRN("Could not allocation memory."); + return false; + } + + // compkt + PPXCOM_ALL pResComAll = CVT_COM_ALL_PTR_PXCOMPKT(pResComPkt); + PPXCOM_STATUS_CHANGE pResStatusChange= CVT_COMPTR_STATUS_CHANGE(pResComAll); + + SET_PXCOMPKT(pResComPkt, CHMPX_COM_STATUS_CHANGE, pComHead->dept_ids.chmpxid, nextchmpxid, false, 0L); // dept chmpxid is not changed + COPY_TIMESPEC(&(pResComPkt->head.reqtime), &(pComHead->reqtime)); // not change + + // change_status(copy) + pResStatusChange->head.type = pStatusChange->head.type; + pResStatusChange->head.result = ResultCode; + pResStatusChange->head.length = pStatusChange->head.length; + COPY_PCHMPXSVR(&(pResStatusChange->server), &(pStatusChange->server)); + + *ppResComPkt = pResComPkt; + } + } + } + return true; +} + +bool ChmEventSock::PxComSendMergeStart(chmpxid_t chmpxid) +{ + if(CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Make packet + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_MERGE_START))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // compkt + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_MERGE_START pMergeStart = CVT_COMPTR_MERGE_START(pComAll); + SET_PXCOMPKT(pComPkt, CHMPX_COM_MERGE_START, pImData->GetSelfChmpxId(), chmpxid, true, 0L); + + pMergeStart->head.type = CHMPX_COM_MERGE_START; + pMergeStart->head.result = CHMPX_COM_RES_SUCCESS; + pMergeStart->head.length = sizeof(PXCOM_MERGE_START); + + // Send request + if(!Send(pComPkt, NULL, 0L)){ + ERR_CHMPRN("Failed to send CHMPX_COM_MERGE_START to chmpxid(0x%016" PRIx64 ").", chmpxid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventSock::PxComReceiveMergeStart(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt) +{ + if(!pComHead || !pComAll || !ppResComPkt){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + PPXCOM_MERGE_START pMergeStart = CVT_COMPTR_MERGE_START(pComAll); + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + *ppResComPkt = NULL; + + if(pComHead->dept_ids.chmpxid == selfchmpxid){ + // around -> next step: do merging + // + if(CHMPX_COM_RES_SUCCESS == pMergeStart->head.result){ + // Succeed + MSG_CHMPRN("PXCOM_MERGE_START is succeed, All server start to merge."); + + // If there is DELETE Pending server, do DELETE done status here! + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + MSG_CHMPRN("Could not get next chmpxid, probably there is no server without self chmpx on RING."); + } + chmpxid_t downchmpxid; + while(CHM_INVALID_CHMPXID != (downchmpxid = pImData->GetChmpxIdByStatus(CHMPXSTS_SRVIN_DOWN_DELPENDING, false))){ + // change down server status + if(!pImData->SetServerStatus(downchmpxid, CHMPXSTS_SRVIN_DOWN_DELETED)){ + ERR_CHMPRN("Failed to change server(0x%016" PRIx64 ") status to CHMPXSTS_SRVIN_DOWN_DELETED.", downchmpxid); + return false; + } + // send down server status update + CHMPXSVR chmpxsvr; + if(!pImData->GetChmpxSvr(downchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Could not get server(0x%016" PRIx64 ") information.", downchmpxid); + return false; + } + if(CHM_INVALID_CHMPXID == nextchmpxid){ + if(!PxComSendSlavesStatusChange(&chmpxsvr)){ + ERR_CHMPRN("Failed to send server(0x%016" PRIx64 ") status change to slaves.", downchmpxid); + return false; + } + }else{ + if(!PxComSendStatusChange(nextchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Failed to send server(0x%016" PRIx64 ") status change.", downchmpxid); + return false; + } + } + } + }else{ + // Something error occured on RING. + ERR_CHMPRN("PXCOM_MERGE_START is failed, could not recover, but returns true."); + return true; + } + }else{ + // update own chmshm & transfer packet. + // + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + ERR_CHMPRN("Could not get next server chmpxid."); + return false; + } + + if(!IsSafeDeptAndNextChmpxId(pComHead->dept_ids.chmpxid, nextchmpxid)){ + // Deperture chmpx maybe DOWN! + // + // [NOTICE] + // This case is very small case, deperture server sends this packet but that server could not + // be connected from in RING server.(ex. down after sending, or FQDN is wrong, etc) + // So we do not transfer this packet, because it could not be stopped in RING. + // + ERR_CHMPRN("Deperture chmpxid(0x%016" PRIx64 ") maybe down, so stop transferring this packet.", pComHead->dept_ids.chmpxid); + *ppResComPkt = NULL; + + }else{ + // make response data buffer + PCOMPKT pResComPkt; + if(NULL == (pResComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_MERGE_START))){ + ERR_CHMPRN("Could not allocation memory."); + return false; + } + + // compkt + PPXCOM_ALL pResComAll = CVT_COM_ALL_PTR_PXCOMPKT(pResComPkt); + PPXCOM_MERGE_START pResMergeStart = CVT_COMPTR_MERGE_START(pResComAll); + + SET_PXCOMPKT(pResComPkt, CHMPX_COM_MERGE_START, pComHead->dept_ids.chmpxid, nextchmpxid, false, 0L); // dept chmpxid is not changed. + COPY_TIMESPEC(&(pResComPkt->head.reqtime), &(pComHead->reqtime)); // not change + + // merge_start(copy) + pResMergeStart->head.type = pMergeStart->head.type; + pResMergeStart->head.result = pMergeStart->head.result; // always no error. + pResMergeStart->head.length = pMergeStart->head.length; + + if(CHMPX_COM_RES_SUCCESS != pMergeStart->head.result){ + // already error occured, so ignore this packet. + WAN_CHMPRN("Already error occured before this server, but doing."); + } + *ppResComPkt = pResComPkt; + } + } + return true; +} + +bool ChmEventSock::PxComSendMergeAbort(chmpxid_t chmpxid) +{ + if(CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Make packet + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_MERGE_ABORT))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // compkt + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_MERGE_ABORT pMergeAbort = CVT_COMPTR_MERGE_ABORT(pComAll); + SET_PXCOMPKT(pComPkt, CHMPX_COM_MERGE_ABORT, pImData->GetSelfChmpxId(), chmpxid, true, 0L); + + pMergeAbort->head.type = CHMPX_COM_MERGE_ABORT; + pMergeAbort->head.result = CHMPX_COM_RES_SUCCESS; + pMergeAbort->head.length = sizeof(PXCOM_MERGE_ABORT); + + // Send request + if(!Send(pComPkt, NULL, 0L)){ + ERR_CHMPRN("Failed to send CHMPX_COM_MERGE_ABORT to chmpxid(0x%016" PRIx64 ").", chmpxid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventSock::PxComReceiveMergeAbort(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt) +{ + if(!pComHead || !pComAll || !ppResComPkt){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + PPXCOM_MERGE_ABORT pMergeAbort = CVT_COMPTR_MERGE_ABORT(pComAll); + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + *ppResComPkt = NULL; + + if(pComHead->dept_ids.chmpxid == selfchmpxid){ + // around -> next step: do merging + // + if(CHMPX_COM_RES_SUCCESS == pMergeAbort->head.result){ + // Succeed + MSG_CHMPRN("PXCOM_MERGE_ABORT is succeed, All server start to merge."); + }else{ + // Something error occured on RING. + ERR_CHMPRN("PXCOM_MERGE_ABORT is failed, could not recover, but returns true."); + return true; + } + }else{ + // update own chmshm & transfer packet. + // + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + ERR_CHMPRN("Could not get next server chmpxid."); + return false; + } + + if(!IsSafeDeptAndNextChmpxId(pComHead->dept_ids.chmpxid, nextchmpxid)){ + // Deperture chmpx maybe DOWN! + // + // [NOTICE] + // This case is very small case, deperture server sends this packet but that server could not + // be connected from in RING server.(ex. down after sending, or FQDN is wrong, etc) + // So we do not transfer this packet, because it could not be stopped in RING. + // + ERR_CHMPRN("Deperture chmpxid(0x%016" PRIx64 ") maybe down, so stop transferring this packet.", pComHead->dept_ids.chmpxid); + *ppResComPkt = NULL; + + }else{ + // make response data buffer + PCOMPKT pResComPkt; + if(NULL == (pResComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_MERGE_ABORT))){ + ERR_CHMPRN("Could not allocation memory."); + return false; + } + + // compkt + PPXCOM_ALL pResComAll = CVT_COM_ALL_PTR_PXCOMPKT(pResComPkt); + PPXCOM_MERGE_ABORT pResMergeAbort = CVT_COMPTR_MERGE_ABORT(pResComAll); + + SET_PXCOMPKT(pResComPkt, CHMPX_COM_MERGE_ABORT, pComHead->dept_ids.chmpxid, nextchmpxid, false, 0L); // dept chmpxid is not changed. + COPY_TIMESPEC(&(pResComPkt->head.reqtime), &(pComHead->reqtime)); // not change + + // merge_start(copy) + pResMergeAbort->head.type = pMergeAbort->head.type; + pResMergeAbort->head.result = pMergeAbort->head.result; // always no error. + pResMergeAbort->head.length = pMergeAbort->head.length; + + if(CHMPX_COM_RES_SUCCESS != pMergeAbort->head.result){ + // already error occured, so ignore this packet. + WAN_CHMPRN("Already error occured before this server, but doing."); + } + *ppResComPkt = pResComPkt; + } + } + return true; +} + +bool ChmEventSock::PxComSendMergeComplete(chmpxid_t chmpxid) +{ + if(CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Make packet + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_MERGE_COMPLETE))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // compkt + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_MERGE_COMPLETE pMergeComplete = CVT_COMPTR_MERGE_COMPLETE(pComAll); + SET_PXCOMPKT(pComPkt, CHMPX_COM_MERGE_COMPLETE, pImData->GetSelfChmpxId(), chmpxid, true, 0L); + + pMergeComplete->head.type = CHMPX_COM_MERGE_COMPLETE; + pMergeComplete->head.result = CHMPX_COM_RES_SUCCESS; + pMergeComplete->head.length = sizeof(PXCOM_MERGE_COMPLETE); + + // Send request + if(!Send(pComPkt, NULL, 0L)){ + ERR_CHMPRN("Failed to send CHMPX_COM_MERGE_COMPLETE to chmpxid(0x%016" PRIx64 ").", chmpxid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventSock::PxComReceiveMergeComplete(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt) +{ + if(!pComHead || !pComAll || !ppResComPkt){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + PPXCOM_MERGE_COMPLETE pMergeComplete = CVT_COMPTR_MERGE_COMPLETE(pComAll); + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + *ppResComPkt = NULL; + + if(pComHead->dept_ids.chmpxid == selfchmpxid){ + // around -> next step: do merging + // + if(CHMPX_COM_RES_SUCCESS == pMergeComplete->head.result){ + // Succeed + MSG_CHMPRN("PXCOM_MERGE_COMPLETE is succeed, All server start to merge."); + + // If there is DELETE Done server, do SERVICEOUT status here! + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + MSG_CHMPRN("Could not get next chmpxid, probably there is no server without self chmpx on RING."); + } + chmpxid_t downchmpxid; + while(CHM_INVALID_CHMPXID != (downchmpxid = pImData->GetChmpxIdByStatus(CHMPXSTS_SRVIN_DOWN_DELETED, false))){ + // change down server status + if(!pImData->SetServerStatus(downchmpxid, CHMPXSTS_SRVOUT_DOWN_NORMAL)){ + ERR_CHMPRN("Failed to change server(0x%016" PRIx64 ") status to CHMPXSTS_SRVOUT_DOWN_NORMAL.", downchmpxid); + return false; + } + // send down server status update + CHMPXSVR chmpxsvr; + if(!pImData->GetChmpxSvr(downchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Could not get server(0x%016" PRIx64 ") information.", downchmpxid); + return false; + } + if(CHM_INVALID_CHMPXID == nextchmpxid){ + if(!PxComSendSlavesStatusChange(&chmpxsvr)){ + ERR_CHMPRN("Failed to send server(0x%016" PRIx64 ") status change to slaves.", downchmpxid); + return false; + } + }else{ + if(!PxComSendStatusChange(nextchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Failed to send server(0x%016" PRIx64 ") status change.", downchmpxid); + return false; + } + } + } + }else{ + // Something error occured on RING. + WAN_CHMPRN("PXCOM_MERGE_COMPLETE is failed, maybe other servers are merging now, thus returns true."); + return true; + } + }else{ + // update own chmshm & transfer packet. + // + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + ERR_CHMPRN("Could not get next server chmpxid."); + return false; + } + + if(!IsSafeDeptAndNextChmpxId(pComHead->dept_ids.chmpxid, nextchmpxid)){ + // Deperture chmpx maybe DOWN! + // + // [NOTICE] + // This case is very small case, deperture server sends this packet but that server could not + // be connected from in RING server.(ex. down after sending, or FQDN is wrong, etc) + // So we do not transfer this packet, because it could not be stopped in RING. + // + ERR_CHMPRN("Deperture chmpxid(0x%016" PRIx64 ") maybe down, so stop transferring this packet.", pComHead->dept_ids.chmpxid); + *ppResComPkt = NULL; + + }else{ + // make response data buffer + PCOMPKT pResComPkt; + if(NULL == (pResComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_MERGE_COMPLETE))){ + ERR_CHMPRN("Could not allocation memory."); + return false; + } + + // compkt + PPXCOM_ALL pResComAll = CVT_COM_ALL_PTR_PXCOMPKT(pResComPkt); + PPXCOM_MERGE_COMPLETE pResMergeComplete = CVT_COMPTR_MERGE_COMPLETE(pResComAll); + + SET_PXCOMPKT(pResComPkt, CHMPX_COM_MERGE_COMPLETE, pComHead->dept_ids.chmpxid, nextchmpxid, false, 0L); // dept chmpxid is not changed. + COPY_TIMESPEC(&(pResComPkt->head.reqtime), &(pComHead->reqtime)); // not change + + // merge_start(copy) + pResMergeComplete->head.type = pMergeComplete->head.type; + pResMergeComplete->head.result = pMergeComplete->head.result; // always no error. + pResMergeComplete->head.length = pMergeComplete->head.length; + + if(CHMPX_COM_RES_SUCCESS != pMergeComplete->head.result){ + // already error occured, so ignore this packet. + WAN_CHMPRN("Already error occured before this server, but doing."); + pResMergeComplete->head.result = pMergeComplete->head.result; + }else{ + // check self status + chmpxsts_t status = pImData->GetSelfStatus(); + if(!IS_CHMPXSTS_SRVOUT(status)){ + if((IS_CHMPXSTS_NOACT(status) || IS_CHMPXSTS_ADD(status)) && IS_CHMPXSTS_SUSPEND(status)){ + if(!IS_CHMPXSTS_DONE(status)){ + WAN_CHMPRN("Server status(0x%016" PRIx64 ":%s) is SUSPEND, so could not change status.", status, STR_CHMPXSTS_FULL(status).c_str()); + pResMergeComplete->head.result = CHMPX_COM_RES_ERROR; + }else{ + // [NOTE] + // maybe, case of SERVICEIN/UP/NOACT/DONE/SUSPEND + // This status can be because serviceout command for other server when there many suspend server. + } + } + }else{ + MSG_CHMPRN("Server status(0x%016" PRIx64 ":%s) is SERVICEOUT, so could not change status, but return success", status, STR_CHMPXSTS_FULL(status).c_str()); + } + } + *ppResComPkt = pResComPkt; + } + } + return true; +} + +bool ChmEventSock::PxComSendServerDown(chmpxid_t chmpxid, chmpxid_t downchmpxid) +{ + if(CHM_INVALID_CHMPXID == chmpxid || CHM_INVALID_CHMPXID == downchmpxid){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Make packet + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_SERVER_DOWN))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // compkt + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_SERVER_DOWN pServerDown = CVT_COMPTR_SERVER_DOWN(pComAll); + SET_PXCOMPKT(pComPkt, CHMPX_COM_SERVER_DOWN, pImData->GetSelfChmpxId(), chmpxid, true, 0L); + + pServerDown->head.type = CHMPX_COM_SERVER_DOWN; + pServerDown->head.result = CHMPX_COM_RES_SUCCESS; + pServerDown->head.length = sizeof(PXCOM_SERVER_DOWN); + pServerDown->chmpxid = downchmpxid; + + // Send request + if(!Send(pComPkt, NULL, 0L)){ + ERR_CHMPRN("Failed to send CHMPX_COM_SERVER_DOWN to chmpxid(0x%016" PRIx64 ").", chmpxid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventSock::PxComReceiveServerDown(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt) +{ + if(!pComHead || !pComAll || !ppResComPkt){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + PPXCOM_SERVER_DOWN pServerDown = CVT_COMPTR_SERVER_DOWN(pComAll); + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + *ppResComPkt = NULL; + + if(pComHead->dept_ids.chmpxid == selfchmpxid){ + // around -> next step: update pending hash value + // + if(CHMPX_COM_RES_SUCCESS == pServerDown->head.result){ + // Succeed notify server down & status update on RING + return true; + + }else{ + // Something error occured on RING, but nothing to do for recovering. + ERR_CHMPRN("PXCOM_SERVER_DOWN is failed, could not recover..."); + return false; + } + }else{ + // update own chmshm & transfer packet. + // + // [NOTICE] + // Before receiving this "SERVER_DOWN", this server got "merge abort" if merging. + // Thus do not care about merge abort. + // + pxcomres_t ResultCode = CHMPX_COM_RES_SUCCESS;; + + // close down server sock (probably all sockets are already closed) + socklist_t socklist; + if(pImData->GetServerSock(pServerDown->chmpxid, socklist) && !socklist.empty()){ + for(socklist_t::iterator iter = socklist.begin(); iter != socklist.end(); iter = socklist.erase(iter)){ + seversockmap.erase(*iter); + } + } + + // set status. + chmpxsts_t status = pImData->GetServerStatus(pServerDown->chmpxid); + + CHANGE_CHMPXSTS_TO_DOWN(status); + if(!pImData->SetServerStatus(pServerDown->chmpxid, status)){ + ERR_CHMPRN("Could not set status(0x%016" PRIx64 ":%s) to chmpxid(0x%016" PRIx64 "), but continue...", status, STR_CHMPXSTS_FULL(status).c_str(), pServerDown->chmpxid); + ResultCode = CHMPX_COM_RES_ERROR; + } + if(CHMPX_COM_RES_SUCCESS != pServerDown->head.result){ + MSG_CHMPRN("Already error occured before this server."); + ResultCode = CHMPX_COM_RES_ERROR; + } + + // get next chmpxid on RING + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + + if(CHM_INVALID_CHMPXID != nextchmpxid){ + if(!IsSafeDeptAndNextChmpxId(pComHead->dept_ids.chmpxid, nextchmpxid)){ + // Deperture chmpx maybe DOWN! + // + // [NOTICE] + // This case is very small case, deperture server sends this packet but that server could not + // be connected from in RING server.(ex. down after sending, or FQDN is wrong, etc) + // So we do not transfer this packet, because it could not be stopped in RING. + // + ERR_CHMPRN("Deperture chmpxid(0x%016" PRIx64 ") maybe down, so stop transferring this packet.", pComHead->dept_ids.chmpxid); + *ppResComPkt = NULL; + + }else{ + // make response data buffer + PCOMPKT pResComPkt; + if(NULL == (pResComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_SERVER_DOWN))){ + ERR_CHMPRN("Could not allocation memory."); + return false; + } + + // compkt + PPXCOM_ALL pResComAll = CVT_COM_ALL_PTR_PXCOMPKT(pResComPkt); + PPXCOM_SERVER_DOWN pResServerDown = CVT_COMPTR_SERVER_DOWN(pResComAll); + + SET_PXCOMPKT(pResComPkt, CHMPX_COM_SERVER_DOWN, pComHead->dept_ids.chmpxid, nextchmpxid, false, 0L); // dept chmpxid is not changed. + COPY_TIMESPEC(&(pResComPkt->head.reqtime), &(pComHead->reqtime)); // not change + + // server_down(copy) + pResServerDown->head.type = pServerDown->head.type; + pResServerDown->head.result = ResultCode; + pResServerDown->head.length = pServerDown->head.length; + pResServerDown->chmpxid = pServerDown->chmpxid; + + *ppResComPkt = pResComPkt; + } + } + } + return true; +} + +bool ChmEventSock::PxCltReceiveJoinNotify(PCOMHEAD pComHead, PPXCLT_ALL pComAll) +{ + if(!pComHead || !pComAll){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); +// PPXCLT_JOIN_NOTIFY pJoinNotify = CVT_CLTPTR_JOIN_NOTIFY(pComAll); + + if(!pImData->IsChmpxProcess()){ + ERR_CHMPRN("CHMPX_CLT_JOIN_NOTIFY must be received on Chmpx process."); + return false; + } + + // check processes + // + // [MEMO] + // We should check pJoinNotify->pid in pidlist, but we do not it. + // After receiving this command, we only check client count and change status. + // + if(!pImData->IsClientPids()){ + ERR_CHMPRN("There is no client process."); + return false; + } + + // Check status + chmpxsts_t status = pImData->GetSelfStatus(); + if(IS_CHMPXSTS_NOSUP(status)){ + MSG_CHMPRN("Already self status(0x%016" PRIx64 ":%s) is NO SUSPEND.", status, STR_CHMPXSTS_FULL(status).c_str()); + return true; + } + + // new status + bool do_merging_now = false; + if(IS_CHMPXSTS_SRVIN(status) && IS_CHMPXSTS_UP(status) && IS_CHMPXSTS_NOACT(status) && IS_CHMPXSTS_NOTHING(status)){ + // [NOTICE] + // CHMPXSTS_SRVIN_UP_MERGING is "doing" status, so we need to start merging. + // + status = CHMPXSTS_SRVIN_UP_MERGING; + do_merging_now = true; + } + CHANGE_CHMPXSTS_TO_NOSUP(status); + + // set status(set suspend in this method) + if(!pImData->SetSelfStatus(status)){ + ERR_CHMPRN("Failed to change server status(0x%016" PRIx64 ").", status); + return false; + } + + // Update pending hash. + if(!pImData->UpdatePendingHash()){ + ERR_CHMPRN("Failed to update pending hash for all servers, so stop update status."); + return false; + } + + // send status update + CHMPXSVR chmpxsvr; + if(!pImData->GetSelfChmpxSvr(&chmpxsvr)){ + ERR_CHMPRN("Could not get self chmpx information."); + return false; + } + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + if(CHM_INVALID_CHMPXID == nextchmpxid){ + WAN_CHMPRN("Could not get next chmpxid, probably there is no server without self chmpx on RING."); + if(!PxComSendSlavesStatusChange(&chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change to slaves."); + return false; + } + }else{ + if(!PxComSendStatusChange(nextchmpxid, &chmpxsvr)){ + ERR_CHMPRN("Failed to send self status change."); + return false; + } + } + + // If do not merge(almost random deliver mode), do merging, completing it. + if(is_auto_merge || do_merging_now){ + // If server does not have been added servicein yet, do it. + if(IS_CHMPXSTS_SRVOUT(status) && IS_CHMPXSTS_UP(status) && IS_CHMPXSTS_NOACT(status)){ + if(!RequestServiceIn()){ + ERR_CHMPRN("Failed to request \"servce in\" status before auto merging."); + return false; + } + } + // start merge automatically. + if(CHM_INVALID_CHMPXID == nextchmpxid){ + if(!MergeStart()){ + ERR_CHMPRN("Failed to merge or complete merge for \"service in\"."); + return false; + } + }else{ + if(!RequestMergeStart()){ + ERR_CHMPRN("Failed to merge or complete merge for \"service in\"."); + return false; + } + } + } + return true; +} + +bool ChmEventSock::PxComSendReqUpdateData(chmpxid_t chmpxid, const PPXCOMMON_MERGE_PARAM pmerge_param) +{ + if(CHM_INVALID_CHMPXID == chmpxid || !pmerge_param){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + while(!fullock::flck_trylock_noshared_mutex(&mergeidmap_lockval)); // LOCK + + // Make packet + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_REQ_UPDATEDATA, (sizeof(PXCOM_REQ_IDMAP) * mergeidmap.size())))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + fullock::flck_unlock_noshared_mutex(&mergeidmap_lockval); // UNLOCK + return false; + } + + // compkt + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_REQ_UPDATEDATA pReqUpdateData = CVT_COMPTR_REQ_UPDATEDATA(pComAll); + + SET_PXCOMPKT(pComPkt, CHMPX_COM_REQ_UPDATEDATA, pImData->GetSelfChmpxId(), chmpxid, true, (sizeof(PXCOM_REQ_IDMAP) * mergeidmap.size())); + + pReqUpdateData->head.type = CHMPX_COM_REQ_UPDATEDATA; + pReqUpdateData->head.result = CHMPX_COM_RES_SUCCESS; + pReqUpdateData->head.length = sizeof(PXCOM_REQ_UPDATEDATA) + (sizeof(PXCOM_REQ_IDMAP) * mergeidmap.size()); + pReqUpdateData->count = static_cast(mergeidmap.size()); + pReqUpdateData->plist_offset = sizeof(PXCOM_REQ_UPDATEDATA); + COPY_COMMON_MERGE_PARAM(&(pReqUpdateData->merge_param), pmerge_param); + + // extra area + PPXCOM_REQ_IDMAP pIdMap = CHM_OFFSET(pReqUpdateData, sizeof(PXCOM_REQ_UPDATEDATA), PPXCOM_REQ_IDMAP); + int idcnt = 0; + for(mergeidmap_t::const_iterator iter = mergeidmap.begin(); iter != mergeidmap.end(); ++iter, ++idcnt){ + pIdMap[idcnt].chmpxid = iter->first; + pIdMap[idcnt].req_status= iter->second; + } + + fullock::flck_unlock_noshared_mutex(&mergeidmap_lockval); // UNLOCK + + // Send request + if(!Send(pComPkt, NULL, 0L)){ + ERR_CHMPRN("Failed to send CHMPX_COM_REQ_UPDATEDATA to chmpxid(0x%016" PRIx64 ").", chmpxid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventSock::PxComReceiveReqUpdateData(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt) +{ + if(!pComHead || !pComAll || !ppResComPkt){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + PPXCOM_REQ_UPDATEDATA pReqUpdateData = CVT_COMPTR_REQ_UPDATEDATA(pComAll); + chmpxid_t selfchmpxid = pImData->GetSelfChmpxId(); + *ppResComPkt = NULL; + + if(pComHead->dept_ids.chmpxid == selfchmpxid){ + // around -> next step: update pending hash value + // + if(CHMPX_COM_RES_SUCCESS == pReqUpdateData->head.result){ + // Succeed request update data + while(!fullock::flck_trylock_noshared_mutex(&mergeidmap_lockval)); // LOCK + + // copy all update data resuest status + PPXCOM_REQ_IDMAP pReqIdMap = CHM_OFFSET(pReqUpdateData, sizeof(PXCOM_REQ_UPDATEDATA), PPXCOM_REQ_IDMAP); + for(long cnt = 0; cnt < pReqUpdateData->count; ++cnt){ + mergeidmap_t::iterator iter = mergeidmap.find(pReqIdMap[cnt].chmpxid); + + if(mergeidmap.end() == iter){ + // why? there is not chmpxid, but set data + WAN_CHMPRN("There is not chmpxid(0x%016" PRIx64 ") in map.", pReqIdMap[cnt].chmpxid); + mergeidmap[pReqIdMap[cnt].chmpxid] = pReqIdMap[cnt].req_status; + }else{ + if(IS_PXCOM_REQ_UPDATE_RESULT(mergeidmap[pReqIdMap[cnt].chmpxid])){ + WAN_CHMPRN("Already result(%s) for chmpxid(0x%016" PRIx64 ") in map, so not set result(%s)", + STRPXCOM_REQ_UPDATEDATA(mergeidmap[pReqIdMap[cnt].chmpxid]), pReqIdMap[cnt].chmpxid, STRPXCOM_REQ_UPDATEDATA(pReqIdMap[cnt].req_status)); + }else{ + mergeidmap[pReqIdMap[cnt].chmpxid] = pReqIdMap[cnt].req_status; + } + } + } + fullock::flck_unlock_noshared_mutex(&mergeidmap_lockval); // UNLOCK + return true; + + }else{ + // Something error occured on RING, but nothing to do for recovering. + ERR_CHMPRN("PXCOM_REQ_UPDATEDATA is failed, could not recover..."); + return false; + } + }else{ + // get next chmpxid on RING + chmpxid_t nextchmpxid = GetNextRingChmpxId(); + + if(CHM_INVALID_CHMPXID != nextchmpxid){ + if(!IsSafeDeptAndNextChmpxId(pComHead->dept_ids.chmpxid, nextchmpxid)){ + // Deperture chmpx maybe DOWN! + // + // [NOTICE] + // This case is very small case, deperture server sends this packet but that server could not + // be connected from in RING server.(ex. down after sending, or FQDN is wrong, etc) + // So we do not transfer this packet, because it could not be stopped in RING. + // + ERR_CHMPRN("Deperture chmpxid(0x%016" PRIx64 ") maybe down, so stop transferring this packet.", pComHead->dept_ids.chmpxid); + + }else{ + // Make packet + PCOMPKT pResComPkt; + if(NULL == (pResComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_REQ_UPDATEDATA, (sizeof(PXCOM_REQ_IDMAP) * pReqUpdateData->count)))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // compkt(copy) + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pResComPkt); + PPXCOM_REQ_UPDATEDATA pResUpdateData = CVT_COMPTR_REQ_UPDATEDATA(pComAll); + SET_PXCOMPKT(pResComPkt, CHMPX_COM_REQ_UPDATEDATA, pComHead->dept_ids.chmpxid, nextchmpxid, false, (sizeof(PXCOM_REQ_IDMAP) * pReqUpdateData->count)); // dept chmpxid is not changed. + COPY_TIMESPEC(&(pResComPkt->head.reqtime), &(pComHead->reqtime)); // not change + + pResUpdateData->head.type = CHMPX_COM_REQ_UPDATEDATA; + pResUpdateData->head.result = CHMPX_COM_RES_SUCCESS; + pResUpdateData->head.length = sizeof(PXCOM_REQ_UPDATEDATA) + (sizeof(PXCOM_REQ_IDMAP) * pReqUpdateData->count); + pResUpdateData->count = pReqUpdateData->count; + pResUpdateData->plist_offset = sizeof(PXCOM_REQ_UPDATEDATA); + COPY_COMMON_MERGE_PARAM(&(pResUpdateData->merge_param), &(pReqUpdateData->merge_param)); + + // extra area(copy) + PPXCOM_REQ_IDMAP pOwnMap = NULL; + PPXCOM_REQ_IDMAP pReqIdMap = CHM_OFFSET(pReqUpdateData, sizeof(PXCOM_REQ_UPDATEDATA), PPXCOM_REQ_IDMAP); + PPXCOM_REQ_IDMAP pResIdMap = CHM_OFFSET(pResUpdateData, sizeof(PXCOM_REQ_UPDATEDATA), PPXCOM_REQ_IDMAP); + for(long cnt = 0; cnt < pReqUpdateData->count; ++cnt){ + pResIdMap[cnt].chmpxid = pReqIdMap[cnt].chmpxid; + pResIdMap[cnt].req_status = pReqIdMap[cnt].req_status; + + if(selfchmpxid == pResIdMap[cnt].chmpxid){ + pOwnMap = &pResIdMap[cnt]; + } + } + + // own chmpxid in target chmpxid list? + if(pOwnMap){ + // get own status + chmpxsts_t status = pImData->GetSelfStatus(); + + // check status + // + // [NOTE] + // Transfer the request to client, if this server is UP/DOWN & NOSUSPEND status. + // Because we merge data from minimum server which has a valid data. + // + if((IS_CHMPXSTS_NOACT(status) || IS_CHMPXSTS_DELETE(status)) && IS_CHMPXSTS_NOSUP(status)){ + // this server needs to update data, so transfer request to client. + + // Make parameter + PXCOMMON_MERGE_PARAM MqMergeParam; + COPY_COMMON_MERGE_PARAM(&MqMergeParam, &(pResUpdateData->merge_param)); + + // transfer command to MQ + if(!pChmCntrl->MergeRequestUpdateData(&MqMergeParam)){ + ERR_CHMPRN("Something error occurred during the request is sent to MQ."); + pOwnMap->req_status = CHMPX_COM_REQ_UPDATE_FAIL; + }else{ + pOwnMap->req_status = CHMPX_COM_REQ_UPDATE_DO; + } + }else{ + // this server does not need to update, so result is success. + pOwnMap->req_status = CHMPX_COM_REQ_UPDATE_NOACT; + } + }else{ + // this chmpx is not target chmpx. + } + // set response data. + *ppResComPkt = pResComPkt; + } + }else{ + // there is no next chnpxid. + } + } + return true; +} + +bool ChmEventSock::PxComSendResUpdateData(chmpxid_t chmpxid, size_t length, const unsigned char* pdata, const struct timespec* pts) +{ + if(CHM_INVALID_CHMPXID == chmpxid || 0 == length || !pdata || !pts){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Make packet + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_RES_UPDATEDATA, length))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // compkt + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_RES_UPDATEDATA pResUpdateData = CVT_COMPTR_RES_UPDATEDATA(pComAll); + + SET_PXCOMPKT(pComPkt, CHMPX_COM_RES_UPDATEDATA, pImData->GetSelfChmpxId(), chmpxid, true, length); + + pResUpdateData->head.type = CHMPX_COM_RES_UPDATEDATA; + pResUpdateData->head.result = CHMPX_COM_RES_SUCCESS; + pResUpdateData->head.length = sizeof(PXCOM_RES_UPDATEDATA) + length; + pResUpdateData->ts.tv_sec = pts->tv_sec; + pResUpdateData->ts.tv_nsec = pts->tv_nsec; + pResUpdateData->length = length; + pResUpdateData->pdata_offset = sizeof(PXCOM_RES_UPDATEDATA); + + unsigned char* psetbase = CHM_OFFSET(pResUpdateData, sizeof(PXCOM_RES_UPDATEDATA), unsigned char*); + memcpy(psetbase, pdata, length); + + // Send request + if(!Send(pComPkt, NULL, 0L)){ + ERR_CHMPRN("Failed to send CHMPX_COM_RES_UPDATEDATA to chmpxid(0x%016" PRIx64 ").", chmpxid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventSock::PxComReceiveResUpdateData(PCOMHEAD pComHead, PPXCOM_ALL pComAll) +{ + if(!pComHead || !pComAll){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + + PPXCOM_RES_UPDATEDATA pResUpdateData = CVT_COMPTR_RES_UPDATEDATA(pComAll); + + // check data + if(0 == pResUpdateData->length || 0 == pResUpdateData->pdata_offset){ + ERR_CHMPRN("Received CHMPX_COM_RES_UPDATEDATA command is somthing wrong."); + return false; + } + unsigned char* pdata = CHM_OFFSET(pResUpdateData, pResUpdateData->pdata_offset, unsigned char*); + + // transfer client + return pChmCntrl->MergeSetUpdateData(pResUpdateData->length, pdata, &(pResUpdateData->ts)); +} + +bool ChmEventSock::PxComSendResultUpdateData(chmpxid_t chmpxid, reqidmapflag_t result_updatedata) +{ + if(CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + ChmIMData* pImData = pChmCntrl->GetImDataObj(); + + // Make packet + PCOMPKT pComPkt; + if(NULL == (pComPkt = ChmEventSock::AllocatePxComPacket(CHMPX_COM_RESULT_UPDATEDATA))){ + ERR_CHMPRN("Could not allocate memory for COMPKT."); + return false; + } + + // compkt + PPXCOM_ALL pComAll = CVT_COM_ALL_PTR_PXCOMPKT(pComPkt); + PPXCOM_RESULT_UPDATEDATA pResultUpdateData = CVT_COMPTR_RESULT_UPDATEDATA(pComAll); + SET_PXCOMPKT(pComPkt, CHMPX_COM_RESULT_UPDATEDATA, pImData->GetSelfChmpxId(), chmpxid, true, 0); + + pResultUpdateData->head.type = CHMPX_COM_RESULT_UPDATEDATA; + pResultUpdateData->head.result = CHMPX_COM_RES_SUCCESS; + pResultUpdateData->chmpxid = pImData->GetSelfChmpxId(); + pResultUpdateData->result = result_updatedata; + + // Send request + if(!Send(pComPkt, NULL, 0L)){ + ERR_CHMPRN("Failed to send CHMPX_COM_RESULT_UPDATEDATA to chmpxid(0x%016" PRIx64 ").", chmpxid); + CHM_Free(pComPkt); + return false; + } + CHM_Free(pComPkt); + + return true; +} + +bool ChmEventSock::PxComReceiveResultUpdateData(PCOMHEAD pComHead, PPXCOM_ALL pComAll) +{ + if(!pComHead || !pComAll){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(IsEmpty()){ + ERR_CHMPRN("Object is not initialized."); + return false; + } + PPXCOM_RESULT_UPDATEDATA pResultUpdateData = CVT_COMPTR_RESULT_UPDATEDATA(pComAll); + + // check data + if(CHM_INVALID_CHMPXID == pResultUpdateData->chmpxid || !IS_PXCOM_REQ_UPDATE_RESULT(pResultUpdateData->result)){ + ERR_CHMPRN("Received CHMPX_COM_RESULT_UPDATEDATA command is somthing wrong."); + return false; + } + + while(!fullock::flck_trylock_noshared_mutex(&mergeidmap_lockval)); // LOCK + + // search map + mergeidmap_t::iterator iter = mergeidmap.find(pResultUpdateData->chmpxid); + if(mergeidmap.end() == iter){ + ERR_CHMPRN("The chmpxid(0x%016" PRIx64 ") in received CHMPX_COM_RESULT_UPDATEDATA does not find in merge id map.", pResultUpdateData->chmpxid); + fullock::flck_unlock_noshared_mutex(&mergeidmap_lockval); // UNLOCK + return false; + } + if(!IS_PXCOM_REQ_UPDATE_RESULT(pResultUpdateData->result)){ + ERR_CHMPRN("received CHMPX_COM_RESULT_UPDATEDATA result(%s) for chmpxid(0x%016" PRIx64 ") is wrong.", STRPXCOM_REQ_UPDATEDATA(pResultUpdateData->result), pResultUpdateData->chmpxid); + return false; + } + // set result + mergeidmap[pResultUpdateData->chmpxid] = pResultUpdateData->result; + + fullock::flck_unlock_noshared_mutex(&mergeidmap_lockval); // UNLOCK + return true; +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmeventsock.h b/lib/chmeventsock.h new file mode 100644 index 0000000..cc7c8b0 --- /dev/null +++ b/lib/chmeventsock.h @@ -0,0 +1,328 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMEVENTSOCK_H +#define CHMEVENTSOCK_H + +#include +#include +#include +#include + +#include "chmeventbase.h" +#include "chmthread.h" +#include "chmlockmap.tcc" + +//--------------------------------------------------------- +// Structure +//--------------------------------------------------------- +typedef struct chm_send_sock_status{ + pid_t tid; // thread id of locking socket + time_t last_time; // last using(locking) time +}CHMSSSTAT, *PCHMSSSTAT; + +//--------------------------------------------------------- +// Typedefs +//--------------------------------------------------------- +typedef std::map mergeidmap_t; +typedef chm_lock_map sock_ids_map_t; +typedef chm_lock_map sock_pending_map_t; +typedef chm_lock_map sock_ssl_map_t; +typedef chm_lock_map sendlockmap_t; + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +#define PXCOMPKT_AUTO_LENGTH (-1) + +#define WAIT_READ_FD 1 +#define WAIT_WRITE_FD 2 +#define WAIT_BOTH_FD (WAIT_READ_FD | WAIT_WRITE_FD) + +#define ISSAFE_WAIT_FD(type) (0 != (type & WAIT_BOTH_FD)) +#define IS_WAIT_READ_FD(type) (0 != (type & WAIT_READ_FD)) +#define IS_WAIT_WRITE_FD(type) (0 != (type & WAIT_WRITE_FD)) + +#define CHMEVENTSOCK_RETRY_DEFAULT (-1) +#define CHMEVENTSOCK_TIMEOUT_DEFAULT (0) + +//--------------------------------------------------------- +// ChmEventSock Class +//--------------------------------------------------------- +// [NOTE] About multi-threading +// +// This class should have exclusion control for socket on multi-threading. +// Then this class controls closing/opening/accepting socket as exclusion, +// thus these processing can not run at same time. +// And this class can send data to particular socket by only one thread at +// same time because the socket is locked by that thread. +// These two exclusion control are independent of each other, so we can +// open new socket during sending data as an example. +// +// And contrary to the above, this class does not lock the socket for +// receiving. The reason is that the receiving from the same socket is +// processed sequentially, thus we do not need to lock the socket for +// receiving. However the processing such as open(close) is caught during +// we are receiving from the socket, but we can probably fail to receive +// by closing the socket. +// Thus we can stop the processing to receive by getting error. +// +class ChmEventSock : public ChmEventBase +{ + public: + static const int DEFAULT_SOCK_THREAD_CNT = 0; // socket processing thread count + static const int DEFAULT_MAX_SOCK_POOL = 1; // max socket count for each chmpx + static const time_t DEFAULT_SOCK_POOL_TIMEOUT = 60; // timeout value till closing for unsed socket in pool + static const time_t NO_SOCK_POOL_TIMEOUT = 0; // no timeout for socket pool + + protected: + typedef enum chm_downsvr_merge_type{ // for ChangeDownSvrStatusBeforeMerge method + CHM_DOWNSVR_MERGE_START, + CHM_DOWNSVR_MERGE_COMPLETE, + CHM_DOWNSVR_MERGE_ABORT + }CHMDOWNSVRMERGE; + // Disconnect Detection = (60 + 10 * 3) sec + static const int DEFAULT_KEEPIDLE = 60; // time until sending keep alive + static const int DEFAULT_KEEPINTERVAL = 10; // interval for sending keep alive + static const int DEFAULT_KEEPCOUNT = 3; // maximum count of sending keep alive + static const int DEFAULT_LISTEN_BACKLOG = 256; + static const int DEFAULT_RETRYCNT = 500; // retry count for send and receive(total default wait = 200us * 500 = 100ms) + static const int DEFAULT_RETRYCNT_CONNECT= 2500; // retry count for connect.( = (500 * 1000) / 200 ) + static const suseconds_t DEFAULT_WAIT_SOCKET = 200; // timeout/count for send and receive.(200us) + static const suseconds_t DEFAULT_WAIT_CONNECT = 500 * 1000; // total timeout for connect.(500ms) + + static int CHM_SSL_VERIFY_DEPTH; // SSL cert verify depth + static const char* strVerifyCBDataIndex; // string keyword for data index + static int verify_cb_data_index; // Data index for verify callback function + static int ssl_session_id; // Session ID + static bool is_self_sigined; // For DEBUG + + sock_ids_map_t seversockmap; // to server sockets + sock_ids_map_t slavesockmap; // from slave sockets + sock_pending_map_t acceptingmap; // from slave sockets before accepting + sock_ids_map_t ctlsockmap; // from client sockets to ctlport + sock_ssl_map_t sslmap; // to server/from slave ssl object maping + sendlockmap_t sendlockmap; // lock socket map for sending + SSL_CTX* svr_sslctx; // SSL Context for server + SSL_CTX* slv_sslctx; // SSL Context for slave + bool is_do_merge; // do merging or not when need to merge + bool is_auto_merge; // automatically start merge at service-in/out + volatile bool is_run_merge; // whichever run or abort in merging + ChmThread procthreads; // processing threads + ChmThread mergethread; // merge thread + volatile int sockfd_lockval; // lock variable for open/close/accept socket + time_t last_check_time; // last unix time at checking socket pool timeout + volatile int dyna_sockfd_lockval; // lock variable for new connection dynamic + volatile int mergeidmap_lockval; // lock variable for chmpxid mapping + mergeidmap_t mergeidmap; // chmpxid mapping for update datas + bool is_server_mode; // cache value for ChmIMData::IsServerMode() + int max_sock_pool; // cache value for ChmIMData::GetMaxSockPool() + time_t sock_pool_timeout; // cache value for ChmIMData::GetSockPoolTimeout() + int sock_retry_count; // cache value for retry count for send and receive + int con_retry_count; // cache value for retry count for connect + suseconds_t sock_wait_time; // cache value for timeout/count for send and receive + suseconds_t con_wait_time; // cache value for total timeout for connect + + protected: + // SSL + static int VerifyCallBackSSL(int preverify_ok, X509_STORE_CTX* store_ctx); + static bool CheckResultSSL(int sock, SSL* ssl, long action_result, int type, bool& is_retry, bool& is_close, int retrycnt = CHMEVENTSOCK_RETRY_DEFAULT, suseconds_t waittime = CHMEVENTSOCK_TIMEOUT_DEFAULT); + static SSL_CTX* MakeSSLContext(const char* CApath, const char* CAfile, const char* server_cert, const char* server_prikey, const char* slave_cert, const char* slave_prikey, bool is_verify_peer); + static SSL* HandshakeSSL(SSL_CTX* ctx, int sock, bool is_accept, int con_retrycnt = CHMEVENTSOCK_RETRY_DEFAULT, suseconds_t con_waittime = CHMEVENTSOCK_TIMEOUT_DEFAULT); + static SSL* AcceptSSL(SSL_CTX* ctx, int sock, int con_retrycnt = CHMEVENTSOCK_RETRY_DEFAULT, suseconds_t con_waittime = CHMEVENTSOCK_TIMEOUT_DEFAULT) { return ChmEventSock::HandshakeSSL(ctx, sock, true, con_retrycnt, con_waittime); } + static SSL* ConnectSSL(SSL_CTX* ctx, int sock, int con_retrycnt = CHMEVENTSOCK_RETRY_DEFAULT, suseconds_t con_waittime = CHMEVENTSOCK_TIMEOUT_DEFAULT) { return ChmEventSock::HandshakeSSL(ctx, sock, false, con_retrycnt, con_waittime); } + static bool ShutdownSSL(int sock, SSL* ssl, int con_retrycnt = CHMEVENTSOCK_RETRY_DEFAULT, suseconds_t con_waittime = CHMEVENTSOCK_TIMEOUT_DEFAULT); + + static bool SetNonblocking(int sock); + static int WaitForReady(int sock, int type, int retrycnt, bool is_check_so_error = false, suseconds_t waittime = CHMEVENTSOCK_TIMEOUT_DEFAULT); + static int Listen(const char* hostname, short port); + static int RawListen(struct addrinfo* paddrinfo); + static int Connect(const char* hostname, short port, bool is_blocking = false, int con_retrycnt = CHMEVENTSOCK_RETRY_DEFAULT, suseconds_t con_waittime = CHMEVENTSOCK_TIMEOUT_DEFAULT); + static bool RawSend(int sock, SSL* ssl, PCOMPKT pComPkt, bool& is_closed, bool is_blocking = false, int retrycnt = CHMEVENTSOCK_RETRY_DEFAULT, suseconds_t waittime = CHMEVENTSOCK_TIMEOUT_DEFAULT); + static bool RawSend(int sock, SSL* ssl, const unsigned char* pbydata, size_t length, bool& is_closed, bool is_blocking = false, int retrycnt = CHMEVENTSOCK_RETRY_DEFAULT, suseconds_t waittime = CHMEVENTSOCK_TIMEOUT_DEFAULT); + static bool RawReceiveByte(int sock, SSL* ssl, bool& is_closed, unsigned char* pbuff, size_t length, bool is_blocking = false, int retrycnt = CHMEVENTSOCK_RETRY_DEFAULT, suseconds_t waittime = CHMEVENTSOCK_TIMEOUT_DEFAULT); + static bool RawReceiveAny(int sock, bool& is_closed, unsigned char* pbuff, size_t* plength, bool is_blocking = false, int retrycnt = CHMEVENTSOCK_RETRY_DEFAULT, suseconds_t waittime = CHMEVENTSOCK_TIMEOUT_DEFAULT); + static bool RawReceive(int sock, SSL* ssl, bool& is_closed, PCOMPKT* ppComPkt, size_t pktlength = 0L, bool is_blocking = false, int retrycnt = CHMEVENTSOCK_RETRY_DEFAULT, suseconds_t waittime = CHMEVENTSOCK_TIMEOUT_DEFAULT); + static bool RawSendCtlPort(const char* hostname, short ctlport, const unsigned char* pbydata, size_t length, std::string& strResult, volatile int& sockfd_lockval, int retrycnt = CHMEVENTSOCK_RETRY_DEFAULT, suseconds_t waittime = CHMEVENTSOCK_TIMEOUT_DEFAULT, int con_retrycnt = CHMEVENTSOCK_RETRY_DEFAULT, suseconds_t con_waittime = CHMEVENTSOCK_TIMEOUT_DEFAULT); + + static bool hton(PCOMPKT pComPkt, size_t& length); + static bool ntoh(PCOMPKT pComPkt, bool is_except_compkt = false); + static PCOMPKT AllocatePxComPacket(pxcomtype_t type, ssize_t ext_length = PXCOMPKT_AUTO_LENGTH); + static PCOMPKT DuplicateComPkt(PCOMPKT pComPkt); + + // callback functions for thread class + static bool ReceiveWorkerProc(void* common_param, chmthparam_t wp_param); + + // callback functions for merging + static bool MergeWorkerFunc(void* common_param, chmthparam_t wp_param); + + // callback functions for locked map class + static bool ServerSockMapCallback(sock_ids_map_t::iterator& iter, void* psockobj); + static bool SlaveSockMapCallback(sock_ids_map_t::iterator& iter, void* psockobj); + static bool AcceptMapCallback(sock_pending_map_t::iterator& iter, void* psockobj); + static bool ControlSockMapCallback(sock_ids_map_t::iterator& iter, void* psockobj); + static bool SslSockMapCallback(sock_ssl_map_t::iterator& iter, void* psockobj); + static bool SendLockMapCallback(sendlockmap_t::iterator& iter, void* psockobj); + + // close connection + bool CloseSelfSocks(void); + bool CloseFromSlavesSocks(void); + bool CloseCtlportClientSocks(void); + bool CloseToServersSocks(void); + bool CloseFromSlavesAcceptingSocks(void); + bool CloseSSL(int sock, bool with_sock = true); + bool CloseAllSSL(void); + bool CloseSocketWithEpoll(int sock); + + // SSL + SSL_CTX* GetSSLContext(const char* CApath, const char* CAfile, const char* server_cert, const char* server_prikey, const char* slave_cert, const char* slave_prikey, bool is_verify_peer); + SSL* GetSSL(int sock); + bool IsSafeParamsForSSL(void); + + // Socket lock for sending + bool LockSendSock(int sock) { return RawLockSendSock(sock, true, false); } + bool TryLockSendSock(int sock) { return RawLockSendSock(sock, true, true); } + bool UnlockSendSock(int sock) { return RawLockSendSock(sock, false, false); } + bool RawLockSendSock(int sock, bool is_lock, bool is_onece); + bool GetLockedSendSock(chmpxid_t chmpxid, int& sock, bool is_check_slave); + + // Send with locking + bool LockedSend(int sock, SSL* ssl, PCOMPKT pComPkt, bool is_blocking = false); + bool LockedSend(int sock, SSL* ssl, const unsigned char* pbydata, size_t length, bool is_blocking = false); + + // Receive + bool RawReceive(int fd, bool& is_closed); + + // connect/accept + bool ConnectServer(chmpxid_t chmpxid, int& sock, bool without_set_imdata); + bool RawConnectServer(chmpxid_t chmpxid, int& sock, bool without_set_imdata, bool is_lock); + bool ConnectRing(void); + bool CloseRechainRing(chmpxid_t nowchmpxid); + bool RechainRing(chmpxid_t newchmpxid); + bool CheckRechainRing(chmpxid_t newchmpxid, bool& is_rechain); + chmpxid_t GetNextRingChmpxId(void); + bool IsSafeDeptAndNextChmpxId(chmpxid_t dept_chmpxid, chmpxid_t next_chmpxid); + bool Accept(int sock); + bool AcceptCtlport(int ctlsock); + bool Processing(int sock, const char* pCommand); + + // for merge + bool IsSuspendServerInRing(void) const; + bool ChangeStatusBeforeMergeStart(void); + bool ChangeDownSvrStatusBeforeMerge(CHMDOWNSVRMERGE mode); + bool MergeStart(void); + bool MergeAbort(void); + bool MergeDone(void); + bool MergeComplete(void); + bool RequestMergeStart(std::string* pstring = NULL); + bool RequestMergeAbort(std::string* pstring = NULL); + bool RequestMergeComplete(std::string* pstring = NULL); + bool RequestServiceIn(std::string* pstring = NULL); + + // notification + bool RawNotifyHup(int fd); + bool ServerDownNotifyHup(chmpxid_t chmpxid); + bool ServerSockNotifyHup(int fd, bool& is_close, chmpxid_t& chmpxid); + bool SlaveSockNotifyHup(int fd, bool& is_close); + bool AcceptingSockNotifyHup(int fd, bool& is_close); + bool ControlSockNotifyHup(int fd, bool& is_close); + + // ctlport commands + bool SendCtlPort(chmpxid_t chmpxid, const unsigned char* pbydata, size_t length, std::string& strResult); + bool CtlComDump(std::string& strResponse); + bool CtlComMergeStart(std::string& strResponse); + bool CtlComMergeAbort(std::string& strResponse); + bool CtlComMergeComplete(std::string& strResponse); + bool CtlComServiceIn(std::string& strResponse); + bool CtlComServiceOut(const char* hostname, short ctlport, const char* pOrgCommand, std::string& strResponse); + bool CtlComSelfStatus(std::string& strResponse); + bool CtlComAllServerStatus(std::string& strResponse); + bool CtlComAllTraceSet(std::string& strResponse, bool enable); + bool CtlComAllTraceView(std::string& strResponse, logtype_t dirmask, logtype_t devmask, long count); + + // px2px commands + bool PxComSendStatusReq(int sock, chmpxid_t chmpxid); + bool PxComReceiveStatusReq(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt); + bool PxComReceiveStatusRes(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt, bool is_init_process = false); + bool PxComSendConinitReq(int sock, chmpxid_t chmpxid); + bool PxComReceiveConinitReq(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt, chmpxid_t& from_chmpxid, short& ctlport); + bool PxComSendConinitRes(int sock, chmpxid_t chmpxid, pxcomres_t result); + bool PxComReceiveConinitRes(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt, pxcomres_t& result); + bool PxComSendJoinRing(chmpxid_t chmpxid, PCHMPXSVR pserver); + bool PxComReceiveJoinRing(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt); + bool PxComSendStatusUpdate(chmpxid_t chmpxid, PCHMPXSVR pchmpxsvrs, long count); + bool PxComReceiveStatusUpdate(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt); + bool PxComSendStatusConfirm(chmpxid_t chmpxid, PCHMPXSVR pchmpxsvrs, long count); + bool PxComReceiveStatusConfirm(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt); + bool PxComSendStatusChange(chmpxid_t chmpxid, PCHMPXSVR pserver, bool is_send_slaves = true); + bool PxComSendSlavesStatusChange(PCHMPXSVR pserver); + bool PxComReceiveStatusChange(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt); + bool PxComSendMergeStart(chmpxid_t chmpxid); + bool PxComReceiveMergeStart(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt); + bool PxComSendMergeAbort(chmpxid_t chmpxid); + bool PxComReceiveMergeAbort(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt); + bool PxComSendMergeComplete(chmpxid_t chmpxid); + bool PxComReceiveMergeComplete(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt); + bool PxComSendServerDown(chmpxid_t chmpxid, chmpxid_t downchmpxid); + bool PxComReceiveServerDown(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt); + + bool PxCltReceiveJoinNotify(PCOMHEAD pComHead, PPXCLT_ALL pComAll); + + bool PxComSendReqUpdateData(chmpxid_t chmpxid, const PPXCOMMON_MERGE_PARAM pmerge_param); + bool PxComReceiveReqUpdateData(PCOMHEAD pComHead, PPXCOM_ALL pComAll, PCOMPKT* ppResComPkt); + bool PxComReceiveResUpdateData(PCOMHEAD pComHead, PPXCOM_ALL pComAll); + bool PxComReceiveResultUpdateData(PCOMHEAD pComHead, PPXCOM_ALL pComAll); + + public: + static void AllowSelfSignedCert(void) { ChmEventSock::is_self_sigined = true; } // For only debugging + static void DenySelfSignedCert(void) { ChmEventSock::is_self_sigined = true; } + + ChmEventSock(int eventqfd = CHM_INVALID_HANDLE, ChmCntrl* pcntrl = NULL); + virtual ~ChmEventSock(); + + bool InitialAllServerStatus(void); + bool ConnectServers(void); + bool DoSuspend(void); + + virtual bool Clean(void); + virtual bool UpdateInternalData(void); + virtual bool GetEventQueueFds(event_fds_t& fds); + virtual bool SetEventQueue(void); // Make Socket for reciver side and add it to event queue fd. + virtual bool UnsetEventQueue(void); // Unset mqfd from event queue fd, and destry it. + virtual bool IsEventQueueFd(int fd); + + virtual bool Send(PCOMPKT pComPkt, const unsigned char* pbody, size_t blength); + virtual bool Receive(int fd); + virtual bool NotifyHup(int fd); + virtual bool Processing(PCOMPKT pComPkt); + + bool PxComSendResUpdateData(chmpxid_t chmpxid, size_t length, const unsigned char* pdata, const struct timespec* pts); + bool PxComSendResultUpdateData(chmpxid_t chmpxid, reqidmapflag_t result_updatedata); +}; + +#endif // CHMEVENTSOCK_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmhash.cc b/lib/chmhash.cc new file mode 100644 index 0000000..819a0e9 --- /dev/null +++ b/lib/chmhash.cc @@ -0,0 +1,89 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ + +#include +#include +#include + +#include "chmcommon.h" +#include "chmutil.h" +#include "chmdbg.h" +#include "chmhash.h" +#include "chmstructure.h" + +using namespace std; + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +#define CHMPXID_PREFIX_HASH_STR "CHMPX" + +//--------------------------------------------------------- +// Utilities +//--------------------------------------------------------- +chmhash_t MakeChmpxId(const char* group, const char* hostname, short port) +{ + if(CHMEMPTYSTR(group) || CHMEMPTYSTR(hostname)){ + ERR_CHMPRN("Parameters are wrong."); + return 0; + } + string strbase1 = group; + strbase1 += ":"; + strbase1 += hostname; + strbase1 += ":"; + strbase1 += to_hexstring(port); + + chmhash_t hash1 = K2H_HASH_FUNC(reinterpret_cast(strbase1.c_str()), strbase1.length()); + chmhash_t hash2 = K2H_2ND_HASH_FUNC(reinterpret_cast(strbase1.c_str()), strbase1.length()); + chmhash_t result= COMPOSE64(hash1, hash2); + + // If hash result is CHM_INVALID_CHMPXID, force to set -2. :-p + if(result == CHM_INVALID_CHMPXID){ + result = static_cast(-2); + } + return result; +} + +chmhash_t MakeBaseMsgId(const char* group, const char* hostname, short port) +{ + if(CHMEMPTYSTR(group) || CHMEMPTYSTR(hostname)){ + ERR_CHMPRN("Parameters are wrong."); + return 0; + } + string strbase1 = group; + strbase1 += ":"; + strbase1 += hostname; + strbase1 += ":"; + strbase1 += to_hexstring(port); + + chmhash_t hash1 = K2H_HASH_FUNC(reinterpret_cast(strbase1.c_str()), strbase1.length()); + chmhash_t result= COMPOSE64(hash1, 0L); + + return result; +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmhash.h b/lib/chmhash.h new file mode 100644 index 0000000..359f333 --- /dev/null +++ b/lib/chmhash.h @@ -0,0 +1,45 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMHASH_H +#define CHMHASH_H + +#include "chmcommon.h" +#include "chmstructure.h" + +DECL_EXTERN_C_START + +//--------------------------------------------------------- +// Utilities +//--------------------------------------------------------- +chmhash_t MakeChmpxId(const char* group, const char* hostname, short port); +chmhash_t MakeBaseMsgId(const char* group, const char* hostname, short port); + +DECL_EXTERN_C_END + +#endif // CHMHASH_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmimdata.cc b/lib/chmimdata.cc new file mode 100644 index 0000000..b6bf1af --- /dev/null +++ b/lib/chmimdata.cc @@ -0,0 +1,2624 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ + +#include +#include +#include +#include +#include +#include + +#include "chmcommon.h" +#include "chmconf.h" +#include "chmstructure.tcc" +#include "chmimdata.h" +#include "chmnetdb.h" +#include "chmeventmq.h" +#include "chmeventsock.h" +#include "chmutil.h" +#include "chmregex.h" +#include "chmlock.h" +#include "chmdbg.h" + +using namespace std; + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +#define CHMSHM_SHMFILE_EXT ".chmpx" +#define CHMSHM_K2HASH_EXT ".k2h" +#define CHMSHM_BACKUP_EXT "bup" +#define CHMSHM_FILE_BASEDIR "/tmp" + +//--------------------------------------------------------- +// Class Valiables +//--------------------------------------------------------- +const int ChmIMData::SYSPAGE_SIZE; + +//--------------------------------------------------------- +// Class Methods +//--------------------------------------------------------- +bool ChmIMData::MakeFilePath(const char* groupname, short port, MKFPMODE mode, string& shmpath) +{ + if(ISEMPTYSTR(groupname)){ + ERR_CHMPRN("parameter is wrong."); + return false; + } + shmpath = CHMSHM_FILE_BASEDIR; + shmpath += "/"; + shmpath += groupname; + if(CHM_INVALID_PORT != port){ + shmpath += "-"; + shmpath += to_string(port); + } + shmpath += ChmIMData::MKFILEPATH_SHM == mode ? CHMSHM_SHMFILE_EXT : CHMSHM_K2HASH_EXT; + + return true; +} + +bool ChmIMData::MakeShmFilePath(const char* groupname, short port, string& shmpath) +{ + return ChmIMData::MakeFilePath(groupname, port, ChmIMData::MKFILEPATH_SHM, shmpath); +} + +bool ChmIMData::MakeK2hashFilePath(const char* groupname, short port, string& shmpath) +{ + return ChmIMData::MakeFilePath(groupname, port, ChmIMData::MKFILEPATH_K2H, shmpath); +} + +bool ChmIMData::CompareChmpxSvrs(PCHMPXSVR pbase, long bcount, PCHMPXSVR pmerge, long mcount, bool is_status) +{ + if(!pbase && 0L == bcount && !pmerge && 0L == mcount){ + return true; + } + if(!pbase || bcount <= 0L || !pmerge || mcount <= 0L){ + return false; + } + if(bcount != mcount){ + return false; + } + + for(long mcnt = 0; mcnt < mcount; mcnt++){ + long bcnt; + for(bcnt = 0; bcnt < bcount; bcnt++){ + if(pbase[bcnt].chmpxid == pmerge[mcnt].chmpxid){ + break; + } + } + if(bcount <= bcnt){ + return false; + } + if( 0 != strcmp(pbase[bcnt].name, pmerge[mcnt].name) || + pbase[bcnt].base_hash != pmerge[mcnt].base_hash || + pbase[bcnt].port != pmerge[mcnt].port || + pbase[bcnt].ctlport != pmerge[mcnt].ctlport || + !CMP_CHMPXSSL(pbase[bcnt].ssl, pmerge[mcnt].ssl) || + (is_status && pbase[bcnt].status != pmerge[mcnt].status) ) + { + return false; + } + } + return true; +} + +//--------------------------------------------------------- +// Methods +//--------------------------------------------------------- +ChmIMData::ChmIMData(bool is_chmpx_proc) : ShmPath(""), pChmShm(NULL), ShmFd(CHM_INVALID_HANDLE), ShmSize(0L), pConfObj(NULL), pK2hash(NULL), eqfd(CHM_INVALID_HANDLE), isChmpxProc(is_chmpx_proc), ChmpxPid(CHM_INVALID_PID) +{ +} + +ChmIMData::~ChmIMData() +{ + Close(); +} + +bool ChmIMData::Close() +{ + bool result = true; + + if(isChmpxProc && pChmShm){ + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + tmpchminfo.Close(eqfd); + } + if(!CloseShm()){ + MSG_CHMPRN("Failed to close SHM, but continue..."); + result = false; + } + if(!CloseK2hash()){ + MSG_CHMPRN("Failed to close K2hash, but continue..."); + result = false; + } + pConfObj= NULL; + eqfd = CHM_INVALID_HANDLE; + + return result; +} + +bool ChmIMData::CloseSocks(int type) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.Close(eqfd, type); +} + +bool ChmIMData::Dump(stringstream& sstream) const +{ + sstream << "==========================================================================" << endl; + sstream << "Shared memory file path = " << ShmPath << endl; + sstream << "Shared memory file size = " << ShmSize << endl; + sstream << "Shared memory file fd = " << ShmFd << endl; + sstream << "Shared memory file Object = " << pChmShm << endl; + sstream << "Configration object = " << pConfObj << endl; + sstream << "K2HASH Object = " << pK2hash->GetK2hashFilePath() << endl; + sstream << "==========================================================================" << endl; + sstream << "Shared memory file Object" << endl; + sstream << "--------------------------------------------------------------------------" << endl; + + if(!IsAttachedShm()){ + sstream << "*** NOT MAPPING" << endl; + }else{ + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + tmpchminfo.Dump(sstream, NULL); + } + sstream << "==========================================================================" << endl; + + return true; +} + +bool ChmIMData::DumpSelfChmpxSvr(stringstream& sstream) const +{ + CHMPXSVR chmpxsvr; + if(!GetSelfChmpxSvr(&chmpxsvr)){ + return false; + } + sstream << "CHMPX HOSTNAME = " << chmpxsvr.name << endl; + sstream << "MODE = " << (IsServerMode() ? "SERVER" : "SLAVE") << endl; + sstream << "PORT = " << chmpxsvr.port << endl; + sstream << "CONTROL PORT = " << chmpxsvr.ctlport << endl; + sstream << "SSL = " << (chmpxsvr.ssl.is_ssl ? "YES" : "NO") << endl; + if(chmpxsvr.ssl.is_ssl){ + sstream << "VERIFY PEER = " << (chmpxsvr.ssl.verify_peer ? "YES" : "NO") << endl; + sstream << "CA PATH TYPE = " << (chmpxsvr.ssl.is_ca_file ? "FILE" : "DIR") << endl; + sstream << "CA PATH = " << chmpxsvr.ssl.capath << endl; + sstream << "SERVER CERT = " << chmpxsvr.ssl.server_cert << endl; + sstream << "SERVER PRIKEY = " << chmpxsvr.ssl.server_prikey << endl; + sstream << "SLAVE CERT = " << chmpxsvr.ssl.slave_cert << endl; + sstream << "SLAVE PRIKEY = " << chmpxsvr.ssl.slave_prikey << endl; + } + sstream << "BASE HASH = " << to_hexstring(chmpxsvr.base_hash) << endl; + sstream << "STATUS = " << STR_CHMPXSTS_FULL(chmpxsvr.status) << endl; + + return true; +} + +PCHMINFOEX ChmIMData::DupAllChmInfo(void) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return NULL; + } + PCHMINFOEX pinfoex; + if(NULL == (pinfoex = reinterpret_cast(calloc(1, sizeof(CHMINFOEX))))){ + ERR_CHMPRN("Could not allocation memory."); + return NULL; + } + pinfoex->shmsize = ShmSize; + strcpy(pinfoex->shmpath, ShmPath.c_str()); + strcpy(pinfoex->k2hashpath, pK2hash->GetK2hashFilePath()); + + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + pinfoex->pchminfo = tmpchminfo.Dup(); + + return pinfoex; +} + +PCHMPX ChmIMData::DupSelfChmpxInfo(void) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return NULL; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.DupSelfChmpxSvr(); +} + +void ChmIMData::FreeDupAllChmInfo(PCHMINFOEX ptr) +{ + if(ptr){ + chminfolap tmpchminfo; // [NOTE] pointer is not on Shm, but we use tmpchminfo object. thus using only Free method. + tmpchminfo.Free(ptr->pchminfo); + K2H_Free(ptr); + } +} + +void ChmIMData::FreeDupSelfChmpxInfo(PCHMPX ptr) +{ + if(ptr){ + chmpxlap tmpchmpx; // [NOTE] pointer is not on Shm, but we use tmpchminfo object. thus using only Free method. + tmpchmpx.Free(ptr); + K2H_Free(ptr); + } +} + +//--------------------------------------------------------- +// Methods for K2hash +//--------------------------------------------------------- +bool ChmIMData::CloseK2hash(void) +{ + if(!IsAttachedK2hash()){ + MSG_CHMPRN("There is already no attached K2hash file."); + return false; + } + string tmppath = pK2hash->GetK2hashFilePath(); + if(!pK2hash->Detach()){ + ERR_CHMPRN("Failed to detach k2hash file %s", tmppath.c_str()); + return false; + } + bool result = true; + if(isChmpxProc){ + if(-1 == unlink(tmppath.c_str())){ + ERR_CHMPRN("Failed to unlink file %s, errno=%d", tmppath.c_str(), errno); + result = false; + } + } + CHM_Delete(pK2hash); + + return result; +} + +bool ChmIMData::InitializeK2hash(void) +{ + if(IsAttachedK2hash()){ + ERR_CHMPRN("Already attach K2hash, must dettach it before initializing K2hash."); + return false; + } + if(!pConfObj){ + ERR_CHMPRN("Configration object is not loaded."); + return false; + } + + const CHMCFGINFO* pchmcfg = pConfObj->GetConfiguration(); + if(!pchmcfg){ + ERR_CHMPRN("Could not get configuration information structure pointer."); + return false; + } + + string k2hfilepath; + if(!ChmIMData::MakeK2hashFilePath(pchmcfg->groupname.c_str(), pchmcfg->self_ctlport, k2hfilepath)){ + ERR_CHMPRN("Failed to make k2hash file path from groupname(%s).", pchmcfg->groupname.c_str()); + return false; + } + if(is_file_exist(k2hfilepath.c_str())){ + // remove old file + if(-1 == unlink(k2hfilepath.c_str())){ + ERR_CHMPRN("Failed to unlink old k2hash file %s, errno=%d", k2hfilepath.c_str(), errno); + return false; + } + } + + // init + pK2hash = new K2HShm(); + if(!pK2hash->Create(k2hfilepath.c_str(), pchmcfg->k2h_fullmap, pchmcfg->k2h_mask_bitcnt, pchmcfg->k2h_cmask_bitcnt, pchmcfg->k2h_max_element, ChmIMData::SYSPAGE_SIZE)){ + ERR_CHMPRN("Failed to create new k2hash file(%s)", k2hfilepath.c_str()); + CHM_Delete(pK2hash); + return false; + } + + return true; +} + +// +// For Client process library +// +bool ChmIMData::AttachK2hash(void) +{ + if(IsAttachedK2hash()){ + ERR_CHMPRN("Already attach K2hash, must dettach it before initializing K2hash."); + return false; + } + if(!pConfObj){ + ERR_CHMPRN("Configration object is not loaded."); + return false; + } + + const CHMCFGINFO* pchmcfg = pConfObj->GetConfiguration(); + if(!pchmcfg){ + ERR_CHMPRN("Could not get configuration information structure pointer."); + return false; + } + + string k2hfilepath; + if(!ChmIMData::MakeK2hashFilePath(pchmcfg->groupname.c_str(), pchmcfg->self_ctlport, k2hfilepath)){ + ERR_CHMPRN("Failed to make k2hash file path from groupname(%s).", pchmcfg->groupname.c_str()); + return false; + } + + if(!is_file_exist(k2hfilepath.c_str())){ + ERR_CHMPRN("K2hash file(%s) does not exist.", k2hfilepath.c_str()); + return false; + } + + // init + pK2hash = new K2HShm(); + if(!pK2hash->Attach(k2hfilepath.c_str(), false, false, false, pchmcfg->k2h_fullmap, pchmcfg->k2h_mask_bitcnt, pchmcfg->k2h_cmask_bitcnt, pchmcfg->k2h_max_element, ChmIMData::SYSPAGE_SIZE)){ + ERR_CHMPRN("Failed to attach k2hash file(%s)", k2hfilepath.c_str()); + CHM_Delete(pK2hash); + return false; + } + + return true; +} + +//--------------------------------------------------------- +// Methods for ChmShm +//--------------------------------------------------------- +bool ChmIMData::Initialize(CHMConf* cfgobj, int eventqfd, bool is_chmpx_proc) +{ + if(!cfgobj){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(CHM_INVALID_HANDLE == eventqfd){ + WAN_CHMPRN("Event fd is invalid."); + } + + if(is_chmpx_proc != isChmpxProc){ + MSG_CHMPRN("Change \"is in CHMPX process\" flag to %s.", is_chmpx_proc ? "true" : "false"); + isChmpxProc = is_chmpx_proc; + } + + // Set conf object. + pConfObj = cfgobj; + + // SHM & K2hash + if(isChmpxProc){ + // Initialize SHM + if(!InitializeShm()){ + ERR_CHMPRN("Failed to initialize SHM."); + pConfObj = NULL; + return false; + } + + // Initialize K2hash + if(!InitializeK2hash()){ + ERR_CHMPRN("Failed to initialize k2hash file."); + pConfObj = NULL; + return false; + } + + // Lock chmpx pid offset + if(!LockChmpxPid()){ + ERR_CHMPRN("Failed to lock chmpx pid offset."); + pConfObj = NULL; + return false; + } + }else{ + // Attach SHM + if(!AttachShm()){ + ERR_CHMPRN("Failed to initialize SHM."); + pConfObj = NULL; + return false; + } + + // Check chmpx process running + if(!IsChmpxProcessRunning(ChmpxPid)){ + ERR_CHMPRN("Chmpx process is not running."); + pConfObj = NULL; + return false; + } + + // Attach K2hash + if(!AttachK2hash()){ + ERR_CHMPRN("Failed to initialize k2hash file."); + pConfObj = NULL; + return false; + } + } + + // Initialize Other + if(!InitializeOther()){ + ERR_CHMPRN("Failed to initialize other."); + pConfObj = NULL; + return false; + } + + // backup + eqfd = eventqfd; + + return true; +} + +bool ChmIMData::CloseShm(void) +{ + if(!IsAttachedShm()){ + MSG_CHMPRN("There is already no CHMSHM."); + return false; + } + + bool result = true; + if(isChmpxProc){ + if(-1 == unlink(ShmPath.c_str())){ + ERR_CHMPRN("Failed to unlink file %s, errno=%d", ShmPath.c_str(), errno); + result = false; + } + + // Unlock chmpx pid offset + if(!UnlockChmpxPid()){ + ERR_CHMPRN("Failed to unlock chmpx pid offset, but continue..."); + result = false; + } + } + CHM_MUMMAP(ShmFd, pChmShm, ShmSize); + ChmpxPid = CHM_INVALID_PID; + + return result; +} + +bool ChmIMData::InitializeShm(void) +{ + if(IsAttachedShm()){ + ERR_CHMPRN("Already attach SHM, must dettach it before initializing SHM."); + return false; + } + if(!pConfObj){ + ERR_CHMPRN("Configration object is not loaded."); + return false; + } + + const CHMCFGINFO* pchmcfg = pConfObj->GetConfiguration(); + if(!pchmcfg){ + ERR_CHMPRN("Could not get configuration information structure pointer."); + return false; + } + + // Check mq size + // + // If chmpx process is mq receiver for all mq sender(client processes), mq size should be max mq count. + // + long maxmsg = pchmcfg->max_server_mq_cnt + pchmcfg->max_client_mq_cnt; + if(!ChmEventMq::InitializeMaxMqSystemSize(maxmsg)){ + ERR_CHMPRN("Could not get mq size = %ld for chmpx process.", pchmcfg->max_client_mq_cnt); + return false; + } + + CHMNODE_CFGINFO self; + if(!pConfObj->GetSelfNodeInfo(self)){ + ERR_CHMPRN("Could not get self node information."); + return false; + } + + return InitializeShmEx(pchmcfg, &self); +} + +bool ChmIMData::InitializeShmEx(const CHMCFGINFO* pchmcfg, const CHMNODE_CFGINFO* pself) +{ + if( !pchmcfg || MAX_GROUP_LENGTH <= pchmcfg->groupname.length() || MAX_CHMPX_COUNT < pchmcfg->max_chmpx_count || + MAX_SERVER_MQ_CNT < pchmcfg->max_server_mq_cnt || MAX_CLIENT_MQ_CNT < pchmcfg->max_client_mq_cnt || + MAX_MQ_PER_CLIENT < pchmcfg->max_mq_per_client || MAX_HISTLOG_COUNT < pchmcfg->max_histlog_count ) + { + ERR_CHMPRN("Configuration information are wrong."); + return false; + } + string shmpath; + if(!ChmIMData::MakeShmFilePath(pchmcfg->groupname.c_str(), pchmcfg->self_ctlport, shmpath)){ + ERR_CHMPRN("Failed to make chmshm file path from groupname(%s).", pchmcfg->groupname.c_str()); + return false; + } + + // check existed file + if(is_file_exist(shmpath.c_str())){ + // found old file, move it. + if(!move_file_to_backup(shmpath.c_str(), CHMSHM_BACKUP_EXT)){ + ERR_CHMPRN("Failed to move(backup) file %s.", shmpath.c_str()); + return false; + } + } + + // make new file + int fd; + if(CHM_INVALID_HANDLE == (fd = open(shmpath.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH))){ + ERR_CHMPRN("Could not create file(%s), errno = %d", shmpath.c_str(), errno); + return false; + } + + // file total size + size_t total_shmsize = sizeof(CHMSHM) + // CHMSHM (this structure is alignmented) + sizeof(CHMPXLIST) * pchmcfg->max_chmpx_count + // CHMPX Area + sizeof(PCHMPX) * pchmcfg->max_chmpx_count * 2 + // PCHMPX array Area + sizeof(MQMSGHEADLIST) * (pchmcfg->max_server_mq_cnt + pchmcfg->max_client_mq_cnt) + // MQUEUE Area + sizeof(CLTPROCLIST) * MAX_CLTPROCLIST_COUNT(pchmcfg->max_client_mq_cnt, pchmcfg->mqcnt_per_attach) + // CLTPROCLIST Area + sizeof(CHMLOGRAW) * pchmcfg->max_histlog_count + // LOG Area + sizeof(CHMSOCKLIST) * pchmcfg->max_chmpx_count * pchmcfg->max_sock_pool * 2; // SOCK array Area([NOTICE] twice for mergin) + + // truncate with filling zero + if(!truncate_filling_zero(fd, total_shmsize, ChmIMData::SYSPAGE_SIZE)){ + ERR_CHMPRN("Could not truncate file(%s) with filling zero.", shmpath.c_str()); + CHM_CLOSE(fd); + return false; + } + + // mmap + void* shmbase; + if(MAP_FAILED == (shmbase = mmap(NULL, total_shmsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0))){ + ERR_CHMPRN("Could not mmap file(%s), errno = %d", shmpath.c_str(), errno); + CHM_CLOSE(fd); + return false; + } + + // initialize data in shm + PCHMSHM pChmBase = reinterpret_cast(shmbase); + PCHMPX* rel_pchmpxarr_base; + PCHMPX* rel_pchmpxarr_pend; + { + PCHMPXLIST rel_chmpxarea = reinterpret_cast( sizeof(CHMSHM)); + PCHMPX* rel_pchmpxarrarea = reinterpret_cast( sizeof(CHMSHM) + + sizeof(CHMPXLIST) * pchmcfg->max_chmpx_count); + PMQMSGHEADLIST rel_chmpxmsgarea = reinterpret_cast( sizeof(CHMSHM) + + sizeof(CHMPXLIST) * pchmcfg->max_chmpx_count + + sizeof(PCHMPX) * pchmcfg->max_chmpx_count * 2); + PCLTPROCLIST rel_chmpxpidarea = reinterpret_cast( sizeof(CHMSHM) + + sizeof(CHMPXLIST) * pchmcfg->max_chmpx_count + + sizeof(PCHMPX) * pchmcfg->max_chmpx_count * 2 + + sizeof(MQMSGHEADLIST) * (pchmcfg->max_server_mq_cnt + pchmcfg->max_client_mq_cnt)); + PCHMLOGRAW rel_lograwarea = reinterpret_cast( sizeof(CHMSHM) + + sizeof(CHMPXLIST) * pchmcfg->max_chmpx_count + + sizeof(PCHMPX) * pchmcfg->max_chmpx_count * 2 + + sizeof(MQMSGHEADLIST) * (pchmcfg->max_server_mq_cnt + pchmcfg->max_client_mq_cnt) + + sizeof(CLTPROCLIST) * MAX_CLTPROCLIST_COUNT(pchmcfg->max_client_mq_cnt, pchmcfg->mqcnt_per_attach)); + PCHMSOCKLIST rel_chmsockarea = reinterpret_cast( sizeof(CHMSHM) + + sizeof(CHMPXLIST) * pchmcfg->max_chmpx_count + + sizeof(PCHMPX) * pchmcfg->max_chmpx_count * 2 + + sizeof(MQMSGHEADLIST) * (pchmcfg->max_server_mq_cnt + pchmcfg->max_client_mq_cnt) + + sizeof(CLTPROCLIST) * MAX_CLTPROCLIST_COUNT(pchmcfg->max_client_mq_cnt, pchmcfg->mqcnt_per_attach) + + sizeof(CHMLOGRAW) * pchmcfg->max_histlog_count); + rel_pchmpxarr_base = rel_pchmpxarrarea; + rel_pchmpxarr_pend = CHM_OFFSET(rel_pchmpxarrarea, static_cast(sizeof(PCHMPX) * pchmcfg->max_chmpx_count), PCHMPX*); + + // initializing each erea + { + PCHMPXLIST chmpxlist = CHM_ABS(shmbase, rel_chmpxarea, PCHMPXLIST); + for(long cnt = 0L; cnt < pchmcfg->max_chmpx_count; cnt++){ + PCHMPXLIST prev = 0 < cnt ? &chmpxlist[cnt - 1] : NULL; + PCHMPXLIST next = (cnt + 1 < pchmcfg->max_chmpx_count) ? &chmpxlist[cnt + 1] : NULL; + + chmpxlistlap tmpchmpxlist(&chmpxlist[cnt], NULL, NULL, NULL, NULL, NULL, shmbase); // absmapptr, chmpx*s are NULL, these can allow only here(calling only Initialize()). + if(!tmpchmpxlist.Initialize(prev, next)){ + ERR_CHMPRN("Failed to initialize No.%ld CHMPXLIST.", cnt); + CHM_MUMMAP(fd, shmbase, total_shmsize); + return false; + } + } + } + { + PCHMPX* pchmpxarr = CHM_ABS(shmbase, rel_pchmpxarrarea, PCHMPX*); + for(long cnt = 0L; cnt < (pchmcfg->max_chmpx_count * 2); cnt++){ + pchmpxarr[cnt] = NULL; + } + } + { + PMQMSGHEADLIST mqmsglist = CHM_ABS(shmbase, rel_chmpxmsgarea, PMQMSGHEADLIST); + for(long cnt = 0L; cnt < (pchmcfg->max_server_mq_cnt + pchmcfg->max_client_mq_cnt); cnt++){ + PMQMSGHEADLIST prev = (0 < cnt) ? &mqmsglist[cnt - 1] : NULL; + PMQMSGHEADLIST next = (cnt + 1 < (pchmcfg->max_server_mq_cnt + pchmcfg->max_client_mq_cnt)) ? &mqmsglist[cnt + 1] : NULL; + + mqmsgheadlistlap tmpmqmsgheadlist(&mqmsglist[cnt], shmbase); + if(!tmpmqmsgheadlist.Initialize(prev, next)){ + ERR_CHMPRN("Failed to initialize No.%ld MQMSGHEADLIST.", cnt); + CHM_MUMMAP(fd, shmbase, total_shmsize); + return false; + } + } + } + { + PCLTPROCLIST cltproclist = CHM_ABS(shmbase, rel_chmpxpidarea, PCLTPROCLIST); + long cltproclist_cnt = MAX_CLTPROCLIST_COUNT(pchmcfg->max_client_mq_cnt, pchmcfg->mqcnt_per_attach); + for(long cnt = 0L; cnt < cltproclist_cnt; cnt++){ + PCLTPROCLIST prev = (0 < cnt) ? &cltproclist[cnt - 1] : NULL; + PCLTPROCLIST next = (cnt + 1 < cltproclist_cnt) ? &cltproclist[cnt + 1] : NULL; + + cltproclistlap tmpcltproclist(&cltproclist[cnt], shmbase); + if(!tmpcltproclist.Initialize(prev, next)){ + ERR_CHMPRN("Failed to initialize No.%ld PCLTPROCLIST.", cnt); + CHM_MUMMAP(fd, shmbase, total_shmsize); + return false; + } + } + } + { + PCHMLOGRAW lograw = CHM_ABS(shmbase, rel_lograwarea, PCHMLOGRAW); + for(long cnt = 0L; cnt < pchmcfg->max_histlog_count; cnt++){ + chmlograwlap tmplograw(&lograw[cnt], shmbase); + if(!tmplograw.Initialize()){ + ERR_CHMPRN("Failed to initialize No.%ld CHMLOGRAW.", cnt); + CHM_MUMMAP(fd, shmbase, total_shmsize); + return false; + } + } + } + { + PCHMSOCKLIST chmsocklist = CHM_ABS(shmbase, rel_chmsockarea, PCHMSOCKLIST); + long chmsocklist_cnt = pchmcfg->max_chmpx_count * pchmcfg->max_sock_pool * 2; + for(long cnt = 0L; cnt < chmsocklist_cnt; cnt++){ + PCHMSOCKLIST prev = (0 < cnt) ? &chmsocklist[cnt - 1] : NULL; + PCHMSOCKLIST next = (cnt + 1 < chmsocklist_cnt) ? &chmsocklist[cnt + 1] : NULL; + + chmsocklistlap tmpchmsocklist(&chmsocklist[cnt], shmbase); + if(!tmpchmsocklist.Initialize(prev, next)){ + ERR_CHMPRN("Failed to initialize No.%ld PCHMSOCKLIST.", cnt); + CHM_MUMMAP(fd, shmbase, total_shmsize); + return false; + } + } + } + + // CHMSHM + pChmBase->rel_chmpxarea = rel_chmpxarea; + pChmBase->rel_pchmpxarrarea = rel_pchmpxarrarea; + pChmBase->rel_chmpxmsgarea = rel_chmpxmsgarea; + pChmBase->rel_chmpxpidarea = rel_chmpxpidarea; + pChmBase->rel_chmsockarea = rel_chmsockarea; + + // CHMSHM.CHMLOG + chmloglap tmpchmlog(&pChmBase->chmpxlog, shmbase); + if(!tmpchmlog.Initialize(rel_lograwarea, pchmcfg->max_histlog_count)){ + ERR_CHMPRN("Failed to initialize CHMLOG."); + CHM_MUMMAP(fd, shmbase, total_shmsize); + return false; + } + + // CHMSHM.CHMINFO + chminfolap tmpchminfo(&pChmBase->info, shmbase); + if(!tmpchminfo.Initialize(pchmcfg, rel_chmpxmsgarea, pself, rel_chmpxarea, rel_chmpxpidarea, rel_chmsockarea, rel_pchmpxarr_base, rel_pchmpxarr_pend)){ + ERR_CHMPRN("Failed to initialize CHMINFO."); + CHM_MUMMAP(fd, shmbase, total_shmsize); + return false; + } + } + ShmPath = shmpath; + pChmShm = pChmBase; + ShmFd = fd; + ShmSize = total_shmsize; + + return true; +} + +// +// For Client process library +// +bool ChmIMData::AttachShm(void) +{ + if(IsAttachedShm()){ + ERR_CHMPRN("Already attach SHM, must dettach it before initializing SHM."); + return false; + } + if(!pConfObj){ + ERR_CHMPRN("Configration object is not loaded."); + return false; + } + + // get config + const CHMCFGINFO* pchmcfg = pConfObj->GetConfiguration(); + if(!pchmcfg){ + ERR_CHMPRN("Could not get configuration information structure pointer."); + return false; + } + if(MAX_GROUP_LENGTH <= pchmcfg->groupname.length()){ + ERR_CHMPRN("Configuration information are wrong."); + return false; + } + + // Check mq size + // + long maxmsg = pchmcfg->max_mq_per_client + pchmcfg->max_server_mq_cnt; + if(!ChmEventMq::InitializeMaxMqSystemSize(maxmsg)){ + ERR_CHMPRN("Could not set mq size = %ld for client process.", maxmsg); + return false; + } + + // make shm path + string shmpath; + if(!ChmIMData::MakeShmFilePath(pchmcfg->groupname.c_str(), pchmcfg->self_ctlport, shmpath)){ + ERR_CHMPRN("Failed to make chmshm file path from groupname(%s).", pchmcfg->groupname.c_str()); + return false; + } + + // shm file size + size_t total_shmsize; + if(!get_file_size(shmpath.c_str(), total_shmsize)){ + ERR_CHMPRN("ChmShm file(%s) does not exist or failed to read file size.", shmpath.c_str()); + return false; + } + + // open shm file + int fd; + if(CHM_INVALID_HANDLE == (fd = open(shmpath.c_str(), O_RDWR))){ + ERR_CHMPRN("Could not open file(%s), errno = %d", shmpath.c_str(), errno); + return false; + } + + // mmap + void* shmbase; + if(MAP_FAILED == (shmbase = mmap(NULL, total_shmsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0))){ + ERR_CHMPRN("Could not mmap file(%s), errno = %d", shmpath.c_str(), errno); + CHM_CLOSE(fd); + return false; + } + + // initialize data + ShmPath = shmpath; + pChmShm = reinterpret_cast(shmbase); + ShmFd = fd; + ShmSize = total_shmsize; + + return true; +} + +//--------------------------------------------------------- +// Methods for Initializing other +//--------------------------------------------------------- +bool ChmIMData::InitializeOther(void) +{ + if(!pConfObj){ + ERR_CHMPRN("Configration object is not loaded."); + return false; + } + const CHMCFGINFO* pchmcfg = pConfObj->GetConfiguration(); + if(!pchmcfg){ + ERR_CHMPRN("Could not get configuration information structure pointer."); + return false; + } + // clear hostanme:ctlport cache + FREE_HNAMESSLMAP(hnamesslmap); + + return true; +} + +bool ChmIMData::ReloadConfigration(void) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + if(!pConfObj){ + ERR_CHMPRN("Configration object is not loaded."); + return false; + } + const CHMCFGINFO* pchmcfg = pConfObj->GetConfiguration(); + if(!pchmcfg){ + ERR_CHMPRN("Could not get configuration information structure pointer."); + return false; + } + + // reload + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + if(!tmpchminfo.ReloadConfigration(pchmcfg)){ + ERR_CHMPRN("Failed to reload configuration file."); + return false; + } + return true; +} + +//--------------------------------------------------------- +// Methods for Accessing MQ +//--------------------------------------------------------- +// For locking MQ, use free_msg_count member address in CHMSHM. +// This method is utility. +// +off_t ChmIMData::GetLockOffsetForMQ(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + long* rel_freemsgcnt = CHM_REL(pChmShm, &(pChmShm->info.free_msg_count), long*); + return reinterpret_cast(rel_freemsgcnt); +} + +msgid_t ChmIMData::GetBaseMsgId(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + ChmLock AutoLock(CHMLT_READ, ShmFd, GetLockOffsetForMQ()); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetBaseMsgId(); +} + +bool ChmIMData::FreeMsg(msgid_t msgid) +{ + if(CHM_INVALID_MSGID == msgid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_WRITE, ShmFd, GetLockOffsetForMQ()); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + if(!tmpchminfo.FreeMsg(msgid)){ + ERR_CHMPRN("Failed to retrive msgid(0x%016" PRIx64 ").", msgid); + return false; + } + return true; +} + +bool ChmIMData::IsMsgidActivated(msgid_t msgid) const +{ + if(CHM_INVALID_MSGID == msgid){ + return false; + } + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_WRITE, ShmFd, GetLockOffsetForMQ()); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + + return tmpchminfo.IsMsgidActivated(msgid); +} + +msgid_t ChmIMData::AssignMsg(bool is_chmpx, bool is_activated) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return CHM_INVALID_MSGID; + } + ChmLock AutoLock(CHMLT_WRITE, ShmFd, GetLockOffsetForMQ()); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.AssignMsg(is_chmpx, is_activated); +} + +bool ChmIMData::ActivateMsgEx(msgid_t msgid, bool is_activate) +{ + if(CHM_INVALID_MSGID == msgid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_WRITE, ShmFd, GetLockOffsetForMQ()); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + bool result; + if(is_activate){ + result = tmpchminfo.SetMqActivated(msgid); + }else{ + result = tmpchminfo.SetMqDisactivated(msgid); + } + return result; +} + +msgid_t ChmIMData::GetRandomMsgId(bool is_chmpx) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return CHM_INVALID_MSGID; + } + ChmLock AutoLock(CHMLT_READ, ShmFd, GetLockOffsetForMQ()); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetRandomMsgId(is_chmpx, true); // Only activated msgid +} + +bool ChmIMData::GetMsgidListByPid(pid_t pid, msgidlist_t& list) +{ + if(CHM_INVALID_PID == pid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_WRITE, ShmFd, GetLockOffsetForMQ()); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetMsgidListByPid(pid, list, true); +} + +bool ChmIMData::GetMsgidListByUniqPid(msgidlist_t& list) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_WRITE, ShmFd, GetLockOffsetForMQ()); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetMsgidListByUniqPid(list, true); +} + +bool ChmIMData::FreeMsgs(const pidlist_t& pidlist, msgidlist_t& freedmsgids) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_WRITE, ShmFd, GetLockOffsetForMQ()); // Lock + + freedmsgids.clear(); + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + + // get all msgids + msgidlist_t msgidlist; + for(pidlist_t::const_iterator iter = pidlist.begin(); iter != pidlist.end(); ++iter){ + if(!tmpchminfo.GetMsgidListByPid(*iter, msgidlist, false)){ + ERR_CHMPRN("Something error occured during getting msgids by pid, but continue..."); + } + } + + // Free msgids + for(msgidlist_t::iterator iter = msgidlist.begin(); iter != msgidlist.end(); ++iter){ + if(!tmpchminfo.FreeMsg(*iter)){ + ERR_CHMPRN("Something error occured during free msgid(0x%016" PRIx64 "), but continue...", *iter); + continue; + } + freedmsgids.push_back(*iter); + } + return true; +} + +//--------------------------------------------------------- +// Methods for Accessing CHMSHM +//--------------------------------------------------------- +bool ChmIMData::GetSelfChmpxSvr(PCHMPXSVR chmpxsvr) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSelfChmpxSvr(chmpxsvr); +} + +bool ChmIMData::GetChmpxSvr(chmpxid_t chmpxid, PCHMPXSVR chmpxsvr) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetChmpxSvr(chmpxid, chmpxsvr); +} + +bool ChmIMData::GetChmpxSvrs(PCHMPXSVR* ppchmpxsvrs, long& count) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetChmpxSvrs(ppchmpxsvrs, count); +} + +bool ChmIMData::CompareChmpxSvrs(PCHMPXSVR pchmpxsvrs, long count) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + PCHMPXSVR pbasechmpxsvrs = NULL; + long basecount = 0L; + if(!tmpchminfo.GetChmpxSvrs(&pbasechmpxsvrs, basecount)){ + ERR_CHMPRN("Could not get now chmpx servers information."); + return false; + } + + // check + if(!ChmIMData::CompareChmpxSvrs(pbasechmpxsvrs, basecount, pchmpxsvrs, count)){ + CHM_Free(pbasechmpxsvrs); + return false; + } + CHM_Free(pbasechmpxsvrs); + return true; +} + +bool ChmIMData::MergeChmpxSvrs(PCHMPXSVR pchmpxsvrs, long count, bool is_remove, bool is_init_process, int eqfd) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.MergeChmpxSvrs(pchmpxsvrs, count, is_remove, is_init_process, eqfd); +} + +bool ChmIMData::MergeChmpxSvrsForStatusUpdate(PCHMPXSVR pchmpxsvrs, long count, int eqfd) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + PCHMPXSVR pbasechmpxsvrs = NULL; + long basecount = 0L; + if(!tmpchminfo.GetChmpxSvrs(&pbasechmpxsvrs, basecount)){ + ERR_CHMPRN("Could not get now chmpx servers information."); + return false; + } + + // check + if(!ChmIMData::CompareChmpxSvrs(pbasechmpxsvrs, basecount, pchmpxsvrs, count, false)){ // without checking status + ERR_CHMPRN("For merging, could not merge from new chmpx servers information to now one, there are many difference."); + CHM_Free(pbasechmpxsvrs); + return false; + } + + // merge + // + // Lock in following methos. + // + if(!MergeChmpxSvrs(pchmpxsvrs, count, false, false, eqfd)){ + ERR_CHMPRN("Failed to merge chmpx server information, try to recover..."); + if(!MergeChmpxSvrs(pbasechmpxsvrs, basecount, false, false, eqfd)){ + ERR_CHMPRN("Failed to recover merging chmpx server information, no more do nothing..."); + } + CHM_Free(pbasechmpxsvrs); + return false; + } + CHM_Free(pbasechmpxsvrs); + + return true; +} + +bool ChmIMData::GetGroup(string& group) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetGroup(group); +} + +bool ChmIMData::IsRandomDeliver(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.IsRandomDeliver(); +} + +bool ChmIMData::IsAutoMergeConf(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.IsAutoMergeConf(); +} + +bool ChmIMData::IsDoMergeConf(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.IsDoMergeConf(); +} + +int ChmIMData::GetSocketThreadCount(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSocketThreadCount(); +} + +int ChmIMData::GetMQThreadCount(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetMQThreadCount(); +} + +int ChmIMData::GetMaxSockPool(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetMaxSockPool(); +} + +time_t ChmIMData::GetSockPoolTimeout(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSockPoolTimeout(); +} + +long ChmIMData::GetMaxMQCount(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetMaxMQCount(); +} + +long ChmIMData::GetChmpxMQCount(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetChmpxMQCount(); +} + +long ChmIMData::GetMaxQueuePerChmpxMQ(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetMaxQueuePerChmpxMQ(); +} + +long ChmIMData::GetMaxQueuePerClientMQ(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetMaxQueuePerClientMQ(); +} + +long ChmIMData::GetMQPerClient(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetMQPerClient(); +} + +long ChmIMData::GetMQPerAttach(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetMQPerAttach(); +} + +bool ChmIMData::IsAckMQ(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.IsAckMQ(); +} + +int ChmIMData::GetSockRetryCnt(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSockRetryCnt(); +} + +suseconds_t ChmIMData::GetSockTimeout(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSockTimeout(); +} + +suseconds_t ChmIMData::GetConnectTimeout(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetConnectTimeout(); +} + +int ChmIMData::GetMQRetryCnt(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetMQRetryCnt(); +} + +long ChmIMData::GetMQTimeout(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetMQTimeout(); +} + +time_t ChmIMData::GetMergeTimeout(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetMergeTimeout(); +} + +bool ChmIMData::IsServerMode(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.IsServerMode(); +} + +bool ChmIMData::IsSlaveMode(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.IsSlaveMode(); +} + +long ChmIMData::GetReplicaCount(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return DEFAULT_REPLICA_COUNT; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetReplicaCount(); +} + +long ChmIMData::GetMaxHistoryLogCount(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + // This value is set only at initializing, so not need locking + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetMaxHistoryLogCount(); +} + +chmpxid_t ChmIMData::GetSelfChmpxId(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return CHM_INVALID_CHMPXID; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSelfChmpxId(); +} + +long ChmIMData::GetServerCount(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetServerCount(); +} + +long ChmIMData::GetSlaveCount(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSlaveCount(); +} + +long ChmIMData::GetUpServerCount(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetUpServerCount(); +} + +chmpxpos_t ChmIMData::GetSelfServerPos(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return CHM_INVALID_CHMPXLISTPOS; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSelfServerPos(); +} + +chmpxpos_t ChmIMData::GetNextServerPos(chmpxpos_t startpos, chmpxpos_t nowpos, bool is_skip_self, bool is_cycle) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return CHM_INVALID_CHMPXLISTPOS; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetNextServerPos(startpos, nowpos, is_skip_self, is_cycle); +} + +bool ChmIMData::GetNextServerBase(string& name, chmpxid_t& chmpxid, short& port, short& ctlport) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + chmpxpos_t nextpos; + if(CHM_INVALID_CHMPXLISTPOS == (nextpos = tmpchminfo.GetNextServerPos(GetSelfServerPos(), GetSelfServerPos(), false, true))){ + ERR_CHMPRN("Could not get next chmpx pos in server list."); + return false; + } + return tmpchminfo.GetServerBase(nextpos, name, chmpxid, port, ctlport); +} + +chmpxid_t ChmIMData::GetNextServerChmpxId(void) const +{ + string name; + chmpxid_t chmpxid = CHM_INVALID_CHMPXID; + short port; + short ctlport; + if(!GetNextServerBase(name, chmpxid, port, ctlport)){ + return CHM_INVALID_CHMPXID; + } + return chmpxid; +} + +chmpxid_t ChmIMData::GetNextRingChmpxId(chmpxid_t chmpxid) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return CHM_INVALID_CHMPXID; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetNextRingChmpxId(chmpxid); +} + +bool ChmIMData::IsServerChmpxId(chmpxid_t chmpxid) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.IsServerChmpxId(chmpxid); +} + +chmpxid_t ChmIMData::GetChmpxIdBySock(int sock, int type) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return CHM_INVALID_CHMPXID; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetChmpxIdBySock(sock, type); +} + +chmpxid_t ChmIMData::GetChmpxIdByToServerName(const char* hostname, short ctlport) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return CHM_INVALID_CHMPXID; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetChmpxIdByToServerName(hostname, ctlport); +} + +chmpxid_t ChmIMData::GetChmpxIdByStatus(chmpxsts_t status, bool part_match) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return CHM_INVALID_CHMPXID; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetChmpxIdByStatus(status, part_match); +} + +chmpxid_t ChmIMData::GetRandomServerChmpxId(bool is_up_servers, bool without_suspend) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return CHM_INVALID_CHMPXID; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetRandomServerChmpxId(is_up_servers, without_suspend); +} + +chmpxid_t ChmIMData::GetServerChmpxIdByHash(chmhash_t hash) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return CHM_INVALID_CHMPXID; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetServerChmpxIdByHash(hash); +} + +bool ChmIMData::GetServerChmHashsByHashs(chmhash_t hash, chmhashlist_t& basehashs, bool with_pending, bool without_down, bool without_suspend) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetServerChmHashsByHashs(hash, basehashs, with_pending, without_down, without_suspend); +} + +bool ChmIMData::GetServerChmpxIdByHashs(chmhash_t hash, chmpxidlist_t& chmpxids, bool with_pending, bool without_down, bool without_suspend) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetServerChmpxIdByHashs(hash, chmpxids, with_pending, without_down, without_suspend); +} + +// +// [NOTE] +// This method returns special chmpxid list for merging. +// The server which is assigned main base hash has replicated another server's base hash. +// And another server has this base hash too. +// So this method returns all server chmpxid list which are related to this base hash's server. +// +long ChmIMData::GetServerChmpxIdByBaseHash(chmhash_t basehash, chmpxidlist_t& chmpxids) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + long replcnt = tmpchminfo.GetReplicaCount(); + chmhash_t max_base = 0; // not use + chmhash_t max_pending = 0; + tmpchminfo.GetMaxHashCount(max_base, max_pending); + + chmhash_t another_basehash; + if(static_cast(replcnt) <= basehash){ + another_basehash = basehash - static_cast(replcnt); + }else{ + another_basehash = basehash + max_base - static_cast(replcnt); + } + + // get two type chmpxid list(without pending, with down server, with suspend server) + chmpxidlist_t another_chmpxids; + tmpchminfo.GetServerChmpxIdByHashs(basehash, chmpxids, false, true, true); + tmpchminfo.GetServerChmpxIdByHashs(another_basehash, another_chmpxids, false, true, true); + + // merge two list to result list. + bool found; + for(chmpxidlist_t::const_iterator iter1 = another_chmpxids.begin(); iter1 != another_chmpxids.end(); ++iter1){ + found = false; + for(chmpxidlist_t::const_iterator iter2 = chmpxids.begin(); iter2 != chmpxids.end(); ++iter2){ + if((*iter1) == (*iter2)){ + found = true; + break; + } + } + if(!found){ + chmpxids.push_back(*iter1); + } + } + return chmpxids.size(); +} + +long ChmIMData::GetServerChmpxIdForMerge(chmpxidlist_t& list) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetServerChmpxIds(list, false, true, true); // without pending, without down server, without suspend server +} + +long ChmIMData::GetServerChmpxIds(chmpxidlist_t& list) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetServerChmpxIds(list, true, true, true); // with pending, without down server, without suspend server +} + +bool ChmIMData::GetServerBase(long pos, string& name, chmpxid_t& chmpxid, short& port, short& ctlport) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetServerBase(pos, name, chmpxid, port, ctlport); +} + +bool ChmIMData::GetServerBase(chmpxid_t chmpxid, std::string& name, short& port, short& ctlport) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetServerBase(chmpxid, name, port, ctlport); +} + +bool ChmIMData::GetServerBase(chmpxid_t chmpxid, CHMPXSSL& ssl) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetServerBase(chmpxid, ssl); +} + +bool ChmIMData::GetServerSocks(chmpxid_t chmpxid, socklist_t& socklist, int& ctlsock) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetServerSocks(chmpxid, socklist, ctlsock); +} + +bool ChmIMData::GetServerHash(chmpxid_t chmpxid, chmhash_t& base, chmhash_t& pending) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetServerHash(chmpxid, base, pending); +} + +bool ChmIMData::GetMaxHashCount(chmhash_t& basehash, chmhash_t& pendinghash) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetMaxHashCount(basehash, pendinghash); +} + +bool ChmIMData::IsPendingExchangeData(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chmhash_t basehash = CHM_INVALID_HASHVAL; + chmhash_t pendinghash = CHM_INVALID_HASHVAL; + chmhash_t max_basehash = CHM_INVALID_HASHVAL; + chmhash_t max_pendinghash = CHM_INVALID_HASHVAL; + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + tmpchminfo.GetMaxHashCount(max_basehash, max_pendinghash); + tmpchminfo.GetSelfHash(basehash, pendinghash); + + return (basehash != pendinghash || max_basehash != max_pendinghash); +} + +long ChmIMData::GetReceiverChmpxids(chmhash_t hash, c2ctype_t c2ctype, chmpxidlist_t& chmpxids) +{ + chmpxid_t selfchmpxid = GetSelfChmpxId(); + + if(IS_C2CTYPE_ROUTING(c2ctype)){ + if(IsRandomDeliver()){ + // Random --> terminal chmpxid is one + chmpxid_t tmpchmpxid; + if(CHM_INVALID_CHMPXID == (tmpchmpxid = GetRandomServerChmpxId(true, true))){ // only up server, not suspend server + ERR_CHMPRN("Could not get random server chmpxid."); + return -1L; + } + chmpxids.push_back(tmpchmpxid); + }else{ + // from hash --> terminal chmpxid is some + chmpxidlist_t tmpchmpxids; + if(!GetServerChmpxIdByHashs(hash, tmpchmpxids)){ + ERR_CHMPRN("Could not get chmpxid by hash(0x%016" PRIx64 ").", hash); + return -1L; + } + + // set first chmpxid + chmpxid_t tmpchmpxid = CHM_INVALID_CHMPXID; + do{ + tmpchmpxid = tmpchmpxids.front(); + // except self chmpxid + if(IS_C2CTYPE_NOT_SELF(c2ctype)){ + if(selfchmpxid == tmpchmpxid){ + tmpchmpxid = CHM_INVALID_CHMPXID; + } + } + }while(tmpchmpxid == CHM_INVALID_CHMPXID && 0 < tmpchmpxids.size()); + + if(CHM_INVALID_CHMPXID == tmpchmpxid){ + ERR_CHMPRN("Could not get chmpxid by hash(0x%016" PRIx64 ") because target chmpxid is probabry down or suspend.", hash); + return -1L; + } + + // one chmpxid is set + chmpxids.push_back(tmpchmpxid); + } + + }else if(IS_C2CTYPE_BROADCAST(c2ctype)){ + // any type --> terminal chmpxid is all + if(!GetServerChmpxIds(chmpxids)){ + ERR_CHMPRN("Could not get all server chmpxids."); + return -1L; + } + // except self chmpxid + if(IS_C2CTYPE_NOT_SELF(c2ctype)){ + for(chmpxidlist_t::iterator iter = chmpxids.begin(); iter != chmpxids.end(); ){ + if(selfchmpxid == *iter){ + iter = chmpxids.erase(iter); + continue; + } + ++iter; + } + } + + }else if(IS_C2CTYPE_RBROADCAST(c2ctype)){ + // any type --> terminal chmpxid is all routing chmpxids for hash value + + // get target chmpxids + if(!GetServerChmpxIdByHashs(hash, chmpxids, true, true, true)){ // with pending, without down and suspend + ERR_CHMPRN("Could not get chmpxid by hash(0x%016" PRIx64 ") because all target chmpxid is probabry down or suspend.", hash); + return -1L; + } + // except self chmpxid + if(IS_C2CTYPE_NOT_SELF(c2ctype)){ + for(chmpxidlist_t::iterator iter = chmpxids.begin(); iter != chmpxids.end(); ){ + if(selfchmpxid == *iter){ + iter = chmpxids.erase(iter); + continue; + } + ++iter; + } + } + + }else{ // COM_C2C_NORMAL or COM_C2C_IGNORE + if(!IS_C2CTYPE_NORMAL(c2ctype)){ + WAN_CHMPRN("COM_C2C type(%s) should be COM_C2C_NORMAL, so continue as COM_C2C_NORMAL.", STRCOMC2CTYPE(c2ctype)); + } + + chmpxid_t tmpchmpxid = CHM_INVALID_CHMPXID; + if(IsRandomDeliver()){ + // Random --> terminal chmpxid is one + if(CHM_INVALID_CHMPXID == (tmpchmpxid = GetRandomServerChmpxId(true, true))){ // only up server, not suspend server + ERR_CHMPRN("Could not get random server chmpxid."); + return -1L; + } + }else{ + // from hash --> terminal chmpxid is one + if(CHM_INVALID_CHMPXID == (tmpchmpxid = GetServerChmpxIdByHash(hash))){ + // could not get main target hash chmpx(down or suspend), + // so try to get another chmpx when hash & replication mode. + // + if(0 < GetReplicaCount()){ + chmpxidlist_t tmpchmpxids; + // find chmpxs assigned by target hash + if(!GetServerChmpxIdByHashs(hash, tmpchmpxids, false) || 0 == tmpchmpxids.size()){ // without assigned pending hash + if(!GetServerChmpxIdByHashs(hash, tmpchmpxids, true) || 0 == tmpchmpxids.size()){ // without assign pending hash + ERR_CHMPRN("Could not get chmpxid by hash(0x%016" PRIx64 ") because target chmpxid is probabry down or suspend.", hash); + return -1L; + } + } + // set first chmpxid + do{ + tmpchmpxid = tmpchmpxids.front(); + // except self chmpxid + if(IS_C2CTYPE_NOT_SELF(c2ctype)){ + if(selfchmpxid == tmpchmpxid){ + tmpchmpxid = CHM_INVALID_CHMPXID; + } + } + }while(tmpchmpxid == CHM_INVALID_CHMPXID && 0 < tmpchmpxids.size()); + + }else{ + ERR_CHMPRN("Replica count is invalid, so could not get chmpxid by hash(0x%016" PRIx64 ").", hash); + return -1L; + } + } + } + if(CHM_INVALID_CHMPXID == tmpchmpxid){ + ERR_CHMPRN("Could not get chmpxid by hash(0x%016" PRIx64 ") because target chmpxid is probabry down or suspend.", hash); + return -1L; + } + // one chmpxid is set + chmpxids.push_back(tmpchmpxid); + } + return static_cast(chmpxids.size()); +} + +chmpxsts_t ChmIMData::GetServerStatus(chmpxid_t chmpxid) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return CHMPXSTS_SRVOUT_DOWN_NORMAL; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetServerStatus(chmpxid); +} + +bool ChmIMData::GetSelfPorts(short& port, short& ctlport) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSelfPorts(port, ctlport); +} + +bool ChmIMData::GetSelfSocks(int& sock, int& ctlsock) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSelfSocks(sock, ctlsock); +} + +bool ChmIMData::GetSelfHash(chmhash_t& base, chmhash_t& pending) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSelfHash(base, pending); +} + +chmpxsts_t ChmIMData::GetSelfStatus(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return CHMPXSTS_SLAVE_DOWN_NORMAL; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSelfStatus(); +} + +bool ChmIMData::GetSelfSsl(CHMPXSSL& ssl) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSelfSsl(ssl); +} + +long ChmIMData::GetSlaveChmpxIds(chmpxidlist_t& list) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSlaveChmpxIds(list); +} + +bool ChmIMData::GetSlaveBase(chmpxid_t chmpxid, std::string& name, short& ctlport) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSlaveBase(chmpxid, name, ctlport); +} + +bool ChmIMData::GetSlaveSock(chmpxid_t chmpxid, socklist_t& socklist) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSlaveSock(chmpxid, socklist); +} + +chmpxsts_t ChmIMData::GetSlaveStatus(chmpxid_t chmpxid) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return CHMPXSTS_SLAVE_DOWN_NORMAL; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetSlaveStatus(chmpxid); +} + +bool ChmIMData::SetServerSocks(chmpxid_t chmpxid, int sock, int ctlsock, int type) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.SetServerSocks(chmpxid, sock, ctlsock, type); +} + +bool ChmIMData::RemoveServerSock(chmpxid_t chmpxid, int sock) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.RemoveServerSock(chmpxid, sock); +} + +bool ChmIMData::SetServerHash(chmpxid_t chmpxid, chmhash_t base, chmhash_t pending, int type) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.SetServerHash(chmpxid, base, pending, type); +} + +bool ChmIMData::SetServerStatus(chmpxid_t chmpxid, chmpxsts_t status) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.SetServerStatus(chmpxid, status); +} + +bool ChmIMData::IsOperating(void) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.IsOperating(); +} + +bool ChmIMData::UpdatePendingHash(bool is_allow_operating) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.UpdatePendingHash(is_allow_operating); +} + +bool ChmIMData::SetSelfSocks(int sock, int ctlsock) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.SetSelfSocks(sock, ctlsock); +} + +bool ChmIMData::SetSelfHash(chmhash_t base, chmhash_t pending, int type) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.SetSelfHash(base, pending, type); +} + +bool ChmIMData::SetSelfStatus(chmpxsts_t status) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.SetSelfStatus(status); +} + +bool ChmIMData::SetSlaveBase(chmpxid_t chmpxid, const char* hostname, short ctlport, const PCHMPXSSL pssl) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.SetSlaveBase(chmpxid, hostname, ctlport, pssl); +} + +bool ChmIMData::SetSlaveSock(chmpxid_t chmpxid, int sock) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.SetSlaveSock(chmpxid, sock); +} + +bool ChmIMData::SetSlaveStatus(chmpxid_t chmpxid, chmpxsts_t status) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.SetSlaveStatus(chmpxid, status); +} + +// [NOTE] +// Must call this method instead of SetSlaveBase and SetSlaveSock and SetSlaveStatus. +// Because we run chmpx on multi-thread, so this method processes these function with locking. +// +bool ChmIMData::SetSlaveAll(chmpxid_t chmpxid, const char* hostname, short ctlport, const PCHMPXSSL pssl, int sock, chmpxsts_t status) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + + // check for existing + string tmpname; + short tmpctlport = CHM_INVALID_PORT; + if(!tmpchminfo.GetSlaveBase(chmpxid, tmpname, tmpctlport) || tmpname != hostname || tmpctlport != ctlport){ + // there is no same slave in chmshm, so set new slave. + if(!tmpchminfo.SetSlaveBase(chmpxid, hostname, ctlport, pssl)){ + return false; + } + }else{ + // found same slave in chmshm + } + // set sock & status + if(!tmpchminfo.SetSlaveSock(chmpxid, sock) || !tmpchminfo.SetSlaveStatus(chmpxid, status)){ + return false; + } + return true; +} + +bool ChmIMData::RemoveSlaveSock(chmpxid_t chmpxid, int sock) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.RemoveSlaveSock(chmpxid, sock); +} + +bool ChmIMData::RemoveSlave(chmpxid_t chmpxid) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.RemoveSlave(chmpxid, eqfd); +} + +//--------------------------------------------------------- +// Methods for stat +//--------------------------------------------------------- +bool ChmIMData::AddStat(chmpxid_t chmpxid, bool is_sent, size_t bodylength, const struct timespec& elapsed_time) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.AddStat(chmpxid, is_sent, bodylength, elapsed_time); +} + +bool ChmIMData::GetStat(PCHMSTAT pserver, PCHMSTAT pslave) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetStat(pserver, pslave); +} + +//--------------------------------------------------------- +// Methods for Trace(History) +//--------------------------------------------------------- +long ChmIMData::GetTraceCount(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return 0L; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chmloglap tmpchmlog(&pChmShm->chmpxlog, pChmShm); + return tmpchmlog.GetHistoryCount(); +} + +bool ChmIMData::IsTraceEnable(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + // This method is called many times, but there is little that this value is changed. + //ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chmloglap tmpchmlog(&pChmShm->chmpxlog, pChmShm); + return tmpchmlog.IsEnable(); +} + +bool ChmIMData::EnableTrace(void) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chmloglap tmpchmlog(&pChmShm->chmpxlog, pChmShm); + return tmpchmlog.Enable(); +} + +bool ChmIMData::DisableTrace(void) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chmloglap tmpchmlog(&pChmShm->chmpxlog, pChmShm); + return tmpchmlog.Disable(); +} + +bool ChmIMData::AddTrace(logtype_t logtype, size_t length, const struct timespec& start, const struct timespec& fin) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_WRITE); // Lock + + chmloglap tmpchmlog(&pChmShm->chmpxlog, pChmShm); + return tmpchmlog.Add(logtype, length, start, fin); +} + +bool ChmIMData::GetTrace(PCHMLOGRAW plograwarr, long& arrsize, logtype_t dirmask, logtype_t devmask) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + ChmLock AutoLock(CHMLT_IMDATA, CHMLT_READ); // Lock + + chmloglap tmpchmlog(&pChmShm->chmpxlog, pChmShm); + return tmpchmlog.Get(plograwarr, arrsize, dirmask, devmask); +} + +//--------------------------------------------------------- +// Methods for PIDs +//--------------------------------------------------------- +// +// Lock Client pid list address in CHMSHM. +// +// Because client_pids and free_pids list are accessed by chmpx and client processes. +// Then we use hard lock those area, but those area does not access often. +// +bool ChmIMData::RawLockClientPidList(FLRwlRcsv& lockobj, bool is_read) const +{ + if(!IsAttachedShm()){ + return false; + } + // get offset + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + off_t offset = reinterpret_cast(tmpchminfo.GetClientPidListOffset()); + + return lockobj.Lock(ShmFd, offset, 1L, is_read); +} + +bool ChmIMData::RetriveClientPid(pid_t pid) +{ + if(CHM_INVALID_PID == pid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + + FLRwlRcsv lockobj; + if(!WriteLockClientPidList(lockobj)){ + return false; + } + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.RetriveClientPid(pid); +} + +bool ChmIMData::AddClientPid(pid_t pid) +{ + if(CHM_INVALID_PID == pid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + + FLRwlRcsv lockobj; + if(!WriteLockClientPidList(lockobj)){ + return false; + } + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.AddClientPid(pid); +} + +bool ChmIMData::GetAllPids(pidlist_t& list) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + + FLRwlRcsv lockobj; + if(!ReadLockClientPidList(lockobj)){ + return false; + } + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.GetAllPids(list); +} + +bool ChmIMData::IsClientPids(void) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + + FLRwlRcsv lockobj; + if(!ReadLockClientPidList(lockobj)){ + return false; + } + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + return tmpchminfo.IsClientPids(); +} + +// +// Lock Chmpx pid address in CHMSHM. +// +bool ChmIMData::LockChmpxPid(void) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + off_t offset; + { + // [NOTICE] now pid address offset is 0. + if(NULL == tmpchminfo.GetChmpxSvrPidAddr(true)){ + ERR_CHMPRN("Could not chmpx pid address(offset) in CHMSHM."); + return false; + } + offset = reinterpret_cast(tmpchminfo.GetChmpxSvrPidAddr(false)); // get offset + } + + return (0 == fullock_rwlock_wrlock(ShmFd, offset, 1L)); +} + +// +// Unlock Chmpx pid address in CHMSHM. +// +bool ChmIMData::UnlockChmpxPid(void) +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + off_t offset; + { + // [NOTICE] now pid address offset is 0. + if(NULL == tmpchminfo.GetChmpxSvrPidAddr(true)){ + ERR_CHMPRN("Could not chmpx pid address(offset) in CHMSHM."); + return false; + } + offset = reinterpret_cast(tmpchminfo.GetChmpxSvrPidAddr(false)); // get offset + } + + return (0 == fullock_rwlock_unlock(ShmFd, offset, 1L)); +} + +// +// Check lock status for Chmpx pid address in CHMSHM. +// +bool ChmIMData::IsChmpxProcessRunning(pid_t& pid) const +{ + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + off_t offset; + { + // [NOTICE] now pid address offset is 0. + if(NULL == tmpchminfo.GetChmpxSvrPidAddr(true)){ + ERR_CHMPRN("Could not chmpx pid address(offset) in CHMSHM."); + return false; + } + offset = reinterpret_cast(tmpchminfo.GetChmpxSvrPidAddr(false)); // get offset + } + + if(!fullock_rwlock_islocked(ShmFd, offset, 1L)){ + MSG_CHMPRN("Chmpx Pid address is not Locked: fd(%d), offset(%jd)", ShmFd, static_cast(offset)); + return false; + } + //MSG_CHMPRN("Chmpx Pid address is Locked: fd(%d), offset(%jd)", ShmFd, static_cast(offset)); + + pid = tmpchminfo.GetChmpxSvrPid(); + + return true; +} + +bool ChmIMData::IsNeedDettach(void) const +{ + pid_t pid = CHM_INVALID_PID; + if(IsChmpxProcessRunning(pid) && ChmpxPid == pid){ + //MSG_CHMPRN("Not need to dettach chmshm."); + return false; + } + MSG_CHMPRN("Need to dettach chmshm."); + return true; +} + +//--------------------------------------------------------- +// Methods for Others +//--------------------------------------------------------- +bool ChmIMData::IsAllowHostname(const char* hostname, const short* pport, PCHMPXSSL* ppssl) +{ + if(CHMEMPTYSTR(hostname)){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!IsAttachedShm()){ + ERR_CHMPRN("There is no attached ChmShm."); + return false; + } + + // for cache key + string strhname(hostname); + if(pport && CHM_INVALID_PORT != *pport){ + strhname += ":"; + strhname += to_string(*pport); + } + + // check cache + if(hnamesslmap.end() != hnamesslmap.find(strhname)){ + // found in cache + if(ppssl){ + *ppssl = hnamesslmap[strhname]; + } + return true; + } + + // check name in server node list. + chminfolap tmpchminfo(&pChmShm->info, pChmShm); + hnamesslmap_t info; + if(!tmpchminfo.GetAllServerName(info)){ + ERR_CHMPRN("Could not get server node names."); + FREE_HNAMESSLMAP(info); + return false; + } + if(info.end() != info.find(strhname)){ + // found & set cache + hnamesslmap[strhname] = info[strhname]; + if(ppssl){ + *ppssl = hnamesslmap[strhname]; + } + info[strhname] = NULL; + FREE_HNAMESSLMAP(info); + return true; + } + FREE_HNAMESSLMAP(info); + + // check name in configration server/slave list. + // + // [NOTE] + // At first using hostname in list(means useing cache), next check DNS for server name if the first checking failed. + // + CHMNODE_CFGINFO nodeinfo; + if(pConfObj->GetNodeInfo(hostname, pport ? *pport : CHM_INVALID_PORT, nodeinfo, false, false) || pConfObj->GetNodeInfo(hostname, pport ? *pport : CHM_INVALID_PORT, nodeinfo, false, true)){ + // found + if(pport){ + PCHMPXSSL pssl = new CHMPXSSL; + CVT_SSL_STRUCTURE(*pssl, nodeinfo); + hnamesslmap[strhname] = pssl; + }else{ + hnamesslmap[strhname] = NULL; + } + if(ppssl){ + *ppssl = hnamesslmap[strhname]; + } + return true; + } + return false; +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmimdata.h b/lib/chmimdata.h new file mode 100644 index 0000000..71c2d34 --- /dev/null +++ b/lib/chmimdata.h @@ -0,0 +1,282 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMIMDATA_H +#define CHMIMDATA_H + +#include +#include +#include + +#include "chmstructure.tcc" + +//--------------------------------------------------------- +// Prototype +//--------------------------------------------------------- +class CHMConf; + +//--------------------------------------------------------- +// Structures +//--------------------------------------------------------- +typedef struct chminfo_ex{ + PCHMINFO pchminfo; + char shmpath[CHM_MAX_PATH_LEN]; + size_t shmsize; + char k2hashpath[CHM_MAX_PATH_LEN]; +}CHMINFOEX, *PCHMINFOEX; + +//--------------------------------------------------------- +// Class ChmIMData +//--------------------------------------------------------- +// Chm Internal Management Data Class +// +class ChmIMData +{ + protected: + typedef enum mkfpath_mode{ + MKFILEPATH_SHM = 0, + MKFILEPATH_K2H + }MKFPMODE; + + protected: + static const int SYSPAGE_SIZE = 4096; + + std::string ShmPath; // ChmShm file path + PCHMSHM pChmShm; // SHM mmap base + int ShmFd; // SHM fd + size_t ShmSize; // SHM size + CHMConf* pConfObj; // CHMConf file object + K2HShm* pK2hash; // K2HASH object + int eqfd; // backup + bool isChmpxProc; // CHMPX process or Client library + pid_t ChmpxPid; // backup chmpx pid for checking process down on slave + hnamesslmap_t hnamesslmap; // Cache for hostname:ctlport mapping(using for checking existed host:port). + + protected: + static bool MakeFilePath(const char* groupname, short port, MKFPMODE mode, std::string& shmpath); + static bool MakeShmFilePath(const char* groupname, short port, std::string& shmpath); + static bool MakeK2hashFilePath(const char* groupname, short port, std::string& shmpath); + static bool CompareChmpxSvrs(PCHMPXSVR pbase, long bcount, PCHMPXSVR pmerge, long mcount, bool is_status = true); + + off_t GetLockOffsetForMQ(void) const; + bool CloseShm(void); + bool InitializeShm(void); + bool InitializeShmEx(const CHMCFGINFO* pchmcfg, const CHMNODE_CFGINFO* pself); + bool IsAttachedShm(void) const { return (NULL != pChmShm); } + bool AttachShm(void); // For client process library + bool InitializeOther(void); + bool CloseK2hash(void); + bool InitializeK2hash(void); + bool IsAttachedK2hash(void) const { return (NULL != pK2hash); } + bool AttachK2hash(void); // For client process library + + msgid_t AssignMsg(bool is_chmpx, bool is_activated); // Assign MQ + bool ActivateMsgEx(msgid_t msgid, bool is_activate); // set msgid activated status + msgid_t GetRandomMsgId(bool is_chmpx); + + // Process running + bool LockChmpxPid(void); + bool UnlockChmpxPid(void); + bool IsChmpxProcessRunning(pid_t& pid) const; + + // Client pid list + bool RawLockClientPidList(FLRwlRcsv& lockobj, bool is_read) const; + bool ReadLockClientPidList(FLRwlRcsv& lockobj) const { return RawLockClientPidList(lockobj, true); } + bool WriteLockClientPidList(FLRwlRcsv& lockobj) const { return RawLockClientPidList(lockobj, false); } + + // Do not call these method, must call SetSlaveAll + bool SetSlaveBase(chmpxid_t chmpxid, const char* hostname, short ctlport, const PCHMPXSSL pssl); + bool SetSlaveSock(chmpxid_t chmpxid, int sock); + bool SetSlaveStatus(chmpxid_t chmpxid, chmpxsts_t status); + + bool RemoveSlave(chmpxid_t chmpxid); + + public: + static void FreeDupAllChmInfo(PCHMINFOEX ptr); // for free memory allocated by DupAllChmInfo() + static void FreeDupSelfChmpxInfo(PCHMPX ptr); // for free memory allocated by DupSelfChmpxInfo() + + ChmIMData(bool is_chmpx_proc = true); + virtual ~ChmIMData(); + + bool Close(void); + bool CloseSocks(int type = CLOSETG_BOTH); + bool IsInitialized(void) const { return (pChmShm && CHM_INVALID_HANDLE != ShmFd && pConfObj && pK2hash); } + bool Initialize(CHMConf* cfgobj, int eventqfd, bool is_chmpx_proc); + bool ReloadConfigration(void); // now reload without server/slave list + const CHMSHM* GetShmBase(void) const { return pChmShm; } + const char* GetShmPath(void) const { return ShmPath.c_str(); } + K2HShm* GetK2hashObj(void) { return pK2hash; } + + bool Dump(std::stringstream& sstream) const; + bool DumpSelfChmpxSvr(std::stringstream& sstream) const; + PCHMINFOEX DupAllChmInfo(void); + PCHMPX DupSelfChmpxInfo(void); + + // MQ + msgid_t GetBaseMsgId(void) const; + bool FreeMsg(msgid_t msgid); // Free msgid + bool IsMsgidActivated(msgid_t msgid) const; + bool FreeMsgs(const pidlist_t& pidlist, msgidlist_t& freedmsgids); // Free msgids by pid + msgid_t AssignMsgOnChmpx(void) { return AssignMsg(true, true); } + msgid_t AssignMsgOnServer(void) { return AssignMsg(false, true); } + msgid_t AssignMsgOnSlave(bool is_activated = true) { return AssignMsg(false, is_activated); } + bool ActivateMsg(msgid_t msgid) { return ActivateMsgEx(msgid, true); } // Activate Msgid + bool DisactivateMsg(msgid_t msgid) { return ActivateMsgEx(msgid, false); } // Disactivate Msgid + msgid_t GetRandomChmpxMsgId(void) { return GetRandomMsgId(true); } // get msgid from assigned list for chmpx + msgid_t GetRandomClientMsgId(void) { return GetRandomMsgId(false); } // get msgid from assigned list for not chmpx + bool GetMsgidListByPid(pid_t pid, msgidlist_t& list); // get all msgid list by pid + bool GetMsgidListByUniqPid(msgidlist_t& list); // get msgid list for all uniq pid + + // CHMSHM + bool GetSelfChmpxSvr(PCHMPXSVR chmpxsvr) const; + bool GetChmpxSvr(chmpxid_t chmpxid, PCHMPXSVR chmpxsvr) const; + bool GetChmpxSvrs(PCHMPXSVR* ppchmpxsvrs, long& count) const; + bool CompareChmpxSvrs(PCHMPXSVR pchmpxsvrs, long count); + bool MergeChmpxSvrs(PCHMPXSVR pchmpxsvrs, long count, bool is_remove = true, bool is_init_process = false, int eqfd = CHM_INVALID_HANDLE); + bool MergeChmpxSvrsForStatusUpdate(PCHMPXSVR pchmpxsvrs, long count, int eqfd); + + bool GetGroup(std::string& group) const; + bool IsRandomDeliver(void) const; + bool IsAutoMergeConf(void) const; + bool IsDoMergeConf(void) const; + int GetSocketThreadCount(void) const; + int GetMQThreadCount(void) const; + int GetMaxSockPool(void) const; + time_t GetSockPoolTimeout(void) const; + long GetMaxMQCount(void) const; + long GetChmpxMQCount(void) const; + long GetMaxQueuePerChmpxMQ(void) const; + long GetMaxQueuePerClientMQ(void) const; + long GetMQPerClient(void) const; + long GetMQPerAttach(void) const; + bool IsAckMQ(void) const; + int GetSockRetryCnt(void) const; + suseconds_t GetSockTimeout(void) const; + suseconds_t GetConnectTimeout(void) const; + int GetMQRetryCnt(void) const; + long GetMQTimeout(void) const; + time_t GetMergeTimeout(void) const; + bool IsServerMode(void) const; + bool IsSlaveMode(void) const; + long GetReplicaCount(void) const; + long GetMaxHistoryLogCount(void) const; + bool IsChmpxProcess(void) const { return isChmpxProc; } + chmpxid_t GetSelfChmpxId(void) const; + + long GetServerCount(void) const; + long GetSlaveCount(void) const; + long GetUpServerCount(void) const; + chmpxpos_t GetSelfServerPos(void) const; + chmpxpos_t GetNextServerPos(chmpxpos_t startpos, chmpxpos_t nowpos, bool is_skip_self, bool is_cycle) const; + bool GetNextServerBase(std::string& name, chmpxid_t& chmpxid, short& port, short& ctlport) const; + chmpxid_t GetNextServerChmpxId(void) const; + chmpxid_t GetNextRingChmpxId(chmpxid_t chmpxid = CHM_INVALID_CHMPXID) const; + + bool IsOperating(void); + bool IsServerChmpxId(chmpxid_t chmpxid) const; + chmpxid_t GetChmpxIdBySock(int sock, int type = CLOSETG_BOTH) const; + chmpxid_t GetChmpxIdByToServerSock(int sock) const { return GetChmpxIdBySock(sock, CLOSETG_SERVERS); } + chmpxid_t GetChmpxIdByFromSlaveSock(int sock) const { return GetChmpxIdBySock(sock, CLOSETG_SLAVES); } + chmpxid_t GetChmpxIdByToServerName(const char* hostname, short ctlport) const; + chmpxid_t GetChmpxIdByStatus(chmpxsts_t status, bool part_match = false) const; + chmpxid_t GetRandomServerChmpxId(bool is_up_servers = false, bool without_suspend = false); + chmpxid_t GetServerChmpxIdByHash(chmhash_t hash) const; + bool GetServerChmHashsByHashs(chmhash_t hash, chmhashlist_t& basehashs, bool with_pending = true, bool without_down = true, bool without_suspend = true); + bool GetServerChmpxIdByHashs(chmhash_t hash, chmpxidlist_t& chmpxids, bool with_pending = true, bool without_down = true, bool without_suspend = true); + long GetServerChmpxIdByBaseHash(chmhash_t basehash, chmpxidlist_t& chmpxids) const; + long GetServerChmpxIdForMerge(chmpxidlist_t& list) const; + long GetServerChmpxIds(chmpxidlist_t& list) const; + bool GetServerBase(long pos, std::string& name, chmpxid_t& chmpxid, short& port, short& ctlport) const; + bool GetServerBase(chmpxid_t chmpxid, std::string& name, short& port, short& ctlport) const; + bool GetServerBase(chmpxid_t chmpxid, CHMPXSSL& ssl) const; + bool GetServerSocks(chmpxid_t chmpxid, socklist_t& socklist, int& ctlsock) const; + bool GetServerSock(chmpxid_t chmpxid, socklist_t& socklist) const { int tmp; return GetServerSocks(chmpxid, socklist, tmp); } + bool IsConnectServer(chmpxid_t chmpxid) const { socklist_t tmpsocklist; return (GetServerSock(chmpxid, tmpsocklist) && 0 < tmpsocklist.size()); } + bool GetServerCtlSock(chmpxid_t chmpxid, int& ctlsock) const { socklist_t tmplist; return GetServerSocks(chmpxid, tmplist, ctlsock); } + bool GetServerHash(chmpxid_t chmpxid, chmhash_t& base, chmhash_t& pending) const; + bool GetServerBaseHash(chmpxid_t chmpxid, chmhash_t& hash) const { chmhash_t tmp; return GetServerHash(chmpxid, hash, tmp); } + bool GetServerPendingHash(chmpxid_t chmpxid, chmhash_t& hash) const { chmhash_t tmp; return GetServerHash(chmpxid, tmp, hash); } + bool GetMaxHashCount(chmhash_t& basehash, chmhash_t& pendinghash) const; + bool GetMaxBaseHashCount(chmhash_t& basehash) const { chmhash_t tmp; return GetMaxHashCount(basehash, tmp); } + bool GetMaxPendingHashCount(chmhash_t& pendinghash) const { chmhash_t tmp; return GetMaxHashCount(tmp, pendinghash); } + bool IsPendingExchangeData(void) const; + long GetReceiverChmpxids(chmhash_t hash, c2ctype_t c2ctype, chmpxidlist_t& chmpxids); + + chmpxsts_t GetServerStatus(chmpxid_t chmpxid) const; + bool GetSelfPorts(short& port, short& ctlport) const; + bool GetSelfSocks(int& sock, int& ctlsock) const; + bool GetSelfHash(chmhash_t& base, chmhash_t& pending) const; + bool GetSelfBaseHash(chmhash_t& hash) const { chmhash_t tmp; return GetSelfHash(hash, tmp); } + bool GetSelfPendingHash(chmhash_t& hash) const { chmhash_t tmp; return GetSelfHash(tmp, hash); } + chmpxsts_t GetSelfStatus(void) const; + bool GetSelfSsl(CHMPXSSL& ssl) const; + long GetSlaveChmpxIds(chmpxidlist_t& list) const; + bool GetSlaveBase(chmpxid_t chmpxid, std::string& name, short& ctlport) const; + bool GetSlaveSock(chmpxid_t chmpxid, socklist_t& socklist) const; + bool IsConnectSlave(chmpxid_t chmpxid) const { socklist_t tmplist; return (GetSlaveSock(chmpxid, tmplist) && 0 < tmplist.size()); } + chmpxsts_t GetSlaveStatus(chmpxid_t chmpxid) const; + + bool SetServerSocks(chmpxid_t chmpxid, int sock, int ctlsock, int type); + bool SetServerHash(chmpxid_t chmpxid, chmhash_t base, chmhash_t pending, int type); + bool SetServerBaseHash(chmpxid_t chmpxid, chmhash_t hash) { return SetServerHash(chmpxid, hash, 0L, HASHTG_BASE); } + bool SetServerPendingHash(chmpxid_t chmpxid, chmhash_t hash) { return SetServerHash(chmpxid, 0L, hash, HASHTG_PENDING); } + bool SetServerStatus(chmpxid_t chmpxid, chmpxsts_t status); + bool RemoveServerSock(chmpxid_t chmpxid, int sock); + bool UpdatePendingHash(bool is_allow_operating = true); + bool SetSelfSocks(int sock, int ctlsock); + bool SetSelfHash(chmhash_t base, chmhash_t pending, int type); + bool SetSelfBaseHash(chmhash_t hash) { return SetSelfHash(hash, 0L, HASHTG_BASE); } + bool SetSelfPendingHash(chmhash_t hash) { return SetSelfHash(0L, hash, HASHTG_PENDING); } + bool SetSelfStatus(chmpxsts_t status); + bool SetSlaveAll(chmpxid_t chmpxid, const char* hostname, short ctlport, const PCHMPXSSL pssl, int sock, chmpxsts_t status); + bool RemoveSlaveSock(chmpxid_t chmpxid, int sock); + + // Stats + bool AddStat(chmpxid_t chmpxid, bool is_sent, size_t bodylength, const struct timespec& elapsed_time); + bool GetStat(PCHMSTAT pserver, PCHMSTAT pslave) const; + + // Trace(History) + long GetTraceCount(void) const; + bool IsTraceEnable(void) const; + bool EnableTrace(void); + bool DisableTrace(void); + bool AddTrace(logtype_t logtype, size_t length, const struct timespec& start, const struct timespec& fin); + bool GetTrace(PCHMLOGRAW plograwarr, long& arrsize, logtype_t dirmask, logtype_t devmask) const; + + // Process running + bool RetriveClientPid(pid_t pid); + bool AddClientPid(pid_t pid); + bool GetAllPids(pidlist_t& list); + bool IsClientPids(void) const; + bool IsNeedDettach(void) const; + + // Others + bool IsAllowHostname(const char* hostname, const short* pport = NULL, PCHMPXSSL* ppssl = NULL); +}; + +#endif // CHMIMDATA_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmkvp.cc b/lib/chmkvp.cc new file mode 100644 index 0000000..daefff9 --- /dev/null +++ b/lib/chmkvp.cc @@ -0,0 +1,419 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include +#include + +#include "chmcommon.h" +#include "chmpx.h" +#include "chmkvp.h" +#include "chmhash.h" +#include "chmutil.h" +#include "chmdbg.h" + +using namespace std; + +//--------------------------------------------------------- +// ChmBinData class +//--------------------------------------------------------- +ChmBinData::ChmBinData(unsigned char* bydata, size_t bylength, bool is_duplicate) : byptr(NULL), length(0L), is_allocate(false) +{ + if(bydata && 0 < bylength){ + Set(bydata, bylength, is_duplicate); + } +} + +ChmBinData::ChmBinData(PCHMBIN pchmbin, bool is_duplicate) : byptr(NULL), length(0L), is_allocate(false) +{ + Set(pchmbin, is_duplicate); +} + +ChmBinData::~ChmBinData() +{ + Clear(); +} + +void ChmBinData::Clear(void) +{ + if(is_allocate){ + CHM_Free(byptr); + } + byptr = NULL; + length = 0L; + is_allocate = false; +} + +const unsigned char* ChmBinData::Get(size_t* plength) const +{ + if(plength){ + *plength = length; + } + return byptr; +} + +const char* ChmBinData::Get(void) const +{ + if(byptr && 0 < length && '\0' != byptr[length - 1]){ + // not string + return NULL; + } + return reinterpret_cast(byptr); +} + +bool ChmBinData::Set(unsigned char* bydata, size_t bylength, bool is_duplicate) +{ + Clear(); + + if(bydata && 0 < bylength){ + if(is_duplicate){ + if(NULL == (byptr = reinterpret_cast(malloc(bylength)))){ + ERR_CHMPRN("Could not allocate memory."); + return false; + }else{ + memcpy(byptr, bydata, bylength); + length = bylength; + is_allocate = true; + } + }else{ + byptr = bydata; + length = bylength; + } + } + return true; +} + +bool ChmBinData::Set(PCHMBIN pchmbin, bool is_duplicate) +{ + Clear(); + + if(!pchmbin || !pchmbin->byptr || 0L == pchmbin->length){ + return false; + } + return Set(pchmbin->byptr, pchmbin->length, is_duplicate); +} + +// +// After calling this method, byptr is always allocated. +// +bool ChmBinData::Overwrite(unsigned char* bydata, size_t bylength, off_t offset) +{ + if(!bydata || 0 == bylength){ + MSG_CHMPRN("Parameter is wrong, so nothing copy."); + return true; + } + + if(0 >= (static_cast(bylength) + offset)){ + MSG_CHMPRN("Parameters(offset + bylength) is short."); + return true; + } + + // new size + size_t newlen; + if(length < static_cast(offset + static_cast(bylength))){ + newlen = static_cast(offset) + bylength; + }else{ + newlen = length; + } + + // allocate + if(newlen != length || !is_allocate){ + if(!is_allocate){ + unsigned char* newptr; + if(NULL == (newptr = reinterpret_cast(malloc(newlen)))){ + ERR_CHMPRN("Could not allocate memory."); + return false; + } + memcpy(newptr, byptr, length); + byptr = newptr; + is_allocate = true; + }else{ + if(NULL == (byptr = reinterpret_cast(realloc(byptr, newlen)))){ + ERR_CHMPRN("Could not allocate memory."); + return false; + } + } + } + + // copy + if(0 <= offset){ + memcpy(&byptr[offset], bydata, bylength); + }else{ + memcpy(byptr, &bydata[imaxabs(static_cast(offset))], static_cast(bylength) + offset); + } + return true; +} + +bool ChmBinData::Overwrite(PCHMBIN pchmbin, off_t offset) +{ + if(!pchmbin){ + return false; + } + return Overwrite(pchmbin->byptr, pchmbin->length, offset); +} + +bool ChmBinData::Load(unsigned char* bydata, bool is_cvt_ntoh, bool is_duplicate) +{ + Clear(); + + if(!bydata){ + return true; + } + size_t datapos = sizeof(size_t); + size_t* ptmplen = reinterpret_cast(bydata); + + if(is_cvt_ntoh){ + length = be64toh(*ptmplen); // To host byte order + }else{ + length = *ptmplen; + } + if(0 < length){ + if(is_duplicate){ + if(NULL == (byptr = reinterpret_cast(malloc(length)))){ + ERR_CHMPRN("Could not allocate memory."); + length = 0L; + return false; + } + memcpy(byptr, &bydata[datapos], length); + is_allocate = true; + }else{ + byptr = &bydata[datapos]; + is_allocate = false; + } + }else{ + byptr = NULL; + is_allocate = false; + } + return true; +} + +bool ChmBinData::Put(unsigned char* bydata, bool is_cvt_hton) const +{ + if(!bydata){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + + size_t datapos = sizeof(size_t); + size_t tmplen = (is_cvt_hton ? htobe64(length) : length); + unsigned char* bylen = reinterpret_cast(&tmplen); + + for(size_t cnt = 0; cnt < datapos; ++cnt){ + bydata[cnt] = bylen[cnt]; + } + if(0 < length){ + memcpy(&bydata[datapos], byptr, length); + } + return true; +} + +unsigned char* ChmBinData::Put(size_t& bylength, bool is_cvt_hton) const +{ + bylength = length + sizeof(size_t); + unsigned char* bydata; + if(NULL == (bydata = reinterpret_cast(malloc(bylength)))){ + ERR_CHMPRN("Could not allocate memory."); + bylength = 0L; + return NULL; + } + + if(!Put(bydata, is_cvt_hton)){ + CHM_Free(bydata); + bylength = 0L; + return NULL; + } + return bydata; +} + +bool ChmBinData::Duplicate(const ChmBinData& other) +{ + return Set(const_cast(other.byptr), other.length, true); +} + +bool ChmBinData::Copy(ChmBinData& other) +{ + return Set(other.byptr, other.length, false); +} + +chmhash_t ChmBinData::GetHash(void) const +{ + if(!byptr || 0L == length){ + return 0L; + } + return K2H_HASH_FUNC(reinterpret_cast(byptr), length); +} + +//--------------------------------------------------------- +// ChmKVPair class +//--------------------------------------------------------- +ChmKVPair::ChmKVPair(unsigned char* bykey, size_t keylen, unsigned char* byval, size_t vallen, bool is_duplicate) : Key(bykey, keylen, is_duplicate), Value(byval, vallen, is_duplicate) +{ +} + +ChmKVPair::ChmKVPair(ChmBinData* pKey, ChmBinData* pValue, bool is_duplicate) +{ + if(pKey){ + Set(*pKey, true, is_duplicate); + } + if(pValue){ + Set(*pValue, false, is_duplicate); + } +} + +ChmKVPair::ChmKVPair(PCHMKVP pkvp, bool is_duplicate) +{ + Set(pkvp, is_duplicate); +} + +ChmKVPair::~ChmKVPair() +{ + Clear(); +} + +void ChmKVPair::Clear(void) +{ + Key.Clear(); + Value.Clear(); +} + +const unsigned char* ChmKVPair::Get(size_t* plength, bool is_key) const +{ + const ChmBinData* pGetObj = (is_key ? &Key : &Value); + return pGetObj->Get(plength); +} + +const char* ChmKVPair::Get(bool is_key) const +{ + const ChmBinData* pGetObj = (is_key ? &Key : &Value); + return pGetObj->Get(); +} + +bool ChmKVPair::Set(unsigned char* bydata, size_t bylength, bool is_key, bool is_duplicate) +{ + ChmBinData* pSetObj = (is_key ? &Key : &Value); + + if(!bydata || 0L == bylength){ + pSetObj->Clear(); + return true; + } + return pSetObj->Set(bydata, bylength, is_duplicate); +} + +bool ChmKVPair::Set(ChmBinData& Data, bool is_key, bool is_duplicate) +{ + ChmBinData* pSetObj = (is_key ? &Key : &Value); + + if(is_duplicate){ + pSetObj->Duplicate(Data); + }else{ + pSetObj->Copy(Data); + } + return true; +} + +bool ChmKVPair::Set(PCHMKVP pkvp, bool is_duplicate) +{ + if(!pkvp){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!Key.Set(&(pkvp->key), is_duplicate) || !Value.Set(&(pkvp->val), is_duplicate)){ + return false; + } + return true; +} + +bool ChmKVPair::Overwrite(unsigned char* bydata, size_t bylength, bool is_key, off_t offset) +{ + if(!bydata || 0L == bylength){ + return true; + } + ChmBinData* pSetObj = (is_key ? &Key : &Value); + + return pSetObj->Overwrite(bydata, bylength, offset); +} + +bool ChmKVPair::Overwrite(ChmBinData& Data, bool is_key, off_t offset) +{ + ChmBinData* pSetObj = (is_key ? &Key : &Value); + + return pSetObj->Overwrite(Data.byptr, Data.length, offset); +} + +bool ChmKVPair::Load(unsigned char* bydata, bool is_cvt_ntoh, bool is_duplicate) +{ + Clear(); + + if(!Key.Load(bydata, is_cvt_ntoh, is_duplicate)){ + Clear(); + return false; + } + if(!Value.Load((bydata ? &bydata[Key.length + sizeof(size_t)] : bydata), is_cvt_ntoh, is_duplicate)){ + Clear(); + return false; + } + return true; +} + +bool ChmKVPair::Put(unsigned char* bydata, bool is_cvt_hton) const +{ + if(!bydata){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!Key.Put(bydata, is_cvt_hton)){ + return false; + } + if(!Value.Put(&bydata[Key.length + sizeof(size_t)], is_cvt_hton)){ + return false; + } + return true; +} + +unsigned char* ChmKVPair::Put(size_t& bylength, bool is_cvt_hton) const +{ + bylength = Key.length + Value.length + sizeof(size_t) * 2; + unsigned char* bydata; + if(NULL == (bydata = reinterpret_cast(malloc(bylength)))){ + ERR_CHMPRN("Could not allocate memory."); + bylength = 0L; + return NULL; + } + if(!Put(bydata, is_cvt_hton)){ + CHM_Free(bydata); + bylength = 0L; + return NULL; + } + return bydata; +} + +chmhash_t ChmKVPair::GetHash(void) const +{ + return Key.GetHash(); +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmkvp.h b/lib/chmkvp.h new file mode 100644 index 0000000..6a38eb4 --- /dev/null +++ b/lib/chmkvp.h @@ -0,0 +1,129 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMKVP_H +#define CHMKVP_H + +#include "chmpx.h" +#include "chmhash.h" + +//--------------------------------------------------------- +// ChmBinData Class +//--------------------------------------------------------- +class ChmKVPair; + +class ChmBinData +{ + friend class ChmKVPair; + + protected: + unsigned char* byptr; + size_t length; + bool is_allocate; + + public: + ChmBinData(unsigned char* bydata = NULL, size_t bylength = 0L, bool is_duplicate = false); + ChmBinData(PCHMBIN pchmbin, bool is_duplicate = false); + virtual ~ChmBinData(); + + void Clear(void); + bool IsEmpty(void) const { return (NULL == byptr); } + + const unsigned char* Get(size_t* plength) const; + const char* Get(void) const; + + bool Set(unsigned char* bydata, size_t bylength, bool is_duplicate = false); + bool Set(PCHMBIN pchmbin, bool is_duplicate = false); + bool Overwrite(unsigned char* bydata, size_t bylength, off_t offset); + bool Overwrite(PCHMBIN pchmbin, off_t offset); + bool Append(unsigned char* bydata, size_t bylength) { return Overwrite(bydata, bylength, static_cast(length)); } + bool Append(PCHMBIN pchmbin) { return Overwrite(pchmbin, static_cast(length)); } + + bool Load(unsigned char* bydata, bool is_cvt_ntoh = true, bool is_duplicate = false); + bool Put(unsigned char* bydata, bool is_cvt_hton = true) const; + unsigned char* Put(size_t& bylength, bool is_cvt_hton = true) const; + + bool Duplicate(const ChmBinData& other); + bool Copy(ChmBinData& other); + chmhash_t GetHash(void) const; +}; + +//--------------------------------------------------------- +// ChmKVPair Class +//--------------------------------------------------------- +class ChmKVPair +{ + protected: + ChmBinData Key; + ChmBinData Value; + + public: + ChmKVPair(unsigned char* bykey = NULL, size_t keylen = 0L, unsigned char* byval = NULL, size_t vallen = 0L, bool is_duplicate = false); + ChmKVPair(ChmBinData* pKey, ChmBinData* pValue, bool is_duplicate = false); + ChmKVPair(PCHMKVP pkvp, bool is_duplicate = false); + virtual ~ChmKVPair(); + + void Clear(void); + + const unsigned char* Get(size_t* plength, bool is_key) const; + const char* Get(bool is_key) const; + const unsigned char* GetKey(size_t* plength) const { return Get(plength, true); } + const unsigned char* GetValue(size_t* plength) const { return Get(plength, false); } + const char* GetKey(void) const { return Get(true); } + const char* GetValue(void) const { return Get(false); } + + bool Set(unsigned char* bydata, size_t bylength, bool is_key, bool is_duplicate = false); + bool Set(ChmBinData& Data, bool is_key, bool is_duplicate = false); + bool Set(PCHMKVP pkvp, bool is_duplicate = false); + bool Overwrite(unsigned char* bydata, size_t bylength, bool is_key, off_t offset); + bool Overwrite(ChmBinData& Data, bool is_key, off_t offset); + bool Append(unsigned char* bydata, size_t bylength, bool is_key) { return Overwrite(bydata, bylength, is_key, static_cast(is_key ? Key.length : Value.length)); } + bool Append(ChmBinData& Data, bool is_key) { return Overwrite(Data, is_key, static_cast(is_key ? Key.length : Value.length)); } + + bool SetKey(unsigned char* bydata, size_t bylength, bool is_duplicate = false) { return Set(bydata, bylength, true, is_duplicate); } + bool SetKey(ChmBinData& Data, bool is_duplicate = false) { return Set(Data, true, is_duplicate); } + bool OverwriteKey(unsigned char* bydata, size_t bylength, off_t offset) { return Overwrite(bydata, bylength, true, offset); } + bool OverwriteKey(ChmBinData& Data, bool is_key, off_t offset) { return Overwrite(Data, true, offset); } + bool AppendKey(unsigned char* bydata, size_t bylength) { return Overwrite(bydata, bylength, true, static_cast(Key.length)); } + bool AppendKey(ChmBinData& Data) { return Overwrite(Data, true, static_cast(Key.length)); } + + bool SetValue(unsigned char* bydata, size_t bylength, bool is_duplicate = false) { return Set(bydata, bylength, false, is_duplicate); } + bool SetValue(ChmBinData& Data, bool is_duplicate = false) { return Set(Data, false, is_duplicate); } + bool OverwriteValue(unsigned char* bydata, size_t bylength, off_t offset) { return Overwrite(bydata, bylength, false, offset); } + bool OverwriteValue(ChmBinData& Data, bool is_key, off_t offset) { return Overwrite(Data, false, offset); } + bool AppendValue(unsigned char* bydata, size_t bylength) { return Overwrite(bydata, bylength, false, static_cast(Value.length)); } + bool AppendValue(ChmBinData& Data) { return Overwrite(Data, false, static_cast(Value.length)); } + + bool Load(unsigned char* bydata, bool is_cvt_ntoh = true, bool is_duplicate = false); + bool Put(unsigned char* bydata, bool is_cvt_hton = true) const; + unsigned char* Put(size_t& bylength, bool is_cvt_hton = true) const; + + chmhash_t GetHash(void) const; +}; + +#endif // CHMKVP_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmlock.cc b/lib/chmlock.cc new file mode 100644 index 0000000..e45ad77 --- /dev/null +++ b/lib/chmlock.cc @@ -0,0 +1,96 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include + +#include "chmcommon.h" +#include "chmutil.h" +#include "chmlock.h" +#include "chmdbg.h" + +using namespace std; + +//--------------------------------------------------------- +// Utility Macros +//--------------------------------------------------------- +#define CHMLOCK_NO_FD_LOCKTYPE(LockType) FLCK_RWLOCK_NO_FD( \ + LockType == CHMLT_IMDATA ? (getpid() | 0x20000000) : \ + LockType == CHMLT_SOCKET ? (getpid() | 0x40000000) : \ + LockType == CHMLT_MQOBJ ? (getpid() | 0x60000000) : \ + 0 ) + +//--------------------------------------------------------- +// Methods +//--------------------------------------------------------- +ChmLock::ChmLock(void) : FLRwlRcsv(FLCK_INVALID_HANDLE, 0L, 1L), kind(CHMLT_IMDATA) +{ +} + +ChmLock::ChmLock(CHMLOCKKIND LockKind, CHMLOCKTYPE LockType) : FLRwlRcsv(CHMLOCK_NO_FD_LOCKTYPE(LockKind), 0L, 1L, (CHMLT_READ == LockType)), kind(LockKind) +{ + assert(CHMLT_MQ != LockKind); +} + +ChmLock::ChmLock(CHMLOCKTYPE LockType, int TargetFd, off_t FileOffset) : FLRwlRcsv(TargetFd, FileOffset, 1L, (CHMLT_READ == LockType)), kind(CHMLT_MQ) +{ + assert(CHM_INVALID_HANDLE != TargetFd); +} + +ChmLock::~ChmLock() +{ +} + +bool ChmLock::Lock(void) +{ + if(is_locked){ + //MSG_CHMPRN("Already locked."); + return true; + } + // check + if(CHM_INVALID_HANDLE == lock_fd){ + ERR_CHMPRN("kind is %s, but fd is %d and type is %s.", CHM_STR_CHMLOCKKIND(kind), lock_fd, STR_FLCKLOCKTYPE(lock_type)); + return false; + } + return FLRwlRcsv::Lock(); +} + +bool ChmLock::UnLock(void) +{ + if(!is_locked){ + //MSG_CHMPRN("Already unlocked."); + return true; + } + // check + if(CHM_INVALID_HANDLE == lock_fd){ + ERR_CHMPRN("kind is %s, but fd is %d and type is %s.", CHM_STR_CHMLOCKKIND(kind), lock_fd, STR_FLCKLOCKTYPE(lock_type)); + return false; + } + return FLRwlRcsv::Unlock(); +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmlock.h b/lib/chmlock.h new file mode 100644 index 0000000..c1d2e65 --- /dev/null +++ b/lib/chmlock.h @@ -0,0 +1,79 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMLOCK_H +#define CHMLOCK_H + +#include +#include +#include + +//--------------------------------------------------------- +// Symbols(enum) +//--------------------------------------------------------- +typedef enum chm_lock_kind{ + CHMLT_IMDATA, + CHMLT_SOCKET, + CHMLT_MQ, + CHMLT_MQOBJ // use internal MQ event object +}CHMLOCKKIND; + +typedef enum chm_lock_type{ + CHMLT_READ, + CHMLT_WRITE +}CHMLOCKTYPE; + +#define CHM_STR_CHMLOCKKIND(kind) ( CHMLT_IMDATA == kind ? "IMDATA" : \ + CHMLT_SOCKET == kind ? "SOCKET" : \ + CHMLT_MQ == kind ? "MQ" : "MQOBJ") +#define CHM_STR_CHMLOCKTYPE(type) ( CHMLT_READ == type ? "READ" : "WRITE" ) + +//--------------------------------------------------------- +// ChmLock Class +//--------------------------------------------------------- +// This class wraps FLRwlRcsv class +// +class ChmLock : public FLRwlRcsv +{ + protected: + CHMLOCKKIND kind; // Lock target kind + + protected: + ChmLock(void); // Not use by any + + public: + ChmLock(CHMLOCKKIND LockKind, CHMLOCKTYPE LockType); + ChmLock(CHMLOCKTYPE LockType, int TargetFd, off_t FileOffset); + virtual ~ChmLock(); + + bool IsLocked(void) const { return is_locked; } + bool Lock(void); + bool UnLock(void); +}; + +#endif // CHMLOCK_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmlockmap.tcc b/lib/chmlockmap.tcc new file mode 100644 index 0000000..3332f5f --- /dev/null +++ b/lib/chmlockmap.tcc @@ -0,0 +1,246 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Mon Feb 15 2016 + * REVISION: + * + */ + +#ifndef CHMLOCKMAP_TCC +#define CHMLOCKMAP_TCC + +#include +#include +#include + +//--------------------------------------------------------- +// chm_lock_map class template +//--------------------------------------------------------- +template +class chm_lock_map +{ + public: + typedef typename std::map st_type; + typedef typename std::vector st_key_list; + typedef typename st_type::iterator iterator; + typedef typename st_type::const_iterator const_iterator; + typedef bool (*chm_lock_map_erase_cb)(iterator&, void*); + + public: // public for using directly. + st_type basemap; + volatile int lockval; + + protected: + chm_lock_map_erase_cb pbasefunc; // default callback function for erasing each element + void* pbaseparam; // default parameter + val_type emptyval; + + protected: + // do not use this + chm_lock_map(void) : lockval(FLCK_NOSHARED_MUTEX_VAL_UNLOCKED), pbasefunc(NULL), pbaseparam(NULL) {} + + public: + chm_lock_map(val_type initval, chm_lock_map_erase_cb pfunc = NULL, void* pparam = NULL) : lockval(FLCK_NOSHARED_MUTEX_VAL_UNLOCKED), pbasefunc(pfunc), pbaseparam(pparam), emptyval(initval) {} + virtual ~chm_lock_map(void) { clear(); } + + inline size_t count(void); + inline val_type operator[](const key_type& key); + inline val_type get(const key_type& key); + inline bool set(const key_type& key, val_type val, bool allow_ow = false); + inline bool find(const key_type& key); + inline void get_keys(st_key_list& list); + inline bool erase(const key_type& key); + inline bool erase(const key_type& key, chm_lock_map_erase_cb pfunc, void* pparam); + inline bool clear(void); + inline bool clear(chm_lock_map_erase_cb pfunc, void* pparam); +}; + +//--------------------------------------------------------- +// chm_lock_map methods +//--------------------------------------------------------- +template +inline size_t chm_lock_map::count(void) +{ + while(!fullock::flck_trylock_noshared_mutex(&lockval)); + size_t count = basemap.size(); + fullock::flck_unlock_noshared_mutex(&lockval); + return count; +} + +// +// This method returns not referance to value, because if returns it, +// this methos must lock. +// So this methos returns copied value. +// +template +inline val_type chm_lock_map::operator[](const key_type& key) +{ + while(!fullock::flck_trylock_noshared_mutex(&lockval)); + + iterator iter = basemap.find(key); + if(basemap.end() == iter){ + fullock::flck_unlock_noshared_mutex(&lockval); + return emptyval; + } + val_type value = iter->second; + fullock::flck_unlock_noshared_mutex(&lockval); + return value; +} + +// +// This method returns not referance to value, because if returns it, +// this methos must lock. +// So this methos returns copied value. +// +template +inline val_type chm_lock_map::get(const key_type& key) +{ + while(!fullock::flck_trylock_noshared_mutex(&lockval)); + + iterator iter = basemap.find(key); + if(basemap.end() == iter){ + fullock::flck_unlock_noshared_mutex(&lockval); + return emptyval; + } + val_type value = iter->second; + fullock::flck_unlock_noshared_mutex(&lockval); + return value; +} + +template +inline bool chm_lock_map::set(const key_type& key, val_type val, bool allow_ow) +{ + while(!fullock::flck_trylock_noshared_mutex(&lockval)); + + iterator iter = basemap.find(key); + if(basemap.end() != iter){ + if(!allow_ow){ + fullock::flck_unlock_noshared_mutex(&lockval); + return false; + } + if(pbasefunc && !pbasefunc(iter, pbaseparam)){ + fullock::flck_unlock_noshared_mutex(&lockval); + return false; + } + } + basemap[key] = val; + fullock::flck_unlock_noshared_mutex(&lockval); + return true; +} + +template +inline bool chm_lock_map::find(const key_type& key) +{ + bool result = true; + + while(!fullock::flck_trylock_noshared_mutex(&lockval)); + if(basemap.end() == basemap.find(key)){ + result = false; + } + fullock::flck_unlock_noshared_mutex(&lockval); + + return result; +} + +template +inline void chm_lock_map::get_keys(st_key_list& list) +{ + while(!fullock::flck_trylock_noshared_mutex(&lockval)); + for(const_iterator iter = basemap.begin(); iter != basemap.end(); ++iter){ + list.push_back(iter->first); + } + fullock::flck_unlock_noshared_mutex(&lockval); +} + +template +inline bool chm_lock_map::erase(const key_type& key) +{ + while(!fullock::flck_trylock_noshared_mutex(&lockval)); + + iterator iter = basemap.find(key); + if(basemap.end() == iter){ + fullock::flck_unlock_noshared_mutex(&lockval); + return false; + } + if(pbasefunc && !pbasefunc(iter, pbaseparam)){ + fullock::flck_unlock_noshared_mutex(&lockval); + return false; + } + basemap.erase(iter); + + fullock::flck_unlock_noshared_mutex(&lockval); + + return true; +} + +template +inline bool chm_lock_map::erase(const key_type& key, chm_lock_map_erase_cb pfunc, void* pparam) +{ + while(!fullock::flck_trylock_noshared_mutex(&lockval)); + + iterator iter = basemap.find(key); + if(basemap.end() == iter){ + fullock::flck_unlock_noshared_mutex(&lockval); + return false; + } + if(pfunc && !pfunc(iter, pparam)){ + fullock::flck_unlock_noshared_mutex(&lockval); + return false; + } + basemap.erase(iter); + + fullock::flck_unlock_noshared_mutex(&lockval); + + return true; +} + +template +inline bool chm_lock_map::clear(void) +{ + while(!fullock::flck_trylock_noshared_mutex(&lockval)); + for(iterator iter = basemap.begin(); iter != basemap.end(); basemap.erase(iter++)){ + if(pbasefunc && !pbasefunc(iter, pbaseparam)){ + fullock::flck_unlock_noshared_mutex(&lockval); + return false; + } + } + fullock::flck_unlock_noshared_mutex(&lockval); + return true; +} + +template +inline bool chm_lock_map::clear(chm_lock_map_erase_cb pfunc, void* pparam) +{ + while(!fullock::flck_trylock_noshared_mutex(&lockval)); + for(iterator iter = basemap.begin(); iter != basemap.end(); basemap.erase(iter++)){ + if(pfunc && !pfunc(iter, pparam)){ + fullock::flck_unlock_noshared_mutex(&lockval); + return false; + } + } + fullock::flck_unlock_noshared_mutex(&lockval); + return true; +} + +#endif // CHMLOCKMAP_TCC + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmnetdb.cc b/lib/chmnetdb.cc new file mode 100644 index 0000000..46ef59c --- /dev/null +++ b/lib/chmnetdb.cc @@ -0,0 +1,526 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "chmcommon.h" +#include "chmutil.h" +#include "chmdbg.h" +#include "chmnetdb.h" + +using namespace std; + +//--------------------------------------------------------- +// Class valiables +//--------------------------------------------------------- +const time_t ChmNetDb::ALIVE_TIME; +ChmNetDb ChmNetDb::singleton; +int ChmNetDb::lockval = FLCK_NOSHARED_MUTEX_VAL_UNLOCKED; + +//--------------------------------------------------------- +// Class methods +//--------------------------------------------------------- +time_t ChmNetDb::SetTimeout(time_t value) +{ + time_t old = ChmNetDb::Get()->timeout; + ChmNetDb::Get()->timeout = value; + return old; +} + + +bool ChmNetDb::Clear(void) +{ + return ChmNetDb::Get()->ClearEx(); +} + +bool ChmNetDb::CacheOut(void) +{ + return ChmNetDb::Get()->CacheOutEx(); +} + +bool ChmNetDb::GetLocalHostname(string& hostname) +{ + if(!ChmNetDb::Get()->GetHostname("localhost", hostname, true)){ + MSG_CHMPRN("Could not get localhost to global hostanme."); + return false; + } + return true; +} + +bool ChmNetDb::GetAnyAddrInfo(short port, struct addrinfo** ppaddrinfo, bool is_inetv6) +{ + if(!ppaddrinfo){ + ERR_CHMPRN("Paramete is wrong."); + return false; + } + *ppaddrinfo = NULL; + + struct addrinfo hints; + int result; + string strPort = to_string(port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = is_inetv6 ? AF_INET6 : AF_INET; + hints.ai_socktype = SOCK_STREAM; + + // addrinfo + if(0 != (result = getaddrinfo(NULL, strPort.c_str(), &hints, ppaddrinfo)) || !(*ppaddrinfo)){ + MSG_CHMPRN("Could not get %s addrinfo for %s, errno=%d.", (is_inetv6 ? "IN6ADDR_ANY_INIT" : "INADDR_ANY"), (is_inetv6 ? "AF_INET6" : "AF_INET"), result); + return false; + } + return true; +} + +bool ChmNetDb::CvtAddrInfoToIpAddress(const struct sockaddr_storage* info, socklen_t infolen, string& stripaddress) +{ + char ipaddress[NI_MAXHOST]; + int result; + + if(!info){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + + // addrinfo -> normalized ipaddress + if(0 != (result = getnameinfo(reinterpret_cast(info), infolen, ipaddress, sizeof(ipaddress), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV))){ + MSG_CHMPRN("Could not convert addrinfo to normalized ipaddress, errno=%d.", result); + return false; + } + stripaddress = ipaddress; + + return true; +} + +bool ChmNetDb::CvtSockToLocalPort(int sock, short& port) +{ + if(CHM_INVALID_SOCK == sock){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + + struct sockaddr_storage info; + socklen_t infolen = (socklen_t)sizeof(struct sockaddr_storage); + if(!getsockname(sock, reinterpret_cast(&info), &infolen)){ + ERR_CHMPRN("Failed to get sock info, errno=%d", errno); + return false; + } + + char szport[NI_MAXHOST]; + int result; + if(0 != (result = getnameinfo(reinterpret_cast(&info), infolen, NULL, 0, szport, sizeof(szport), NI_NUMERICHOST | NI_NUMERICSERV))){ + MSG_CHMPRN("Could not convert addrinfo to port number, errno=%d.", result); + return false; + } + port = static_cast(atoi(szport)); + + return true; +} + +bool ChmNetDb::CvtSockToPeerPort(int sock, short& port) +{ + if(CHM_INVALID_SOCK == sock){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + + struct sockaddr_storage info; + socklen_t infolen = (socklen_t)sizeof(struct sockaddr_storage); + if(!getpeername(sock, reinterpret_cast(&info), &infolen)){ + ERR_CHMPRN("Failed to get sock info, errno=%d", errno); + return false; + } + + char szport[NI_MAXHOST]; + int result; + if(0 != (result = getnameinfo(reinterpret_cast(&info), infolen, NULL, 0, szport, sizeof(szport), NI_NUMERICHOST | NI_NUMERICSERV))){ + MSG_CHMPRN("Could not convert addrinfo to port number, errno=%d.", result); + return false; + } + port = static_cast(atoi(szport)); + + return true; +} + +bool ChmNetDb::CvtV4MappedAddrInfo(struct sockaddr_storage* info, socklen_t& addrlen) +{ + if(!info){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(AF_INET6 != info->ss_family){ + // nothing to do + return true; + } + + struct sockaddr_in6* in6 = reinterpret_cast(info); + if(IN6_IS_ADDR_V4MAPPED(&(in6->sin6_addr))){ + struct sockaddr_in in4; + + memset(&in4, 0, sizeof(struct sockaddr_in)); + in4.sin_family = AF_INET; + in4.sin_port = in6->sin6_port; + + memcpy(&(in4.sin_addr.s_addr), in6->sin6_addr.s6_addr + 12, sizeof(in4.sin_addr.s_addr)); + memcpy(info, &in4, sizeof(struct sockaddr_in)); + + addrlen = sizeof(struct sockaddr_in); + } + return true; +} + +//--------------------------------------------------------- +// Methods +//--------------------------------------------------------- +ChmNetDb::ChmNetDb() : timeout(ChmNetDb::ALIVE_TIME), fulllocalname(""), localname("") +{ + if(this == ChmNetDb::Get()){ + InitializeLocalHostName(); + }else{ + assert(false); + } +} + +ChmNetDb::~ChmNetDb() +{ + if(this == ChmNetDb::Get()){ + ClearEx(); + }else{ + assert(false); + } +} + +bool ChmNetDb::InitializeLocalHostName(void) +{ + struct addrinfo hints; + struct addrinfo* res_info = NULL; + struct addrinfo* tmpaddrinfo; + char hostname[NI_MAXHOST]; + int result; + struct utsname buf; + + // Get local hostanme by uname + if(-1 == uname(&buf)){ + ERR_CHMPRN("Failed to get own host(node) name, errno=%d", errno); + return false; + } + fulllocalname = buf.nodename; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + // local hostname -> addrinfo + if(0 != (result = getaddrinfo(buf.nodename, NULL, &hints, &res_info)) || !res_info){ // port is NULL + MSG_CHMPRN("Could not get addrinfo from %s, errno=%d.", buf.nodename, result); + if(res_info){ + freeaddrinfo(res_info); + } + return false; + } + + // addrinfo(list) -> hostname + for(tmpaddrinfo = res_info; tmpaddrinfo; tmpaddrinfo = tmpaddrinfo->ai_next){ + if(0 != (result = getnameinfo(tmpaddrinfo->ai_addr, tmpaddrinfo->ai_addrlen, hostname, sizeof(hostname), NULL, 0, NI_NAMEREQD | NI_NUMERICSERV))){ + MSG_CHMPRN("Could not get hostname %s, errno=%d.", buf.nodename, result); + }else{ + break; + } + } + if(!tmpaddrinfo){ + MSG_CHMPRN("Could not get hostname %s.", buf.nodename); + freeaddrinfo(res_info); + return false; + } + + // When local hostname without domain name is set in /ec/hosts, + // "hostname" is short name. + // (if other server name is set, this class do not care it.) + // + localname = hostname; + + freeaddrinfo(res_info); + + return true; +} + +bool ChmNetDb::ClearEx(void) +{ + while(!fullock::flck_trylock_noshared_mutex(&ChmNetDb::lockval)); + cachemap.clear(); + fullock::flck_unlock_noshared_mutex(&ChmNetDb::lockval); + + return true; +} + +bool ChmNetDb::CacheOutEx(void) +{ + time_t timeouted = time(NULL) + timeout; + + while(!fullock::flck_trylock_noshared_mutex(&ChmNetDb::lockval)); + + for(chmndbmap_t::iterator iter = cachemap.begin(); iter != cachemap.end(); ){ + if(timeouted < iter->second.cached_time){ + cachemap.erase(iter++); + }else{ + ++iter; + } + } + fullock::flck_unlock_noshared_mutex(&ChmNetDb::lockval); + + return true; +} + +// [TODO] +// If one FQDN has many address(ex. it specifies in /etc/hosts), this method returns one address. +// But this should return all names and addresses. +// If it is needed, should use res_query etc for checking DNS entry. +// +bool ChmNetDb::GetHostAddressInfo(const char* target, string& strhostname, string& stripaddress) +{ + struct addrinfo hints; + struct addrinfo* res_info = NULL; + struct addrinfo* tmpaddrinfo; + char hostname[NI_MAXHOST]; + char ipaddress[NI_MAXHOST]; + int result; + + if(CHMEMPTYSTR(target)){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + // target -> addrinfo + if(0 != (result = getaddrinfo(target, NULL, &hints, &res_info)) || !res_info){ // port is NULL + //MSG_CHMPRN("Could not get addrinfo from %s, errno=%d.", target, result); + return false; + } + + // addrinfo(list) -> hostname + for(tmpaddrinfo = res_info; tmpaddrinfo; tmpaddrinfo = tmpaddrinfo->ai_next){ + if(0 != (result = getnameinfo(tmpaddrinfo->ai_addr, tmpaddrinfo->ai_addrlen, hostname, sizeof(hostname), NULL, 0, NI_NAMEREQD | NI_NUMERICSERV))){ + MSG_CHMPRN("Could not get hostname %s, errno=%d.", target, result); + }else{ + break; + } + } + if(!tmpaddrinfo){ + MSG_CHMPRN("Could not get hostname %s.", target); + freeaddrinfo(res_info); + return false; + } + + // addrinfo -> normalized ipaddress + for(tmpaddrinfo = res_info; tmpaddrinfo; tmpaddrinfo = tmpaddrinfo->ai_next){ + if(0 != (result = getnameinfo(tmpaddrinfo->ai_addr, tmpaddrinfo->ai_addrlen, ipaddress, sizeof(ipaddress), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV))){ + MSG_CHMPRN("Could not convert normalized ipaddress %s, errno=%d.", target, result); + }else{ + break; + } + } + freeaddrinfo(res_info); + if(!tmpaddrinfo){ + MSG_CHMPRN("Could not get ipaddress %s.", target); + return false; + } + + // if short local hostname, returns fully hostname. + if(hostname == localname){ + strhostname = fulllocalname; + }else{ + strhostname = hostname; + } + stripaddress= ipaddress; + + return true; +} + +bool ChmNetDb::SearchCache(const char* target, CHMNDBCACHE& data) +{ + if(CHMEMPTYSTR(target)){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + bool result = false; + + while(!fullock::flck_trylock_noshared_mutex(&ChmNetDb::lockval)); + + string strtarget(target); + if(cachemap.end() != cachemap.find(strtarget)){ + if(0 != timeout && timeout < (time(NULL) - cachemap[strtarget].cached_time)){ + MSG_CHMPRN("find cache but it is old, do removing cache."); + + string tmpipaddress= cachemap[strtarget].ipaddress; + string tmphostname = cachemap[strtarget].hostname; + if(cachemap.end() != cachemap.find(tmpipaddress)){ + cachemap.erase(tmpipaddress); + } + if(cachemap.end() != cachemap.find(tmphostname)){ + cachemap.erase(tmphostname); + } + if(cachemap.end() != cachemap.find(strtarget)){ + cachemap.erase(strtarget); + } + }else{ + data.ipaddress = cachemap[strtarget].ipaddress; + data.hostname = cachemap[strtarget].hostname; + data.cached_time = cachemap[strtarget].cached_time; + result = true; + } + } + fullock::flck_unlock_noshared_mutex(&ChmNetDb::lockval); + + return result; +} + +bool ChmNetDb::Search(const char* target, CHMNDBCACHE& data, bool is_cvt_localhost) +{ + if(CHMEMPTYSTR(target)){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(SearchCache(target, data)){ + return true; // Hit cache + } + + string hostname; + string ipaddress; + if(!GetHostAddressInfo(target, hostname, ipaddress)){ + //MSG_CHMPRN("Could not find hostname or IP address by %s.", target); + return false; + } + + // If localhost, using global name, ip. + if(is_cvt_localhost && (hostname == "localhost" || ipaddress == "127.0.0.1" || ipaddress == "::1")){ + char localname[NI_MAXHOST]; + if(0 != gethostname(localname, sizeof(localname))){ + MSG_CHMPRN("Could not get localhost name, errno=%d", errno); + return false; + } + // retry + if(!GetHostAddressInfo(localname, hostname, ipaddress)){ + MSG_CHMPRN("Could not find hostname or IP address by %s.", localname); + return false; + } + } + data.ipaddress = ipaddress; + data.hostname = hostname; + data.cached_time = time(NULL); + + // set cache + while(!fullock::flck_trylock_noshared_mutex(&ChmNetDb::lockval)); + + cachemap[ipaddress] = data; + cachemap[hostname] = data; + + fullock::flck_unlock_noshared_mutex(&ChmNetDb::lockval); + + return true; +} + +bool ChmNetDb::GetAddrInfo(const char* target, short port, struct addrinfo** ppaddrinfo, bool is_cvt_localhost) +{ + if(CHMEMPTYSTR(target) || !ppaddrinfo){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + *ppaddrinfo = NULL; + + CHMNDBCACHE data; + if(!Search(target, data, is_cvt_localhost)){ + MSG_CHMPRN("Failed to convert %s to addrinfo structure.", target); + return false; + } + + struct addrinfo hints; + int result; + string strPort = to_string(port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + // addrinfo + if(0 != (result = getaddrinfo(data.ipaddress.c_str(), strPort.c_str(), &hints, ppaddrinfo)) || !(*ppaddrinfo)){ + MSG_CHMPRN("Could not get addrinfo from %s[%d], errno=%d.", target, port, result); + return false; + } + return true; +} + +bool ChmNetDb::GetHostname(const char* target, string& hostname, bool is_cvt_localhost) +{ + if(CHMEMPTYSTR(target)){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + + CHMNDBCACHE data; + if(!Search(target, data, is_cvt_localhost)){ + //MSG_CHMPRN("Failed to convert %s to addrinfo structure.", target); + return false; + } + hostname = data.hostname; + + return true; +} + +bool ChmNetDb::GetIpAddressString(const char* target, string& ipaddress, bool is_cvt_localhost) +{ + if(CHMEMPTYSTR(target)){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + + CHMNDBCACHE data; + if(!Search(target, data, is_cvt_localhost)){ + MSG_CHMPRN("Failed to convert %s to addrinfo structure.", target); + return false; + } + ipaddress = data.ipaddress; + + return true; +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmnetdb.h b/lib/chmnetdb.h new file mode 100644 index 0000000..4f80a84 --- /dev/null +++ b/lib/chmnetdb.h @@ -0,0 +1,92 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMNETDB_H +#define CHMNETDB_H + +#include +#include + +#include "chmcommon.h" +#include "chmutil.h" + +//--------------------------------------------------------- +// Structure +//--------------------------------------------------------- +typedef struct chm_netdb_cache{ + std::string ipaddress; + std::string hostname; + time_t cached_time; +}CHMNDBCACHE, *PCHMNDBCACHE; + +typedef std::map chmndbmap_t; // Key is ipaddress or hostname + +//--------------------------------------------------------- +// Class ChmNetDb +//--------------------------------------------------------- +class ChmNetDb +{ + protected: + static const time_t ALIVE_TIME = 60; // default 60s + static ChmNetDb singleton; + static int lockval; // like mutex + + chmndbmap_t cachemap; + time_t timeout; // 0 means no timeout + std::string fulllocalname; // local hostname from uname(gethostname), this is Full FQDN. + std::string localname; // local hostname from getnameinfo, sometimes this name is without domain name if set in /etc/hosts. + + protected: + bool InitializeLocalHostName(void); + bool ClearEx(void); + bool CacheOutEx(void); + bool GetHostAddressInfo(const char* target, std::string& strhostname, std::string& stripaddress); + bool SearchCache(const char* target, CHMNDBCACHE& data); + bool Search(const char* target, CHMNDBCACHE& data, bool is_cvt_localhost); + + public: + static ChmNetDb* Get(void) { return &ChmNetDb::singleton; } + static time_t SetTimeout(time_t value); + static bool Clear(void); + static bool CacheOut(void); + static bool GetLocalHostname(std::string& hostname); + static bool GetAnyAddrInfo(short port, struct addrinfo** ppaddrinfo, bool is_inetv6); + static bool CvtAddrInfoToIpAddress(const struct sockaddr_storage* info, socklen_t infolen, std::string& stripaddress); + static bool CvtSockToLocalPort(int sock, short& port); + static bool CvtSockToPeerPort(int sock, short& port); + static bool CvtV4MappedAddrInfo(struct sockaddr_storage* info, socklen_t& addrlen); + + ChmNetDb(); + virtual ~ChmNetDb(); + + bool GetAddrInfo(const char* target, short port, struct addrinfo** ppaddrinfo, bool is_cvt_localhost); // Must freeaddrinfo for *ppaddrinfo + bool GetHostname(const char* target, std::string& hostname, bool is_cvt_localhost); + bool GetIpAddressString(const char* target, std::string& ipaddress, bool is_cvt_localhost); +}; + +#endif // CHMNETDB_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmopts.cc b/lib/chmopts.cc new file mode 100644 index 0000000..189b683 --- /dev/null +++ b/lib/chmopts.cc @@ -0,0 +1,137 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include + +#include "chmcommon.h" +#include "chmopts.h" +#include "chmdbg.h" + +using namespace std; + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +#define OPT_PARAM_SEP "\r\n" + +//--------------------------------------------------------- +// Constructor/Destructor +//--------------------------------------------------------- +ChmOpts::ChmOpts(int argc, char** argv, const char* strsepchars) : sepchars(CHMEMPTYSTR(strsepchars) ? OPT_PARAM_SEP : strsepchars) +{ + if(argv && 0 < argc){ + Initialize(argc, argv); + } +} + +ChmOpts::~ChmOpts(void) +{ +} + +//--------------------------------------------------------- +// Methods +//--------------------------------------------------------- +bool ChmOpts::Initialize(int argc, char** argv) +{ + if(!argv || 0 == argc){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + optmap.clear(); + + for(int cnt = 0; cnt < argc; cnt++){ + string option; + strlst_t paramlist; + + if('-' != argv[cnt][0]){ + option = ""; + }else{ + option = &(argv[cnt][1]); + } + option = upper(option); + + paramlist.clear(); + int pcnt; + for(pcnt = 0; (cnt + pcnt + 1) < argc; pcnt++){ + if('-' == argv[cnt + pcnt + 1][0]){ + break; + } + str_paeser(argv[cnt + pcnt + 1], paramlist, OPT_PARAM_SEP); + } + optmap[option] = paramlist; + cnt += pcnt; + } + return true; +} + +bool ChmOpts::Get(const char* popt, string& param) +{ + string stropt; + if(CHMEMPTYSTR(popt)){ + stropt = ""; + }else{ + stropt = upper(string(popt)); + } + if(optmap.end() == optmap.find(stropt)){ + return false; + } + if(optmap[stropt].empty()){ + param = ""; + }else{ + param = optmap[stropt].front(); + } + return true; +} + +bool ChmOpts::Get(const char* popt, strlst_t& params) +{ + string stropt; + if(CHMEMPTYSTR(popt)){ + stropt = ""; + }else{ + stropt = upper(string(popt)); + } + if(optmap.end() == optmap.find(stropt)){ + return false; + } + params = optmap[stropt]; + return true; +} + +bool ChmOpts::Find(const char* popt) const +{ + if(CHMEMPTYSTR(popt)){ + return false; + } + string stropt = upper(string(popt)); + if(optmap.end() == optmap.find(stropt)){ + return false; + } + return true; +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmopts.h b/lib/chmopts.h new file mode 100644 index 0000000..a6f8380 --- /dev/null +++ b/lib/chmopts.h @@ -0,0 +1,55 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMOPTS_H +#define CHMOPTS_H + +#include "chmutil.h" + +//--------------------------------------------------------- +// ChmOpts Class +//--------------------------------------------------------- +class ChmOpts +{ + protected: + strlstmap_t optmap; + std::string sepchars; + + public: + ChmOpts(int argc = 0, char** argv = NULL, const char* strsepchars = NULL); + virtual ~ChmOpts(); + + bool Initialize(int argc, char** argv); + bool Get(const char* popt, std::string& param); + bool Get(const char* popt, strlst_t& params); + bool Find(const char* popt) const; + long Count(void) const { return static_cast(optmap.size()); } +}; + +#endif // CHMOPTS_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ + diff --git a/lib/chmpx.cc b/lib/chmpx.cc new file mode 100644 index 0000000..f2d5fcb --- /dev/null +++ b/lib/chmpx.cc @@ -0,0 +1,602 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include + +#include "chmcommon.h" +#include "chmutil.h" +#include "chmdbg.h" +#include "chmhash.h" +#include "chmcntrl.h" +#include "chmkvp.h" +#include "chmpx.h" + +using namespace std; + +//--------------------------------------------------------- +// Functions - Debug +//--------------------------------------------------------- +void chmpx_bump_debug_level(void) +{ + ::BumpupChmDbgMode(); +} + +void chmpx_set_debug_level_silent(void) +{ + ::SetChmDbgMode(CHMDBG_SILENT); +} + +void chmpx_set_debug_level_error(void) +{ + ::SetChmDbgMode(CHMDBG_ERR); +} + +void chmpx_set_debug_level_warning(void) +{ + ::SetChmDbgMode(CHMDBG_WARN); +} + +void chmpx_set_debug_level_message(void) +{ + ::SetChmDbgMode(CHMDBG_MSG); +} + +void chmpx_set_debug_level_dump(void) +{ + ::SetChmDbgMode(CHMDBG_DUMP); +} + +bool chmpx_set_debug_file(const char* filepath) +{ + bool result; + if(CHMEMPTYSTR(filepath)){ + result = ::UnsetChmDbgFile(); + }else{ + result = ::SetChmDbgFile(filepath); + } + return result; +} + +bool chmpx_unset_debug_file(void) +{ + return ::UnsetChmDbgFile(); +} + +bool chmpx_load_debug_env(void) +{ + return ::LoadChmDbgEnv(); +} + +//--------------------------------------------------------- +// Functions - Load hash library +//--------------------------------------------------------- +bool chmpx_load_hash_library(const char* libpath) +{ + return ::k2h_load_hash_library(libpath); +} + +bool chmpx_unload_hash_library(void) +{ + return ::k2h_unload_hash_library(); +} + +//--------------------------------------------------------- +// Functions - Create/Destroy +//--------------------------------------------------------- +chmpx_h chmpx_create_ex(const char* conffile, bool is_on_server, bool is_auto_rejoin, chm_merge_get_cb getfp, chm_merge_set_cb setfp, chm_merge_lastts_cb lastupdatefp) +{ + if(CHMEMPTYSTR(conffile)){ + ERR_CHMPRN("Configration file path is empty."); + return CHM_INVALID_CHMPXHANDLE; + } + + ChmCntrl* pCntrlObj = new ChmCntrl(); + bool result; + if(is_on_server){ + result = pCntrlObj->InitializeOnServer(conffile, is_auto_rejoin, getfp, setfp, lastupdatefp); + }else{ + result = pCntrlObj->InitializeOnSlave(conffile, is_auto_rejoin); + } + if(!result){ + return CHM_INVALID_CHMPXHANDLE; + } + return reinterpret_cast(pCntrlObj); +} + +chmpx_h chmpx_create(const char* conffile, bool is_on_server, bool is_auto_rejoin) +{ + return chmpx_create_ex(conffile, is_on_server, is_auto_rejoin, NULL, NULL, NULL); +} + +bool chmpx_destroy(chmpx_h handle) +{ + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + CHM_Delete(pCntrlObj); + + return true; +} + +bool chmpx_pkth_destroy(chmpx_pkt_h pckthandle) +{ + PCOMPKT pComPkt = reinterpret_cast(pckthandle); + if(!pComPkt){ + return false; + } + CHM_Free(pComPkt); + return true; +} + +//--------------------------------------------------------- +// Functions - Send/Receive for server side +//--------------------------------------------------------- +bool chmpx_svr_send(chmpx_h handle, const unsigned char* pbody, size_t blength, chmhash_t hash, bool is_routing) +{ + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + return pCntrlObj->Send(pbody, blength, hash, is_routing); +} + +bool chmpx_svr_send_kvp(chmpx_h handle, const PCHMKVP pkvp, bool is_routing) +{ + return chmpx_svr_send_kvp_ex(handle, pkvp, is_routing, false); +} + +bool chmpx_svr_send_kvp_ex(chmpx_h handle, const PCHMKVP pkvp, bool is_routing, bool without_self) +{ + if(!pkvp){ + ERR_CHMPRN("CHMKVP is NULL."); + return false; + } + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + + ChmKVPair kvpair(pkvp); + unsigned char* pbody; + size_t length = 0L; + if(NULL == (pbody = kvpair.Put(length))){ + ERR_CHMPRN("Could not convert CHMKVP to Object."); + return false; + } + chmhash_t hash = kvpair.GetHash(); + bool result = pCntrlObj->Send(pbody, length, hash, is_routing, without_self); + CHM_Free(pbody); + + return result; +} + +bool chmpx_svr_send_kv(chmpx_h handle, unsigned char* pkey, size_t keylen, unsigned char* pval, size_t vallen, bool is_routing) +{ + return chmpx_svr_send_kv_ex(handle, pkey, keylen, pval, vallen, is_routing, false); +} + +bool chmpx_svr_send_kv_ex(chmpx_h handle, unsigned char* pkey, size_t keylen, unsigned char* pval, size_t vallen, bool is_routing, bool without_self) +{ + if(!pkey || 0L == keylen){ + ERR_CHMPRN("Key is invalid."); + return false; + } + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + + ChmKVPair kvpair(pkey, keylen, pval, vallen); + unsigned char* pbody; + size_t length = 0L; + if(NULL == (pbody = kvpair.Put(length))){ + ERR_CHMPRN("Could not convert CHMKVP to Object."); + return false; + } + chmhash_t hash = kvpair.GetHash(); + bool result = pCntrlObj->Send(pbody, length, hash, is_routing, without_self); + CHM_Free(pbody); + + return result; +} + +bool chmpx_svr_broadcast(chmpx_h handle, const unsigned char* pbody, size_t blength, chmhash_t hash) +{ + return chmpx_svr_broadcast_ex(handle, pbody, blength, hash, false); +} + +bool chmpx_svr_broadcast_ex(chmpx_h handle, const unsigned char* pbody, size_t blength, chmhash_t hash, bool without_self) +{ + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + return pCntrlObj->Broadcast(pbody, blength, hash, without_self); +} + +bool chmpx_svr_replicate(chmpx_h handle, const unsigned char* pbody, size_t blength, chmhash_t hash) +{ + return chmpx_svr_replicate_ex(handle, pbody, blength, hash, true); // default: without self +} + +bool chmpx_svr_replicate_ex(chmpx_h handle, const unsigned char* pbody, size_t blength, chmhash_t hash, bool without_self) +{ + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + return pCntrlObj->Replicate(pbody, blength, hash, without_self); +} + +bool chmpx_svr_receive(chmpx_h handle, chmpx_pkt_h* ppckthandle, unsigned char** ppbody, size_t* plength, int timeout_ms, bool no_giveup_rejoin) +{ + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + PCOMPKT* ppComPkt = reinterpret_cast(ppckthandle); + if(!ppComPkt){ + ERR_CHMPRN("Invalid packet handle pointer."); + return false; + } + return pCntrlObj->Receive(ppComPkt, ppbody, plength, timeout_ms, no_giveup_rejoin); +} + +//--------------------------------------------------------- +// Functions - Send/Receive for slave side +//--------------------------------------------------------- +msgid_t chmpx_open(chmpx_h handle, bool no_giveup_rejoin) +{ + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return CHM_INVALID_MSGID; + } + return pCntrlObj->Open(no_giveup_rejoin); +} + +bool chmpx_close(chmpx_h handle, msgid_t msgid) +{ + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + return pCntrlObj->Close(msgid); +} + +bool chmpx_msg_send(chmpx_h handle, msgid_t msgid, const unsigned char* pbody, size_t blength, chmhash_t hash, long* preceivercnt, bool is_routing) +{ + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + return pCntrlObj->Send(msgid, pbody, blength, hash, preceivercnt, is_routing); +} + +bool chmpx_msg_send_kvp(chmpx_h handle, msgid_t msgid, const PCHMKVP pkvp, long* preceivercnt, bool is_routing) +{ + if(!pkvp){ + ERR_CHMPRN("CHMKVP is NULL."); + return false; + } + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + ChmKVPair kvpair(pkvp); + unsigned char* pbody; + size_t length = 0L; + if(NULL == (pbody = kvpair.Put(length))){ + ERR_CHMPRN("Could not convert CHMKVP to Object."); + return false; + } + chmhash_t hash = kvpair.GetHash(); + bool result = pCntrlObj->Send(msgid, pbody, length, hash, preceivercnt, is_routing); + CHM_Free(pbody); + + return result; +} + +bool chmpx_msg_send_kv(chmpx_h handle, msgid_t msgid, unsigned char* pkey, size_t keylen, unsigned char* pval, size_t vallen, long* preceivercnt, bool is_routing) +{ + if(!pkey || 0L == keylen){ + ERR_CHMPRN("Key is invalid."); + return false; + } + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + ChmKVPair kvpair(pkey, keylen, pval, vallen); + unsigned char* pbody; + size_t length = 0L; + if(NULL == (pbody = kvpair.Put(length))){ + ERR_CHMPRN("Could not convert CHMKVP to Object."); + return false; + } + chmhash_t hash = kvpair.GetHash(); + bool result = pCntrlObj->Send(msgid, pbody, length, hash, preceivercnt, is_routing); + CHM_Free(pbody); + + return result; +} + +bool chmpx_msg_broadcast(chmpx_h handle, msgid_t msgid, const unsigned char* pbody, size_t blength, chmhash_t hash, long* preceivercnt) +{ + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + return pCntrlObj->Broadcast(msgid, pbody, blength, hash, preceivercnt); +} + +bool chmpx_msg_replicate(chmpx_h handle, msgid_t msgid, const unsigned char* pbody, size_t blength, chmhash_t hash, long* preceivercnt) +{ + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + return pCntrlObj->Replicate(msgid, pbody, blength, hash, preceivercnt); +} + +bool chmpx_msg_receive(chmpx_h handle, msgid_t msgid, chmpx_pkt_h* ppckthandle, unsigned char** ppbody, size_t* plength, int timeout_ms) +{ + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + PCOMPKT* ppComPkt = reinterpret_cast(ppckthandle); + if(!ppComPkt){ + ERR_CHMPRN("Invalid packet handle pointer."); + return false; + } + return pCntrlObj->Receive(msgid, ppComPkt, ppbody, plength, timeout_ms); +} + +//--------------------------------------------------------- +// Functions - Reply for both server and slave side +//--------------------------------------------------------- +bool chmpx_msg_reply(chmpx_h handle, chmpx_pkt_h pckthandle, const unsigned char* pbody, size_t blength) +{ + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + PCOMPKT pComPkt = reinterpret_cast(pckthandle); + if(!pComPkt){ + ERR_CHMPRN("Invalid packet handle."); + return false; + } + return pCntrlObj->Reply(pComPkt, pbody, blength); +} + +bool chmpx_msg_reply_kvp(chmpx_h handle, chmpx_pkt_h pckthandle, const PCHMKVP pkvp) +{ + if(!pkvp){ + ERR_CHMPRN("CHMKVP is NULL."); + return false; + } + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + PCOMPKT pComPkt = reinterpret_cast(pckthandle); + if(!pComPkt){ + ERR_CHMPRN("Invalid packet handle."); + return false; + } + + ChmKVPair kvpair(pkvp); + unsigned char* pbody; + size_t length = 0L; + if(NULL == (pbody = kvpair.Put(length))){ + ERR_CHMPRN("Could not convert CHMKVP to Object."); + return false; + } + + bool result = pCntrlObj->Reply(pComPkt, pbody, length); + CHM_Free(pbody); + + return result; +} + +bool chmpx_msg_reply_kv(chmpx_h handle, chmpx_pkt_h pckthandle, unsigned char* pkey, size_t keylen, unsigned char* pval, size_t vallen) +{ + if(!pkey || 0L == keylen){ + ERR_CHMPRN("Key is invalid."); + return false; + } + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + PCOMPKT pComPkt = reinterpret_cast(pckthandle); + if(!pComPkt){ + ERR_CHMPRN("Invalid packet handle."); + return false; + } + + ChmKVPair kvpair(pkey, keylen, pval, vallen); + unsigned char* pbody; + size_t length = 0L; + if(NULL == (pbody = kvpair.Put(length))){ + ERR_CHMPRN("Could not convert CHMKVP to Object."); + return false; + } + + bool result = pCntrlObj->Reply(pComPkt, pbody, length); + CHM_Free(pbody); + + return result; +} + +//--------------------------------------------------------- +// Functions - Chmpx Process exists +//--------------------------------------------------------- +bool is_chmpx_proc_exists(chmpx_h handle) +{ + ChmCntrl* pCntrlObj = reinterpret_cast(handle); + if(!pCntrlObj){ + ERR_CHMPRN("Invalid chmpx handle."); + return false; + } + return !pCntrlObj->IsChmpxExit(); +} + +//--------------------------------------------------------- +// Functions - Version +//--------------------------------------------------------- +extern char chmpx_commit_hash[]; + +void chmpx_print_version(FILE* stream) +{ + static const char format[] = + "\n" + "CHMPX Version %s (commit: %s)\n" + "\n" + "Copyright 2014 Yahoo! JAPAN corporation.\n" + "\n" + "CHMPX is inprocess data exchange by MQ with consistent hashing.\n" + "CHMPX is made for the purpose of the construction of original\n" + "messaging system and the offer of the client library. CHMPX\n" + "transfers messages between the client and the server/slave. CHMPX\n" + "based servers are dispersed by consistent hashing and are\n" + "automatically layouted. As a result, it provides a high performance,\n" + "a high scalability.\n" + "\n"; + fprintf(stream, format, VERSION, chmpx_commit_hash); +} + +//--------------------------------------------------------- +// Key Value Pair Utilities +//--------------------------------------------------------- +unsigned char* cvt_kvp_bin(const PCHMKVP pkvp, size_t* plength) +{ + if(!pkvp || !plength){ + ERR_CHMPRN("Parameters are wrong."); + return NULL; + } + *plength = pkvp->key.length + pkvp->val.length + sizeof(size_t) * 2; + + unsigned char* pResult; + if(NULL == (pResult = reinterpret_cast(malloc(*plength)))){ + ERR_CHMPRN("Could not allocate memory."); + return NULL; + } + + // set key + size_t setpos = 0; + size_t tmplen = htobe64(pkvp->key.length); // To network byte order + unsigned char* bylen = reinterpret_cast(&tmplen); + for(size_t cnt = 0; cnt < sizeof(size_t); ++cnt){ + pResult[setpos++] = bylen[cnt]; + } + if(0 < pkvp->key.length){ + memcpy(&pResult[setpos], pkvp->key.byptr, pkvp->key.length); + setpos += pkvp->key.length; + } + + // set value + tmplen = htobe64(pkvp->val.length); // To network byte order + for(size_t cnt = 0; cnt < sizeof(size_t); ++cnt){ + pResult[setpos++] = bylen[cnt]; + } + if(0 < pkvp->val.length){ + memcpy(&pResult[setpos], pkvp->val.byptr, pkvp->val.length); + } + return pResult; +} + +bool cvt_bin_kvp(PCHMKVP pkvp, unsigned char* bydata, size_t length) +{ + if(!pkvp || !bydata || length < (sizeof(size_t) * 2)){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + + // set key + size_t readpos = 0; + size_t* ptmplen = reinterpret_cast(&bydata[readpos]); + pkvp->key.length = be64toh(*ptmplen); // To host byte order + readpos += sizeof(size_t); + if(0 < pkvp->key.length){ + pkvp->key.byptr = &bydata[readpos]; + readpos += pkvp->key.length; + }else{ + pkvp->key.byptr = NULL; + } + + // set value + ptmplen = reinterpret_cast(&bydata[readpos]); + pkvp->val.length = be64toh(*ptmplen); // To host byte order + readpos += sizeof(size_t); + if(0 < pkvp->val.length){ + pkvp->val.byptr = &bydata[readpos]; + }else{ + pkvp->val.byptr = NULL; + } + return true; +} + +chmhash_t make_chmbin_hash(const PCHMBIN pchmbin) +{ + if(!pchmbin){ + ERR_CHMPRN("Parameters are wrong."); + return 0L; + } + return K2H_HASH_FUNC(reinterpret_cast(pchmbin->byptr), pchmbin->length); +} + +chmhash_t make_kvp_hash(const PCHMKVP pkvp) +{ + if(!pkvp || !pkvp->key.byptr || 0L == pkvp->key.length){ + ERR_CHMPRN("Parameters are wrong."); + return 0L; + } + return make_chmbin_hash(&(pkvp->key)); +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmpx.h b/lib/chmpx.h new file mode 100644 index 0000000..8a42583 --- /dev/null +++ b/lib/chmpx.h @@ -0,0 +1,241 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMPX_H +#define CHMPX_H + +#include + +#include "chmcommon.h" +#include "chmstructure.h" + +DECL_EXTERN_C_START + +//--------------------------------------------------------- +// Typedefs +//--------------------------------------------------------- +typedef uint64_t chmpx_h; // ChmCntrl object handle +typedef uint64_t chmpx_pkt_h; // COMPKT handle + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +#define RCV_NO_WAIT 0 // no wait for receiving(=EVENT_NOWAIT) + +//--------------------------------------------------------- +// Structure & Utilities +//--------------------------------------------------------- +// Utility structure for binary message which has key and value. +// +// Some function gets CHMKVP pointer for sending message. +// These structure and functions help you make binary message pack +// and make hash code. +// +// This structure types are based and defined in k2hash. +// +typedef K2HBIN CHMBIN; +typedef PK2HBIN PCHMBIN; + +#define CHM_FREE_CHMBIN free_k2hbin +#define CHM_FREE_CHMBINS free_k2hbins + +typedef struct chmpx_kv_pair{ + CHMBIN key; + CHMBIN val; +}CHMKVP, *PCHMKVP; + +typedef struct chm_merge_getparam{ + chmhash_t starthash; + struct timespec startts; // start time for update datas + struct timespec endts; // end time for update datas + chmhash_t target_hash; // target hash value(0 ... target_max_hash) + chmhash_t target_max_hash; // max target hash value + chmhash_t old_hash; // old hash value(0 ... old_max_hash) + chmhash_t old_max_hash; // max old target hash value + long target_hash_range; // range for hash value(use both target_hash and old_hash) + bool is_expire_check; // whether checking expire time +}CHM_MERGE_GETPARAM, *PCHM_MERGE_GETPARAM; + +//--------------------------------------------------------- +// Prototype functions +//--------------------------------------------------------- +// Callback prototype function for merging +// +// chm_merge_lastts_cb +// This callback function is called for getting lastest update time in +// client dadabase before merging. +// The return value(time) from this function will be used start time of +// target data for merging. +// If you did not specify this callback at chmpx_create_ex, the start +// time should be zero for merging. It means the target data is all of +// database. +// +// chm_merge_get_cb +// This callback function is called for getting target data for merging. +// The parameter CHM_MERGE_GETPARAM means start of hash value, range +// from start hash value and time range(start to end). +// This callback function should return the datas which has minimum hash +// value in range of hash value. +// If there are some target hash datas, you have to return all of them. +// The return value for pnexthash should be set next start hash value. +// When there is no more data, this function have to return the zero in +// pdatacnt. +// If you did not specify this callback at chmpx_create_ex, no data is +// merged. +// +// chm_merge_set_cb +// This callback function is called for setting one data for merging. +// The parameters are datas which must be merged into database. +// If you did not specify this callback at chmpx_create_ex, no data is +// merged. +// +typedef bool (*chm_merge_lastts_cb)(chmpx_h handle, struct timespec* pts); +typedef bool (*chm_merge_get_cb)(chmpx_h handle, const PCHM_MERGE_GETPARAM pparam, chmhash_t* pnexthash, PCHMBIN* ppdatas, size_t* pdatacnt); +typedef bool (*chm_merge_set_cb)(chmpx_h handle, size_t length, const unsigned char* pdata, const struct timespec* pts); + +//--------------------------------------------------------- +// Functions +//--------------------------------------------------------- +// [debug] +// +// chmpx_bump_debug_level bumpup debugging level(silent -> error -> warning -> messages ->...) +// chmpx_set_debug_level_silent set silent for debugging level +// chmpx_set_debug_level_error set error for debugging level +// chmpx_set_debug_level_warning set warning for debugging level +// chmpx_set_debug_level_message set message for debugging level +// chmpx_set_debug_level_dump set dump for debugging level +// chmpx_set_debug_file set file path for debugging message +// chmpx_unset_debug_file unset file path for debugging message to stderr(default) +// chmpx_load_debug_env set debugging level and file path by loading environment. +// +extern void chmpx_bump_debug_level(void); +extern void chmpx_set_debug_level_silent(void); +extern void chmpx_set_debug_level_error(void); +extern void chmpx_set_debug_level_warning(void); +extern void chmpx_set_debug_level_message(void); +extern void chmpx_set_debug_level_dump(void); +extern bool chmpx_set_debug_file(const char* filepath); +extern bool chmpx_unset_debug_file(void); +extern bool chmpx_load_debug_env(void); + +// [load hash library] +// +// chmpx_load_hash_library load extra hash library(lap k2hash function) +// chmpx_unload_hash_library unload extra hash library(lap k2hash function) +// +extern bool chmpx_load_hash_library(const char* libpath); +extern bool chmpx_unload_hash_library(void); + +// [create/destroy] +// +// chmpx_create_ex create(join) chmpx process as client with callback function(only join to server) +// chmpx_create create(join) chmpx process as client +// chmpx_destroy destroy(leave) chmpx process +// chmpx_pkth_destroy destroy compkt handle +// +extern chmpx_h chmpx_create_ex(const char* conffile, bool is_on_server, bool is_auto_rejoin, chm_merge_get_cb getfp, chm_merge_set_cb setfp, chm_merge_lastts_cb lastupdatefp); +extern chmpx_h chmpx_create(const char* conffile, bool is_on_server, bool is_auto_rejoin); +extern bool chmpx_destroy(chmpx_h handle); +extern bool chmpx_pkth_destroy(chmpx_pkt_h pckthandle); + +// [send/receive for server side] +// +// chmpx_svr_send send message +// chmpx_svr_send_kvp send message by CHMKVP structure +// chmpx_svr_send_kvp_ex send message with(out) self chmpx mode by CHMKVP structure +// chmpx_svr_send_kv send message by key and value +// chmpx_svr_send_kv_ex send message with(out) self chmpx mode by key and value +// chmpx_svr_broadcast send(broadcast) message +// chmpx_svr_broadcast_ex send(broadcast) message with(out) self chmpx mode by key and value +// chmpx_svr_replicate send(replicate) message without self chmpx mode +// chmpx_svr_replicate_ex send(replicate) message with(out) self chmpx mode by key and value +// chmpx_svr_receive receive message(ppbody, plength are allowed NULL, timeout_ms can be set RCV_NO_WAIT) +// +extern bool chmpx_svr_send(chmpx_h handle, const unsigned char* pbody, size_t blength, chmhash_t hash, bool is_routing); +extern bool chmpx_svr_send_kvp(chmpx_h handle, const PCHMKVP pkvp, bool is_routing); +extern bool chmpx_svr_send_kvp_ex(chmpx_h handle, const PCHMKVP pkvp, bool is_routing, bool without_self); +extern bool chmpx_svr_send_kv(chmpx_h handle, unsigned char* pkey, size_t keylen, unsigned char* pval, size_t vallen, bool is_routing); +extern bool chmpx_svr_send_kv_ex(chmpx_h handle, unsigned char* pkey, size_t keylen, unsigned char* pval, size_t vallen, bool is_routing, bool without_self); +extern bool chmpx_svr_broadcast(chmpx_h handle, const unsigned char* pbody, size_t blength, chmhash_t hash); +extern bool chmpx_svr_broadcast_ex(chmpx_h handle, const unsigned char* pbody, size_t blength, chmhash_t hash, bool without_self); +extern bool chmpx_svr_replicate(chmpx_h handle, const unsigned char* pbody, size_t blength, chmhash_t hash); +extern bool chmpx_svr_replicate_ex(chmpx_h handle, const unsigned char* pbody, size_t blength, chmhash_t hash, bool without_self); +extern bool chmpx_svr_receive(chmpx_h handle, chmpx_pkt_h* ppckthandle, unsigned char** ppbody, size_t* plength, int timeout_ms, bool no_giveup_rejoin); + +// [send/receive for slave side] +// +// chmpx_open open message handle +// chmpx_close close message handle +// chmpx_msg_send send message +// chmpx_msg_send_kvp send message by CHMKVP +// chmpx_msg_send_kv send message by key and value +// chmpx_msg_broadcast send(broadcast) message +// chmpx_msg_replicate send(replicate) message +// chmpx_msg_receive receive message(timeout_ms can be set RCV_NO_WAIT) +// +extern msgid_t chmpx_open(chmpx_h handle, bool no_giveup_rejoin); +extern bool chmpx_close(chmpx_h handle, msgid_t msgid); +extern bool chmpx_msg_send(chmpx_h handle, msgid_t msgid, const unsigned char* pbody, size_t blength, chmhash_t hash, long* preceivercnt, bool is_routing); +extern bool chmpx_msg_send_kvp(chmpx_h handle, msgid_t msgid, const PCHMKVP pkvp, long* preceivercnt, bool is_routing); +extern bool chmpx_msg_send_kv(chmpx_h handle, msgid_t msgid, unsigned char* pkey, size_t keylen, unsigned char* pval, size_t vallen, long* preceivercnt, bool is_routing); +extern bool chmpx_msg_broadcast(chmpx_h handle, msgid_t msgid, const unsigned char* pbody, size_t blength, chmhash_t hash, long* preceivercnt); +extern bool chmpx_msg_replicate(chmpx_h handle, msgid_t msgid, const unsigned char* pbody, size_t blength, chmhash_t hash, long* preceivercnt); +extern bool chmpx_msg_receive(chmpx_h handle, msgid_t msgid, chmpx_pkt_h* ppckthandle, unsigned char** ppbody, size_t* plength, int timeout_ms); + +// [reply for both server and slave side] +// +// chmpx_msg_reply reply message +// chmpx_msg_reply_kvp reply message by CHMKVP +// chmpx_msg_reply_kv reply message by key and value +// +extern bool chmpx_msg_reply(chmpx_h handle, chmpx_pkt_h pckthandle, const unsigned char* pbody, size_t blength); +extern bool chmpx_msg_reply_kvp(chmpx_h handle, chmpx_pkt_h pckthandle, const PCHMKVP pkvp); +extern bool chmpx_msg_reply_kv(chmpx_h handle, chmpx_pkt_h pckthandle, unsigned char* pkey, size_t keylen, unsigned char* pval, size_t vallen); + +// [check chmpx process] +// +// is_chmpx_proc_exists check chmpx process exists +// +extern bool is_chmpx_proc_exists(chmpx_h handle); + +//--------------------------------------------------------- +// Version +//--------------------------------------------------------- +extern void chmpx_print_version(FILE* stream); + +//--------------------------------------------------------- +// Utility Functions(Key Value Pair) +//--------------------------------------------------------- +extern unsigned char* cvt_kvp_bin(const PCHMKVP pkvp, size_t* plength); +extern bool cvt_bin_kvp(PCHMKVP pkvp, unsigned char* bydata, size_t length); +extern chmhash_t make_chmbin_hash(const PCHMBIN pchmbin); +extern chmhash_t make_kvp_hash(const PCHMKVP pkvp); + +DECL_EXTERN_C_END + +#endif // CHMPX_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmregex.cc b/lib/chmregex.cc new file mode 100644 index 0000000..9d31ef1 --- /dev/null +++ b/lib/chmregex.cc @@ -0,0 +1,365 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "chmcommon.h" +#include "chmutil.h" +#include "chmdbg.h" +#include "chmregex.h" +#include "chmnetdb.h" + +using namespace std; + +//--------------------------------------------------------- +// Simple regex +//--------------------------------------------------------- +// Chmpx configuration allows FQDN and custom simple regex +// for hostname. +// FQDN must be listed by DNS(or /etc/hosts) and do not have +// to specify full FQDN. +// Custom simple regex is following: +// server[xx,yy].yahoo.co.jp - Many strings +// server[0-9].yahoo.co.jp - Number range +// server[A-K].yahoo.co.jp - Alphabetical range, Must be a-z or A-Z. +// s[A-K][0-9].yahoo.co.jp - Mixed +// And can specify own server by 'localhost'. +// Last, IP address(IPv4 or IPv6) can be specified. But if it +// is specified in server list, that IP address must be reverse +// to FQDN by DNS(or /etc/hosts). +// If it is in slave list, not need to reverse. +// +// The chmpx process checks hostname list when the other chmpx +// accesses to them. +// +// If the slave chmpx accesses to server when chmpx runs as +// server mode, the server chmpx checks only IP address against +// hostname(FQDN, IP..) list from configuration file. +// Another if the server mode chmpx accesses to another server +// chmpx, the server checks FQDN converted from IP address. +// +// The chmpx makes RING by consistent hashing, they need to +// make order for servers, so that the server chmpx must have +// FQDN. But the slave chmpx dows not have FQDN, only need +// to list in configuration file. +// +// Then following functions are for simple regex. +// +static bool expand_simple_regex_string(const string& str_part_regex, strlst_t& expand_lst) +{ + string strtarget; + strlst_t sep_commma_lst; + string::size_type pos; + + expand_lst.clear(); + + // parse ',' + for(strtarget = trim(str_part_regex); strtarget.length(); strtarget = trim(strtarget)){ + if(string::npos == (pos = strtarget.find(","))){ + sep_commma_lst.push_back(strtarget); + strtarget = ""; + }else{ + string tmp = strtarget.substr(0, pos); + tmp = trim(tmp); + if(tmp.length()){ + sep_commma_lst.push_back(tmp); + } + strtarget = strtarget.substr(pos + 1); + } + } + + // parse '-' in comma sepalated array + for(strlst_t::const_iterator iter = sep_commma_lst.begin(); iter != sep_commma_lst.end(); ++iter){ + if(string::npos == (pos = iter->find("-"))){ + expand_lst.push_back(*iter); + }else{ + // found '-' + string tmp1 = iter->substr(0, pos); + string tmp2 = iter->substr(pos + 1); + tmp1 = trim(tmp1); + tmp2 = trim(tmp2); + if(0 == tmp1.length() || 0 == tmp2.length()){ + MSG_CHMPRN("Area strings sepalated are empty."); + return false; + } + if(string::npos != tmp2.find("-")){ + MSG_CHMPRN("Found many area sepalator."); + return false; + } + + if(is_string_number(tmp1.c_str()) && is_string_number(tmp2.c_str())){ + // Number + int num1 = atoi(tmp1.c_str()); + int num2 = atoi(tmp2.c_str()); + if(num2 < num1){ + MSG_CHMPRN("Number range are wrong."); + return false; + } + for(; num1 <= num2; num1++){ + expand_lst.push_back(to_string(num1)); + } + }else{ + // Alpha + if(1 != tmp1.length() || 1 != tmp2.length()){ + MSG_CHMPRN("Charactor range must be specified by one charactor."); + return false; + } + char cTmp1 = tmp1[0]; + char cTmp2 = tmp2[0]; + if(!(('A' <= cTmp1 && cTmp1 <= 'Z') || ('a' <= cTmp1 && cTmp1 <= 'z')) || !(('A' <= cTmp2 && cTmp2 <= 'Z') || ('a' <= cTmp2 && cTmp2 <= 'z'))){ + MSG_CHMPRN("Charactor range must be specified by a-z or A-Z."); + return false; + } + if(cTmp2 < cTmp1 || !(('a' <= cTmp1 && 'a' <= cTmp2) || (cTmp1 <= 'Z' && cTmp2 <= 'Z'))){ + MSG_CHMPRN("Both charactor word does not same range."); + return false; + } + for(; cTmp1 <= cTmp2; cTmp1++){ + expand_lst.push_back(string(1, cTmp1)); + } + } + } + } + return true; +} + +static bool expand_simple_regex(const string& simple_regex, strlst_t& expand_lst) +{ + strlst_t simple_regex_lst; + string one_simple_regex; + + for(simple_regex_lst.push_back(trim(simple_regex)); 0 < simple_regex_lst.size(); ){ + one_simple_regex = simple_regex_lst.front(); + simple_regex_lst.pop_front(); + + string::size_type pos; + string::size_type pos2 = one_simple_regex.find("]"); + + if(string::npos == (pos = one_simple_regex.find("["))){ + if(string::npos != pos2){ + MSG_CHMPRN("Found \']\' seplator word without \'[\' word."); + return false; + } + expand_lst.push_back(one_simple_regex); + + }else{ + // found '[' + string prefix_str = one_simple_regex.substr(0, pos); + one_simple_regex = one_simple_regex.substr(pos + 1); + + if(string::npos != pos2 && pos2 < pos){ + MSG_CHMPRN("Found \']\' seplator word without \'[\' word."); + return false; + } + + if(string::npos == (pos = one_simple_regex.find("]"))){ + MSG_CHMPRN("Not found \']\' seplator word."); + return false; + } + string str_part_regex = one_simple_regex.substr(0, pos); + string suffix_str = one_simple_regex.substr(pos + 1); + + str_part_regex = trim(str_part_regex); + if(0 == str_part_regex.length()){ + MSG_CHMPRN("There is no string in \'[\' to \']\' area."); + return false; + } + + if(string::npos != str_part_regex.find("[")){ + MSG_CHMPRN("Found many \'[\' seplator word."); + return false; + } + + // parse [...] to string array + strlst_t expandarea; + if(!expand_simple_regex_string(str_part_regex, expandarea)){ + MSG_CHMPRN("Could not expand simple regex, maybe string is wrong."); + return false; + } + + // push target array for recheck + for(strlst_t::const_iterator iter = expandarea.begin(); iter != expandarea.end(); ++iter){ + string tmp; + tmp = prefix_str; + tmp += *iter; + tmp += suffix_str; + simple_regex_lst.push_back(tmp); + } + } + } + return true; +} + +//--------------------------------------------------------- +// Utilities +//--------------------------------------------------------- +// For server hostname +// +// This function expands hostname list from hostname which +// has simple regex rule. +// If is_cvt_fqdn is ture, all hostname is checked by NetDB. +// The other does not check. +// If is_cvt_localhost is true, hostanme which is "localhost" +// or "127.0.0.1" or "::1" is changed FQDN. +// +bool ExpandSimpleRegxHostname(const char* hostname, strlst_t& expand_lst, bool is_cvt_localhost, bool is_cvt_fqdn, bool is_strict) +{ + if(CHMEMPTYSTR(hostname)){ + ERR_CHMPRN("Parameter is NULL."); + return false; + } + string strhost = hostname; + + expand_lst.clear(); + if(!expand_simple_regex(strhost, expand_lst) || 0 == expand_lst.size()){ + ERR_CHMPRN("Failed to expand simple regex."); + return false; + } + + if(is_cvt_fqdn){ + // check & convert IP address or localhost to FQDN. + strlst_t tmp_lst = expand_lst; + expand_lst.clear(); + for(strlst_t::const_iterator iter = tmp_lst.begin(); iter != tmp_lst.end(); ++iter){ + string fqdn; + if(!ChmNetDb::Get()->GetHostname(iter->c_str(), fqdn, is_cvt_localhost)){ + if(is_strict){ + ERR_CHMPRN("Failed to convert FQDN from %s, so break because this function stric mode", iter->c_str()); + return false; + }else{ + //MSG_CHMPRN("Failed to convert FQDN from %s, but skip it and continue...", iter->c_str()); + continue; + } + } + expand_lst.push_back(fqdn); + } + } + return true; +} + +// For server hostname +// +// This function is checking hostanme in expanded hostname list +// for server list. The hostname_lst should be expanded by +// ExpandSimpleRegxHostname() with is_cvt_localhost = true and +// is_cvt_fqdn = true. +// If the hostname matches in array, matchhostname is set as +// matched hostname(FQDN or localhost or IP address). +// +bool IsInHostnameList(const char* hostname, strlst_t& hostname_lst, string& matchhostname) +{ + if(CHMEMPTYSTR(hostname)){ + ERR_CHMPRN("Parameter is NULL."); + return false; + } + + if(!ChmNetDb::Get()->GetHostname(hostname, matchhostname, true)){ // If "localhost", convert local host name + ERR_CHMPRN("Failed to convert FQDN from %s.", hostname); + return false; + } + for(strlst_t::const_iterator iter = hostname_lst.begin(); iter != hostname_lst.end(); ++iter){ + if(matchhostname == (*iter)){ + // found! + return true; + } + } + return false; +} + +// For slave hostname +// +// This function is checking hostanme in hostname array which +// are wrote regex. +// If the hostname matches in array, matchhostname is set as +// matched hostname(FQDN or localhost or IP address). +// +bool IsMatchHostname(const char* hostname, strlst_t& regex_lst, string& matchhostname) +{ + if(CHMEMPTYSTR(hostname)){ + ERR_CHMPRN("Parameter is NULL."); + return false; + } + string fqdn; + string ipaddress; + string if_ipaddress; + strlst_t target_lst; // order by global hostname, ip address, i/f ip address, localhost + + // make from hostname to FQDN & IP address list. + // + if(!ChmNetDb::Get()->GetHostname(hostname, fqdn, true)){ // If "localhost", convert FQDN host name + ERR_CHMPRN("Failed to convert FQDN from %s.", hostname); + return false; + } + target_lst.push_back(fqdn); // global name is added at first of list + + if(!ChmNetDb::Get()->GetIpAddressString(fqdn.c_str(), ipaddress, false)){ // If "localhost", not convert + ERR_CHMPRN("Failed to convert IP address from %s.", hostname); + return false; + } + if(fqdn != ipaddress){ + target_lst.push_back(ipaddress); // if hostname is not ipaddress, set ipaddress + } + if(!ChmNetDb::Get()->GetIpAddressString(fqdn.c_str(), if_ipaddress, true)){ // If "localhost", convert I/F ip address + ERR_CHMPRN("Failed to convert I/F IP address from %s.", hostname); + return false; + } + if(if_ipaddress != ipaddress){ + target_lst.push_back(if_ipaddress); + } + if(0 != strcmp(hostname, fqdn.c_str())){ + target_lst.push_back(string(hostname)); // if hostname is not same as globalname, add to end of list. + } + + // Matching check + for(strlst_t::const_iterator iter = regex_lst.begin(); iter != regex_lst.end(); ++iter){ + regex_t regex_obj; + int result; + if(0 != (result = regcomp(®ex_obj, iter->c_str(), REG_EXTENDED | REG_NOSUB))){ + ERR_CHMPRN("Failed to compile regex for %s.", iter->c_str()); + return false; + } + + for(strlst_t::const_iterator titer = target_lst.begin(); titer != target_lst.end(); ++titer){ + if(0 == regexec(®ex_obj, titer->c_str(), 0, NULL, 0)){ + // match! + matchhostname = (*titer); + regfree(®ex_obj); + return true; + } + } + regfree(®ex_obj); + } + return false; +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmregex.h b/lib/chmregex.h new file mode 100644 index 0000000..df40990 --- /dev/null +++ b/lib/chmregex.h @@ -0,0 +1,39 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMREGEX_H +#define CHMREGEX_H + +//--------------------------------------------------------- +// Utilities +//--------------------------------------------------------- +bool ExpandSimpleRegxHostname(const char* hostname, strlst_t& expand_lst, bool is_cvt_localhost, bool is_cvt_fqdn = true, bool is_strict = false); +bool IsInHostnameList(const char* hostname, strlst_t& hostname_lst, std::string& matchhostname); +bool IsMatchHostname(const char* hostname, strlst_t& regex_lst, std::string& matchhostname); + +#endif // CHMREGEX_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmsigcntrl.cc b/lib/chmsigcntrl.cc new file mode 100644 index 0000000..a7a0ef6 --- /dev/null +++ b/lib/chmsigcntrl.cc @@ -0,0 +1,207 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include +#include + +#include "chmcommon.h" +#include "chmsigcntrl.h" +#include "chmutil.h" +#include "chmdbg.h" + +using namespace std; + +//--------------------------------------------------------- +// Class variable +//--------------------------------------------------------- +ChmSigCntrl ChmSigCntrl::singleton; +sigset_map_t ChmSigCntrl::signalmap; +int ChmSigCntrl::lockval = FLCK_NOSHARED_MUTEX_VAL_UNLOCKED; + +//--------------------------------------------------------- +// Methods +//--------------------------------------------------------- +ChmSigCntrl::ChmSigCntrl() +{ +} + +ChmSigCntrl::~ChmSigCntrl() +{ + if(this == (&ChmSigCntrl::singleton)){ + // Do not need to unset signal handler. + // There is a case that already ChmSigCntrl::signalmap object is deleted, + // so invalide access to ChmSigCntrl::signalmap address. + // + //ResetAllHandler(); + } +} + +bool ChmSigCntrl::Initialize(int* psignums, int count) +{ + while(!fullock::flck_trylock_noshared_mutex(&ChmSigCntrl::lockval)); + + ChmSigCntrl::signalmap.clear(); + + // static signals + ChmSigCntrl::signalmap[SIGILL] = false; + ChmSigCntrl::signalmap[SIGABRT] = false; + ChmSigCntrl::signalmap[SIGFPE] = false; + ChmSigCntrl::signalmap[SIGKILL] = false; + ChmSigCntrl::signalmap[SIGSEGV] = false; + ChmSigCntrl::signalmap[SIGBUS] = false; + ChmSigCntrl::signalmap[SIGCONT] = false; + ChmSigCntrl::signalmap[SIGSTOP] = false; + ChmSigCntrl::signalmap[SIGTSTP] = false; + + if(!psignums || 0 >= count){ + fullock::flck_unlock_noshared_mutex(&ChmSigCntrl::lockval); + return true; + } + for(int cnt = 0; cnt < count; cnt++){ + signalmap[psignums[cnt]] = false; + } + fullock::flck_unlock_noshared_mutex(&ChmSigCntrl::lockval); + + return true; +} + +bool ChmSigCntrl::GetSignalMask(sigset_t& sigset) +{ + if(0 != sigfillset(&sigset)){ + ERR_CHMPRN("Failed to fill to sigset."); + return false; + } + + while(!fullock::flck_trylock_noshared_mutex(&ChmSigCntrl::lockval)); + + for(sigset_map_t::const_iterator iter = ChmSigCntrl::signalmap.begin(); iter != ChmSigCntrl::signalmap.end(); ++iter){ + if(0 != sigdelset(&sigset, iter->first)){ + ERR_CHMPRN("Failed to unset signal(%d).", iter->first); + fullock::flck_unlock_noshared_mutex(&ChmSigCntrl::lockval); + return false; + } + } + fullock::flck_unlock_noshared_mutex(&ChmSigCntrl::lockval); + + return true; +} + +bool ChmSigCntrl::SetSignalProcMask(void) +{ + sigset_t sigset; + if(!GetSignalMask(sigset)){ + ERR_CHMPRN("Failed to set sigset value."); + return false; + } + if(0 != sigprocmask(SIG_SETMASK, &sigset, NULL)){ + ERR_CHMPRN("Failed to set signal proc mask(errno=%d).", errno); + return false; + } + return true; +} + +bool ChmSigCntrl::FindSignalEx(int signum, bool is_locked) +{ + if(!is_locked){ + while(!fullock::flck_trylock_noshared_mutex(&ChmSigCntrl::lockval)); + } + + bool result = true; + if(ChmSigCntrl::signalmap.end() == ChmSigCntrl::signalmap.find(signum)){ + result = false; + } + + if(!is_locked){ + fullock::flck_unlock_noshared_mutex(&ChmSigCntrl::lockval); + } + return result; +} + +bool ChmSigCntrl::SetHandlerEx(int signum, sighandler_t handler, bool is_locked) +{ + if(!FindSignalEx(signum, is_locked)){ + ERR_CHMPRN("Signal(%d) is not set this class.", signum); + return false; + } + bool enable = (NULL != handler && SIG_DFL != handler && SIG_IGN != handler); // Simplify + + if(!is_locked){ + while(!fullock::flck_trylock_noshared_mutex(&ChmSigCntrl::lockval)); + } + if(enable == ChmSigCntrl::signalmap[signum]){ + MSG_CHMPRN("Signal(%d) handler is already %s.", signum, enable ? "set" : "unset"); + if(!enable){ + if(!is_locked){ + fullock::flck_unlock_noshared_mutex(&ChmSigCntrl::lockval); + } + return true; + } + // if enable, over set signal + } + + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, signum); + sa.sa_flags = enable ? 0 : SA_RESETHAND; + sa.sa_handler = enable ? handler : SIG_DFL; + + if(0 > sigaction(signum, &sa, NULL)){ + ERR_CHMPRN("Could not %s signal(%d) handler, errno=%d", enable ? "set" : "unset", signum, errno); + if(!is_locked){ + fullock::flck_unlock_noshared_mutex(&ChmSigCntrl::lockval); + } + return false; + } + ChmSigCntrl::signalmap[signum] = enable; + + if(!is_locked){ + fullock::flck_unlock_noshared_mutex(&ChmSigCntrl::lockval); + } + return true; +} + +bool ChmSigCntrl::ResetAllHandler(void) +{ + while(!fullock::flck_trylock_noshared_mutex(&ChmSigCntrl::lockval)); + + for(sigset_map_t::iterator iter = ChmSigCntrl::signalmap.begin(); iter != ChmSigCntrl::signalmap.end(); ++iter){ + if(iter->second){ + if(!SetHandlerEx(iter->first, SIG_DFL, true)){ + ERR_CHMPRN("Could not reset signal(%d) handler.", iter->first); + continue; + } + iter->second = false; + } + } + fullock::flck_unlock_noshared_mutex(&ChmSigCntrl::lockval); + + return true; +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmsigcntrl.h b/lib/chmsigcntrl.h new file mode 100644 index 0000000..9eefbe5 --- /dev/null +++ b/lib/chmsigcntrl.h @@ -0,0 +1,68 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMSIGCNTRL_H +#define CHMSIGCNTRL_H + +#include +#include + +#include +#include + +//--------------------------------------------------------- +// Typedefs +//--------------------------------------------------------- +typedef std::map sigset_map_t; + +//--------------------------------------------------------- +// ChmSigCntrl Class +//--------------------------------------------------------- +class ChmSigCntrl +{ + protected: + static ChmSigCntrl singleton; + static sigset_map_t signalmap; + static int lockval; // like mutex + + protected: + bool SetHandlerEx(int signum, sighandler_t handler, bool is_locked); // can set NULL/SIG_DFL/SIG_IGN + bool FindSignalEx(int signum, bool is_locked); + bool ResetAllHandler(void); + + public: + ChmSigCntrl(void); + virtual ~ChmSigCntrl(); + + bool Initialize(int* psignums, int count); + bool GetSignalMask(sigset_t& sigset); + bool SetSignalProcMask(void); + bool SetHandler(int signum, sighandler_t handler) { return SetHandlerEx(signum, handler, false); } +}; + +#endif // CHMSIGCNTRL_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmstream.h b/lib/chmstream.h new file mode 100644 index 0000000..09371b8 --- /dev/null +++ b/lib/chmstream.h @@ -0,0 +1,925 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMSTREAM_H +#define CHMSTREAM_H + +#include + +#include "chmcommon.h" +#include "chmpx.h" +#include "chmutil.h" +#include "chmdbg.h" +#include "chmcntrl.h" +#include "chmkvp.h" + +//--------------------------------------------------------- +// Template basic_chmstreambuf +//--------------------------------------------------------- +template +class basic_chmstreambuf : public std::basic_streambuf +{ + public: + typedef CharT char_type; + typedef Traits traits_type; + typedef typename traits_type::int_type int_type; + typedef typename traits_type::pos_type pos_type; + typedef typename traits_type::off_type off_type; + typedef std::ios_base::openmode open_mode; + typedef std::basic_streambuf streambuf_type; + + private: + static const size_t pagesize = 4096; + static const int default_timeout = 1 * 10; // 10ms + + ChmCntrl* pchmcntrl; + PCOMPKT plastpkt; + msgid_t msgid; + open_mode mode; + chmhash_t init_hash; + bool is_init_hash; + bool is_init_key; + size_t input_buff_size; + size_t output_buff_size; + char_type* input_buff; + char_type* output_buff; + char_type* output_val_pos; + int read_timeout; + + protected: + virtual int sync(void); + virtual int_type overflow(int_type ch = traits_type::eof()); + virtual int_type pbackfail(int_type ch = traits_type::eof()); + virtual int_type underflow(void); + + virtual pos_type seekoff(off_type offset, std::ios_base::seekdir bpostype, open_mode opmode = std::ios_base::in | std::ios_base::out); + virtual pos_type seekpos(pos_type abspos, open_mode opmode = std::ios_base::in | std::ios_base::out); + + private: + bool init_output_buff(void); + bool send_sync(void); + bool receive_sync(void); + + public: + basic_chmstreambuf(open_mode opmode = std::ios_base::in | std::ios_base::out); // Do not call this + basic_chmstreambuf(ChmCntrl* pchmobj, open_mode opmode = std::ios_base::in | std::ios_base::out); + basic_chmstreambuf(ChmCntrl* pchmobj, chmhash_t hash, open_mode opmode = std::ios_base::in | std::ios_base::out); + basic_chmstreambuf(ChmCntrl* pchmobj, const std::string& strkey, open_mode opmode = std::ios_base::in | std::ios_base::out); + virtual ~basic_chmstreambuf(); + + bool reset(void); + bool init(ChmCntrl* pchmobj, open_mode opmode = std::ios_base::in | std::ios_base::out); + bool init(ChmCntrl* pchmobj, chmhash_t hash, open_mode opmode = std::ios_base::in | std::ios_base::out); + bool init(ChmCntrl* pchmobj, const std::string& strkey, open_mode opmode = std::ios_base::in | std::ios_base::out); + + chmhash_t receivedhash(void) const { return (plastpkt ? plastpkt->head.hash : CHM_INVALID_HASHVAL); } +}; + +//--------------------------------------------------------- +// template basic_chmstreambuf +//--------------------------------------------------------- +template +basic_chmstreambuf::basic_chmstreambuf(open_mode opmode) : pchmcntrl(NULL), plastpkt(NULL), msgid(CHM_INVALID_MSGID), mode(opmode), init_hash(CHM_INVALID_HASHVAL), is_init_hash(false), is_init_key(false), input_buff_size(0), output_buff_size(0), input_buff(reinterpret_cast(NULL)), output_buff(reinterpret_cast(NULL)), output_val_pos(reinterpret_cast(NULL)) +{ +} + +template +basic_chmstreambuf::basic_chmstreambuf(ChmCntrl* pchmobj, open_mode opmode) : pchmcntrl(NULL), plastpkt(NULL), msgid(CHM_INVALID_MSGID), mode(opmode), init_hash(CHM_INVALID_HASHVAL), is_init_hash(false), is_init_key(false), input_buff_size(0), output_buff_size(0), input_buff(reinterpret_cast(NULL)), output_buff(reinterpret_cast(NULL)), output_val_pos(reinterpret_cast(NULL)) +{ + if(!pchmobj){ + ERR_CHMPRN("Parameter is wrong."); + return; + } + if(!init(pchmobj, opmode)){ + WAN_CHMPRN("Parameter is sonmething wrong"); + return; + } +} + +template +basic_chmstreambuf::basic_chmstreambuf(ChmCntrl* pchmobj, chmhash_t hash, open_mode opmode) : pchmcntrl(NULL), plastpkt(NULL), msgid(CHM_INVALID_MSGID), mode(opmode), init_hash(hash), is_init_hash(false), is_init_key(false), input_buff_size(0), output_buff_size(0), input_buff(reinterpret_cast(NULL)), output_buff(reinterpret_cast(NULL)), output_val_pos(reinterpret_cast(NULL)) +{ + if(!pchmobj){ + ERR_CHMPRN("Parameter is wrong."); + return; + } + if(!init(pchmobj, hash, opmode)){ + WAN_CHMPRN("Parameter is sonmething wrong"); + return; + } +} + +template +basic_chmstreambuf::basic_chmstreambuf(ChmCntrl* pchmobj, const std::string& strkey, open_mode opmode) : pchmcntrl(NULL), plastpkt(NULL), msgid(CHM_INVALID_MSGID), mode(opmode), init_hash(CHM_INVALID_HASHVAL), is_init_hash(false), is_init_key(false), input_buff_size(0), output_buff_size(0), input_buff(reinterpret_cast(NULL)), output_buff(reinterpret_cast(NULL)), output_val_pos(reinterpret_cast(NULL)) +{ + if(!pchmobj){ + ERR_CHMPRN("Parameter is wrong."); + return; + } + if(!init(pchmobj, strkey, opmode)){ + WAN_CHMPRN("Parameter is sonmething wrong"); + return; + } +} + +template +basic_chmstreambuf::~basic_chmstreambuf(void) +{ + reset(); +} + +template +bool basic_chmstreambuf::reset(void) +{ + // If there is no-flushed write buffer, flush it here. + // + if(pchmcntrl && streambuf_type::pbase() < streambuf_type::pptr()){ + // If there are left data, thun send it. + // Be careful, if is_init_key is true, current posision must be over start os value data. + // + if(!is_init_key || (reinterpret_cast(&output_buff[strlen(output_buff) + 1]) < streambuf_type::pptr())){ + if(!send_sync()){ + WAN_CHMPRN("Could not put left write buffer into."); + } + } + } + + if(pchmcntrl && CHM_INVALID_MSGID != msgid){ + // close msgid + if(!pchmcntrl->Close(msgid)){ + WAN_CHMPRN("Could not close MQ."); + } + } + pchmcntrl = NULL; + msgid = CHM_INVALID_MSGID; + + mode = std::ios_base::in | std::ios_base::out; + init_hash = CHM_INVALID_HASHVAL; + is_init_hash = false; + is_init_key = false; + input_buff_size = 0; + output_buff_size= 0; + output_val_pos = reinterpret_cast(NULL); + read_timeout = basic_chmstreambuf::default_timeout; + + CHM_Free(input_buff); + CHM_Free(output_buff); + CHM_Free(plastpkt); + + return true; +} + +template +bool basic_chmstreambuf::init(ChmCntrl* pchmobj, open_mode opmode) +{ + reset(); + + if(!pchmobj){ + return true; + } + if(pchmobj->IsChmpxType()){ + ERR_CHMPRN("ChmCntrl type does not server(slave) side."); + return false; + } + pchmcntrl = pchmobj; + mode = (opmode & (std::ios_base::in | std::ios_base::out)); + + if(opmode & std::ios_base::out){ + output_buff_size = basic_chmstreambuf::pagesize; + + if(reinterpret_cast(NULL) == (output_buff = reinterpret_cast(calloc(output_buff_size, sizeof(char_type))))){ + ERR_CHMPRN("Could not allocate memory."); + reset(); + return false; + } + streambuf_type::setp(output_buff, reinterpret_cast(reinterpret_cast(output_buff) + static_cast(output_buff_size))); + } + if(opmode & std::ios_base::in){ + // input_buff = NULL + streambuf_type::setg(input_buff, input_buff, reinterpret_cast(reinterpret_cast(input_buff) + static_cast(input_buff_size))); + } + return true; +} + +template +bool basic_chmstreambuf::init(ChmCntrl* pchmobj, chmhash_t hash, open_mode opmode) +{ + reset(); + + if(!pchmobj){ + return true; + } + if(pchmobj->IsChmpxType()){ + ERR_CHMPRN("ChmCntrl type does not server(slave) side."); + return false; + } + if(0 == (opmode & std::ios_base::out)){ + ERR_CHMPRN("open_mode does not have std::ios_base::out."); + return false; + } + pchmcntrl = pchmobj; + mode = (opmode & (std::ios_base::in | std::ios_base::out)); + init_hash = hash; + is_init_hash= true; + + if(opmode & std::ios_base::out){ + output_buff_size = basic_chmstreambuf::pagesize; + + if(reinterpret_cast(NULL) == (output_buff = reinterpret_cast(calloc(output_buff_size, sizeof(char_type))))){ + ERR_CHMPRN("Could not allocate memory."); + reset(); + return false; + } + streambuf_type::setp(output_buff, reinterpret_cast(reinterpret_cast(output_buff) + static_cast(output_buff_size))); + } + if(opmode & std::ios_base::in){ + // input_buff = NULL + streambuf_type::setg(input_buff, input_buff, reinterpret_cast(reinterpret_cast(input_buff) + static_cast(input_buff_size))); + } + return true; +} + +template +bool basic_chmstreambuf::init(ChmCntrl* pchmobj, const std::string& strkey, open_mode opmode) +{ + reset(); + + if(!pchmobj){ + return true; + } + if(pchmobj->IsChmpxType()){ + ERR_CHMPRN("ChmCntrl type does not server(slave) side."); + return false; + } + if(0 == (opmode & std::ios_base::out)){ + ERR_CHMPRN("open_mode does not have std::ios_base::out."); + return false; + } + pchmcntrl = pchmobj; + mode = (opmode & (std::ios_base::in | std::ios_base::out)); + is_init_key = true; + + if(opmode & std::ios_base::out){ + output_buff_size = ((strkey.length() + 1/* for \0 */ + 1/* for new adding buffer */) / basic_chmstreambuf::pagesize + 1) * basic_chmstreambuf::pagesize; + + if(reinterpret_cast(NULL) == (output_buff = reinterpret_cast(calloc(output_buff_size, sizeof(char_type))))){ + ERR_CHMPRN("Could not allocate memory."); + reset(); + return false; + } + strcpy(output_buff, strkey.c_str()); + + // start position is after strkey string. + streambuf_type::setp(output_buff, reinterpret_cast(reinterpret_cast(output_buff) + static_cast(output_buff_size))); + streambuf_type::pbump(strlen(output_buff) + 1); + + // set value start pos + output_val_pos = &output_buff[strlen(output_buff) + 1]; + } + if(opmode & std::ios_base::in){ + // input_buff = NULL + streambuf_type::setg(input_buff, input_buff, reinterpret_cast(reinterpret_cast(input_buff) + static_cast(input_buff_size))); + } + return true; +} + +template +bool basic_chmstreambuf::init_output_buff(void) +{ + if(!pchmcntrl){ + ERR_CHMPRN("This object did not initialized."); + return false; + } + + if(output_buff && is_init_key){ + // do not erase key area, so only reset value pos. + output_val_pos = &output_buff[strlen(output_buff) + 1]; + + // start position is after strkey string. + streambuf_type::setp(output_buff, reinterpret_cast(reinterpret_cast(output_buff) + static_cast(output_buff_size))); + streambuf_type::pbump(strlen(output_buff) + 1); + + }else{ + // clear + if(!output_buff || 0 == output_buff_size){ + CHM_Free(output_buff); + + output_buff_size = basic_chmstreambuf::pagesize; + if(reinterpret_cast(NULL) == (output_buff = reinterpret_cast(calloc(output_buff_size, sizeof(char_type))))){ + ERR_CHMPRN("Could not allocate memory."); + reset(); + return false; + } + } + *output_buff = static_cast('\0'); + output_val_pos = reinterpret_cast(NULL); + + streambuf_type::setp(output_buff, reinterpret_cast(reinterpret_cast(output_buff) + static_cast(output_buff_size))); + } + return true; +} + +template +bool basic_chmstreambuf::send_sync(void) +{ + if(!pchmcntrl){ + ERR_CHMPRN("This object did not initialized."); + return false; + } + + // raw data + chmhash_t local_hash = (is_init_hash ? init_hash : CHM_INVALID_HASHVAL); + char_type* val_pos = output_val_pos ? output_val_pos : output_buff; // backup value before reinitializing + size_t rawlength = 0L; + unsigned char* rawdata = NULL; + { + if(!output_buff || 0 == output_buff_size){ + ERR_CHMPRN("There is no data for sending."); + init_output_buff(); + return false; + } + + // make key and value structure + ChmBinData Key; + ChmBinData Value; + if((output_buff != val_pos && !Key.Set(reinterpret_cast(output_buff), strlen(output_buff) + 1)) || !Value.Set(reinterpret_cast(val_pos), strlen(val_pos) + 1)){ + ERR_CHMPRN("Could not set key and value to chmbindata."); + init_output_buff(); + return false; + } + + // make chmkvp + ChmKVPair chmkvp; + if(!chmkvp.SetKey(Key) || !chmkvp.SetValue(Value)){ + ERR_CHMPRN("Could not set key and value to chmkvp."); + init_output_buff(); + return false; + } + + // check hash value(for only slave) + if(pchmcntrl->IsClientOnSlvType() && !is_init_hash){ + if(output_buff == val_pos){ + ERR_CHMPRN("Could not make hash value for sinding on slave because of empty key value."); + init_output_buff(); + return false; + } + local_hash = chmkvp.GetHash(); + } + + // make raw data + if(NULL == (rawdata = chmkvp.Put(rawlength))){ + ERR_CHMPRN("Could not convert raw data for sending."); + init_output_buff(); + return false; + } + + // reset buffer + init_output_buff(); + } + + // sending + if(pchmcntrl->IsClientOnSvrType()){ + if(!plastpkt){ + ERR_CHMPRN("There is no sending terminal."); + CHM_Free(rawdata); + return false; + } + + if(!pchmcntrl->Reply(plastpkt, rawdata, rawlength)){ + ERR_CHMPRN("Failed replying to server chmpx."); + CHM_Free(rawdata); + return false; + } + + }else{ + if(CHM_INVALID_MSGID == msgid){ + if(CHM_INVALID_MSGID == (msgid = pchmcntrl->Open())){ + ERR_CHMPRN("Could not open MQ."); + CHM_Free(rawdata); + return false; + } + } + + if(!pchmcntrl->Send(msgid, rawdata, rawlength, local_hash)){ + ERR_CHMPRN("Failed sending to slave chmpx."); + CHM_Free(rawdata); + return false; + } + } + CHM_Free(rawdata); + + return true; +} + +template +bool basic_chmstreambuf::receive_sync(void) +{ + if(!pchmcntrl){ + MSG_CHMPRN("This object did not initialized or could not open key because of not existing."); + return false; + } + + // receiving + PCOMPKT pComPkt = NULL; + unsigned char* pbody = NULL; + size_t length = 0L; + + if(pchmcntrl->IsClientOnSvrType()){ + // receive + if(!pchmcntrl->Receive(&pComPkt, &pbody, &length, read_timeout) || !pbody || 0 == length){ + //MSG_CHMPRN("Failed to receive from server chmpx."); + CHM_Free(pbody); + return false; + } + }else{ + if(CHM_INVALID_MSGID == msgid){ + ERR_CHMPRN("There is no receiving terminal."); + return false; + } + if(!pchmcntrl->Receive(msgid, &pComPkt, &pbody, &length, read_timeout) || !pbody || 0 == length){ + //MSG_CHMPRN("Failed to receive from slave chmpx."); + CHM_Free(pbody); + return false; + } + } + + // convert + ChmKVPair chmkvp; + if(!chmkvp.Load(pbody)){ + ERR_CHMPRN("Could not load message body to chmkvp."); + CHM_Free(pComPkt); + CHM_Free(pbody); + return false; + } + + // make buffer + const char* pkey = chmkvp.GetKey(); + const char* pval = chmkvp.GetValue(); + size_t newsize = (pkey ? (strlen(pkey) + 1) : 1) + (pval ? (strlen(pval) + 1) : 1); + char_type* newbuff; + if(reinterpret_cast(NULL) == (newbuff = reinterpret_cast(calloc(newsize, sizeof(char_type))))){ + ERR_CHMPRN("Could not allocate memory."); + CHM_Free(pComPkt); + CHM_Free(pbody); + return false; + } + + // set input buffer(key terminated by LF) + size_t setpos = 0; + if(pkey){ + strcpy(&newbuff[setpos], pkey); + newbuff[setpos + strlen(pkey)] = static_cast('\n'); // \0 -> \n + setpos += strlen(pkey) + 1; + }else{ + newbuff[setpos++] = '\n'; + } + if(pval){ + strcpy(&newbuff[setpos], pval); + }else{ + newbuff[setpos++] = '\0'; + } + CHM_Free(pbody); + + // reset input buffer + CHM_Free(input_buff); + CHM_Free(plastpkt); + input_buff_size = 0; + streambuf_type::setg(input_buff, input_buff, reinterpret_cast(reinterpret_cast(input_buff) + static_cast(input_buff_size))); + + // set internal buffer + plastpkt = pComPkt; + input_buff = newbuff; + input_buff_size = newsize; + + streambuf_type::setg(input_buff, input_buff, reinterpret_cast(reinterpret_cast(input_buff) + static_cast(input_buff_size))); + + return true; +} + +// +// This stream only receives Manipulator(std::endl). +// On server side: +// If this gets Manipulator, sends output buffer as soon as possible. +// But the object must start reciveing at first, so if the compkt pointer +// does not exist, fails to send(reply). +// If the object has not got key value, sends with key value empty. +// +// On slave side: +// If this gets Manipulator, depends on key value and default hash value. +// If gets Manipulator when the object does not have default hash and key +// value, this does not send and wait to get key value until getting next +// Manipulator. +// If gets when the object has default hash value, this sends with empty +// key value.(not wait to get next Manipulator) +// If gets second Manipulator, this sends key and value as soon as possible. +// +template +int basic_chmstreambuf::sync(void) +{ + if(!pchmcntrl){ + ERR_CHMPRN("This object did not initialized."); + return -1; + } + if(reinterpret_cast(NULL) == streambuf_type::pptr()){ + MSG_CHMPRN("There is no current put pointer."); + return -1; + } + + // [NOTE] + // If on slave chmpx and does not have default hash value and has not put key data. + // Thus this sync means for putting key data, and waiting value data after this sync. + // + if(pchmcntrl->IsClientOnSlvType() && !is_init_hash && reinterpret_cast(NULL) == output_val_pos){ + // check key length + if(streambuf_type::pbase() == streambuf_type::pptr()){ + ERR_CHMPRN("There is no key value."); + return -1; + } + *(streambuf_type::pptr()) = static_cast('\0'); + + // check last charactor(LF) + streambuf_type::pbump(-1); + if(traits_type::eq(static_cast('\n'), *(streambuf_type::pptr()))){ + *(streambuf_type::pptr()) = static_cast('\0'); + + // recheck key length + if(streambuf_type::pbase() == streambuf_type::pptr()){ + ERR_CHMPRN("key value is empty."); + return -1; + } + } + streambuf_type::pbump(1); + + // key is OK. + output_val_pos = streambuf_type::pptr(); + + // do not send, wait for value. + return 0; + } + + // check last charactor(LF) + if(streambuf_type::pbase() < streambuf_type::pptr()){ + *(streambuf_type::pptr()) = static_cast('\0'); + + streambuf_type::pbump(-1); + if(traits_type::eq(static_cast('\n'), *(streambuf_type::pptr()))){ + *(streambuf_type::pptr()) = static_cast('\0'); + } + streambuf_type::pbump(1); + } + + if(!send_sync()){ + return -1; + } + return 0; +} + +template +typename basic_chmstreambuf::int_type basic_chmstreambuf::underflow(void) +{ + if(reinterpret_cast(NULL) == streambuf_type::eback() || streambuf_type::egptr() <= streambuf_type::gptr()){ + // reached end of input buffer or nothing read data, so it means EOF. + // try to read new data. + if(!receive_sync()){ + // reset input buffer + CHM_Free(input_buff); + input_buff_size = 0; + streambuf_type::setg(input_buff, input_buff, reinterpret_cast(reinterpret_cast(input_buff) + static_cast(input_buff_size))); + + return traits_type::eof(); + } + } + // Check EOF + if(reinterpret_cast(NULL) == streambuf_type::eback() || reinterpret_cast(NULL) == streambuf_type::egptr()){ + return traits_type::eof(); + } + + return traits_type::to_int_type(streambuf_type::gptr()[0]); +} + +template +typename basic_chmstreambuf::int_type basic_chmstreambuf::overflow(int_type ch) +{ + if(!pchmcntrl){ + ERR_CHMPRN("This object did not initialized."); + return traits_type::eof(); + } + + // for value posision + off_type val_offset = (reinterpret_cast(NULL) == output_val_pos || output_val_pos < output_buff) ? static_cast(-1) : (output_val_pos - output_buff); + + // expand buffer area + size_t newsize = output_buff_size + basic_chmstreambuf::pagesize; + char_type* newbuff; + if(reinterpret_cast(NULL) == (newbuff = reinterpret_cast(realloc(output_buff, newsize)))){ + ERR_CHMPRN("Could not allocate memory."); + return traits_type::eof(); + } + + // set newbuff + streambuf_type::setp(newbuff, reinterpret_cast(reinterpret_cast(newbuff) + static_cast(newsize))); + streambuf_type::pbump(output_buff_size); + + // add ch + *(streambuf_type::pptr()) = ch; + streambuf_type::pbump(1); + + // set internal data + output_buff_size = newsize; + output_buff = newbuff; + output_val_pos = (static_cast(-1) == val_offset) ? reinterpret_cast(NULL) : (output_buff + val_offset); + + return traits_type::to_int_type(ch); +} + +template +typename basic_chmstreambuf::int_type basic_chmstreambuf::pbackfail(int_type ch) +{ + if(!pchmcntrl){ + ERR_CHMPRN("This object did not initialized."); + return traits_type::eof(); + } + + if(streambuf_type::eback() == streambuf_type::gptr()){ + // now first position in reading buffer(data) + return traits_type::eof(); + + }else if(streambuf_type::eback() < streambuf_type::gptr()){ + if(!traits_type::eq(traits_type::to_char_type(ch), streambuf_type::gptr()[-1])){ + streambuf_type::gbump(-1); + return traits_type::eof(); + } + // decrement current + streambuf_type::gbump(-1); + + }else{ + // why + ERR_CHMPRN("Something error in pbackfail."); + return traits_type::eof(); + } + return traits_type::to_int_type(ch); +} + +template +typename basic_chmstreambuf::pos_type basic_chmstreambuf::seekoff(off_type offset, std::ios_base::seekdir bpostype, open_mode opmode) +{ + if(!(opmode & (std::ios_base::out | std::ios_base::in))){ + ERR_CHMPRN("Parameter is wrong"); + return pos_type(off_type(-1)); + } + if(!pchmcntrl){ + ERR_CHMPRN("This object did not initialized."); + return pos_type(off_type(-1)); + } + + // seekp() + if(opmode & std::ios_base::out){ + // [NOTE] + // Do not care for a case of that output_buff is NULL. + // Following codes works good when it is NULL. + // + // make new current + char_type* newcur = reinterpret_cast(NULL); + off_type newoffset = offset; + if(std::ios_base::beg == bpostype){ + newcur = streambuf_type::pbase() + offset; + newoffset -= (streambuf_type::pptr() - streambuf_type::pbase()); + }else if(std::ios_base::end == bpostype){ + char_type* endpos; + if(reinterpret_cast(NULL) != output_val_pos){ + endpos = &output_val_pos[strlen(output_val_pos)]; + }else{ + endpos = &output_buff[strlen(output_buff)]; + } + newcur = endpos + offset; + newoffset = newcur - streambuf_type::pptr(); + }else if(std::ios_base::cur == bpostype){ + newcur = streambuf_type::pptr() + offset; + } + + // check key value + if(pchmcntrl->IsClientOnSlvType() && reinterpret_cast(NULL) != output_val_pos){ + if(is_init_key){ + // do not erase key area + if(newcur < output_val_pos){ + ERR_CHMPRN("Could not seek by underflow."); + return pos_type(off_type(-1)); + } + }else{ + if(newcur < output_val_pos){ + // This case is seek backward to over key value sepalator. + output_val_pos = reinterpret_cast(NULL); + } + } + } + + // check overflow + if(streambuf_type::epptr() < newcur){ + // re-calc(from buffer start) + newoffset = newcur - streambuf_type::pbase(); + + // buffer overflow -> expand + size_t newsize = ((newcur - streambuf_type::pbase() + 1) / basic_chmstreambuf::pagesize + 1) * basic_chmstreambuf::pagesize; + char_type* newbuff; + if(reinterpret_cast(NULL) == (newbuff = reinterpret_cast(realloc(output_buff, newsize)))){ + ERR_CHMPRN("Could not allocate memory."); + return pos_type(off_type(-1)); + } + output_buff_size= newsize; + output_buff = newbuff; + + // reset buffer + streambuf_type::setp(output_buff, reinterpret_cast(reinterpret_cast(output_buff) + static_cast(output_buff_size))); + } + // move current + streambuf_type::pbump(newoffset); + // terminate + *(streambuf_type::pptr()) = static_cast('\0'); + } + + // seekg() + if(opmode & std::ios_base::in){ + if(reinterpret_cast(NULL) == input_buff){ + // Now have not loaded yet, so try to receiving. + if(!receive_sync()){ + WAN_CHMPRN("There is no reciving data, and failed to reciving, thus could not seek."); + return pos_type(off_type(-1)); + } + } + + // make new current + char_type* newcur = reinterpret_cast(NULL); + off_type newoffset = offset; // offset from current + if(std::ios_base::end == bpostype){ + WAN_CHMPRN("Over end of receiving data, thus could not seek."); + return pos_type(off_type(-1)); + }else if(std::ios_base::beg == bpostype){ + newcur = streambuf_type::eback() + offset; + newoffset -= (streambuf_type::gptr() - streambuf_type::eback()); + }else if(std::ios_base::cur == bpostype){ + newcur = streambuf_type::gptr() + offset; + } + + // check over end of input buffer + if(streambuf_type::egptr() < newcur){ + WAN_CHMPRN("Over end of receiving data, thus could not seek."); + return pos_type(off_type(-1)); + } + + // move current + streambuf_type::gbump(newoffset); + } + return pos_type(off_type(0)); +} + +template +typename basic_chmstreambuf::pos_type basic_chmstreambuf::seekpos(pos_type abspos, open_mode opmode) +{ + return seekoff(off_type(abspos), std::ios_base::beg, opmode); +} + +//--------------------------------------------------------- +// Template basic_ichmstream +//--------------------------------------------------------- +template +class basic_ichmstream : public std::basic_istream +{ + public: + typedef CharT char_type; + typedef Traits traits_type; + typedef typename traits_type::int_type int_type; + typedef typename traits_type::pos_type pos_type; + typedef typename traits_type::off_type off_type; + typedef std::ios_base::openmode open_mode; + typedef basic_chmstreambuf chmstreambuf_type; + typedef std::basic_istream istream_type; + typedef std::basic_ios ios_type; + + private: + chmstreambuf_type chmstreambuf; + + protected: + basic_ichmstream(open_mode opmode = std::ios_base::in) : istream_type(), chmstreambuf(opmode | std::ios_base::in) { ios_type::init(&chmstreambuf); } + + public: + basic_ichmstream(ChmCntrl* pchmobj, open_mode opmode = std::ios_base::in) : istream_type(), chmstreambuf(pchmobj, opmode | std::ios_base::in) { ios_type::init(&chmstreambuf); } + ~basic_ichmstream() { } + + chmstreambuf_type* rdbuf() const { return const_cast(&chmstreambuf); } + chmhash_t receivedhash(void) const { return chmstreambuf.receivedhash(); } +}; + + +//--------------------------------------------------------- +// Template basic_ochmstream +//--------------------------------------------------------- +template +class basic_ochmstream : public std::basic_ostream +{ + public: + typedef CharT char_type; + typedef Traits traits_type; + typedef typename traits_type::int_type int_type; + typedef typename traits_type::pos_type pos_type; + typedef typename traits_type::off_type off_type; + typedef std::ios_base::openmode open_mode; + typedef basic_chmstreambuf chmstreambuf_type; + typedef std::basic_ostream ostream_type; + typedef std::basic_ios ios_type; + + private: + chmstreambuf_type chmstreambuf; + + protected: + basic_ochmstream(open_mode opmode = std::ios_base::out) : ostream_type(), chmstreambuf(opmode | std::ios_base::out) { ios_type::init(&chmstreambuf); } + + public: + basic_ochmstream(ChmCntrl* pchmobj, open_mode opmode = std::ios_base::out) : ostream_type(), chmstreambuf(pchmobj, opmode | std::ios_base::out) { ios_type::init(&chmstreambuf); } + basic_ochmstream(ChmCntrl* pchmobj, chmhash_t hash, open_mode opmode = std::ios_base::out) : ostream_type(), chmstreambuf(pchmobj, hash, opmode | std::ios_base::out) { ios_type::init(&chmstreambuf); } + basic_ochmstream(ChmCntrl* pchmobj, const std::string& strkey, open_mode opmode = std::ios_base::out) : ostream_type(), chmstreambuf(pchmobj, strkey, opmode | std::ios_base::out) { ios_type::init(&chmstreambuf); } + ~basic_ochmstream() { } + + chmstreambuf_type* rdbuf() const { return const_cast(&chmstreambuf); } + chmhash_t receivedhash(void) const { return chmstreambuf.receivedhash(); } +}; + + +//--------------------------------------------------------- +// Template basic_chmstream +//--------------------------------------------------------- +template +class basic_chmstream : public std::basic_iostream +{ + public: + typedef CharT char_type; + typedef Traits traits_type; + typedef typename traits_type::int_type int_type; + typedef typename traits_type::pos_type pos_type; + typedef typename traits_type::off_type off_type; + typedef std::ios_base::openmode open_mode; + typedef basic_chmstreambuf chmstreambuf_type; + typedef std::basic_iostream iostream_type; + typedef std::basic_ios ios_type; + + private: + chmstreambuf_type chmstreambuf; + + protected: + basic_chmstream(open_mode opmode = std::ios_base::out | std::ios_base::in) : iostream_type(), chmstreambuf(opmode | std::ios_base::out | std::ios_base::in) { ios_type::init(&chmstreambuf); } + + public: + basic_chmstream(ChmCntrl* pchmobj, open_mode opmode = std::ios_base::out | std::ios_base::in) : iostream_type(), chmstreambuf(pchmobj, opmode | std::ios_base::out | std::ios_base::in) { ios_type::init(&chmstreambuf); } + basic_chmstream(ChmCntrl* pchmobj, chmhash_t hash, open_mode opmode = std::ios_base::out | std::ios_base::in) : iostream_type(), chmstreambuf(pchmobj, hash, opmode | std::ios_base::out | std::ios_base::in) { ios_type::init(&chmstreambuf); } + basic_chmstream(ChmCntrl* pchmobj, const std::string& strkey, open_mode opmode = std::ios_base::out | std::ios_base::in) : iostream_type(), chmstreambuf(pchmobj, strkey, opmode | std::ios_base::out | std::ios_base::in) { ios_type::init(&chmstreambuf); } + ~basic_chmstream() { } + + chmstreambuf_type* rdbuf() const { return const_cast(&chmstreambuf); } + chmhash_t receivedhash(void) const { return chmstreambuf.receivedhash(); } +}; + +//--------------------------------------------------------- +// Typedefs +//--------------------------------------------------------- +template > class basic_chmstreambuf; +template > class basic_ichmstream; +template > class basic_ochmstream; +template > class basic_chmstream; + +typedef basic_chmstreambuf chmstreambuf; +typedef basic_ichmstream ichmstream; +typedef basic_ochmstream ochmstream; +typedef basic_chmstream chmstream; + +#ifdef _GLIBCXX_USE_WCHAR_T +typedef basic_chmstreambuf wchmstreambuf; +typedef basic_ichmstream wichmstream; +typedef basic_ochmstream wochmstream; +typedef basic_chmstream wchmstream; +#endif // _GLIBCXX_USE_WCHAR_T + +#endif // CHMSTREAM_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmstructure.h b/lib/chmstructure.h new file mode 100644 index 0000000..22bac32 --- /dev/null +++ b/lib/chmstructure.h @@ -0,0 +1,787 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMSTRUCTURE_H +#define CHMSTRUCTURE_H + +#include +#include + +#include "chmcommon.h" + +DECL_EXTERN_C_START + +//--------------------------------------------------------- +// Valiable types +//--------------------------------------------------------- +typedef uint64_t chmpxid_t; +typedef uint64_t msgid_t; +typedef uint64_t serial_t; +typedef unsigned char msgflag_t; +typedef k2h_hash_t chmhash_t; +typedef uint64_t chmpxsts_t; +typedef uint64_t chmpxpos_t; +typedef uint64_t logtype_t; + +typedef enum chmpx_mode{ + CHMPX_UNKNOWN = 0, + CHMPX_SERVER, + CHMPX_SLAVE +}CHMPXMODE; + +//--------------------------------------------------------- +// Symbols & Macros +//--------------------------------------------------------- +#define CHM_INVALID_ID (-1) +#define CHM_INVALID_PORT (-1) +#define CHM_INVALID_CHMPXLISTPOS 0L +#define CHM_INVALID_PID 0 + +DECL_EXTERN_C_END + +#if defined(__cplusplus) +#define CHM_INVALID_CHMPXID static_cast(CHM_INVALID_ID) +#define CHM_INVALID_MSGID static_cast(CHM_INVALID_ID) +#define CHM_INVALID_HASHVAL static_cast(-1) // when hash range is under 64bit. +#else // __cplusplus +#define CHM_INVALID_CHMPXID (chmpxid_t)(CHM_INVALID_ID) +#define CHM_INVALID_MSGID (msgid_t)(CHM_INVALID_ID) +#define CHM_INVALID_HASHVAL (chmhash_t)(-1) // when hash range is under 64bit. +#endif // __cplusplus + +DECL_EXTERN_C_START + +#define MAX_GROUP_LENGTH 256 +#define DEFAULT_CHMPX_COUNT 1024 // default maximum node(server & slave) count +#define MAX_CHMPX_COUNT (1024 * 32) // maximum node(server & slave) count +#define DEFAULT_REPLICA_COUNT 0 // default replica count(always random mode sets this value) +#define CHMPXID_MAP_SIZE 4096 // chmpxid map array size(maximum average conflict MAX_CHMPX_COUNT / CHMPXID_MAP_SIZE) +#define CHMPXID_MAP_MASK (CHMPXID_MAP_SIZE - 1) // 0xFFF + +#define MAX_MQUEUE_COUNT 32768 // Maximum MQ count in boxes +#define MAX_SERVER_MQ_CNT 128 // Maximum MQ count server used +#define DEFAULT_SERVER_MQ_CNT 1 // +#define MAX_CLIENT_MQ_CNT (MAX_MQUEUE_COUNT - MAX_SERVER_MQ_CNT) // Maximum MQ count all client processes used +#define DEFAULT_CLIENT_MQ_CNT 8192 // +#define MAX_QUEUE_PER_SERVERMQ 128 // Maximum Queue count by each server/slave node MQ +#define DEFAULT_QUEUE_PER_SERVERMQ 8 // +#define MAX_QUEUE_PER_CLIENTMQ 128 // Maximum Queue count by each client processes MQ +#define DEFAULT_QUEUE_PER_CLIENTMQ 1 // +#define MAX_MQ_PER_CLIENT MAX_SERVER_MQ_CNT // Maximum MQ count by each client processes +#define DEFAULT_MQ_PER_CLIENT 1 // +#define MAX_MQ_PER_ATTACH MAX_SERVER_MQ_CNT // Maximum MQ count by attaching +#define DEFAULT_MQ_PER_ATTACH 1 // +#define DEFAULT_MQUEUE_COUNT (DEFAULT_CLIENT_MQ_CNT + DEFAULT_SERVER_MQ_CNT) // Default MQ count in boxes + +#define DEFAULT_HISTLOG_COUNT 8192 // default +#define MAX_HISTLOG_COUNT 32768 // upper limit + +//--------------------- +// CHMPX Status(chmpxsts_t) +//--------------------- +// Status value is following bit parts. +// +//------------------------------------------------------------------------------- +// LIVE RING/SERVICE ACTION OPERATE SUSPEND(NOT JOIN CLIENT) +//------------------------------------------------------------------------------- +// UP SLAVE NOACT NOTHING NOSUP +// DOWN SERVIVE IN ADD PENDING SUSPEND +// SERVICE OUT DELETE DOING +// DONE +//------------------------------------------------------------------------------- +// +#define CHMPXSTS_MASK_LIVE 0x1 // +#define CHMPXSTS_MASK_RING 0x30 // +#define CHMPXSTS_MASK_ACTION 0x300 // +#define CHMPXSTS_MASK_OPERATE 0x3000 // +#define CHMPXSTS_MASK_SUSPEND 0x10000 // +#define CHMPXSTS_MASK_ALL (CHMPXSTS_MASK_LIVE | CHMPXSTS_MASK_RING | CHMPXSTS_MASK_ACTION | CHMPXSTS_MASK_OPERATE | CHMPXSTS_MASK_SUSPEND) + +//--------------------- // (status & CHMPXSTS_MASK_LIVE) +#define CHMPXSTS_VAL_DOWN 0x0 // Server/Slave is DOWN +#define CHMPXSTS_VAL_UP 0x1 // Server/Slave is UP + // (status & CHMPXSTS_MASK_RING) +#define CHMPXSTS_VAL_SLAVE 0x00 // SLAVE (not on RING) +#define CHMPXSTS_VAL_SRVOUT 0x10 // SERVER is SERVIVE OUT (on RING) +#define CHMPXSTS_VAL_SRVIN 0x20 // SERVER is SERVIVE IN (on RING) + // (status & CHMPXSTS_MASK_ACTION) +#define CHMPXSTS_VAL_NOACT 0x000 // No Action +#define CHMPXSTS_VAL_ADD 0x100 // Action is ADD +#define CHMPXSTS_VAL_DELETE 0x200 // Action is DELETE + // (status & CHMPXSTS_MASK_ACTION) +#define CHMPXSTS_VAL_NOTHING 0x0000 // No Operating +#define CHMPXSTS_VAL_PENDING 0x1000 // PENDING +#define CHMPXSTS_VAL_DOING 0x2000 // DOING +#define CHMPXSTS_VAL_DONE 0x3000 // DONE + // (status & CHMPXSTS_MASK_SUSPEND) +#define CHMPXSTS_VAL_NOSUP 0x00000 // NO SUSPEND +#define CHMPXSTS_VAL_SUSPEND 0x10000 // SUSPEND + +//--------------------- +#define CHMPXSTS_SLAVE_UP_NORMAL (CHMPXSTS_VAL_UP | CHMPXSTS_VAL_SLAVE | CHMPXSTS_VAL_NOACT | CHMPXSTS_VAL_NOTHING) +#define CHMPXSTS_SLAVE_DOWN_NORMAL (CHMPXSTS_VAL_DOWN | CHMPXSTS_VAL_SLAVE | CHMPXSTS_VAL_NOACT | CHMPXSTS_VAL_NOTHING) + +#define CHMPXSTS_SRVOUT_UP_NORMAL (CHMPXSTS_VAL_UP | CHMPXSTS_VAL_SRVOUT | CHMPXSTS_VAL_NOACT | CHMPXSTS_VAL_NOTHING) +#define CHMPXSTS_SRVOUT_DOWN_NORMAL (CHMPXSTS_VAL_DOWN | CHMPXSTS_VAL_SRVOUT | CHMPXSTS_VAL_NOACT | CHMPXSTS_VAL_NOTHING) + +#define CHMPXSTS_SRVIN_UP_NORMAL (CHMPXSTS_VAL_UP | CHMPXSTS_VAL_SRVIN | CHMPXSTS_VAL_NOACT | CHMPXSTS_VAL_NOTHING) +#define CHMPXSTS_SRVIN_UP_MERGING (CHMPXSTS_VAL_UP | CHMPXSTS_VAL_SRVIN | CHMPXSTS_VAL_NOACT | CHMPXSTS_VAL_DOING) +#define CHMPXSTS_SRVIN_UP_MERGED (CHMPXSTS_VAL_UP | CHMPXSTS_VAL_SRVIN | CHMPXSTS_VAL_NOACT | CHMPXSTS_VAL_DONE) + +#define CHMPXSTS_SRVIN_UP_ADDPENDING (CHMPXSTS_VAL_UP | CHMPXSTS_VAL_SRVIN | CHMPXSTS_VAL_ADD | CHMPXSTS_VAL_PENDING) +#define CHMPXSTS_SRVIN_UP_ADDING (CHMPXSTS_VAL_UP | CHMPXSTS_VAL_SRVIN | CHMPXSTS_VAL_ADD | CHMPXSTS_VAL_DOING) +#define CHMPXSTS_SRVIN_UP_ADDED (CHMPXSTS_VAL_UP | CHMPXSTS_VAL_SRVIN | CHMPXSTS_VAL_ADD | CHMPXSTS_VAL_DONE) + +#define CHMPXSTS_SRVIN_UP_DELPENDING (CHMPXSTS_VAL_UP | CHMPXSTS_VAL_SRVIN | CHMPXSTS_VAL_DELETE | CHMPXSTS_VAL_PENDING) +#define CHMPXSTS_SRVIN_UP_DELETING (CHMPXSTS_VAL_UP | CHMPXSTS_VAL_SRVIN | CHMPXSTS_VAL_DELETE | CHMPXSTS_VAL_DOING) +#define CHMPXSTS_SRVIN_UP_DELETED (CHMPXSTS_VAL_UP | CHMPXSTS_VAL_SRVIN | CHMPXSTS_VAL_DELETE | CHMPXSTS_VAL_DONE) + +#define CHMPXSTS_SRVIN_DOWN_NORMAL (CHMPXSTS_VAL_DOWN | CHMPXSTS_VAL_SRVIN | CHMPXSTS_VAL_NOACT | CHMPXSTS_VAL_NOTHING) +#define CHMPXSTS_SRVIN_DOWN_DELPENDING (CHMPXSTS_VAL_DOWN | CHMPXSTS_VAL_SRVIN | CHMPXSTS_VAL_DELETE | CHMPXSTS_VAL_PENDING) +#define CHMPXSTS_SRVIN_DOWN_DELETED (CHMPXSTS_VAL_DOWN | CHMPXSTS_VAL_SRVIN | CHMPXSTS_VAL_DELETE | CHMPXSTS_VAL_DONE) + +//--------------------- +#define SET_CHMPXSTS_DOWN(status) (status = (status & ~CHMPXSTS_MASK_LIVE) ) +#define SET_CHMPXSTS_UP(status) (status = (status & ~CHMPXSTS_MASK_LIVE) | CHMPXSTS_VAL_UP) + +#define SET_CHMPXSTS_SLAVE(status) (status = (status & ~CHMPXSTS_MASK_RING) ) +#define SET_CHMPXSTS_SRVOUT(status) (status = (status & ~CHMPXSTS_MASK_RING) | CHMPXSTS_VAL_SRVOUT) +#define SET_CHMPXSTS_SRVIN(status) (status = (status & ~CHMPXSTS_MASK_RING) | CHMPXSTS_VAL_SRVIN) + +#define SET_CHMPXSTS_NOACT(status) (status = (status & ~CHMPXSTS_MASK_ACTION) ) +#define SET_CHMPXSTS_ADD(status) (status = (status & ~CHMPXSTS_MASK_ACTION) | CHMPXSTS_VAL_ADD) +#define SET_CHMPXSTS_DELETE(status) (status = (status & ~CHMPXSTS_MASK_ACTION) | CHMPXSTS_VAL_DELETE) + +#define SET_CHMPXSTS_NOTHING(status) (status = (status & ~CHMPXSTS_MASK_OPERATE) ) +#define SET_CHMPXSTS_PENDING(status) (status = (status & ~CHMPXSTS_MASK_OPERATE) | CHMPXSTS_VAL_PENDING) +#define SET_CHMPXSTS_DOING(status) (status = (status & ~CHMPXSTS_MASK_OPERATE) | CHMPXSTS_VAL_DOING) +#define SET_CHMPXSTS_DONE(status) (status = (status & ~CHMPXSTS_MASK_OPERATE) | CHMPXSTS_VAL_DONE) + +#define SET_CHMPXSTS_NOSUP(status) (status = (status & ~CHMPXSTS_MASK_SUSPEND) ) +#define SET_CHMPXSTS_SUSPEND(status) (status = (status & ~CHMPXSTS_MASK_SUSPEND) | CHMPXSTS_VAL_SUSPEND) + +//--------------------- +#define IS_CHMPXSTS_DOWN(status) (CHMPXSTS_VAL_DOWN == (status & CHMPXSTS_MASK_LIVE)) +#define IS_CHMPXSTS_UP(status) (CHMPXSTS_VAL_UP == (status & CHMPXSTS_MASK_LIVE)) + +#define IS_CHMPXSTS_SLAVE(status) (CHMPXSTS_VAL_SLAVE == (status & CHMPXSTS_MASK_RING)) +#define IS_CHMPXSTS_SRVOUT(status) (CHMPXSTS_VAL_SRVOUT == (status & CHMPXSTS_MASK_RING)) +#define IS_CHMPXSTS_SRVIN(status) (CHMPXSTS_VAL_SRVIN == (status & CHMPXSTS_MASK_RING)) +#define IS_CHMPXSTS_SERVER(status) (IS_CHMPXSTS_SRVOUT(status) || IS_CHMPXSTS_SRVIN(status)) +#define IS_CHMPXSTS_ONRING(status) IS_CHMPXSTS_SERVER(status) + +#define IS_CHMPXSTS_NOACT(status) (CHMPXSTS_VAL_NOACT == (status & CHMPXSTS_MASK_ACTION)) +#define IS_CHMPXSTS_ADD(status) (CHMPXSTS_VAL_ADD == (status & CHMPXSTS_MASK_ACTION)) +#define IS_CHMPXSTS_DELETE(status) (CHMPXSTS_VAL_DELETE == (status & CHMPXSTS_MASK_ACTION)) + +#define IS_CHMPXSTS_NOTHING(status) (CHMPXSTS_VAL_NOTHING == (status & CHMPXSTS_MASK_OPERATE)) +#define IS_CHMPXSTS_PENDING(status) (CHMPXSTS_VAL_PENDING == (status & CHMPXSTS_MASK_OPERATE)) +#define IS_CHMPXSTS_DOING(status) (CHMPXSTS_VAL_DOING == (status & CHMPXSTS_MASK_OPERATE)) +#define IS_CHMPXSTS_DONE(status) (CHMPXSTS_VAL_DONE == (status & CHMPXSTS_MASK_OPERATE)) + +#define IS_CHMPXSTS_NOSUP(status) (CHMPXSTS_VAL_NOSUP == (status & CHMPXSTS_MASK_SUSPEND)) +#define IS_CHMPXSTS_SUSPEND(status) (CHMPXSTS_VAL_SUSPEND == (status & CHMPXSTS_MASK_SUSPEND)) + +#define IS_CHMPXSTS_OPERATING(status) (IS_CHMPXSTS_DOING(status) || IS_CHMPXSTS_DONE(status)) + +#define IS_SAFE_CHMPXSTS_EX(status) ( CHMPXSTS_SLAVE_UP_NORMAL == status || \ + CHMPXSTS_SLAVE_DOWN_NORMAL == status || \ + CHMPXSTS_SRVIN_UP_NORMAL == status || \ + CHMPXSTS_SRVIN_UP_MERGING == status || \ + CHMPXSTS_SRVIN_UP_MERGED == status || \ + CHMPXSTS_SRVIN_UP_ADDPENDING == status || \ + CHMPXSTS_SRVIN_UP_ADDING == status || \ + CHMPXSTS_SRVIN_UP_ADDED == status || \ + CHMPXSTS_SRVIN_UP_DELPENDING == status || \ + CHMPXSTS_SRVIN_UP_DELETING == status || \ + CHMPXSTS_SRVIN_UP_DELETED == status || \ + CHMPXSTS_SRVIN_DOWN_NORMAL == status || \ + CHMPXSTS_SRVIN_DOWN_DELPENDING == status || \ + CHMPXSTS_SRVIN_DOWN_DELETED == status || \ + CHMPXSTS_SRVOUT_UP_NORMAL == status || \ + CHMPXSTS_SRVOUT_DOWN_NORMAL == status ) + +#define IS_SAFE_CHMPXSTS(status) ( (0 == (status & ~CHMPXSTS_MASK_ALL)) && \ + IS_SAFE_CHMPXSTS_EX((status & ~CHMPXSTS_MASK_SUSPEND)) ) + +//--------------------- +#define IS_CHMPXSTS_BASEHASH(status) ( CHMPXSTS_SRVIN_UP_NORMAL == (status & ~CHMPXSTS_MASK_SUSPEND) || \ + CHMPXSTS_SRVIN_UP_MERGING == (status & ~CHMPXSTS_MASK_SUSPEND) || \ + CHMPXSTS_SRVIN_UP_MERGED == (status & ~CHMPXSTS_MASK_SUSPEND) || \ + CHMPXSTS_SRVIN_UP_DELPENDING == (status & ~CHMPXSTS_MASK_SUSPEND) || \ + CHMPXSTS_SRVIN_UP_DELETING == (status & ~CHMPXSTS_MASK_SUSPEND) || \ + CHMPXSTS_SRVIN_UP_DELETED == (status & ~CHMPXSTS_MASK_SUSPEND) || \ + CHMPXSTS_SRVIN_DOWN_NORMAL == (status & ~CHMPXSTS_MASK_SUSPEND) || \ + CHMPXSTS_SRVIN_DOWN_DELPENDING == (status & ~CHMPXSTS_MASK_SUSPEND) || \ + CHMPXSTS_SRVIN_DOWN_DELETED == (status & ~CHMPXSTS_MASK_SUSPEND) ) + +#define IS_CHMPXSTS_PENDINGHASH(status) ( CHMPXSTS_SRVIN_UP_NORMAL == (status & ~CHMPXSTS_MASK_SUSPEND) || \ + CHMPXSTS_SRVIN_UP_MERGING == (status & ~CHMPXSTS_MASK_SUSPEND) || \ + CHMPXSTS_SRVIN_UP_MERGED == (status & ~CHMPXSTS_MASK_SUSPEND) || \ + CHMPXSTS_SRVIN_UP_ADDPENDING == (status & ~CHMPXSTS_MASK_SUSPEND) || \ + CHMPXSTS_SRVIN_UP_ADDING == (status & ~CHMPXSTS_MASK_SUSPEND) || \ + CHMPXSTS_SRVIN_UP_ADDED == (status & ~CHMPXSTS_MASK_SUSPEND) || \ + CHMPXSTS_SRVIN_DOWN_NORMAL == (status & ~CHMPXSTS_MASK_SUSPEND) ) + +//--------------------- +#define STR_CHMPXSTS_ISSAFE(status) ( IS_SAFE_CHMPXSTS(status) ? "SAFE" : "NOT SAFE") +#define STR_CHMPXSTS_LIVE(status) ( IS_CHMPXSTS_DOWN(status) ? "[DOWN]" : "[UP]" ) +#define STR_CHMPXSTS_RING(status) ( IS_CHMPXSTS_SLAVE(status) ? "[SLAVE]" : \ + IS_CHMPXSTS_SRVOUT(status) ? "[SERVICE OUT]" : \ + IS_CHMPXSTS_SRVIN(status) ? "[SERVICE IN]" : "[Unknown RING]") +#define STR_CHMPXSTS_ACTION(status) ( IS_CHMPXSTS_NOACT(status) ? "[n/a]" : \ + IS_CHMPXSTS_ADD(status) ? "[ADD]" : \ + IS_CHMPXSTS_DELETE(status) ? "[DELETE]" : "[unknown ACTION]") +#define STR_CHMPXSTS_OPERATE(status) ( IS_CHMPXSTS_NOTHING(status) ? "[Nothing]" : \ + IS_CHMPXSTS_PENDING(status) ? "[Pending]" : \ + IS_CHMPXSTS_DOING(status) ? "[Doing]" : \ + IS_CHMPXSTS_DONE(status) ? "[Done]" : "[unknown OPRATION]") +#define STR_CHMPXSTS_SUSPEND(status) ( IS_CHMPXSTS_NOSUP(status) ? "[NoSuspend]" : \ + IS_CHMPXSTS_SUSPEND(status) ? "[Suspend]" : "[unknown SUSPEND]") + +//--------------------- +#define CHANGE_CHMPXSTS_TO_DOWN(status) \ + { \ + if(IS_CHMPXSTS_ADD(status)){ \ + status = CHMPXSTS_SRVOUT_DOWN_NORMAL; \ + }else if(IS_CHMPXSTS_SRVIN(status)){ \ + if(CHMPXSTS_SRVIN_UP_DELETING == status){ \ + status = CHMPXSTS_SRVIN_DOWN_DELETED; \ + }else if(IS_CHMPXSTS_DELETE(status)){ \ + SET_CHMPXSTS_DOWN(status); \ + SET_CHMPXSTS_NOSUP(status); \ + }else{ \ + status = CHMPXSTS_SRVIN_DOWN_NORMAL; \ + } \ + }else{ \ + SET_CHMPXSTS_DOWN(status); \ + SET_CHMPXSTS_NOSUP(status); \ + } \ + } + +#define CHANGE_CHMPXSTS_TO_MERGESTOP(status) \ + { \ + if(IS_CHMPXSTS_ADD(status) || IS_CHMPXSTS_DELETE(status)){ \ + SET_CHMPXSTS_PENDING(status); \ + }else if(IS_CHMPXSTS_NOACT(status)){ \ + SET_CHMPXSTS_NOTHING(status); \ + } \ + } + +#define CHANGE_CHMPXSTS_SUSPEND_FLAG(status, is_suspend) \ + { \ + if(IS_CHMPXSTS_DOWN(status) || IS_CHMPXSTS_SLAVE(status)){ \ + SET_CHMPXSTS_NOSUP(status); \ + }else{ \ + if(is_suspend){ \ + SET_CHMPXSTS_SUSPEND(status); \ + }else{ \ + SET_CHMPXSTS_NOSUP(status); \ + } \ + } \ + } + +#define CHANGE_CHMPXSTS_TO_SUSPEND(status) CHANGE_CHMPXSTS_SUSPEND_FLAG(status, true) +#define CHANGE_CHMPXSTS_TO_NOSUP(status) CHANGE_CHMPXSTS_SUSPEND_FLAG(status, false) + +//--------------------- +// MQMSGHEAD flag +//--------------------- +// This flag has two status. +// 1) Assigned or not assigned. +// 2) Activated or disactivated in assigned MQ +// * Not assigned flag must not be set with activated flag. +// +#define MQFLAG_VAL_NOTASSIGNED 0x0 // not assigned +#define MQFLAG_VAL_ASSIGNED 0x1 // assigned +#define MQFLAG_VAL_ACTIVATED 0x2 // activated +#define MQFLAG_VAL_CHMPXPROC 0x4 // chmpx process used +#define MQFLAG_VAL_CLIENTPROC 0x8 // client process used + +#define MQFLAG_MASK_ASSIGNED (MQFLAG_VAL_ASSIGNED) +#define MQFLAG_MASK_ACTIVATED (MQFLAG_VAL_ACTIVATED) +#define MQFLAG_MASK_KIND (MQFLAG_VAL_CHMPXPROC | MQFLAG_VAL_CLIENTPROC) +#define MQFLAG_MASK_STATUS (MQFLAG_MASK_ASSIGNED | MQFLAG_MASK_ACTIVATED) +#define MQFLAG_MASK_ALL (MQFLAG_MASK_ASSIGNED | MQFLAG_MASK_ACTIVATED | MQFLAG_MASK_KIND) + +#define SET_MQFLAG_INITIALIZED(flag) (flag = 0x0) +#define SET_MQFLAG_NOTASSIGNED(flag) (flag = (flag & ~MQFLAG_MASK_STATUS) ) +#define SET_MQFLAG_ASSIGNED(flag) (flag = (flag & ~MQFLAG_MASK_STATUS) | MQFLAG_VAL_ASSIGNED) +#define SET_MQFLAG_ACTIVATED(flag) (flag = (flag & ~MQFLAG_MASK_STATUS) | MQFLAG_VAL_ASSIGNED | MQFLAG_VAL_ACTIVATED) +#define SET_MQFLAG_DISACTIVATED(flag) SET_MQFLAG_ASSIGNED(flag) +#define SET_MQFLAG_CHMPXPROC(flag) (flag = (flag & ~MQFLAG_MASK_KIND) | MQFLAG_VAL_CHMPXPROC) +#define SET_MQFLAG_CLIENTPROC(flag) (flag = (flag & ~MQFLAG_MASK_KIND) | MQFLAG_VAL_CLIENTPROC) + +#define IS_MQFLAG_NOTASSIGNED(flag) (MQFLAG_VAL_NOTASSIGNED == (flag & MQFLAG_MASK_ASSIGNED)) +#define IS_MQFLAG_ASSIGNED(flag) (MQFLAG_VAL_ASSIGNED == (flag & MQFLAG_MASK_ASSIGNED)) +#define IS_MQFLAG_ACTIVATED(flag) (MQFLAG_VAL_ACTIVATED == (flag & MQFLAG_MASK_ACTIVATED)) +#define IS_MQFLAG_DISACTIVATED(flag) (MQFLAG_VAL_ACTIVATED != (flag & MQFLAG_MASK_ACTIVATED)) +#define IS_MQFLAG_CHMPXPROC(flag) (MQFLAG_VAL_CHMPXPROC == (flag & MQFLAG_MASK_KIND)) +#define IS_MQFLAG_CLIENTPROC(flag) (MQFLAG_VAL_CLIENTPROC == (flag & MQFLAG_MASK_KIND)) + +#define STR_MQFLAG_ASSIGNED(flag) (IS_MQFLAG_ASSIGNED(flag) ? "[Assigned]" : "[Not Assigned]" ) +#define STR_MQFLAG_ACTIVATED(flag) (IS_MQFLAG_ACTIVATED(flag) ? "[Activated]" : "[Disactivated]" ) +#define STR_MQFLAG_KIND(flag) (IS_MQFLAG_CHMPXPROC(flag) ? "[Chmpx]" : IS_MQFLAG_CLIENTPROC(flag) ? "[Client]" : "[Free]" ) + +//--------------------- +// CHMLOG type +//--------------------- +#define CHMLOG_TYPE_UNKOWN 0x0 +#define CHMLOG_TYPE_ASSIGNED 0x1 +#define CHMLOG_TYPE_OUTPUT 0x2 +#define CHMLOG_TYPE_INPUT 0x4 +#define CHMLOG_TYPE_SOCKET 0x8 +#define CHMLOG_TYPE_MQ 0x10 + +#define CHMLOG_MASK_ASSIGNED (CHMLOG_TYPE_ASSIGNED) +#define CHMLOG_MASK_DIRECTION (CHMLOG_TYPE_OUTPUT | CHMLOG_TYPE_INPUT) +#define CHMLOG_MASK_DEVICE (CHMLOG_TYPE_SOCKET | CHMLOG_TYPE_MQ) + +#define CHMLOG_TYPE_NOTASSIGNED ( CHMLOG_TYPE_UNKOWN ) +#define CHMLOG_TYPE_OUT_SOCKET ( CHMLOG_TYPE_ASSIGNED | CHMLOG_TYPE_OUTPUT | CHMLOG_TYPE_SOCKET ) +#define CHMLOG_TYPE_IN_SOCKET ( CHMLOG_TYPE_ASSIGNED | CHMLOG_TYPE_INPUT | CHMLOG_TYPE_SOCKET ) +#define CHMLOG_TYPE_OUT_MQ ( CHMLOG_TYPE_ASSIGNED | CHMLOG_TYPE_OUTPUT | CHMLOG_TYPE_MQ ) +#define CHMLOG_TYPE_IN_MQ ( CHMLOG_TYPE_ASSIGNED | CHMLOG_TYPE_INPUT | CHMLOG_TYPE_MQ ) + +#define IS_CHMLOG_ASSIGNED(logtype) ( CHMLOG_TYPE_ASSIGNED == ( logtype & CHMLOG_MASK_ASSIGNED) ) +#define IS_CHMLOG_INPUT(logtype) ( (CHMLOG_TYPE_ASSIGNED | CHMLOG_TYPE_INPUT) == ( logtype & (CHMLOG_MASK_ASSIGNED | CHMLOG_TYPE_INPUT)) ) +#define IS_CHMLOG_OUTPUT(logtype) ( (CHMLOG_TYPE_ASSIGNED | CHMLOG_TYPE_OUTPUT) == ( logtype & (CHMLOG_MASK_ASSIGNED | CHMLOG_TYPE_OUTPUT)) ) +#define IS_CHMLOG_SOCKET(logtype) ( (CHMLOG_TYPE_ASSIGNED | CHMLOG_TYPE_SOCKET) == ( logtype & (CHMLOG_MASK_ASSIGNED | CHMLOG_TYPE_SOCKET)) ) +#define IS_CHMLOG_MQ(logtype) ( (CHMLOG_TYPE_ASSIGNED | CHMLOG_TYPE_MQ) == ( logtype & (CHMLOG_MASK_ASSIGNED | CHMLOG_TYPE_MQ)) ) + +#define IS_SAFE_CHMLOG_TYPE(logtype) ( CHMLOG_TYPE_NOTASSIGNED == logtype || \ + CHMLOG_TYPE_OUT_SOCKET == logtype || \ + CHMLOG_TYPE_IN_SOCKET == logtype || \ + CHMLOG_TYPE_OUT_MQ == logtype || \ + CHMLOG_TYPE_IN_MQ == logtype ) + +#define IS_SAFE_CHMLOG_MASK(logtype) ( 0 != (logtype & CHMLOG_MASK_ASSIGNED) || \ + 0 != (logtype & CHMLOG_MASK_DIRECTION) || \ + 0 != (logtype & CHMLOG_MASK_DEVICE) ) + +#define STR_CHMLOG_TYPE(logtype) ( CHMLOG_TYPE_NOTASSIGNED == logtype ? "[NotAssigned]" : \ + CHMLOG_TYPE_OUT_SOCKET == logtype ? "[Output Socket]" : \ + CHMLOG_TYPE_IN_SOCKET == logtype ? "[Input Socket]" : \ + CHMLOG_TYPE_OUT_MQ == logtype ? "[Output MQ]" : \ + CHMLOG_TYPE_IN_MQ == logtype ? "[Input MQ]" : "[Unsafe type]" ) + +//--------------------- +// Set/Get sock flag +//--------------------- +#define SOCKTG_SOCK 0x1 +#define SOCKTG_CTLSOCK 0x2 +#define SOCKTG_SELFSOCK 0x4 +#define SOCKTG_SELFCTLSOCK 0x8 +#define SOCKTG_BOTH (SOCKTG_SOCK | SOCKTG_CTLSOCK) +#define SOCKTG_BOTHSELF (SOCKTG_SELFSOCK | SOCKTG_SELFCTLSOCK) +#define SOCKTG_ALL (SOCKTG_SOCK | SOCKTG_CTLSOCK | SOCKTG_SELFSOCK | SOCKTG_SELFCTLSOCK) + +#define IS_SOCKTG_SOCK(type) (0 != (type & SOCKTG_SOCK)) +#define IS_SOCKTG_CTLSOCK(type) (0 != (type & SOCKTG_CTLSOCK)) +#define IS_SOCKTG_SELFSOCK(type) (0 != (type & SOCKTG_SELFSOCK)) +#define IS_SOCKTG_SELFCTLSOCK(type) (0 != (type & SOCKTG_SELFCTLSOCK)) + +//--------------------- +// Close sock flag +//--------------------- +#define CLOSETG_SERVERS 1 +#define CLOSETG_SLAVES 2 +#define CLOSETG_BOTH (CLOSETG_SERVERS | CLOSETG_SLAVES) + +#define IS_CLOSETG_SERVERS(type) (0 != (type & CLOSETG_SERVERS)) +#define IS_CLOSETG_SLAVES(type) (0 != (type & CLOSETG_SLAVES)) + +//--------------------- +// Set/Get hash flag +//--------------------- +#define HASHTG_BASE 1 +#define HASHTG_PENDING 2 +#define HASHTG_BOTH (HASHTG_BASE | HASHTG_PENDING) + +#define IS_HASHTG_BASE(type) (0 != (type & HASHTG_BASE)) +#define IS_HASHTG_PENDING(type) (0 != (type & HASHTG_PENDING)) + +//--------------------- +// CLTPROCLIST +//--------------------- +#define MAX_CLTPROCLIST_COUNT(max_client_mq_cnt, mqcnt_per_attach) (1 + (max_client_mq_cnt / mqcnt_per_attach) + 1) + +//--------------------- +// Others +//--------------------- +#define MASK64_HIBIT 0xFFFFFFFF00000000 +#define MASK64_LOWBIT 0x00000000FFFFFFFF +#define COMPOSE64(hi, low) (((hi & MASK64_LOWBIT) << 32) | (low & MASK64_LOWBIT)) + +// CHMPXMODE enum +#define STRCHMPXMODE(mode) (CHMPX_SERVER == mode ? "server" : CHMPX_SLAVE == mode ? "slave" : CHMPX_UNKNOWN == mode ? "n/a" : "unknown") + +//--------------------------------------------------------- +// Structure : Shared memory +//--------------------------------------------------------- +// CHMSHM +// +-----------------------------------------+ +// | CHMINFO | +// | +---------------------------------+ | +// | | .... | | +// | | CHMPXMAN | | +// | | +-------------------------+ | | +// | | | .... | | | +// | | | PCHMPX | | |------------+ +// | | | PCHMPXLIST | | |------------+ +// | | | PCHMPXLIST[ARRAY] | | |------------+ +// | | | PCHMPX[ARRAY] | | |--------------+ +// | | | PCHMPX[ARRAY] | | |--------------+ +// | | | CHMSTAT | | | | | +// | | | +-----------------+ | | | | | +// | | | | .... | | | | | | +// | | | +-----------------+ | | | | | +// | | | CHMSTAT | | | | | +// | | | +-----------------+ | | | | | +// | | | | .... | | | | | | +// | | | +-----------------+ | | | | | +// | | +-------------------------+ | | | | +// | | .... | | | | +// | | PMQMSGHEADLIST | |----------+ | | +// | | PMQMSGHEADLIST | |----------+ | | +// | +---------------------------------+ | | | | +// | PCHMPXLIST |--------+ | | | +// | MQMSGHEADLIST |------+ | | | | +// | CLTPROCLIST |----+ | | | | | +// | CHMLOG | | | | | | | +// | +---------------------------------+ | | | | | | | +// | | .... | | | | | | | | +// | | PCHMLOGRAW | |--+ | | | | | | +// | | PCHMLOGRAW | |--+ | | | | | | +// | | PCHMLOGRAW | |--+ | | | | | | +// | +---------------------------------+ | | | | | | | | +// +-----------------------------------------+ | | | | | | | +// +-----------------------------------------+ | | | | | | | +// | CHMPXLIST[XXX] |<-------+---+ | +// | | | | | | | +// +-----------------------------------------+ | | | | | +// +-----------------------------------------+ | | | | | +// | PCHMPX[XXX] |<-------------+ +// | | | | | | +// +-----------------------------------------+ | | | | +// +-----------------------------------------+ | | | | +// | MQMSGHEADLIST[XXX] |<-----+---+ +// | | | | +// +-----------------------------------------+ | | +// +-----------------------------------------+ | | +// | CLTPROCLIST[XXX] |<---+ +// | | | +// +-----------------------------------------+ | +// +-----------------------------------------+ | +// | CHMLOGRAW[XXX] |<-+ +// | | +// +-----------------------------------------+ +// +-----------------------------------------+ +// | CHMSOCKLIST[XXX] | +// | (pointed from CHMPX) | +// +-----------------------------------------+ +// +// CHMSHM has all data for chmpx servers list, stats, mq list, and logs. +// We can have the chmpx servers list in class, but stats and status +// should be read from another process. And server's data should be had +// one place. So that, CHMPX managed datas are in one CHMSHM. +// +// [NOTICE] +// These structure is not set packed attribute, because chmpx +// uses each member with locking it. Then each member is aligned +// 64bit byte order. +// +//--------------------------------- +// Socket List +typedef struct chm_sock_list{ + struct chm_sock_list* prev; + struct chm_sock_list* next; + int sock; +}CHMSOCKLIST, *PCHMSOCKLIST; + +//--------------------------------- +// Chmpx +typedef struct chmpx{ + chmpxid_t chmpxid; + char name[NI_MAXHOST]; // = 1025 for getnameinfo() + CHMPXMODE mode; + chmhash_t base_hash; + chmhash_t pending_hash; + short port; + short ctlport; + bool is_ssl; // ssl mode + bool verify_peer; // verify ssl client peer on server(always false on client chmpx) + bool is_ca_file; // flag for CA path is directory or file + char capath[CHM_MAX_PATH_LEN]; // CA directory path + char server_cert[CHM_MAX_PATH_LEN]; // signed server cert file path + char server_prikey[CHM_MAX_PATH_LEN]; // signed server private key file path + char slave_cert[CHM_MAX_PATH_LEN]; // signed slave cert file path + char slave_prikey[CHM_MAX_PATH_LEN]; // signed slave private key file path + PCHMSOCKLIST socklist; // sock means session connecting to server port. + int ctlsock; // ctlsock means session connecting to server ctlport. + int selfsock; // If only self chmpx, selfsock means listening port. not self does not use. + int selfctlsock; // If only self chmpx, selfctlsock means listening ctlport. not self does not use. + time_t last_status_time; + chmpxsts_t status; +}CHMPX, *PCHMPX; + +// Chmpx list +typedef struct chmpx_list{ + struct chmpx_list* prev; // For RING list + struct chmpx_list* next; // For RING list + struct chmpx_list* same; // For conflicting Hash list + CHMPX chmpx; +}CHMPXLIST, *PCHMPXLIST; + +// Stats for chmpx process +typedef struct chm_stat{ + long total_sent_count; + long total_received_count; + size_t total_body_bytes; + size_t min_body_bytes; + size_t max_body_bytes; + struct timespec total_elapsed_time; + struct timespec min_elapsed_time; + struct timespec max_elapsed_time; +}CHMSTAT, *PCHMSTAT; + +// All Chmpx manage structure +// +// chmpxid_map mapping for chmpxid to chmpxlist pointer(offset) +// care for some same chmpxid in this mapping array and +// server/slave chmpxid in this. +// chmpx_base_srvs This is sequence, and holds a pointer to chmpx* in +// order of base hash value. +// chmpx_pend_srvs This is sequence, and holds a pointer to chmpx* in +// order of pending hash value. +// chmpx_self for own bind port/ctlport +// chmpx_servers server mode(service in/out) on RING, information for +// connection to servers(self -> other server) +// chmpx_slaves slaves information which is connection from other +// chmpx to own(other server/slave -> self) +// +typedef struct chmpx_manage{ + char group[MAX_GROUP_LENGTH]; + long cfg_revision; + time_t cfg_date; + long replica_count; // replication count + long chmpx_count; // = max CHMPX count(server + slave) + PCHMPXLIST chmpxid_map[CHMPXID_MAP_SIZE]; // 32KB = 4K * pointer(8byte) + PCHMPX* chmpx_base_srvs; // offset pointer to CHMPX array for base hash servers + PCHMPX* chmpx_pend_srvs; // offset pointer to CHMPX array for pending hash servers + PCHMPXLIST chmpx_self; // own chmpx, if server mode, this is included pointer in servers list. + long chmpx_bhash_count; // base hash chmpx count + long chmpx_server_count; + PCHMPXLIST chmpx_servers; // servers chmpx list( with self chmpx list if server mode ) + long chmpx_slave_count; + PCHMPXLIST chmpx_slaves; // slaves chmpx list( with self chmpx list if slave mode ) + long chmpx_free_count; + PCHMPXLIST chmpx_frees; + long sock_free_count; + PCHMSOCKLIST sock_frees; // free socket list + bool is_operating; // operating + chmpxid_t last_chmpxid; // last random chmpxid + CHMSTAT server_stat; + CHMSTAT slave_stat; +}CHMPXMAN, *PCHMPXMAN; + +//--------------------------------- +// Head for Message queue +typedef struct mq_msg_head{ + msgid_t msgid; + msgflag_t flag; + pid_t pid; +}MQMSGHEAD, *PMQMSGHEAD; + +// Message queue head list +typedef struct mq_msg_head_list{ + struct mq_msg_head_list* prev; + struct mq_msg_head_list* next; + MQMSGHEAD msghead; +}MQMSGHEADLIST, *PMQMSGHEADLIST; + +//--------------------------------- +// Client processes list +typedef struct client_proc_list{ + struct client_proc_list* prev; + struct client_proc_list* next; + pid_t pid; +}CLTPROCLIST, *PCLTPROCLIST; + +//--------------------------------- +// chmpx information +typedef struct chminfo{ + pid_t pid; + time_t start_time; + CHMPXMAN chmpx_man; + int evsock_thread_cnt; // socket thread count + int evmq_thread_cnt; // MQ thread count + bool is_random_deliver; + bool is_auto_merge; + bool is_do_merge; + long max_mqueue; // = max MQ count + long chmpx_mqueue; // = Chmpx MQ count + long max_q_per_chmpxmq; // = max queue per chmpx MQ + long max_q_per_cltmq; // = max queue per client MQ + long max_mq_per_client; // = MQ per client + long mq_per_attach; // = MQ per attach + bool mq_ack; // MQ ACK + int max_sock_pool; // max socket count per chmpx + time_t sock_pool_timeout; // timeout value till closing for unsed socket in pool + int sock_retrycnt; // retry count for socket + suseconds_t timeout_wait_socket; // wait timeout for read/write socket + suseconds_t timeout_wait_connect; // wait timeout for connect socket + int mq_retrycnt; // retry count for MQ + long timeout_wait_mq; // wait timeout for MQ + time_t timeout_merge; // wait timeout for merging + msgid_t base_msgid; + long chmpx_msg_count; // MQ count for chmpx process + PMQMSGHEADLIST chmpx_msgs; // MQ list for chmpx process + long activated_msg_count; // Activated MQ count for client process + PMQMSGHEADLIST activated_msgs; // Activated MQ list for client process + long assigned_msg_count; // Assigned(NOT activated) MQ count for client process + PMQMSGHEADLIST assigned_msgs; // Assigned(NOT activated) MQ list for client process + long free_msg_count; + PMQMSGHEADLIST free_msgs; + msgid_t last_msgid_chmpx; // last random msgid for chmpx process + msgid_t last_msgid_activated; // last random activated msgid for client process + msgid_t last_msgid_assigned; // last random assigned msgid for client process + PMQMSGHEADLIST rel_chmpxmsgarea; // same as in CHMSHM + PCLTPROCLIST client_pids; // Client process(pid) list + PCLTPROCLIST free_pids; + bool k2h_fullmap; + int k2h_mask_bitcnt; + int k2h_cmask_bitcnt; + int k2h_max_element; + long histlog_count; // history log count +}CHMINFO, *PCHMINFO; + +//--------------------------------- +// Loggin History Raw +typedef struct chm_log_raw{ + logtype_t log_type; + size_t length; + struct timespec start_time; // time for starting sending/receiving + struct timespec fin_time; // time for end of sending/receiving +}CHMLOGRAW, *PCHMLOGRAW; + +// Loggin History Area +typedef struct chm_log{ + bool enable; + time_t start_time; // time for enabled log + time_t stop_time; // time for disabled log + long max_log_count; + long next_pos; + PCHMLOGRAW start_log_rel_area; +}CHMLOG, *PCHMLOG; + +//--------------------------------- +// Shared memory for chmpx +typedef struct chmshm{ + CHMINFO info; + PCHMPXLIST rel_chmpxarea; + PCHMPX* rel_pchmpxarrarea; + PMQMSGHEADLIST rel_chmpxmsgarea; + PCLTPROCLIST rel_chmpxpidarea; + PCHMSOCKLIST rel_chmsockarea; + CHMLOG chmpxlog; +}CHMSHM, *PCHMSHM; + +//--------------------------------------------------------- +// Structure for communication +//--------------------------------------------------------- +// Chmpx ssl infromation(notice: all member is byte order) +typedef struct chmpx_ssl{ + bool is_ssl; + bool verify_peer; + bool is_ca_file; + char capath[CHM_MAX_PATH_LEN]; + char server_cert[CHM_MAX_PATH_LEN]; + char server_prikey[CHM_MAX_PATH_LEN]; + char slave_cert[CHM_MAX_PATH_LEN]; + char slave_prikey[CHM_MAX_PATH_LEN]; +}CHMPX_ATTR_PACKED CHMPXSSL, *PCHMPXSSL; + +// Chmpx server infromation +typedef struct chmpx_server{ + chmpxid_t chmpxid; + char name[NI_MAXHOST]; // = 1025 for getnameinfo() + chmhash_t base_hash; + chmhash_t pending_hash; + short port; + short ctlport; + CHMPXSSL ssl; + time_t last_status_time; + chmpxsts_t status; +}CHMPX_ATTR_PACKED CHMPXSVR, *PCHMPXSVR; + +#define COPY_PCHMPXSVR(pdest, psrc) \ + { \ + (pdest)->chmpxid = (psrc)->chmpxid; \ + (pdest)->base_hash = (psrc)->base_hash; \ + (pdest)->pending_hash = (psrc)->pending_hash; \ + (pdest)->port = (psrc)->port; \ + (pdest)->ctlport = (psrc)->ctlport; \ + (pdest)->ssl.is_ssl = (psrc)->ssl.is_ssl; \ + (pdest)->ssl.verify_peer = (psrc)->ssl.verify_peer; \ + (pdest)->ssl.is_ca_file = (psrc)->ssl.is_ca_file; \ + (pdest)->last_status_time = (psrc)->last_status_time; \ + (pdest)->status = (psrc)->status; \ + memcpy((pdest)->name, (psrc)->name, NI_MAXHOST); \ + memcpy((pdest)->ssl.capath, (psrc)->ssl.capath, CHM_MAX_PATH_LEN); \ + memcpy((pdest)->ssl.server_cert, (psrc)->ssl.server_cert, CHM_MAX_PATH_LEN); \ + memcpy((pdest)->ssl.server_prikey, (psrc)->ssl.server_prikey, CHM_MAX_PATH_LEN); \ + memcpy((pdest)->ssl.slave_cert, (psrc)->ssl.slave_cert, CHM_MAX_PATH_LEN); \ + memcpy((pdest)->ssl.slave_prikey, (psrc)->ssl.slave_prikey, CHM_MAX_PATH_LEN); \ + } + +#define HTON_PCHMPXSVR(pdata) \ + { \ + (pdata)->chmpxid = htobe64((pdata)->chmpxid); \ + (pdata)->base_hash = htobe64((pdata)->base_hash); \ + (pdata)->pending_hash = htobe64((pdata)->pending_hash); \ + (pdata)->port = htobe16((pdata)->port); \ + (pdata)->ctlport = htobe16((pdata)->ctlport); \ + (pdata)->last_status_time = htobe64((pdata)->last_status_time); \ + (pdata)->status = htobe64((pdata)->status); \ + } + +#define NTOH_PCHMPXSVR(pdata) \ + { \ + (pdata)->chmpxid = be64toh((pdata)->chmpxid); \ + (pdata)->base_hash = be64toh((pdata)->base_hash); \ + (pdata)->pending_hash = be64toh((pdata)->pending_hash); \ + (pdata)->port = be16toh((pdata)->port); \ + (pdata)->ctlport = be16toh((pdata)->ctlport); \ + (pdata)->last_status_time = be64toh((pdata)->last_status_time); \ + (pdata)->status = be64toh((pdata)->status); \ + } + +DECL_EXTERN_C_END + +#endif // CHMSTRUCTURE_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmstructure.tcc b/lib/chmstructure.tcc new file mode 100644 index 0000000..f01a9b5 --- /dev/null +++ b/lib/chmstructure.tcc @@ -0,0 +1,8239 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMSTRUCTURE_TCC +#define CHMSTRUCTURE_TCC + +#include +#include +#include +#include +#include +#include +#include + +#include "chmcommon.h" +#include "chmutil.h" +#include "chmdbg.h" +#include "chmstructure.h" +#include "chmconf.h" +#include "chmhash.h" + +//--------------------------------------------------------- +// Typedefs +//--------------------------------------------------------- +typedef std::list chmpxidlist_t; +typedef std::map chmpxidmap_t; +typedef std::list chmhashlist_t; +typedef std::map chmhashmap_t; +typedef std::map hnamesslmap_t; +typedef std::list pidlist_t; +typedef std::map pidbmap_t; +typedef std::vector msgidlist_t; +typedef std::list socklist_t; + +//--------------------------------------------------------- +// Utility +//--------------------------------------------------------- +template +std::string STR_CHMPXSTS_FULL(T status) +{ + std::string str_status; + str_status = STR_CHMPXSTS_ISSAFE(status); + str_status += " -> "; + str_status += STR_CHMPXSTS_RING(status); + str_status += STR_CHMPXSTS_LIVE(status); + str_status += STR_CHMPXSTS_ACTION(status); + str_status += STR_CHMPXSTS_OPERATE(status); + str_status += STR_CHMPXSTS_SUSPEND(status); + + return str_status; +} + +template +std::string STR_MQFLAG_FULL(T flag) +{ + std::string str_flag; + str_flag = STR_MQFLAG_KIND(flag); + str_flag += STR_MQFLAG_ASSIGNED(flag); + str_flag += STR_MQFLAG_ACTIVATED(flag); + + return str_flag; +} + +template +void CVT_SSL_STRUCTURE(T1& chmpx_ssl, const T2& chmnode_cfginfo) +{ + chmpx_ssl.is_ssl = chmnode_cfginfo.is_ssl; + chmpx_ssl.verify_peer = chmnode_cfginfo.verify_peer; + chmpx_ssl.is_ca_file = chmnode_cfginfo.is_ca_file; + + strcpy(chmpx_ssl.capath, chmnode_cfginfo.capath.c_str()); + strcpy(chmpx_ssl.server_cert, chmnode_cfginfo.server_cert.c_str()); + strcpy(chmpx_ssl.server_prikey, chmnode_cfginfo.server_prikey.c_str()); + strcpy(chmpx_ssl.slave_cert, chmnode_cfginfo.slave_cert.c_str()); + strcpy(chmpx_ssl.slave_prikey, chmnode_cfginfo.slave_prikey.c_str()); +} + +template +bool CMP_CHMPXSSL(const T& src1, const T& src2) +{ + if(src1.is_ssl != src2.is_ssl){ + return false; + } + if(!src1.is_ssl){ + return true; + } + if( src1.verify_peer == src2.verify_peer && + src1.is_ca_file == src2.is_ca_file && + 0 == strcmp(src1.capath, src2.capath) && + 0 == strcmp(src1.server_cert, src2.server_cert) && + 0 == strcmp(src1.server_prikey, src2.server_prikey) && + 0 == strcmp(src1.slave_cert, src2.slave_cert) && + 0 == strcmp(src1.slave_prikey, src2.slave_prikey) ) + { + return true; + } + return false; +} + +template +void FREE_HNAMESSLMAP(T& info) +{ + for(typename T::iterator iter = info.begin(); iter != info.end(); info.erase(iter++)){ + if(iter->second){ + delete iter->second; + } + } +} + +//--------------------------------------------------------- +// Basic template +//--------------------------------------------------------- +template +class structure_lap +{ + public: + typedef T st_type; + typedef T* st_ptr_type; + + protected: + st_ptr_type pAbsPtr; + const void* pShmBase; + + public: + structure_lap(st_ptr_type ptr = NULL, const void* shmbase = NULL, bool is_abs = true); + virtual ~structure_lap(); + + st_ptr_type GetAbsPtr(void) const { return pAbsPtr; } + st_ptr_type GetRelPtr(void) const { return CHM_REL(pShmBase, pAbsPtr, st_ptr_type); } + + virtual void Reset(st_ptr_type ptr, const void* shmbase, bool is_abs = true); + virtual bool Initialize(void); + virtual bool Dump(std::stringstream& sstream, const char* spacer) const; + virtual bool Clear(void); +}; + +template +structure_lap::structure_lap(st_ptr_type ptr, const void* shmbase, bool is_abs) +{ + Reset(ptr, shmbase, is_abs); +} + +template +structure_lap::~structure_lap() +{ +} + +template +void structure_lap::Reset(st_ptr_type ptr, const void* shmbase, bool is_abs) +{ + if(is_abs){ + pAbsPtr = ptr; + pShmBase= shmbase; + }else{ + pAbsPtr = CHM_ABS(shmbase, ptr, st_ptr_type); + pShmBase= shmbase; + } +} + +template +bool structure_lap::Initialize(void) +{ + return true; +} + +template +bool structure_lap::Dump(std::stringstream& sstream, const char* spacer) const +{ + return true; +} + +template +bool structure_lap::Clear(void) +{ + if(!pAbsPtr || !pShmBase){ + ERR_CHMPRN("Object does not set."); + return false; + } + return true; +} + +//--------------------------------------------------------- +// For CHMSOCKLIST +//--------------------------------------------------------- +template +class chmsocklist_lap : public structure_lap +{ + public: + typedef T st_type; + typedef T* st_ptr_type; + typedef structure_lap basic_type; + + public: + chmsocklist_lap(st_ptr_type ptr = NULL, const void* shmbase = NULL, bool is_abs = true); + + virtual bool Initialize(void); + bool Initialize(st_ptr_type prev, st_ptr_type next, bool is_abs = true); + bool Initialize(int sock); + bool Clear(void); + virtual bool Dump(std::stringstream& sstream, const char* spacer) const; + st_ptr_type Dup(void); + + st_ptr_type GetFirstPtr(bool is_abs = true); + bool ToFirst(void); + bool ToLast(void); + bool ToNext(void); + long Count(void); + bool Insert(st_ptr_type ptr, bool is_abs); + st_ptr_type Retrive(int sock); + st_ptr_type Retrive(void); + st_ptr_type Find(int sock, bool is_abs); + + bool GetAllSocks(socklist_t& list); +}; + +template +chmsocklist_lap::chmsocklist_lap(st_ptr_type ptr, const void* shmbase, bool is_abs) +{ + structure_lap::Reset(ptr, shmbase, is_abs); +} + +template +bool chmsocklist_lap::Initialize(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSOCKLIST does not set."); + return false; + } + basic_type::pAbsPtr->prev = NULL; + basic_type::pAbsPtr->next = NULL; + basic_type::pAbsPtr->sock = CHM_INVALID_SOCK; + return true; +} + +template +bool chmsocklist_lap::Initialize(st_ptr_type prev, st_ptr_type next, bool is_abs) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSOCKLIST does not set."); + return false; + } + basic_type::pAbsPtr->prev = is_abs ? CHM_REL(basic_type::pShmBase, prev, st_ptr_type) : prev; + basic_type::pAbsPtr->next = is_abs ? CHM_REL(basic_type::pShmBase, next, st_ptr_type) : next; + basic_type::pAbsPtr->sock = CHM_INVALID_SOCK; + return true; +} + +template +bool chmsocklist_lap::Initialize(int sock) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSOCKLIST does not set."); + return false; + } + basic_type::pAbsPtr->sock = sock; + return true; +} + +template +bool chmsocklist_lap::Clear(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSOCKLIST does not set."); + return false; + } + basic_type::pAbsPtr->prev = NULL; + basic_type::pAbsPtr->next = NULL; + basic_type::pAbsPtr->sock = CHM_INVALID_SOCK; + return true; +} + +template +bool chmsocklist_lap::Dump(std::stringstream& sstream, const char* spacer) const +{ + sstream << (spacer ? spacer : "") << "sock = "; + + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + //ERR_CHMPRN("PCHMSOCKLIST does not set."); + sstream << static_cast(CHM_INVALID_SOCK) << std::endl; + return true; + } + + // To first + st_ptr_type cur; + for(cur = const_cast(basic_type::pAbsPtr); cur->prev; cur = CHM_ABS(basic_type::pShmBase, cur->prev, st_ptr_type)); + + long count; + for(count = 0L; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type), count++){ + sstream << (0L < count ? ", " : "") << cur->sock; + } + if(0L == count){ + sstream << static_cast(CHM_INVALID_SOCK); + } + sstream << std::endl; + + return true; +} + +template +typename chmsocklist_lap::st_ptr_type chmsocklist_lap::Dup(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + //ERR_CHMPRN("PCHMSOCKLIST does not set."); + return NULL; + } + + long count = Count(); + if(0 == count){ + return NULL; + } + + // To first + st_ptr_type cur; + for(cur = const_cast(basic_type::pAbsPtr); cur->prev; cur = CHM_ABS(basic_type::pShmBase, cur->prev, st_ptr_type)); + + // duplicate + st_ptr_type pdst; + if(NULL == (pdst = reinterpret_cast(calloc(count, sizeof(st_type))))){ + ERR_CHMPRN("Could not allocation memory."); + return NULL; + } + for(st_ptr_type pdstcur = pdst, pdstprev = NULL; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type), pdstprev = pdstcur++){ + if(pdstprev){ + pdstprev->next = pdstcur; + } + pdstcur->prev = pdstprev; + pdstcur->sock = cur->sock; + } + + return pdst; +} + +template +typename chmsocklist_lap::st_ptr_type chmsocklist_lap::GetFirstPtr(bool is_abs) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + //MSG_CHMPRN("PCHMSOCKLIST does not set."); + return NULL; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + return (is_abs ? chmsocklist_lap::GetAbsPtr() : chmsocklist_lap::GetRelPtr()); + +} + +template +bool chmsocklist_lap::ToFirst(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSOCKLIST does not set."); + return false; + } + for(; basic_type::pAbsPtr->prev; basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->prev, st_ptr_type)); + return true; +} + +template +bool chmsocklist_lap::ToLast(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSOCKLIST does not set."); + return false; + } + for(; basic_type::pAbsPtr->next; basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type)); + return true; +} + +template +bool chmsocklist_lap::ToNext(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSOCKLIST does not set."); + return false; + } + if(!basic_type::pAbsPtr->next){ + MSG_CHMPRN("Reached end of client process id list."); + return false; + } + basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type); + return true; +} + +template +long chmsocklist_lap::Count(void) +{ + if(!basic_type::pShmBase){ + ERR_CHMPRN("PCHMSOCKLIST does not set."); + return 0L; + } + if(!basic_type::pAbsPtr){ + //MSG_CHMPRN("PCHMSOCKLIST does not set."); + return 0L; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + long count; + st_ptr_type pos; + for(count = 1L, pos = basic_type::pAbsPtr; pos->next; pos = CHM_ABS(basic_type::pShmBase, pos->next, st_ptr_type), count++); + + return count; +} + +// +// This method uses liner searching, so not good performance. +// +template +bool chmsocklist_lap::Insert(st_ptr_type ptr, bool is_abs) +{ + if(!ptr){ + ERR_CHMPRN("ptr is null."); + return false; + } + if(!basic_type::pShmBase){ // allow basic_type::pAbsPtr is NULL + ERR_CHMPRN("PCHMSOCKLIST does not set."); + return false; + } + st_ptr_type relptr = is_abs ? CHM_REL(basic_type::pShmBase, ptr, st_ptr_type) : ptr; + st_ptr_type absptr = is_abs ? ptr : CHM_ABS(basic_type::pShmBase, ptr, st_ptr_type); + + if(absptr == basic_type::pAbsPtr){ + ERR_CHMPRN("Already set socklist, something wrong but continue..."); + return true; + } + absptr->prev = NULL; + absptr->next = NULL; + + if(!basic_type::pAbsPtr){ + // If list is empty, add ptr to first position. + basic_type::pAbsPtr = absptr; + return true; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + + basic_type::pAbsPtr->prev = relptr; + absptr->next = CHM_REL(basic_type::pShmBase, basic_type::pAbsPtr, st_ptr_type); + basic_type::pAbsPtr = absptr; + + return true; +} + +// +// [CAREFUL] +// After calling this method, be careful for pAbsPtr is NULL. +// And this method is very slow, should use Retrieve(void) +// +template +typename chmsocklist_lap::st_ptr_type chmsocklist_lap::Retrive(int sock) +{ + if(CHM_INVALID_SOCK == sock){ + ERR_CHMPRN("Parameter is wrong."); + return NULL; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSOCKLIST does not set."); + return NULL; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + if(cur->sock == sock){ + // found + if(cur == basic_type::pAbsPtr){ + // basic_type::pAbsPtr is first object in list. + if(basic_type::pAbsPtr->next){ + basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type); + basic_type::pAbsPtr->prev = NULL; // for safty + }else{ + basic_type::pAbsPtr = NULL; + } + } + st_ptr_type prevlist = CHM_ABS(basic_type::pShmBase, cur->prev, st_ptr_type); + st_ptr_type nextlist = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type); + if(prevlist){ + prevlist->next = cur->next; + } + if(nextlist){ + nextlist->prev = cur->prev; + } + cur->prev = NULL; + cur->next = NULL; + return cur; + } + } + + MSG_CHMPRN("Could not find sock(%d).", sock); + return NULL; +} + +// +// [CAREFUL] +// After calling this method, be careful for pAbsPtr is NULL. +// +template +typename chmsocklist_lap::st_ptr_type chmsocklist_lap::Retrive(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSOCKLIST does not set."); + return NULL; + } + + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + st_ptr_type abs_cur = basic_type::pAbsPtr; + st_ptr_type abs_next = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type); + + if(abs_next){ + abs_next->prev = NULL; + } + basic_type::pAbsPtr = abs_next; + abs_cur->prev = NULL; + abs_cur->next = NULL; + + return abs_cur; +} + +// +// This method is not good performance. +// +template +typename chmsocklist_lap::st_ptr_type chmsocklist_lap::Find(int sock, bool is_abs) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSOCKLIST does not set."); + return NULL; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + if(cur->sock == sock){ + return (is_abs ? cur : CHM_REL(basic_type::pShmBase, cur, st_ptr_type)); + } + } + return NULL; +} + +template +bool chmsocklist_lap::GetAllSocks(socklist_t& list) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSOCKLIST does not set."); + return false; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + list.clear(); + + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + list.push_back(cur->sock); + } + return true; +} + +typedef chmsocklist_lap chmsocklistlap; + +//--------------------------------------------------------- +// For CHMPX +//--------------------------------------------------------- +template +class chmpx_lap : public structure_lap +{ + public: + typedef T st_type; + typedef T* st_ptr_type; + typedef structure_lap basic_type; + + protected: + st_ptr_type* abs_base_arr; + st_ptr_type* abs_pend_arr; + long* abs_sock_free_cnt; + PCHMSOCKLIST* abs_sock_frees; + + protected: + bool Initialize(const char* hostname, const char* group, CHMPXMODE mode, short port, short ctlport, const CHMPXSSL& ssl); + + public: + chmpx_lap(st_ptr_type ptr = NULL, st_ptr_type* pchmpxarrbase = NULL, st_ptr_type* pchmpxarrpend = NULL, long* psockfreecnt = NULL, PCHMSOCKLIST* pabssockfrees = NULL, const void* shmbase = NULL, bool is_abs = true); + + void Reset(st_ptr_type ptr, st_ptr_type* pchmpxarrbase, st_ptr_type* pchmpxarrpend, long* psockfreecnt, PCHMSOCKLIST* pabssockfrees, const void* shmbase, bool is_abs = true); + virtual bool Initialize(void); + virtual bool Dump(std::stringstream& sstream, const char* spacer) const; + virtual bool Clear(void); + bool Copy(st_ptr_type ptr); + void Free(st_ptr_type ptr) const; + + bool Close(int eqfd = CHM_INVALID_HANDLE); + bool InitializeServer(const char* hostname, const char* group, short port, short ctlport, const CHMPXSSL& ssl); + bool InitializeServer(PCHMPXSVR chmpxsvr, const char* group); + bool InitializeSlave(const char* hostname, const char* group, short ctlport, const CHMPXSSL& ssl); + bool Set(int sock, int ctlsock, int selfsock, int selfctlsock, int type); + bool Set(chmhash_t base, chmhash_t pending, int type); + bool SetStatus(chmpxsts_t status); + bool Remove(int sock); + + bool Get(std::string& name, chmpxid_t& chmpxid, short& port, short& ctlport) const; + bool Get(socklist_t& socklist, int& ctlsock, int& selfsock, int& selfctlsock) const; + int GetSock(int type); + bool FindSock(int sock); + bool Get(chmhash_t& base, chmhash_t& pending) const; + bool GetChmpxSvr(PCHMPXSVR chmpxsvr) const; + bool MergeChmpxSvr(PCHMPXSVR chmpxsvr, bool is_force = false, int eqfd = CHM_INVALID_HANDLE); + + bool IsServerMode(void) const { return (basic_type::pAbsPtr && CHMPX_SERVER == basic_type::pAbsPtr->mode); } + bool IsSlaveMode(void) const { return (basic_type::pAbsPtr && CHMPX_SLAVE == basic_type::pAbsPtr->mode); } + chmpxid_t GetChmpxId(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->chmpxid : CHM_INVALID_CHMPXID); } + chmpxsts_t GetStatus(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->status : CHMPXSTS_SLAVE_DOWN_NORMAL); } + + bool IsSsl(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->is_ssl : false); } + bool IsVerifyPeer(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->verify_peer : false); } + const char* GetCApath(void) const { return ((basic_type::pAbsPtr && !basic_type::pAbsPtr->is_ca_file && !CHMEMPTYSTR(basic_type::pAbsPtr->capath)) ? basic_type::pAbsPtr->capath : NULL); } + const char* GetCAfile(void) const { return ((basic_type::pAbsPtr && basic_type::pAbsPtr->is_ca_file && !CHMEMPTYSTR(basic_type::pAbsPtr->cafile)) ? basic_type::pAbsPtr->capath : NULL); } + const char* GetSvrCert(void) const { return ((basic_type::pAbsPtr && !CHMEMPTYSTR(basic_type::pAbsPtr->server_cert)) ? basic_type::pAbsPtr->server_cert : NULL); } + const char* GetSvrPriKey(void) const { return ((basic_type::pAbsPtr && !CHMEMPTYSTR(basic_type::pAbsPtr->server_prikey)) ? basic_type::pAbsPtr->server_prikey : NULL); } + const char* GetSlvCert(void) const { return ((basic_type::pAbsPtr && !CHMEMPTYSTR(basic_type::pAbsPtr->slave_cert)) ? basic_type::pAbsPtr->slave_cert : NULL); } + const char* GetSlvPriKey(void) const { return ((basic_type::pAbsPtr && !CHMEMPTYSTR(basic_type::pAbsPtr->slave_prikey)) ? basic_type::pAbsPtr->slave_prikey : NULL); } + bool GetSslStructure(CHMPXSSL& ssl) const; + + int compare_name(const chmpx_lap& other) const; +}; + +template +chmpx_lap::chmpx_lap(st_ptr_type ptr, st_ptr_type* pchmpxarrbase, st_ptr_type* pchmpxarrpend, long* psockfreecnt, PCHMSOCKLIST* pabssockfrees, const void* shmbase, bool is_abs) +{ + Reset(ptr, pchmpxarrbase, pchmpxarrpend, psockfreecnt, pabssockfrees, shmbase, is_abs); +} + +template +void chmpx_lap::Reset(st_ptr_type ptr, st_ptr_type* pchmpxarrbase, st_ptr_type* pchmpxarrpend, long* psockfreecnt, PCHMSOCKLIST* psockfrees, const void* shmbase, bool is_abs) +{ + structure_lap::Reset(ptr, shmbase, is_abs); + abs_base_arr = pchmpxarrbase; // abs + abs_pend_arr = pchmpxarrpend; // abs + abs_sock_free_cnt = psockfreecnt; // abs + abs_sock_frees = psockfrees; // abs +} + +template +bool chmpx_lap::Initialize(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + basic_type::pAbsPtr->chmpxid = CHM_INVALID_CHMPXID; + basic_type::pAbsPtr->name[0] = '\0'; + basic_type::pAbsPtr->mode = CHMPX_UNKNOWN; + basic_type::pAbsPtr->base_hash = CHM_INVALID_HASHVAL; + basic_type::pAbsPtr->pending_hash = CHM_INVALID_HASHVAL; + basic_type::pAbsPtr->port = CHM_INVALID_PORT; + basic_type::pAbsPtr->ctlport = CHM_INVALID_PORT; + basic_type::pAbsPtr->is_ssl = false; + basic_type::pAbsPtr->verify_peer = false; + basic_type::pAbsPtr->is_ca_file = false; + basic_type::pAbsPtr->capath[0] = '\0'; + basic_type::pAbsPtr->server_cert[0] = '\0'; + basic_type::pAbsPtr->server_prikey[0] = '\0'; + basic_type::pAbsPtr->slave_cert[0] = '\0'; + basic_type::pAbsPtr->slave_prikey[0] = '\0'; + basic_type::pAbsPtr->socklist = NULL; + basic_type::pAbsPtr->ctlsock = CHM_INVALID_SOCK; + basic_type::pAbsPtr->selfsock = CHM_INVALID_SOCK; + basic_type::pAbsPtr->selfctlsock = CHM_INVALID_SOCK; + basic_type::pAbsPtr->last_status_time = 0; + basic_type::pAbsPtr->status = CHMPXSTS_SLAVE_DOWN_NORMAL; // temporary + + return true; +} + +template +bool chmpx_lap::Dump(std::stringstream& sstream, const char* spacer) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + sstream << (spacer ? spacer : "") << "chmpxid = " << reinterpret_cast(basic_type::pAbsPtr->chmpxid) << std::endl; + sstream << (spacer ? spacer : "") << "name = " << basic_type::pAbsPtr->name << std::endl; + sstream << (spacer ? spacer : "") << "mode = " << STRCHMPXMODE(basic_type::pAbsPtr->mode) << std::endl; + sstream << (spacer ? spacer : "") << "base_hash = " << reinterpret_cast(basic_type::pAbsPtr->base_hash) << std::endl; + sstream << (spacer ? spacer : "") << "pending_hash = " << reinterpret_cast(basic_type::pAbsPtr->pending_hash) << std::endl; + sstream << (spacer ? spacer : "") << "port = " << basic_type::pAbsPtr->port << std::endl; + sstream << (spacer ? spacer : "") << "ctlport = " << basic_type::pAbsPtr->ctlport << std::endl; + sstream << (spacer ? spacer : "") << "is_ssl = " << (basic_type::pAbsPtr->is_ssl ? "true" : "false") << std::endl; + sstream << (spacer ? spacer : "") << "verify_peer = " << (basic_type::pAbsPtr->verify_peer ? "true" : "false") << std::endl; + sstream << (spacer ? spacer : "") << "is_ca_file = " << (basic_type::pAbsPtr->is_ca_file ? "true" : "false") << std::endl; + sstream << (spacer ? spacer : "") << "capath = " << basic_type::pAbsPtr->capath << std::endl; + sstream << (spacer ? spacer : "") << "server_cert = " << basic_type::pAbsPtr->server_cert << std::endl; + sstream << (spacer ? spacer : "") << "server_prikey = " << basic_type::pAbsPtr->server_prikey << std::endl; + sstream << (spacer ? spacer : "") << "slave_cert = " << basic_type::pAbsPtr->slave_cert << std::endl; + sstream << (spacer ? spacer : "") << "slave_prikey = " << basic_type::pAbsPtr->slave_prikey << std::endl; + + if(basic_type::pAbsPtr->socklist){ + chmsocklistlap socklist(basic_type::pAbsPtr->socklist, basic_type::pShmBase, false); // From Relative + socklist.Dump(sstream, spacer); + }else{ + sstream << (spacer ? spacer : "") << "sock = " << static_cast(CHM_INVALID_SOCK) << std::endl; + } + + sstream << (spacer ? spacer : "") << "ctlsock = " << basic_type::pAbsPtr->ctlsock << std::endl; + sstream << (spacer ? spacer : "") << "selfsock = " << basic_type::pAbsPtr->selfsock << std::endl; + sstream << (spacer ? spacer : "") << "selfctlsock = " << basic_type::pAbsPtr->selfctlsock << std::endl; + sstream << (spacer ? spacer : "") << "last_status_time = " << basic_type::pAbsPtr->last_status_time << std::endl; + sstream << (spacer ? spacer : "") << "status = " << STR_CHMPXSTS_FULL(basic_type::pAbsPtr->status) << std::endl; + + return true; +} + +template +bool chmpx_lap::Copy(st_ptr_type ptr) +{ + if(!ptr){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + ptr->chmpxid = basic_type::pAbsPtr->chmpxid; + ptr->mode = basic_type::pAbsPtr->mode; + ptr->base_hash = basic_type::pAbsPtr->base_hash; + ptr->pending_hash = basic_type::pAbsPtr->pending_hash; + ptr->port = basic_type::pAbsPtr->port; + ptr->ctlport = basic_type::pAbsPtr->ctlport; + ptr->is_ssl = basic_type::pAbsPtr->is_ssl; + ptr->verify_peer = basic_type::pAbsPtr->verify_peer; + ptr->is_ca_file = basic_type::pAbsPtr->is_ca_file; + ptr->ctlsock = basic_type::pAbsPtr->ctlsock; + ptr->selfsock = basic_type::pAbsPtr->selfsock; + ptr->selfctlsock = basic_type::pAbsPtr->selfctlsock; + ptr->last_status_time = basic_type::pAbsPtr->last_status_time; + ptr->status = basic_type::pAbsPtr->status; + + strcpy(ptr->name, basic_type::pAbsPtr->name); + strcpy(ptr->capath, basic_type::pAbsPtr->capath); + strcpy(ptr->server_cert, basic_type::pAbsPtr->server_cert); + strcpy(ptr->server_prikey, basic_type::pAbsPtr->server_prikey); + strcpy(ptr->slave_cert, basic_type::pAbsPtr->slave_cert); + strcpy(ptr->slave_prikey, basic_type::pAbsPtr->slave_prikey); + + if(basic_type::pAbsPtr->socklist){ + chmsocklistlap socklist(basic_type::pAbsPtr->socklist, basic_type::pShmBase, false); // From Relative + ptr->socklist = socklist.Dup(); + }else{ + ptr->socklist = NULL; + } + return true; +} + +template +void chmpx_lap::Free(st_ptr_type ptr) const +{ + if(ptr){ + K2H_Free(ptr->socklist); + } +} + +template +bool chmpx_lap::Initialize(const char* hostname, const char* group, CHMPXMODE mode, short port, short ctlport, const CHMPXSSL& ssl) +{ + if(CHMEMPTYSTR(hostname) || NI_MAXHOST <= strlen(hostname) || CHMEMPTYSTR(group)){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + + strcpy(basic_type::pAbsPtr->name, hostname); + strcpy(basic_type::pAbsPtr->capath, ssl.capath); + strcpy(basic_type::pAbsPtr->server_cert, ssl.server_cert); + strcpy(basic_type::pAbsPtr->server_prikey, ssl.server_prikey); + strcpy(basic_type::pAbsPtr->slave_cert, ssl.slave_cert); + strcpy(basic_type::pAbsPtr->slave_prikey, ssl.slave_prikey); + + basic_type::pAbsPtr->chmpxid = MakeChmpxId(group, hostname, ctlport); + basic_type::pAbsPtr->base_hash = CHM_INVALID_HASHVAL; + basic_type::pAbsPtr->pending_hash = CHM_INVALID_HASHVAL; + basic_type::pAbsPtr->mode = mode; + basic_type::pAbsPtr->port = port; + basic_type::pAbsPtr->ctlport = ctlport; + basic_type::pAbsPtr->is_ssl = ssl.is_ssl; + basic_type::pAbsPtr->verify_peer = ssl.verify_peer; + basic_type::pAbsPtr->is_ca_file = ssl.is_ca_file; + basic_type::pAbsPtr->socklist = NULL; + basic_type::pAbsPtr->ctlsock = CHM_INVALID_SOCK; + basic_type::pAbsPtr->selfsock = CHM_INVALID_SOCK; + basic_type::pAbsPtr->selfctlsock = CHM_INVALID_SOCK; + basic_type::pAbsPtr->last_status_time = 0; + basic_type::pAbsPtr->status = CHMPX_SERVER == mode ? CHMPXSTS_SRVOUT_DOWN_NORMAL : CHMPXSTS_SLAVE_DOWN_NORMAL; + + return true; +} + +template +bool chmpx_lap::Clear(void) +{ + return Initialize(); +} + +template +bool chmpx_lap::Close(int eqfd) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + + // sock is list, so loop + if(basic_type::pAbsPtr->socklist){ + chmsocklistlap socklist(basic_type::pAbsPtr->socklist, basic_type::pShmBase, false); // From Relative + chmsocklistlap freesocklist(*abs_sock_frees, basic_type::pShmBase, false); // From Relative + + for(PCHMSOCKLIST psocklist = socklist.Retrive(); psocklist; psocklist = (socklist.GetFirstPtr(false) ? socklist.Retrive() : NULL)){ + if(CHM_INVALID_SOCK != psocklist->sock){ + if(CHM_INVALID_HANDLE != eqfd){ + epoll_ctl(eqfd, EPOLL_CTL_DEL, psocklist->sock, NULL); + } + CHM_CLOSESOCK(psocklist->sock); + } + if(freesocklist.Insert(psocklist, true)){ // Set abs + ++(*abs_sock_free_cnt); + } + } + // Set free list pointer + *abs_sock_frees = freesocklist.GetFirstPtr(false); + // clear + basic_type::pAbsPtr->socklist = NULL; + } + + if(CHM_INVALID_SOCK != basic_type::pAbsPtr->ctlport && CHM_INVALID_HANDLE != eqfd){ + epoll_ctl(eqfd, EPOLL_CTL_DEL, basic_type::pAbsPtr->ctlport, NULL); + } + if(CHM_INVALID_SOCK != basic_type::pAbsPtr->selfsock && CHM_INVALID_HANDLE != eqfd){ + epoll_ctl(eqfd, EPOLL_CTL_DEL, basic_type::pAbsPtr->selfsock, NULL); + } + if(CHM_INVALID_SOCK != basic_type::pAbsPtr->selfctlsock && CHM_INVALID_HANDLE != eqfd){ + epoll_ctl(eqfd, EPOLL_CTL_DEL, basic_type::pAbsPtr->selfctlsock, NULL); + } + CHM_CLOSESOCK(basic_type::pAbsPtr->ctlsock); + CHM_CLOSESOCK(basic_type::pAbsPtr->selfsock); + CHM_CLOSESOCK(basic_type::pAbsPtr->selfctlsock); + + return true; +} + +template +bool chmpx_lap::InitializeServer(const char* hostname, const char* group, short port, short ctlport, const CHMPXSSL& ssl) +{ + return chmpx_lap::Initialize(hostname, group, CHMPX_SERVER, port, ctlport, ssl); +} + +template +bool chmpx_lap::InitializeServer(PCHMPXSVR chmpxsvr, const char* group) +{ + if(!chmpxsvr || CHMEMPTYSTR(group)){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(!chmpx_lap::InitializeServer(chmpxsvr->name, group, chmpxsvr->port, chmpxsvr->ctlport, chmpxsvr->ssl)){ + return false; + } + if(chmpxsvr->chmpxid != basic_type::pAbsPtr->chmpxid){ + ERR_CHMPRN("Why does not same chmpxid(0x%016" PRIx64 " - 0x%016" PRIx64 ").", chmpxsvr->chmpxid, basic_type::pAbsPtr->chmpxid); + return false; + } + basic_type::pAbsPtr->base_hash = chmpxsvr->base_hash; + basic_type::pAbsPtr->pending_hash = chmpxsvr->pending_hash; + basic_type::pAbsPtr->last_status_time = chmpxsvr->last_status_time; + basic_type::pAbsPtr->status = chmpxsvr->status; + + // [NOTICE] + // If base(pending) hash value is valid, set always pointer to chmpxlist array. + // But not clear old pointer when old hash values is valid. + // So it means always over writing array but not clear old.(take care for read + // array value.) + // + if(CHM_INVALID_HASHVAL != basic_type::pAbsPtr->base_hash){ + if(!abs_base_arr){ + ERR_CHMPRN("abs_base_arr is NULL, could not update base hash array."); + }else{ + abs_base_arr[basic_type::pAbsPtr->base_hash] = chmpx_lap::GetRelPtr(); + } + } + if(CHM_INVALID_HASHVAL != basic_type::pAbsPtr->pending_hash){ + if(!abs_pend_arr){ + ERR_CHMPRN("abs_pend_arr is NULL, could not update pending hash array."); + }else{ + abs_pend_arr[basic_type::pAbsPtr->pending_hash] = chmpx_lap::GetRelPtr(); + } + } + return true; +} + +template +bool chmpx_lap::InitializeSlave(const char* hostname, const char* group, short ctlport, const CHMPXSSL& ssl) +{ + return chmpx_lap::Initialize(hostname, group, CHMPX_SLAVE, CHM_INVALID_PORT, ctlport, ssl); +} + +template +bool chmpx_lap::Set(int sock, int ctlsock, int selfsock, int selfctlsock, int type) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + if(CHMEMPTYSTR(basic_type::pAbsPtr->name)){ + ERR_CHMPRN("PCHMPX does not initialized."); + return false; + } + + if(IS_SOCKTG_SOCK(type)){ + if(CHM_INVALID_SOCK == sock){ + // socket is invalid, so do nothing + MSG_CHMPRN("sock is %d, thus do not set(should call remove sock).", CHM_INVALID_SOCK); + }else{ + // add socket to socklist + if(0L >= (*abs_sock_free_cnt)){ + ERR_CHMPRN("Could not get free sock list."); + return false; + } + // get one sock list + chmsocklistlap freesocklist(*abs_sock_frees, basic_type::pShmBase, false); // From Relative + PCHMSOCKLIST pnewsocklist; + if(NULL == (pnewsocklist = freesocklist.Retrive())){ + ERR_CHMPRN("Could not get free sock list."); + return false; + } + --(*abs_sock_free_cnt); + *abs_sock_frees = freesocklist.GetFirstPtr(false); + + // set(next/prev members are set null in Retrive method) + pnewsocklist->sock = sock; + + // set socklist + chmsocklistlap socklist(basic_type::pAbsPtr->socklist, basic_type::pShmBase, false); // From Relative + if(!socklist.Insert(pnewsocklist, true)){ // Set abs + ERR_CHMPRN("Could not set sock to sock list."); + // recovering + pnewsocklist->sock = CHM_INVALID_SOCK; + if(freesocklist.Insert(pnewsocklist, true)){ + ++(*abs_sock_free_cnt); + *abs_sock_frees = freesocklist.GetFirstPtr(false); + } + return false; + } + basic_type::pAbsPtr->socklist = socklist.GetFirstPtr(false); + } + } + if(IS_SOCKTG_CTLSOCK(type)){ + basic_type::pAbsPtr->ctlsock = ctlsock; + } + if(IS_SOCKTG_SELFSOCK(type)){ + basic_type::pAbsPtr->selfsock = selfsock; + } + if(IS_SOCKTG_SELFCTLSOCK(type)){ + basic_type::pAbsPtr->selfctlsock = selfctlsock; + } + return true; +} + +template +bool chmpx_lap::Set(chmhash_t base, chmhash_t pending, int type) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + if(CHMEMPTYSTR(basic_type::pAbsPtr->name)){ + ERR_CHMPRN("PCHMPX does not initialized."); + return false; + } + + // [NOTICE] + // If base(pending) hash value is valid, set always pointer to chmpxlist array. + // But not clear old pointer when old hash values is valid. + // So it means always over writing array but not clear old.(take care for read + // array value.) + // + bool is_set = false; + if(IS_HASHTG_BASE(type)){ + is_set = true; + basic_type::pAbsPtr->base_hash = base; + if(CHM_INVALID_HASHVAL != basic_type::pAbsPtr->base_hash){ + if(!abs_base_arr){ + ERR_CHMPRN("abs_base_arr is NULL, could not update base hash array."); + }else{ + abs_base_arr[basic_type::pAbsPtr->base_hash] = chmpx_lap::GetRelPtr(); + } + } + } + if(IS_HASHTG_PENDING(type)){ + is_set = true; + basic_type::pAbsPtr->pending_hash = pending; + if(CHM_INVALID_HASHVAL != basic_type::pAbsPtr->pending_hash){ + if(!abs_pend_arr){ + ERR_CHMPRN("abs_pend_arr is NULL, could not update pending hash array."); + }else{ + abs_pend_arr[basic_type::pAbsPtr->pending_hash] = chmpx_lap::GetRelPtr(); + } + } + } + if(is_set){ + basic_type::pAbsPtr->last_status_time = time(NULL); // Only update status time + } + return is_set; +} + +template +bool chmpx_lap::Remove(int sock) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + if(CHMEMPTYSTR(basic_type::pAbsPtr->name)){ + ERR_CHMPRN("PCHMPX does not initialized."); + return false; + } + if(CHM_INVALID_SOCK == sock){ + ERR_CHMPRN("sock is invalid."); + return false; + } + + if(!basic_type::pAbsPtr->socklist){ + //MSG_CHMPRN("socklist is NULL."); + return true; + } + + // retrive + chmsocklistlap socklist(basic_type::pAbsPtr->socklist, basic_type::pShmBase, false); // From Relative + PCHMSOCKLIST prmsocklist; + if(NULL == (prmsocklist = socklist.Retrive(sock))){ + WAN_CHMPRN("Could not find sock(%d) in socklist, already remove it.", sock); + return true; + } + basic_type::pAbsPtr->socklist = socklist.GetFirstPtr(false); + + // set(next/prev members are set null in Retrive method) + prmsocklist->sock = CHM_INVALID_SOCK; + + // add free + chmsocklistlap freesocklist(*abs_sock_frees, basic_type::pShmBase, false); // From Relative + if(!freesocklist.Insert(prmsocklist, true)){ + WAN_CHMPRN("Could not insert free socklist, but continue..."); + }else{ + ++(*abs_sock_free_cnt); + *abs_sock_frees = freesocklist.GetFirstPtr(false); + } + return true; +} + +template +bool chmpx_lap::SetStatus(chmpxsts_t status) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + if(CHMEMPTYSTR(basic_type::pAbsPtr->name)){ + ERR_CHMPRN("PCHMPX does not initialized."); + return false; + } + if(!IS_SAFE_CHMPXSTS(status)){ + ERR_CHMPRN("Parameter status(0x%016" PRIx64 ":%s) iw not safe value.", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + + basic_type::pAbsPtr->status = status; + basic_type::pAbsPtr->last_status_time = time(NULL); // Only update status time + return true; +} + +template +bool chmpx_lap::Get(std::string& name, chmpxid_t& chmpxid, short& port, short& ctlport) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + if(CHMEMPTYSTR(basic_type::pAbsPtr->name)){ + ERR_CHMPRN("PCHMPX does not initialized."); + return false; + } + name = basic_type::pAbsPtr->name; + chmpxid = basic_type::pAbsPtr->chmpxid; + port = basic_type::pAbsPtr->port; + ctlport = basic_type::pAbsPtr->ctlport; + return true; +} + +template +bool chmpx_lap::Get(socklist_t& socklist, int& ctlsock, int& selfsock, int& selfctlsock) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + if(CHMEMPTYSTR(basic_type::pAbsPtr->name)){ + ERR_CHMPRN("PCHMPX does not initialized."); + return false; + } + + socklist.clear(); + if(basic_type::pAbsPtr->socklist){ + chmsocklistlap tmpsocklist(basic_type::pAbsPtr->socklist, basic_type::pShmBase, false); // From Relative + if(!tmpsocklist.GetAllSocks(socklist)){ + ERR_CHMPRN("Failed to list sockets from socklist."); + return false; + } + } + ctlsock = basic_type::pAbsPtr->ctlsock; + selfsock = basic_type::pAbsPtr->selfsock; + selfctlsock = basic_type::pAbsPtr->selfctlsock; + + return true; +} + +// [NOTE] +// If type is SOCKTG_SOCK, return a sock which is listed at first position in socklist. +// +template +int chmpx_lap::GetSock(int type) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return CHM_INVALID_SOCK; + } + if(CHMEMPTYSTR(basic_type::pAbsPtr->name)){ + ERR_CHMPRN("PCHMPX does not initialized."); + return CHM_INVALID_SOCK; + } + int rtsock = CHM_INVALID_SOCK; + if(SOCKTG_SOCK == type){ + if(basic_type::pAbsPtr->socklist){ + chmsocklistlap tmpsocklist(basic_type::pAbsPtr->socklist, basic_type::pShmBase, false); // From Relative + socklist_t sockarr; + if(tmpsocklist.GetAllSocks(sockarr) && 0 < sockarr.size()){ + // return first sock. + rtsock = sockarr.front(); + } + } + } + if(SOCKTG_CTLSOCK == type){ + rtsock = basic_type::pAbsPtr->ctlsock; + } + if(SOCKTG_SELFSOCK == type){ + rtsock = basic_type::pAbsPtr->selfsock; + } + if(SOCKTG_SELFCTLSOCK == type){ + rtsock = basic_type::pAbsPtr->selfctlsock; + } + return rtsock; +} + +template +bool chmpx_lap::FindSock(int sock) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + if(CHMEMPTYSTR(basic_type::pAbsPtr->name)){ + ERR_CHMPRN("PCHMPX does not initialized."); + return false; + } + if(basic_type::pAbsPtr->socklist){ + chmsocklistlap tmpsocklist(basic_type::pAbsPtr->socklist, basic_type::pShmBase, false); // From Relative + if(NULL != tmpsocklist.Find(sock, true)){ + return true; + } + } + return false; +} + +template +bool chmpx_lap::Get(chmhash_t& base, chmhash_t& pending) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + if(CHMEMPTYSTR(basic_type::pAbsPtr->name)){ + ERR_CHMPRN("PCHMPX does not initialized."); + return false; + } + base = basic_type::pAbsPtr->base_hash; + pending = basic_type::pAbsPtr->pending_hash; + return true; +} + +template +bool chmpx_lap::GetChmpxSvr(PCHMPXSVR chmpxsvr) const +{ + if(!chmpxsvr){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + + strcpy(chmpxsvr->name, basic_type::pAbsPtr->name); + strcpy(chmpxsvr->ssl.capath, basic_type::pAbsPtr->capath); + strcpy(chmpxsvr->ssl.server_cert, basic_type::pAbsPtr->server_cert); + strcpy(chmpxsvr->ssl.server_prikey, basic_type::pAbsPtr->server_prikey); + strcpy(chmpxsvr->ssl.slave_cert, basic_type::pAbsPtr->slave_cert); + strcpy(chmpxsvr->ssl.slave_prikey, basic_type::pAbsPtr->slave_prikey); + + chmpxsvr->chmpxid = basic_type::pAbsPtr->chmpxid; + chmpxsvr->base_hash = basic_type::pAbsPtr->base_hash; + chmpxsvr->pending_hash = basic_type::pAbsPtr->pending_hash; + chmpxsvr->port = basic_type::pAbsPtr->port; + chmpxsvr->ctlport = basic_type::pAbsPtr->ctlport; + chmpxsvr->ssl.is_ssl = basic_type::pAbsPtr->is_ssl; + chmpxsvr->ssl.verify_peer = basic_type::pAbsPtr->verify_peer; + chmpxsvr->ssl.is_ca_file = basic_type::pAbsPtr->is_ca_file; + chmpxsvr->last_status_time = basic_type::pAbsPtr->last_status_time; + chmpxsvr->status = basic_type::pAbsPtr->status; + + return true; +} + +// [NOTICE] +// About merge CHMPX from CHMPXSVR +// If member value is not same, each member is merged by following rule. +// +// name If this value is not same, it means another chmpx data. +// So this function is failed. +// chmpxid There is no reason about these values are different. +// So this function is failed. +// base_hash Over write CHMPXSVR's value. +// pending_hash Over write CHMPXSVR's value. +// port If sock member is an effective value, this value is not +// over wrote. The other case, the value is over wrote.(*1) +// ctlport If ctlsock member is an effective value, this value is not +// over wrote. The other case, the value is over wrote.(*1) +// about ssl If sock and ctlsock members are an effective value, these +// value are not over wrote. The other case, the values are +// over wrote.(*1) +// status If CHMPXSVR last_status_time member is latest, this value +// is over wrote. +// last_status_time If CHMPXSVR last_status_time member is latest, this value +// is over wrote. +// (*1) +// Should not be this case by upper layer, the upper layer should close sockets +// before calling this function. +// +template +bool chmpx_lap::MergeChmpxSvr(PCHMPXSVR chmpxsvr, bool is_force, int eqfd) +{ + if(!chmpxsvr){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + if(is_force){ + if(0 != strcmp(basic_type::pAbsPtr->name, chmpxsvr->name) || basic_type::pAbsPtr->chmpxid != chmpxsvr->chmpxid){ + strcpy(basic_type::pAbsPtr->name, chmpxsvr->name); + basic_type::pAbsPtr->chmpxid = chmpxsvr->chmpxid; + } + if(basic_type::pAbsPtr->port != chmpxsvr->port){ + if(basic_type::pAbsPtr->socklist){ + chmsocklistlap socklist(basic_type::pAbsPtr->socklist, basic_type::pShmBase, false); // From Relative + chmsocklistlap freesocklist(*abs_sock_frees, basic_type::pShmBase, false); // From Relative + + for(PCHMSOCKLIST psocklist = socklist.Retrive(); psocklist; psocklist = (socklist.GetFirstPtr(false) ? socklist.Retrive() : NULL)){ + if(CHM_INVALID_SOCK != psocklist->sock){ + WAN_CHMPRN("port(%d) is opened(sock:%d), so it is closed.", basic_type::pAbsPtr->port, psocklist->sock); + if(CHM_INVALID_HANDLE != eqfd){ + epoll_ctl(eqfd, EPOLL_CTL_DEL, psocklist->sock, NULL); + } + CHM_CLOSESOCK(psocklist->sock); + } + if(freesocklist.Insert(psocklist, true)){ // Set abs + ++(*abs_sock_free_cnt); + } + } + // Set free list pointer + *abs_sock_frees = freesocklist.GetFirstPtr(false); + // clear + basic_type::pAbsPtr->socklist = NULL; + } + + if(CHM_INVALID_SOCK != basic_type::pAbsPtr->selfsock){ + WAN_CHMPRN("port(%d) is opened(selfsock:%d), so it is closed.", basic_type::pAbsPtr->port, basic_type::pAbsPtr->selfsock); + if(CHM_INVALID_HANDLE != eqfd){ + epoll_ctl(eqfd, EPOLL_CTL_DEL, basic_type::pAbsPtr->selfsock, NULL); + } + CHM_CLOSESOCK(basic_type::pAbsPtr->selfsock); + } + basic_type::pAbsPtr->port = chmpxsvr->port; + } + if(basic_type::pAbsPtr->ctlport != chmpxsvr->ctlport){ + if(CHM_INVALID_SOCK != basic_type::pAbsPtr->ctlsock){ + WAN_CHMPRN("ctlport(%d) is opened(ctlsock:%d), it is closed.", basic_type::pAbsPtr->ctlport, basic_type::pAbsPtr->ctlsock); + if(CHM_INVALID_HANDLE != eqfd){ + epoll_ctl(eqfd, EPOLL_CTL_DEL, basic_type::pAbsPtr->ctlsock, NULL); + } + CHM_CLOSESOCK(basic_type::pAbsPtr->ctlsock); + } + if(CHM_INVALID_SOCK != basic_type::pAbsPtr->selfctlsock){ + WAN_CHMPRN("ctlport(%d) is opened(selfctlsock:%d), it is closed.", basic_type::pAbsPtr->ctlport, basic_type::pAbsPtr->selfctlsock); + if(CHM_INVALID_HANDLE != eqfd){ + epoll_ctl(eqfd, EPOLL_CTL_DEL, basic_type::pAbsPtr->selfctlsock, NULL); + } + CHM_CLOSESOCK(basic_type::pAbsPtr->selfctlsock); + } + basic_type::pAbsPtr->ctlport = chmpxsvr->ctlport; + } + + }else{ + if(0 != strcmp(basic_type::pAbsPtr->name, chmpxsvr->name) || basic_type::pAbsPtr->chmpxid != chmpxsvr->chmpxid){ + ERR_CHMPRN("name(%s - %s) or chmpxid(0x%016" PRIx64 " - 0x%016" PRIx64 ") is different.", basic_type::pAbsPtr->name, chmpxsvr->name, basic_type::pAbsPtr->chmpxid, chmpxsvr->chmpxid); + return false; + } + if(basic_type::pAbsPtr->port != chmpxsvr->port){ + if(basic_type::pAbsPtr->socklist){ + ERR_CHMPRN("port(%d) is opened yet.", basic_type::pAbsPtr->port); + return false; + } + if(CHM_INVALID_SOCK != basic_type::pAbsPtr->selfsock){ + ERR_CHMPRN("port(%d) is opened(selfsock:%d) yet.", basic_type::pAbsPtr->port, basic_type::pAbsPtr->selfsock); + return false; + } + } + if(basic_type::pAbsPtr->ctlport != chmpxsvr->ctlport){ + if(CHM_INVALID_SOCK != basic_type::pAbsPtr->ctlsock){ + ERR_CHMPRN("ctlport(%d) is opened(ctlsock:%d) yet.", basic_type::pAbsPtr->ctlport, basic_type::pAbsPtr->ctlsock); + return false; + } + if(CHM_INVALID_SOCK != basic_type::pAbsPtr->selfctlsock){ + ERR_CHMPRN("ctlport(%d) is opened(selfctlsock:%d) yet.", basic_type::pAbsPtr->ctlport, basic_type::pAbsPtr->selfctlsock); + return false; + } + } + if(basic_type::pAbsPtr->port != chmpxsvr->port){ + basic_type::pAbsPtr->port = chmpxsvr->port; + } + if(basic_type::pAbsPtr->ctlport != chmpxsvr->ctlport){ + basic_type::pAbsPtr->ctlport = chmpxsvr->ctlport; + } + } + + // [NOTICE] + // If base(pending) hash value is valid, set always pointer to chmpxlist array. + // But not clear old pointer when old hash values is valid. + // So it means always over writing array but not clear old.(take care for read + // array value.) + // + if(basic_type::pAbsPtr->base_hash != chmpxsvr->base_hash){ + basic_type::pAbsPtr->base_hash = chmpxsvr->base_hash; + if(CHM_INVALID_HASHVAL != basic_type::pAbsPtr->base_hash){ + if(!abs_base_arr){ + ERR_CHMPRN("abs_base_arr is NULL, could not update base hash array."); + }else{ + abs_base_arr[basic_type::pAbsPtr->base_hash] = chmpx_lap::GetRelPtr(); + } + } + } + if(basic_type::pAbsPtr->pending_hash != chmpxsvr->pending_hash){ + basic_type::pAbsPtr->pending_hash = chmpxsvr->pending_hash; + if(CHM_INVALID_HASHVAL != basic_type::pAbsPtr->pending_hash){ + if(!abs_pend_arr){ + ERR_CHMPRN("abs_pend_arr is NULL, could not update pending hash array."); + }else{ + abs_pend_arr[basic_type::pAbsPtr->pending_hash] = chmpx_lap::GetRelPtr(); + } + } + } + + if(basic_type::pAbsPtr->last_status_time <= chmpxsvr->last_status_time){ + basic_type::pAbsPtr->last_status_time = chmpxsvr->last_status_time; + basic_type::pAbsPtr->status = chmpxsvr->status; + } + return true; +} + +template +bool chmpx_lap::GetSslStructure(CHMPXSSL& ssl) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPX does not set."); + return false; + } + strcpy(ssl.capath, basic_type::pAbsPtr->capath); + strcpy(ssl.server_cert, basic_type::pAbsPtr->server_cert); + strcpy(ssl.server_prikey, basic_type::pAbsPtr->server_prikey); + strcpy(ssl.slave_cert, basic_type::pAbsPtr->slave_cert); + strcpy(ssl.slave_prikey, basic_type::pAbsPtr->slave_prikey); + + ssl.is_ssl = basic_type::pAbsPtr->is_ssl; + ssl.verify_peer = basic_type::pAbsPtr->verify_peer; + ssl.is_ca_file = basic_type::pAbsPtr->is_ca_file; + + return true; +} + +// +// return < 0, means other is small +// return > 0, means other is big +// +template +int chmpx_lap::compare_name(const chmpx_lap& other) const +{ + if(0 == strcmp(other.pAbsPtr->name, basic_type::pAbsPtr->name)){ + return static_cast(other.pAbsPtr->ctlport - basic_type::pAbsPtr->ctlport); + }else{ + return strcmp(other.pAbsPtr->name, basic_type::pAbsPtr->name); + } +} + +typedef chmpx_lap chmpxlap; + +//--------------------------------------------------------- +// For CHMPXLIST +//--------------------------------------------------------- +template +class chmpxlist_lap : public structure_lap +{ + public: + typedef T st_type; + typedef T* st_ptr_type; + typedef structure_lap basic_type; + + protected: + st_ptr_type* pchmpxid_map; + PCHMPX* abs_base_arr; + PCHMPX* abs_pend_arr; + long* abs_sock_free_cnt; + PCHMSOCKLIST* abs_sock_frees; + + protected: + bool SaveChmpxIdMap(st_ptr_type ptr, bool is_abs); + bool RetriveChmpxIdMap(st_ptr_type ptr, bool is_abs); + st_ptr_type SearchChmpxid(chmpxid_t chmpxid); + + public: + chmpxlist_lap(st_ptr_type ptr = NULL, st_ptr_type* absmapptr = NULL, PCHMPX* pchmpxarrbase = NULL, PCHMPX* pchmpxarrpend = NULL, long* psockfreecnt = NULL, PCHMSOCKLIST* psockfrees = NULL, const void* shmbase = NULL, bool is_abs = true); + + PCHMPX GetAbsChmpxPtr(void) const { return (basic_type::pAbsPtr ? &basic_type::pAbsPtr->chmpx : NULL); } + PCHMPX GetRelChmpxPtr(void) const { return (basic_type::pAbsPtr ? CHM_REL(basic_type::pShmBase, &basic_type::pAbsPtr->chmpx, PCHMPX) : NULL); } + + void Reset(st_ptr_type ptr, st_ptr_type* absmapptr, PCHMPX* pchmpxarrbase, PCHMPX* pchmpxarrpend, long* psockfreecnt, PCHMSOCKLIST* psockfrees, const void* shmbase, bool is_abs = true); + virtual bool Initialize(void); + virtual bool Dump(std::stringstream& sstream, const char* spacer) const; + virtual bool Clear(void); + bool Clear(int eqfd); + st_ptr_type Dup(bool only_list_top = false); + void Free(st_ptr_type ptr) const; + + bool Initialize(st_ptr_type prev, st_ptr_type next, bool is_abs = true); + bool SaveChmpxIdMap(void) { return SaveChmpxIdMap(basic_type::pAbsPtr, true); } + bool RetriveChmpxIdMap(void) { return RetriveChmpxIdMap(basic_type::pAbsPtr, true); } + + st_ptr_type GetFirstPtr(bool is_abs = true); + bool ToFirst(void); + bool ToLast(void); + bool ToNext(bool is_cycle = true, bool is_base_hash = false, bool is_up_servers = false, bool is_allow_same_server = true, bool without_suspend = false); + long Count(void); + long Count(bool toward_normal); + long GetChmpxIds(chmpxidlist_t& list, chmpxsts_t status_mask = CHMPXSTS_MASK_ALL, bool part_match = true, bool is_to_first = false); + long GetChmpxIds(chmpxidlist_t& list, bool with_pending, bool without_down = true, bool without_suspend = true, bool is_to_first = false); + + long BaseHashCount(bool is_to_first = false); + long PendingHashCount(bool is_to_first = false); + bool SetPendingHash(bool is_to_first = false); + bool IsOperating(bool is_to_first = false); + + st_ptr_type PopFront(void); + st_ptr_type PopAny(void); + bool Push(st_ptr_type ptr, bool is_abs, bool is_to_first = false); + bool PushBack(st_ptr_type ptr, bool is_abs); + bool Insert(st_ptr_type ptr, bool is_abs); + bool Find(chmpxid_t chmpxid); + bool Find(int sock, bool is_to_first = false); + bool FindByHash(chmhash_t hash, bool is_base_hash = true, bool is_to_first = false); + chmpxid_t FindByStatus(chmpxsts_t status, bool part_match = false, bool is_to_first = false); + chmpxid_t GetRandomChmpxId(bool is_up_servers = false); + chmpxid_t GetChmpxIdByHash(chmhash_t hash); + st_ptr_type Retrive(PCHMPX ptr, bool is_abs); + st_ptr_type Retrive(void); +}; + +template +chmpxlist_lap::chmpxlist_lap(st_ptr_type ptr, st_ptr_type* absmapptr, PCHMPX* pchmpxarrbase, PCHMPX* pchmpxarrpend, long* psockfreecnt, PCHMSOCKLIST* psockfrees, const void* shmbase, bool is_abs) : pchmpxid_map(absmapptr) +{ + Reset(ptr, absmapptr, pchmpxarrbase, pchmpxarrpend, psockfreecnt, psockfrees, shmbase, is_abs); +} + +template +void chmpxlist_lap::Reset(st_ptr_type ptr, st_ptr_type* absmapptr, PCHMPX* pchmpxarrbase, PCHMPX* pchmpxarrpend, long* psockfreecnt, PCHMSOCKLIST* psockfrees, const void* shmbase, bool is_abs) +{ + structure_lap::Reset(ptr, shmbase, is_abs); + pchmpxid_map = absmapptr; + abs_base_arr = pchmpxarrbase; + abs_pend_arr = pchmpxarrpend; + abs_sock_free_cnt = psockfreecnt; + abs_sock_frees = psockfrees; +} + +template +bool chmpxlist_lap::Initialize(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return false; + } + basic_type::pAbsPtr->prev = NULL; + basic_type::pAbsPtr->next = NULL; + basic_type::pAbsPtr->same = NULL; + + chmpxlap tmpchmpx(&basic_type::pAbsPtr->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + if(!tmpchmpx.Initialize()){ + ERR_CHMPRN("Failed to initialize CHMPX."); + return false; + } + return true; +} + +template +bool chmpxlist_lap::Initialize(st_ptr_type prev, st_ptr_type next, bool is_abs) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return false; + } + basic_type::pAbsPtr->prev = is_abs ? CHM_REL(basic_type::pShmBase, prev, st_ptr_type) : prev; + basic_type::pAbsPtr->next = is_abs ? CHM_REL(basic_type::pShmBase, next, st_ptr_type) : next; + basic_type::pAbsPtr->same = NULL; + + chmpxlap tmpchmpx(&basic_type::pAbsPtr->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + if(!tmpchmpx.Initialize()){ + ERR_CHMPRN("Failed to initialize CHMPX."); + return false; + } + return true; +} + +template +bool chmpxlist_lap::Dump(std::stringstream& sstream, const char* spacer) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return false; + } + std::string tmpspacer1 = spacer ? spacer : ""; + std::string tmpspacer2; + tmpspacer1 += " "; + tmpspacer2 += tmpspacer1 + " "; + + // To first + st_ptr_type cur; + for(cur = const_cast(basic_type::pAbsPtr); cur->prev; cur = CHM_ABS(basic_type::pShmBase, cur->prev, st_ptr_type)); + + for(long count = 0L; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type), count++){ + sstream << (spacer ? spacer : "") << "[" << count << "] = {" << std::endl; + + sstream << tmpspacer1 << "prev = " << cur->prev << std::endl; + sstream << tmpspacer1 << "next = " << cur->next << std::endl; + sstream << tmpspacer1 << "same = " << cur->same << std::endl; + + sstream << tmpspacer1 << "chmpx{" << std::endl; + + chmpxlap tmpchmpx(&cur->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + tmpchmpx.Dump(sstream, tmpspacer2.c_str()); + sstream << tmpspacer1 << "}" << std::endl; + + sstream << (spacer ? spacer : "") << "}" << std::endl; + } + return true; +} + +// [NOTE] +// only_list_top is true, duplicate only one list(always it is self) +// +template +typename chmpxlist_lap::st_ptr_type chmpxlist_lap::Dup(bool only_list_top) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return NULL; + } + + long count = (only_list_top ? 1 : Count()); + if(0 == count){ + return NULL; + } + + // To first + st_ptr_type cur; + if(only_list_top){ + cur = const_cast(basic_type::pAbsPtr); + }else{ + for(cur = const_cast(basic_type::pAbsPtr); cur->prev; cur = CHM_ABS(basic_type::pShmBase, cur->prev, st_ptr_type)); + } + + // duplicate + st_ptr_type pdst; + if(NULL == (pdst = reinterpret_cast(calloc(count, sizeof(st_type))))){ + ERR_CHMPRN("Could not allocation memory."); + return NULL; + } + if(only_list_top){ + pdst->next = NULL; + pdst->prev = NULL; + chmpxlap tmpchmpx(&cur->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + if(!tmpchmpx.Copy(&(pdst->chmpx))){ + ERR_CHMPRN("Failed to copy top of chmpx structure list."); + CHM_Free(pdst); + return NULL; + } + }else{ + for(st_ptr_type pdstcur = pdst, pdstprev = NULL; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type), pdstprev = pdstcur++){ + if(pdstprev){ + pdstprev->next = pdstcur; + } + pdstcur->prev = pdstprev; + + chmpxlap tmpchmpx(&cur->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + if(!tmpchmpx.Copy(&(pdstcur->chmpx))){ + WAN_CHMPRN("Failed to copy chmpx structure, but continue..."); + } + } + } + return pdst; +} + +template +void chmpxlist_lap::Free(st_ptr_type ptr) const +{ + if(ptr){ + for(st_ptr_type pcur = ptr; pcur; pcur = pcur->next){ + chmpxlap tmpchmpx; // [NOTE] pointer is not on Shm, but we use chmpxlap object. thus using only Free method. + tmpchmpx.Free(&pcur->chmpx); + } + K2H_Free(ptr); + } +} + +template +bool chmpxlist_lap::Clear(void) +{ + return Clear(CHM_INVALID_HANDLE); +} + +template +bool chmpxlist_lap::Clear(int eqfd) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPCLIST does not set."); + return false; + } + RetriveChmpxIdMap(); // basic_type::pAbsPtr->same + basic_type::pAbsPtr->prev = NULL; + basic_type::pAbsPtr->next = NULL; + + chmpxlap tmpchmpx(&basic_type::pAbsPtr->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + if(CHM_INVALID_HANDLE != eqfd){ + tmpchmpx.Close(eqfd); + } + if(!tmpchmpx.Clear()){ + ERR_CHMPRN("Failed to clear CHMPX."); + return false; + } + return true; +} + +template +bool chmpxlist_lap::SaveChmpxIdMap(st_ptr_type ptr, bool is_abs) +{ + if(!ptr){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPCLIST does not set."); + return false; + } + st_ptr_type absptr = is_abs ? ptr : CHM_ABS(basic_type::pShmBase, ptr, st_ptr_type); + chmpxlap tmpchmpx(&absptr->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + chmpxid_t chmpxid = tmpchmpx.GetChmpxId(); + + if(CHM_INVALID_CHMPXID == chmpxid){ + return true; // Not need to save + } + if(!pchmpxid_map){ + ERR_CHMPRN("pchmpxid_map is NULL."); + return false; + } + + // set to last pos(check same pointer exists) + st_ptr_type* ppabsmap; + st_ptr_type pabsmap; + for(ppabsmap = &pchmpxid_map[chmpxid & CHMPXID_MAP_MASK], pabsmap = CHM_ABS(basic_type::pShmBase, pchmpxid_map[chmpxid & CHMPXID_MAP_MASK], st_ptr_type); + pabsmap; + ppabsmap = &pabsmap->same, pabsmap = CHM_ABS(basic_type::pShmBase, pabsmap->same, st_ptr_type)) + { + if(pabsmap == absptr){ + // already set.(why?) + break; + } + } + if(!pabsmap){ + *ppabsmap = CHM_REL(basic_type::pShmBase, absptr, st_ptr_type); + } + if(absptr->same){ + WAN_CHMPRN("ptr->same is not NULL, so force to set NULL."); + absptr->same = NULL; + } + + return true; +} + +template +bool chmpxlist_lap::RetriveChmpxIdMap(st_ptr_type ptr, bool is_abs) +{ + if(!ptr){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPCLIST does not set."); + return false; + } + st_ptr_type absptr = is_abs ? ptr : CHM_ABS(basic_type::pShmBase, ptr, st_ptr_type); + chmpxlap tmpchmpx(&absptr->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + chmpxid_t chmpxid = tmpchmpx.GetChmpxId(); + + if(!pchmpxid_map){ + WAN_CHMPRN("pchmpxid_map is NULL."); + } + if(CHM_INVALID_CHMPXID != chmpxid && pchmpxid_map){ + st_ptr_type* ppabsmap; + st_ptr_type pabsmap; + for(ppabsmap = &pchmpxid_map[chmpxid & CHMPXID_MAP_MASK], pabsmap = CHM_ABS(basic_type::pShmBase, pchmpxid_map[chmpxid & CHMPXID_MAP_MASK], st_ptr_type); + pabsmap; + ppabsmap = &pabsmap->same, pabsmap = CHM_ABS(basic_type::pShmBase, pabsmap->same, st_ptr_type)) + { + if(pabsmap == absptr){ + *ppabsmap = absptr->same; + break; + } + } + } + absptr->same = NULL; + + return true; +} + +template +typename chmpxlist_lap::st_ptr_type chmpxlist_lap::SearchChmpxid(chmpxid_t chmpxid) +{ + if(CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Parameter is wrong."); + return NULL; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPCLIST does not set."); + return NULL; + } + if(!pchmpxid_map){ + ERR_CHMPRN("pchmpxid_map is NULL."); + return NULL; + } + + chmpxlap tmpchmpx(&basic_type::pAbsPtr->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + bool is_server = tmpchmpx.IsServerMode(); + + for(st_ptr_type pabsmap = CHM_ABS(basic_type::pShmBase, pchmpxid_map[chmpxid & CHMPXID_MAP_MASK], st_ptr_type); pabsmap; pabsmap = CHM_ABS(basic_type::pShmBase, pabsmap->same, st_ptr_type)){ + tmpchmpx.Reset(&pabsmap->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + if(is_server == tmpchmpx.IsServerMode() && chmpxid == tmpchmpx.GetChmpxId()){ + // found same mode & chmpxid + return pabsmap; + } + } + // Not found + return NULL; +} + +template +typename chmpxlist_lap::st_ptr_type chmpxlist_lap::GetFirstPtr(bool is_abs) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + //MSG_CHMPRN("PCHMPXLIST does not set."); + return NULL; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + return (is_abs ? chmpxlist_lap::GetAbsPtr() : chmpxlist_lap::GetRelPtr()); + +} + +template +bool chmpxlist_lap::ToFirst(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return false; + } + for(; basic_type::pAbsPtr->prev; basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->prev, st_ptr_type)); + return true; +} + +template +bool chmpxlist_lap::ToLast(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return false; + } + for(; basic_type::pAbsPtr->next; basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type)); + return true; +} + +// +// If is_allow_same_server is false when is_cycle is true, this method returns false when the next server +// is same as start server. +// +template +bool chmpxlist_lap::ToNext(bool is_cycle, bool is_base_hash, bool is_up_servers, bool is_allow_same_server, bool without_suspend) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return false; + } + st_ptr_type startpos = basic_type::pAbsPtr; + + if(!basic_type::pAbsPtr->next){ + if(!is_cycle){ + return is_allow_same_server; + } + ToFirst(); + }else{ + basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type); + } + + chmpxlap curchmpx; + while(startpos != basic_type::pAbsPtr){ + if(!is_base_hash && !is_up_servers){ + return true; + } + curchmpx.Reset(&basic_type::pAbsPtr->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + + chmpxsts_t status = curchmpx.GetStatus(); + if((!is_base_hash || IS_CHMPXSTS_BASEHASH(status)) && (!is_up_servers || IS_CHMPXSTS_UP(status)) && (!without_suspend || IS_CHMPXSTS_NOSUP(status))){ + return true; + } + + if(!basic_type::pAbsPtr->next){ + if(!is_cycle){ + basic_type::pAbsPtr = startpos; // restore + return is_allow_same_server; + } + ToFirst(); + }else{ + basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type); + } + } + return is_allow_same_server; +} + +template +long chmpxlist_lap::Count(void) +{ + if(!basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return 0L; + } + if(!basic_type::pAbsPtr){ + //MSG_CHMPRN("PCHMPXLIST does not set."); + return 0L; + } + ToFirst(); + long count; + st_ptr_type pos; + for(count = 1L, pos = basic_type::pAbsPtr; pos->next; pos = CHM_ABS(basic_type::pShmBase, pos->next, st_ptr_type), count++); + + return count; +} + +template +long chmpxlist_lap::Count(bool toward_normal) +{ + if(!basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return 0L; + } + if(!basic_type::pAbsPtr){ + //MSG_CHMPRN("PCHMPXLIST does not set."); + return 0L; + } + long count; + st_ptr_type pos; + for(count = 1L, pos = basic_type::pAbsPtr; NULL != (toward_normal ? pos->next : pos->prev); pos = CHM_ABS(basic_type::pShmBase, (toward_normal ? pos->next : pos->prev), st_ptr_type), count++); + + return count; +} + +template +long chmpxlist_lap::GetChmpxIds(chmpxidlist_t& list, chmpxsts_t status_mask, bool part_match, bool is_to_first) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + //MSG_CHMPRN("PCHMPXLIST does not set."); + return 0L; + } + if(is_to_first){ + ToFirst(); + } + list.clear(); + + chmpxlap curchmpx; + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + curchmpx.Reset(&cur->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + + chmpxsts_t status = curchmpx.GetStatus(); + if(part_match){ + if(0 == (status & status_mask)){ + continue; + } + }else{ + if(status_mask != (status & status_mask)){ + continue; + } + } + list.push_back(curchmpx.GetChmpxId()); + } + return static_cast(list.size()); +} + +template +long chmpxlist_lap::GetChmpxIds(chmpxidlist_t& list, bool with_pending, bool without_down, bool without_suspend, bool is_to_first) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return 0L; + } + if(is_to_first){ + ToFirst(); + } + list.clear(); + + chmpxlap curchmpx; + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + curchmpx.Reset(&cur->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + + chmpxsts_t status = curchmpx.GetStatus(); + if(without_down && IS_CHMPXSTS_DOWN(status)){ + continue; + } + if(without_suspend && IS_CHMPXSTS_SUSPEND(status)){ + continue; + } + if(IS_CHMPXSTS_BASEHASH(status) || (with_pending && IS_CHMPXSTS_PENDINGHASH(status))){ + list.push_back(curchmpx.GetChmpxId()); + } + } + return static_cast(list.size()); +} + +// +// This method is linear search in list, then it is not good performance. +// +template +long chmpxlist_lap::BaseHashCount(bool is_to_first) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return 0L; + } + if(is_to_first){ + ToFirst(); + } + + long count; + chmpxlap curchmpx; + st_ptr_type cur; + for(count = 0L, cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + curchmpx.Reset(&cur->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + + chmpxsts_t status = curchmpx.GetStatus(); + if(IS_CHMPXSTS_BASEHASH(status)){ + count++; + } + } + return count; +} + +// +// This method is linear search in list, then it is not good performance. +// +template +long chmpxlist_lap::PendingHashCount(bool is_to_first) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return 0L; + } + if(is_to_first){ + ToFirst(); + } + long count; + chmpxlap curchmpx; + st_ptr_type cur; + for(count = 0L, cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + curchmpx.Reset(&cur->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + + chmpxsts_t status = curchmpx.GetStatus(); + if(IS_CHMPXSTS_PENDINGHASH(status)){ + count++; + } + } + return count; +} + +// +// This method is linear search in list, then it is not good performance. +// +template +bool chmpxlist_lap::SetPendingHash(bool is_to_first) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return false; + } + if(is_to_first){ + ToFirst(); + } + + st_ptr_type cur; + chmpxlap curchmpx; + chmpxsts_t status; + chmhash_t base; + chmhash_t pending; + chmhash_t newpending; + + ToFirst(); + for(newpending = 0L, cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + curchmpx.Reset(&cur->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + + status = curchmpx.GetStatus(); + base = CHM_INVALID_HASHVAL; + pending = CHM_INVALID_HASHVAL; + + if(!curchmpx.Get(base, pending)){ + ERR_CHMPRN("Failed to get hash values."); + return false; + } + if(IS_CHMPXSTS_PENDINGHASH(status)){ + if(pending == newpending){ + // OK, nothing to update. + }else{ + // Set new pending hash value + curchmpx.Set(0L, newpending, HASHTG_PENDING); + } + newpending++; + } + } + return true; +} + +// +// This method is linear search in list, then it is not good performance. +// +template +bool chmpxlist_lap::IsOperating(bool is_to_first) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return false; + } + if(is_to_first){ + ToFirst(); + } + + st_ptr_type cur; + chmpxlap curchmpx; + for(cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + curchmpx.Reset(&cur->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + + chmpxsts_t status = curchmpx.GetStatus(); + if(IS_CHMPXSTS_OPERATING(status)){ + return true; + } + } + return false; +} + +template +typename chmpxlist_lap::st_ptr_type chmpxlist_lap::PopFront(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return NULL; + } + // find list first + st_ptr_type first; + for(first = basic_type::pAbsPtr; first->prev; first = CHM_ABS(basic_type::pShmBase, first->prev, st_ptr_type)); + + if(NULL != (basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, first->next, st_ptr_type))){ + basic_type::pAbsPtr->prev = NULL; + } + first->next = NULL; + + // Do not change "same" member at here. + // If first has valid chmpxid, it maps in pchmpxid_map. + + return first; // Absolute +} + +// +// [NOTICE] +// This method retrives current chmpxlist object if it is not first of list object. +// +template +typename chmpxlist_lap::st_ptr_type chmpxlist_lap::PopAny(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return NULL; + } + + st_ptr_type resultptr = NULL; + st_ptr_type tmpptr = NULL; + if(basic_type::pAbsPtr->next){ + resultptr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type); + tmpptr = CHM_ABS(basic_type::pShmBase, resultptr->next, st_ptr_type); + + basic_type::pAbsPtr->next = resultptr->next; + if(tmpptr){ + tmpptr->prev = CHM_REL(basic_type::pShmBase, basic_type::pAbsPtr, st_ptr_type); + } + }else{ + resultptr = basic_type::pAbsPtr; + if(basic_type::pAbsPtr->prev){ + basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->prev, st_ptr_type); + basic_type::pAbsPtr->next = NULL; + }else{ + basic_type::pAbsPtr = NULL; + } + } + resultptr->prev = NULL; + resultptr->next = NULL; + + // Do not change "same" member at here. + // If first has valid chmpxid, it maps in pchmpxid_map. + + return resultptr; +} + +template +bool chmpxlist_lap::Push(st_ptr_type ptr, bool is_abs, bool is_to_first) +{ + if(!ptr){ + ERR_CHMPRN("ptr is null."); + return false; + } + if(!basic_type::pShmBase){ // allow basic_type::pAbsPtr is NULL + ERR_CHMPRN("PCHMPXLIST does not set."); + return false; + } + + st_ptr_type current = basic_type::pAbsPtr; + if(is_to_first){ + for(; current && current->prev; current = CHM_ABS(basic_type::pShmBase, current->prev, st_ptr_type)); + } + + st_ptr_type relptr = is_abs ? CHM_REL(basic_type::pShmBase, ptr, st_ptr_type) : ptr; + st_ptr_type absptr = is_abs ? ptr : CHM_ABS(basic_type::pShmBase, ptr, st_ptr_type); + st_ptr_type prevptr = current ? CHM_REL(basic_type::pShmBase, current->prev, st_ptr_type) : NULL; + + if(prevptr){ + prevptr->next = relptr; + } + absptr->prev = current ? current->prev : NULL; + absptr->next = current ? CHM_REL(basic_type::pShmBase, current, st_ptr_type) : NULL; + if(current){ + current->prev = relptr; + } + basic_type::pAbsPtr = absptr; + + if(!SaveChmpxIdMap(absptr, true)){ + ERR_CHMPRN("Failed to set into chmpxid map area."); + return false; + } + return true; +} + +template +bool chmpxlist_lap::PushBack(st_ptr_type ptr, bool is_abs) +{ + if(!ptr){ + ERR_CHMPRN("ptr is null."); + return false; + } + if(!basic_type::pShmBase){ // allow basic_type::pAbsPtr is NULL + ERR_CHMPRN("PCHMPXLIST does not set."); + return false; + } + // find list last + st_ptr_type last; + for(last = basic_type::pAbsPtr; last && last->next; last = CHM_ABS(basic_type::pShmBase, last->next, st_ptr_type)); + + st_ptr_type relptr = is_abs ? CHM_REL(basic_type::pShmBase, ptr, st_ptr_type) : ptr; + st_ptr_type absptr = is_abs ? ptr : CHM_ABS(basic_type::pShmBase, ptr, st_ptr_type); + + if(last){ + last->next = relptr; + } + absptr->prev = last ? CHM_REL(basic_type::pShmBase, last, st_ptr_type) : NULL; + absptr->next = NULL; + basic_type::pAbsPtr = absptr; + + if(!SaveChmpxIdMap(absptr, true)){ + ERR_CHMPRN("Failed to set into chmpxid map area."); + return false; + } + return true; +} + +template +bool chmpxlist_lap::Insert(st_ptr_type ptr, bool is_abs) +{ + if(!ptr){ + ERR_CHMPRN("ptr is null."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return false; + } + // To top + ToFirst(); + + // insert + st_ptr_type newptr = is_abs ? ptr : CHM_ABS(basic_type::pShmBase, ptr, st_ptr_type); + chmpxlap target(&newptr->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + chmpxlap curchmpx; + st_ptr_type cur; + st_ptr_type last; + for(cur = basic_type::pAbsPtr, last = NULL; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type), last = cur){ + curchmpx.Reset(&cur->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + if(0 > curchmpx.compare_name(target)){ + st_ptr_type prev= CHM_ABS(basic_type::pShmBase, cur->prev, st_ptr_type); + newptr->prev = CHM_REL(basic_type::pShmBase, prev, st_ptr_type); + newptr->next = CHM_REL(basic_type::pShmBase, cur, st_ptr_type); + cur->prev = CHM_REL(basic_type::pShmBase, newptr, st_ptr_type); + if(prev){ + prev->next = CHM_REL(basic_type::pShmBase, newptr, st_ptr_type); + } + break; + } + } + if(!cur){ + if(last){ + last->next = CHM_REL(basic_type::pShmBase, newptr, st_ptr_type); + newptr->prev = CHM_REL(basic_type::pShmBase, last, st_ptr_type); + newptr->next = NULL; + }else{ + // come here, on slave case when no slave. + return PushBack(ptr, is_abs); + } + } + if(!SaveChmpxIdMap(newptr, true)){ + ERR_CHMPRN("Failed to set into chmpxid map area."); + return false; + } + return true; +} + +template +bool chmpxlist_lap::Find(chmpxid_t chmpxid) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return false; + } + + st_ptr_type found = SearchChmpxid(chmpxid); + if(!found){ + return false; + } + + basic_type::pAbsPtr = found; + return true; +} + +template +bool chmpxlist_lap::Find(int sock, bool is_to_first) +{ + if(CHM_INVALID_SOCK == sock){ + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return false; + } + if(is_to_first){ + ToFirst(); + } + + // find + chmpxlap curchmpx; + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + curchmpx.Reset(&cur->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + if(curchmpx.FindSock(sock)){ + // set current + basic_type::pAbsPtr = cur; + return true; + } + } + return false; +} + +// +// This method searches liner in list, so do not use this as possible as you can. +// +template +bool chmpxlist_lap::FindByHash(chmhash_t hash, bool is_base_hash, bool is_to_first) +{ + if(hash == CHM_INVALID_HASHVAL){ + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return false; + } + if(is_to_first){ + ToFirst(); + } + + // find + chmpxlap curchmpx; + chmhash_t basehash; + chmhash_t pendinghash; + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + curchmpx.Reset(&cur->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + basehash = CHM_INVALID_HASHVAL; + pendinghash = CHM_INVALID_HASHVAL; + if(curchmpx.Get(basehash, pendinghash)){ + if((is_base_hash && basehash == hash) || (!is_base_hash && pendinghash == hash)){ + // set current + basic_type::pAbsPtr = cur; + return true; + } + } + } + return false; +} + +// +// Returns found first same status chmpxid in list. +// +// This method is linear search in list, then it is not good performance. +// +template +chmpxid_t chmpxlist_lap::FindByStatus(chmpxsts_t status, bool part_match, bool is_to_first) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return CHM_INVALID_CHMPXID; + } + if(is_to_first){ + ToFirst(); + } + + chmpxlap curchmpx; + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + curchmpx.Reset(&cur->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + if(part_match){ + if(status == (curchmpx.GetStatus() & status)){ + return curchmpx.GetChmpxId(); + } + }else{ + if(status == curchmpx.GetStatus()){ + return curchmpx.GetChmpxId(); + } + } + } + return CHM_INVALID_CHMPXID; +} + +// +// This method is not used now.(don't use this beacuse not good performance) +// +// This method calls BaseHashCount(), then it is not good performance. +// And rand() function is not good. +// Should use chmpxman_lap::GetRandomServerChmpxId() instead of this. +// +template +chmpxid_t chmpxlist_lap::GetRandomChmpxId(bool is_up_servers) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return CHM_INVALID_CHMPXID; + } + ToFirst(); + + // for random position. + // Using rand(), and BaseHashCount() is all enable server count. + // + long basecnt = BaseHashCount(false); + if(0L >= basecnt){ + return CHM_INVALID_CHMPXID; + } + long pos = static_cast(rand()) % basecnt; // random position. + + chmpxlap curchmpx; + chmpxsts_t status; + for(; 0 < pos && basic_type::pAbsPtr->next; basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type)){ + curchmpx.Reset(&basic_type::pAbsPtr->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + status = curchmpx.GetStatus(); + if(IS_CHMPXSTS_BASEHASH(status) && (!is_up_servers || IS_CHMPXSTS_UP(status))){ + --pos; + } + } + curchmpx.Reset(&basic_type::pAbsPtr->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + return curchmpx.GetChmpxId(); +} + +// +// This method is not used now.(don't use this beacuse not good performance) +// +// This method calls BaseHashCount(), then it is not good performance. +// Should use chmpxman_lap::GetServerChmpxIdByHash() instead of this. +// +template +chmpxid_t chmpxlist_lap::GetChmpxIdByHash(chmhash_t hash) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return CHM_INVALID_CHMPXID; + } + ToFirst(); + + long basecnt = BaseHashCount(false); + if(0L >= basecnt){ + return CHM_INVALID_CHMPXID; + } + hash = hash % static_cast(basecnt); + + ToFirst(); + chmpxlap curchmpx; + chmhash_t base; + chmhash_t pending; + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + curchmpx.Reset(&cur->chmpx, abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase); + if(curchmpx.Get(base, pending) && base == hash){ + return curchmpx.GetChmpxId(); + } + } + return CHM_INVALID_CHMPXID; +} + +// +// This method is not used now.(don't use this beacuse not good performance) +// +// [CAREFUL] +// After calling this method, be careful for pAbsPtr is NULL. +// +template +typename chmpxlist_lap::st_ptr_type chmpxlist_lap::Retrive(PCHMPX ptr, bool is_abs) +{ + if(!ptr){ + ERR_CHMPRN("ptr is null."); + return NULL; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return NULL; + } + ToFirst(); + + if(!is_abs){ + ptr = CHM_ABS(basic_type::pShmBase, ptr, PCHMPX); + } + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + if(&(cur->chmpx) == ptr){ + // found + if(cur == basic_type::pAbsPtr){ + // basic_type::pAbsPtr is first object in list. + if(basic_type::pAbsPtr->next){ + basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type); + }else{ + basic_type::pAbsPtr = NULL; + } + } + st_ptr_type prevlist = CHM_ABS(basic_type::pShmBase, cur->prev, st_ptr_type); + st_ptr_type nextlist = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type); + if(prevlist){ + prevlist->next = cur->next; + } + if(nextlist){ + nextlist->prev = cur->prev; + } + cur->next = NULL; + cur->prev = NULL; + + RetriveChmpxIdMap(cur, true); // basic_type::pAbsPtr->same + return cur; + } + } + WAN_CHMPRN("Could not find %p PCHMPX.", ptr); + return NULL; +} + +template +typename chmpxlist_lap::st_ptr_type chmpxlist_lap::Retrive(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXLIST does not set."); + return NULL; + } + st_ptr_type current = basic_type::pAbsPtr; + + if(basic_type::pAbsPtr->next){ + basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type); + }else if(basic_type::pAbsPtr->prev){ + basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->prev, st_ptr_type); + }else{ + basic_type::pAbsPtr = NULL; + } + + st_ptr_type prevlist= CHM_ABS(basic_type::pShmBase, current->prev, st_ptr_type); + st_ptr_type nextlist= CHM_ABS(basic_type::pShmBase, current->next, st_ptr_type); + if(prevlist){ + prevlist->next = current->next; + } + if(nextlist){ + nextlist->prev = current->prev; + } + current->next = NULL; + current->prev = NULL; + + if(basic_type::pAbsPtr){ + RetriveChmpxIdMap(current, true); // basic_type::pAbsPtr->same + } + return current; +} + +typedef chmpxlist_lap chmpxlistlap; + +//--------------------------------------------------------- +// For CHMSTAT +//--------------------------------------------------------- +template +class chmstat_lap : public structure_lap +{ + public: + typedef T st_type; + typedef T* st_ptr_type; + typedef structure_lap basic_type; + + public: + chmstat_lap(st_ptr_type ptr = NULL, const void* shmbase = NULL, bool is_abs = true); + + virtual bool Initialize(void); + virtual bool Dump(std::stringstream& sstream, const char* spacer) const; + virtual bool Clear(void); + bool Copy(st_ptr_type ptr) const; + + bool Add(bool is_sent, size_t length, const struct timespec& elapsed_time); + bool Get(st_ptr_type pstat) const; +}; + +template +chmstat_lap::chmstat_lap(st_ptr_type ptr, const void* shmbase, bool is_abs) +{ + structure_lap::Reset(ptr, shmbase, is_abs); +} + +template +bool chmstat_lap::Initialize(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSTAT does not set."); + return false; + } + basic_type::pAbsPtr->total_sent_count = 0L; + basic_type::pAbsPtr->total_received_count = 0L; + basic_type::pAbsPtr->total_body_bytes = 0L; + basic_type::pAbsPtr->min_body_bytes = 0L; + basic_type::pAbsPtr->max_body_bytes = 0L; + INIT_TIMESPEC(&basic_type::pAbsPtr->total_elapsed_time); + INIT_TIMESPEC(&basic_type::pAbsPtr->min_elapsed_time); + INIT_TIMESPEC(&basic_type::pAbsPtr->max_elapsed_time); + + return true; +} + +template +bool chmstat_lap::Dump(std::stringstream& sstream, const char* spacer) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSTAT does not set."); + return false; + } + sstream << (spacer ? spacer : "") << "total_sent_count = " << basic_type::pAbsPtr->total_sent_count << std::endl; + sstream << (spacer ? spacer : "") << "total_received_count = " << basic_type::pAbsPtr->total_received_count << std::endl; + sstream << (spacer ? spacer : "") << "total_body_bytes = " << basic_type::pAbsPtr->total_body_bytes << std::endl; + sstream << (spacer ? spacer : "") << "min_body_bytes = " << basic_type::pAbsPtr->min_body_bytes << std::endl; + sstream << (spacer ? spacer : "") << "max_body_bytes = " << basic_type::pAbsPtr->max_body_bytes << std::endl; + sstream << (spacer ? spacer : "") << "total_elapsed_time = " << basic_type::pAbsPtr->total_elapsed_time.tv_sec << "s " << basic_type::pAbsPtr->total_elapsed_time.tv_nsec << "ns" << std::endl; + sstream << (spacer ? spacer : "") << "min_elapsed_time = " << basic_type::pAbsPtr->min_elapsed_time.tv_sec << "s " << basic_type::pAbsPtr->min_elapsed_time.tv_nsec << "ns" << std::endl; + sstream << (spacer ? spacer : "") << "max_elapsed_time = " << basic_type::pAbsPtr->max_elapsed_time.tv_sec << "s " << basic_type::pAbsPtr->max_elapsed_time.tv_nsec << "ns" << std::endl; + + return true; +} + +template +bool chmstat_lap::Copy(st_ptr_type ptr) const +{ + if(!ptr){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSTAT does not set."); + return false; + } + ptr->total_sent_count = basic_type::pAbsPtr->total_sent_count; + ptr->total_received_count = basic_type::pAbsPtr->total_received_count; + ptr->total_body_bytes = basic_type::pAbsPtr->total_body_bytes; + ptr->min_body_bytes = basic_type::pAbsPtr->min_body_bytes; + ptr->max_body_bytes = basic_type::pAbsPtr->max_body_bytes; + ptr->total_elapsed_time.tv_sec = basic_type::pAbsPtr->total_elapsed_time.tv_sec; + ptr->total_elapsed_time.tv_nsec = basic_type::pAbsPtr->total_elapsed_time.tv_nsec; + ptr->min_elapsed_time.tv_sec = basic_type::pAbsPtr->min_elapsed_time.tv_sec; + ptr->min_elapsed_time.tv_nsec = basic_type::pAbsPtr->min_elapsed_time.tv_nsec; + ptr->max_elapsed_time.tv_sec = basic_type::pAbsPtr->max_elapsed_time.tv_sec; + ptr->max_elapsed_time.tv_nsec = basic_type::pAbsPtr->max_elapsed_time.tv_nsec; + + return true; +} + +template +bool chmstat_lap::Clear(void) +{ + return Initialize(); +} + +template +bool chmstat_lap::Add(bool is_sent, size_t length, const struct timespec& elapsed_time) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSTAT does not set."); + return false; + } + + if(is_sent){ + basic_type::pAbsPtr->total_sent_count++; + }else{ + basic_type::pAbsPtr->total_received_count++; + } + + if(0 == basic_type::pAbsPtr->min_body_bytes || length < basic_type::pAbsPtr->min_body_bytes){ + basic_type::pAbsPtr->min_body_bytes = length; + } + if(basic_type::pAbsPtr->max_body_bytes < length){ + basic_type::pAbsPtr->max_body_bytes = length; + } + basic_type::pAbsPtr->total_body_bytes += length; + + if((0 == basic_type::pAbsPtr->min_elapsed_time.tv_sec && 0 == basic_type::pAbsPtr->min_elapsed_time.tv_nsec) || 0 > COMPARE_TIMESPEC(&(basic_type::pAbsPtr->min_elapsed_time), &elapsed_time)){ + COPY_TIMESPEC(&(basic_type::pAbsPtr->min_elapsed_time), &elapsed_time); + } + if(0 < COMPARE_TIMESPEC(&(basic_type::pAbsPtr->max_elapsed_time), &elapsed_time)){ + COPY_TIMESPEC(&(basic_type::pAbsPtr->max_elapsed_time), &elapsed_time); + } + ADD_TIMESPEC(&(basic_type::pAbsPtr->total_elapsed_time), &elapsed_time); + + return true; +} + +template +bool chmstat_lap::Get(st_ptr_type pstat) const +{ + if(!pstat){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMSTAT does not set."); + return false; + } + + pstat->total_sent_count = basic_type::pAbsPtr->total_sent_count; + pstat->total_received_count = basic_type::pAbsPtr->total_received_count; + pstat->total_body_bytes = basic_type::pAbsPtr->total_body_bytes; + pstat->min_body_bytes = basic_type::pAbsPtr->min_body_bytes; + pstat->max_body_bytes = basic_type::pAbsPtr->max_body_bytes; + COPY_TIMESPEC(&(pstat->total_elapsed_time), &(basic_type::pAbsPtr->total_elapsed_time)); + COPY_TIMESPEC(&(pstat->min_elapsed_time), &(basic_type::pAbsPtr->min_elapsed_time)); + COPY_TIMESPEC(&(pstat->max_elapsed_time), &(basic_type::pAbsPtr->max_elapsed_time)); + + return true; +} + +typedef chmstat_lap chmstatlap; + +//--------------------------------------------------------- +// For MQMSGHEAD +//--------------------------------------------------------- +template +class mqmsghead_lap : public structure_lap +{ + public: + typedef T st_type; + typedef T* st_ptr_type; + typedef structure_lap basic_type; + + public: + mqmsghead_lap(st_ptr_type ptr = NULL, const void* shmbase = NULL, bool is_abs = true); + + virtual bool Initialize(void); + virtual bool Dump(std::stringstream& sstream, const char* spacer) const; + virtual bool Clear(void); + bool Copy(st_ptr_type ptr) const; + + msgid_t GetMsgId(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->msgid : CHM_INVALID_MSGID); } + pid_t GetPid(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->pid : CHM_INVALID_PID); } + + bool IsChmpxProc(void) const { return (basic_type::pAbsPtr ? IS_MQFLAG_CHMPXPROC(basic_type::pAbsPtr->flag) : false); } + bool IsClientProc(void) const { return (basic_type::pAbsPtr ? IS_MQFLAG_CLIENTPROC(basic_type::pAbsPtr->flag) : false); } + bool IsAssigned(void) const { return (basic_type::pAbsPtr ? IS_MQFLAG_ASSIGNED(basic_type::pAbsPtr->flag) : false); } + bool IsNotAssigned(void) const { return (basic_type::pAbsPtr ? IS_MQFLAG_NOTASSIGNED(basic_type::pAbsPtr->flag) : true); } + bool IsActivated(void) const { return (basic_type::pAbsPtr ? IS_MQFLAG_ACTIVATED(basic_type::pAbsPtr->flag) : false); } + bool IsDisactivated(void) const { return (basic_type::pAbsPtr ? IS_MQFLAG_DISACTIVATED(basic_type::pAbsPtr->flag) : true); } + + bool NotAccountMqFlag(void); + bool InitializeMqFlag(bool is_chmpxproc, bool is_activated); + bool SetMqFlagStatus(bool is_assigned, bool is_activated); +}; + +template +mqmsghead_lap::mqmsghead_lap(st_ptr_type ptr, const void* shmbase, bool is_abs) +{ + structure_lap::Reset(ptr, shmbase, is_abs); +} + +template +bool mqmsghead_lap::Initialize(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEAD does not set."); + return false; + } + basic_type::pAbsPtr->msgid = CHM_INVALID_MSGID; + basic_type::pAbsPtr->pid = CHM_INVALID_PID; + NotAccountMqFlag(); + + return true; +} + +template +bool mqmsghead_lap::Clear(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEAD does not set."); + return false; + } + NotAccountMqFlag(); + + return true; +} + +template +bool mqmsghead_lap::Dump(std::stringstream& sstream, const char* spacer) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEAD does not set."); + return false; + } + sstream << (spacer ? spacer : "") << "msgid = " << reinterpret_cast(basic_type::pAbsPtr->msgid) << std::endl; + sstream << (spacer ? spacer : "") << "pid = " << basic_type::pAbsPtr->pid << std::endl; + sstream << (spacer ? spacer : "") << "flag = " << reinterpret_cast(basic_type::pAbsPtr->flag) << " - " << STR_MQFLAG_FULL(basic_type::pAbsPtr->flag) << std::endl; + + return true; +} + +template +bool mqmsghead_lap::Copy(st_ptr_type ptr) const +{ + if(!ptr){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEAD does not set."); + return false; + } + ptr->msgid = basic_type::pAbsPtr->msgid; + ptr->flag = basic_type::pAbsPtr->flag; + ptr->pid = basic_type::pAbsPtr->pid; + + return true; +} + +template +bool mqmsghead_lap::NotAccountMqFlag(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEAD does not set."); + return false; + } + SET_MQFLAG_INITIALIZED(basic_type::pAbsPtr->flag); + basic_type::pAbsPtr->pid = CHM_INVALID_PID; + return true; +} + +template +bool mqmsghead_lap::InitializeMqFlag(bool is_chmpxproc, bool is_activated) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEAD does not set."); + return false; + } + + if(is_chmpxproc){ + if(IS_MQFLAG_CLIENTPROC(basic_type::pAbsPtr->flag)){ + WAN_CHMPRN("msgid(0x%016" PRIx64 ") is client process used, but force to set.", basic_type::pAbsPtr->msgid); + } + SET_MQFLAG_CHMPXPROC(basic_type::pAbsPtr->flag); + is_activated = true; // force + }else{ + if(IS_MQFLAG_CHMPXPROC(basic_type::pAbsPtr->flag)){ + WAN_CHMPRN("msgid(0x%016" PRIx64 ") is chmpx process used, but force to set", basic_type::pAbsPtr->msgid); + } + SET_MQFLAG_CLIENTPROC(basic_type::pAbsPtr->flag); + } + + if(is_activated){ + SET_MQFLAG_ACTIVATED(basic_type::pAbsPtr->flag); + }else{ + SET_MQFLAG_DISACTIVATED(basic_type::pAbsPtr->flag); + } + + basic_type::pAbsPtr->pid = getpid(); + + return true; +} + +template +bool mqmsghead_lap::SetMqFlagStatus(bool is_assigned, bool is_activated) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEAD does not set."); + return false; + } + + if(IS_MQFLAG_CHMPXPROC(basic_type::pAbsPtr->flag)){ + if(is_assigned && !is_activated){ + WAN_CHMPRN("msgid(0x%016" PRIx64 ") is used by chmpx process, but specified assinged and disactivated, so force activated.", basic_type::pAbsPtr->msgid); + is_activated = true; + } + }else if(!IS_MQFLAG_CLIENTPROC(basic_type::pAbsPtr->flag)){ + ERR_CHMPRN("msgid(0x%016" PRIx64 ") kind is not initialized.", basic_type::pAbsPtr->msgid); + return false; + } + + if(!is_assigned){ + if(is_activated){ + WAN_CHMPRN("Specified actived & not assigned, so parameter is wrong, force set not assigned(disactivated)."); + } + + if(IS_MQFLAG_NOTASSIGNED(basic_type::pAbsPtr->flag) && IS_MQFLAG_DISACTIVATED(basic_type::pAbsPtr->flag)){ + MSG_CHMPRN("MQ flag=%s is already not assigned.", STR_MQFLAG_FULL(basic_type::pAbsPtr->flag).c_str()); + }else{ + SET_MQFLAG_NOTASSIGNED(basic_type::pAbsPtr->flag); + } + basic_type::pAbsPtr->pid = CHM_INVALID_PID; + + }else if(is_assigned && !is_activated){ + if(IS_MQFLAG_ASSIGNED(basic_type::pAbsPtr->flag) && IS_MQFLAG_DISACTIVATED(basic_type::pAbsPtr->flag)){ + MSG_CHMPRN("MQ flag=%s is already assigned & disactivated.", STR_MQFLAG_FULL(basic_type::pAbsPtr->flag).c_str()); + }else{ + SET_MQFLAG_DISACTIVATED(basic_type::pAbsPtr->flag); + } + basic_type::pAbsPtr->pid = getpid(); + + }else if(is_assigned && is_activated){ + if(IS_MQFLAG_ASSIGNED(basic_type::pAbsPtr->flag) && IS_MQFLAG_ACTIVATED(basic_type::pAbsPtr->flag)){ + MSG_CHMPRN("MQ flag=%s is already assigned & activated.", STR_MQFLAG_FULL(basic_type::pAbsPtr->flag).c_str()); + }else{ + SET_MQFLAG_ACTIVATED(basic_type::pAbsPtr->flag); + } + basic_type::pAbsPtr->pid = getpid(); + } + return true; +} + +typedef mqmsghead_lap mqmsgheadlap; + +//--------------------------------------------------------- +// For MQMSGHEADLIST(Array) +//--------------------------------------------------------- +template +class mqmsgheadarr_lap : public structure_lap +{ + public: + typedef T st_type; + typedef T* st_ptr_type; + typedef structure_lap basic_type; + + private: + long chmpxmsg_count; + + public: + mqmsgheadarr_lap(st_ptr_type ptr = NULL, const void* shmbase = NULL, long max_msg_count = 0L, bool is_abs = true); + + bool FillMsgId(msgid_t base_msgid); + long Count(void) const { return chmpxmsg_count; } + st_ptr_type Find(msgid_t msgid, bool is_abs = true); +}; + +template +mqmsgheadarr_lap::mqmsgheadarr_lap(st_ptr_type ptr, const void* shmbase, long max_msg_count, bool is_abs) : chmpxmsg_count(max_msg_count) +{ + structure_lap::Reset(ptr, shmbase, is_abs); +} + +template +bool mqmsgheadarr_lap::FillMsgId(msgid_t base_msgid) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return false; + } + for(long cnt = 0L; cnt < chmpxmsg_count; cnt++){ + (basic_type::pAbsPtr)[cnt].msghead.msgid = base_msgid++; + } + return true; +} + +template +typename mqmsgheadarr_lap::st_ptr_type mqmsgheadarr_lap::Find(msgid_t msgid, bool is_abs) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return NULL; + } + if(msgid < basic_type::pAbsPtr->msghead.msgid || (basic_type::pAbsPtr->msghead.msgid + chmpxmsg_count) <= msgid){ + ERR_CHMPRN("msgid(0x%016" PRIx64 ") is over range(0x%016" PRIx64 " - 0x%016" PRIx64 ").", msgid, basic_type::pAbsPtr->msghead.msgid, (basic_type::pAbsPtr->msghead.msgid + chmpxmsg_count)); + return NULL; + } + st_ptr_type resultptr = &(basic_type::pAbsPtr[msgid - basic_type::pAbsPtr->msghead.msgid]); + + if(resultptr->msghead.msgid != msgid){ + ERR_CHMPRN("%p is found in msghead area for msgid(0x%016" PRIx64 "), but msgid(0x%016" PRIx64 ") is not same msgid.", resultptr, msgid, resultptr->msghead.msgid); + return NULL; + } + if(!is_abs){ + resultptr = CHM_REL(basic_type::pShmBase, resultptr, st_ptr_type); + } + return resultptr; +} + +typedef mqmsgheadarr_lap mqmsgheadarrlap; + +//--------------------------------------------------------- +// For MQMSGHEADLIST +//--------------------------------------------------------- +template +class mqmsgheadlist_lap : public structure_lap +{ + public: + typedef T st_type; + typedef T* st_ptr_type; + typedef structure_lap basic_type; + + public: + mqmsgheadlist_lap(st_ptr_type ptr = NULL, const void* shmbase = NULL, bool is_abs = true); + + PMQMSGHEAD GetAbsMqMsgHeadPtr(void) const { return (basic_type::pAbsPtr ? &basic_type::pAbsPtr->msghead : NULL); } + PMQMSGHEAD GetRelMqMsgHeadPtr(void) const { return (basic_type::pAbsPtr ? CHM_REL(basic_type::pShmBase, &basic_type::pAbsPtr->msghead, PMQMSGHEAD) : NULL); } + + virtual bool Initialize(void); + bool Initialize(st_ptr_type prev, st_ptr_type next, bool is_abs = true); + bool Clear(void); + virtual bool Dump(std::stringstream& sstream, const char* spacer) const; + st_ptr_type Dup(void); + void Free(st_ptr_type ptr) const; + + st_ptr_type GetFirstPtr(bool is_abs = true); + bool ToFirst(void); + bool ToLast(void); + bool ToNext(bool is_cycle = false); + long Count(void); + long Count(bool toward_normal); + bool Push(st_ptr_type ptr, bool is_abs, bool is_to_first = false); + bool PushBack(st_ptr_type ptr, bool is_abs); + st_ptr_type PopFront(void); + st_ptr_type Retrive(msgid_t msgid); + st_ptr_type Retrive(void); + + PMQMSGHEAD Find(msgid_t msgid, bool is_abs = true, bool is_to_first = false); + msgid_t GetRandomMsgId(void); + bool GetMsgidListByPid(pid_t pid, msgidlist_t& list, bool is_clear_list = false); + bool GetMsgidListByUniqPid(msgidlist_t& list, bool is_clear_list = false); +}; + +template +mqmsgheadlist_lap::mqmsgheadlist_lap(st_ptr_type ptr, const void* shmbase, bool is_abs) +{ + structure_lap::Reset(ptr, shmbase, is_abs); +} + +template +bool mqmsgheadlist_lap::Initialize(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return false; + } + basic_type::pAbsPtr->prev = NULL; + basic_type::pAbsPtr->next = NULL; + + mqmsgheadlap tmpmqmsghead(&basic_type::pAbsPtr->msghead, basic_type::pShmBase); + if(!tmpmqmsghead.Initialize()){ + ERR_CHMPRN("Failed to initialize CHMMQMSGHEAD."); + return false; + } + return true; +} + +template +bool mqmsgheadlist_lap::Initialize(st_ptr_type prev, st_ptr_type next, bool is_abs) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return false; + } + basic_type::pAbsPtr->prev = is_abs ? CHM_REL(basic_type::pShmBase, prev, st_ptr_type) : prev; + basic_type::pAbsPtr->next = is_abs ? CHM_REL(basic_type::pShmBase, next, st_ptr_type) : next; + + mqmsgheadlap tmpmqmsghead(&basic_type::pAbsPtr->msghead, basic_type::pShmBase); + if(!tmpmqmsghead.Initialize()){ + ERR_CHMPRN("Failed to initialize CHMMQMSGHEAD."); + return false; + } + return true; +} + +template +bool mqmsgheadlist_lap::Clear(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return false; + } + basic_type::pAbsPtr->prev = NULL; + basic_type::pAbsPtr->next = NULL; + + mqmsgheadlap tmpmqmsghead(&basic_type::pAbsPtr->msghead, basic_type::pShmBase); + if(!tmpmqmsghead.Clear()){ + ERR_CHMPRN("Failed to clear CHMMQMSGHEAD."); + return false; + } + return true; +} + +template +bool mqmsgheadlist_lap::Dump(std::stringstream& sstream, const char* spacer) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return false; + } + std::string tmpspacer1 = spacer ? spacer : ""; + std::string tmpspacer2; + tmpspacer1 += " "; + tmpspacer2 += tmpspacer1 + " "; + + // To first + st_ptr_type cur; + for(cur = const_cast(basic_type::pAbsPtr); cur->prev; cur = CHM_ABS(basic_type::pShmBase, cur->prev, st_ptr_type)); + + for(long count = 0L; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type), count++){ + sstream << (spacer ? spacer : "") << "[" << count << "] = {" << std::endl; + + sstream << tmpspacer1 << "prev = " << cur->prev << std::endl; + sstream << tmpspacer1 << "next = " << cur->next << std::endl; + + sstream << tmpspacer1 << "msghead{" << std::endl; + mqmsgheadlap tmpmqmsghead(&cur->msghead, basic_type::pShmBase); + tmpmqmsghead.Dump(sstream, tmpspacer2.c_str()); + sstream << tmpspacer1 << "}" << std::endl; + + sstream << (spacer ? spacer : "") << "}" << std::endl; + } + return true; +} + +template +typename mqmsgheadlist_lap::st_ptr_type mqmsgheadlist_lap::Dup(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return NULL; + } + + long count = Count(); + if(0 == count){ + return NULL; + } + + // To first + st_ptr_type cur; + for(cur = const_cast(basic_type::pAbsPtr); cur->prev; cur = CHM_ABS(basic_type::pShmBase, cur->prev, st_ptr_type)); + + // duplicate + st_ptr_type pdst; + if(NULL == (pdst = reinterpret_cast(calloc(count, sizeof(st_type))))){ + ERR_CHMPRN("Could not allocation memory."); + return NULL; + } + for(st_ptr_type pdstcur = pdst, pdstprev = NULL; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type), pdstprev = pdstcur++){ + if(pdstprev){ + pdstprev->next = pdstcur; + } + pdstcur->prev = pdstprev; + + mqmsgheadlap tmpmqmsghead(&cur->msghead, basic_type::pShmBase); + if(!tmpmqmsghead.Copy(&(pdstcur->msghead))){ + WAN_CHMPRN("Failed to copy msghead structure, but continue..."); + } + } + return pdst; +} + +template +void mqmsgheadlist_lap::Free(st_ptr_type ptr) const +{ + if(ptr){ + K2H_Free(ptr); + } +} + +template +typename mqmsgheadlist_lap::st_ptr_type mqmsgheadlist_lap::GetFirstPtr(bool is_abs) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + //MSG_CHMPRN("PMQMSGHEADLIST does not set."); + return NULL; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + return (is_abs ? mqmsgheadlist_lap::GetAbsPtr() : mqmsgheadlist_lap::GetRelPtr()); + +} + +template +bool mqmsgheadlist_lap::ToFirst(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return false; + } + for(; basic_type::pAbsPtr->prev; basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->prev, st_ptr_type)); + return true; +} + +template +bool mqmsgheadlist_lap::ToLast(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return false; + } + for(; basic_type::pAbsPtr->next; basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type)); + return true; +} + +template +bool mqmsgheadlist_lap::ToNext(bool is_cycle) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return false; + } + if(!basic_type::pAbsPtr->next){ + if(!is_cycle){ + MSG_CHMPRN("Reached end of msg list."); + return false; + } + ToFirst(); + }else{ + basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type); + } + return true; +} + +template +long mqmsgheadlist_lap::Count(void) +{ + if(!basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return 0L; + } + if(!basic_type::pAbsPtr){ + //MSG_CHMPRN("PMQMSGHEADLIST does not set."); + return 0L; + } + ToFirst(); + long count; + st_ptr_type pos; + for(count = 1L, pos = basic_type::pAbsPtr; pos->next; pos = CHM_ABS(basic_type::pShmBase, pos->next, st_ptr_type), count++); + + return count; +} + +template +long mqmsgheadlist_lap::Count(bool toward_normal) +{ + if(!basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return 0L; + } + if(!basic_type::pAbsPtr){ + //MSG_CHMPRN("PMQMSGHEADLIST does not set."); + return 0L; + } + long count; + st_ptr_type pos; + for(count = 1L, pos = basic_type::pAbsPtr; NULL != (toward_normal ? pos->next : pos->prev); pos = CHM_ABS(basic_type::pShmBase, (toward_normal ? pos->next : pos->prev), st_ptr_type), count++); + + return count; +} + +template +bool mqmsgheadlist_lap::Push(st_ptr_type ptr, bool is_abs, bool is_to_first) +{ + if(!ptr){ + ERR_CHMPRN("ptr is null."); + return false; + } + if(!basic_type::pShmBase){ // allow basic_type::pAbsPtr is NULL + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return false; + } + + st_ptr_type current = basic_type::pAbsPtr; + if(is_to_first){ + for(; current && current->prev; current = CHM_ABS(basic_type::pShmBase, current->prev, st_ptr_type)); + } + + st_ptr_type relptr = is_abs ? CHM_REL(basic_type::pShmBase, ptr, st_ptr_type) : ptr; + st_ptr_type absptr = is_abs ? ptr : CHM_ABS(basic_type::pShmBase, ptr, st_ptr_type); + st_ptr_type prevptr = current ? CHM_ABS(basic_type::pShmBase, current->prev, st_ptr_type) : NULL; + + if(prevptr){ + prevptr->next = relptr; + } + absptr->prev = current ? current->prev : NULL; + absptr->next = current ? CHM_REL(basic_type::pShmBase, current, st_ptr_type) : NULL; + if(current){ + current->prev = relptr; + } + basic_type::pAbsPtr = absptr; + + return true; +} + +template +bool mqmsgheadlist_lap::PushBack(st_ptr_type ptr, bool is_abs) +{ + if(!ptr){ + ERR_CHMPRN("ptr is null."); + return false; + } + if(!basic_type::pShmBase){ // allow basic_type::pAbsPtr is NULL + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return false; + } + // find list last + st_ptr_type last; + for(last = basic_type::pAbsPtr; last && last->next; last = CHM_ABS(basic_type::pShmBase, last->next, st_ptr_type)); + + st_ptr_type relptr = is_abs ? CHM_REL(basic_type::pShmBase, ptr, st_ptr_type) : ptr; + st_ptr_type absptr = is_abs ? ptr : CHM_ABS(basic_type::pShmBase, ptr, st_ptr_type); + + if(last){ + last->next = relptr; + } + absptr->prev = last ? CHM_REL(basic_type::pShmBase, last, st_ptr_type) : NULL; + absptr->next = NULL; + basic_type::pAbsPtr = absptr; + + return true; +} + +template +typename mqmsgheadlist_lap::st_ptr_type mqmsgheadlist_lap::PopFront(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return NULL; + } + // find list first + st_ptr_type first; + for(first = basic_type::pAbsPtr; first->prev; first = CHM_ABS(basic_type::pShmBase, first->prev, st_ptr_type)); + + if(NULL != (basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, first->next, st_ptr_type))){ + basic_type::pAbsPtr->prev = NULL; + } + first->next = NULL; + + return first; // Absolute +} + +// +// [CAREFUL] +// After calling this method, be careful for pAbsPtr is NULL. +// And this method is very slow, should use Retrieve(void) +// +template +typename mqmsgheadlist_lap::st_ptr_type mqmsgheadlist_lap::Retrive(msgid_t msgid) +{ + if(CHM_INVALID_MSGID == msgid){ + ERR_CHMPRN("Parameter is wrong."); + return NULL; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return NULL; + } + ToFirst(); + + mqmsgheadlap mqmsghead; + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + mqmsghead.Reset(&cur->msghead, basic_type::pShmBase); + if(msgid == mqmsghead.GetMsgId()){ + // found + if(cur == basic_type::pAbsPtr){ + // basic_type::pAbsPtr is first object in list. + if(basic_type::pAbsPtr->next){ + basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type); + }else{ + basic_type::pAbsPtr = NULL; + } + } + st_ptr_type prevlist = CHM_ABS(basic_type::pShmBase, cur->prev, st_ptr_type); + st_ptr_type nextlist = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type); + if(prevlist){ + prevlist->next = cur->next; + } + if(nextlist){ + nextlist->prev = cur->prev; + } + cur->prev = NULL; + cur->next = NULL; + return cur; + } + } + WAN_CHMPRN("Could not find msgid(x%016" PRIx64 ").", msgid); + return NULL; +} + +// +// [CAREFUL] +// After calling this method, be careful for pAbsPtr is NULL. +// +template +typename mqmsgheadlist_lap::st_ptr_type mqmsgheadlist_lap::Retrive(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return NULL; + } + + st_ptr_type abs_cur = basic_type::pAbsPtr; + st_ptr_type abs_prev = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->prev, st_ptr_type); + st_ptr_type abs_next = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type); + + if(abs_prev){ + abs_prev->next = basic_type::pAbsPtr->next; + if(abs_next){ + abs_next->prev = basic_type::pAbsPtr->prev; + basic_type::pAbsPtr = abs_next; + }else{ + basic_type::pAbsPtr = abs_prev; + } + }else{ + if(abs_next){ + abs_next->prev = basic_type::pAbsPtr->prev; + basic_type::pAbsPtr = abs_next; + }else{ + basic_type::pAbsPtr = NULL; + } + } + abs_cur->prev = NULL; + abs_cur->next = NULL; + return abs_cur; +} + +// +// This method is not good performance. +// Should use mqmsgheadarr_lap::Find() instead of this. +// +template +PMQMSGHEAD mqmsgheadlist_lap::Find(msgid_t msgid, bool is_abs, bool is_to_first) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return NULL; + } + if(is_to_first){ + ToFirst(); + } + + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + mqmsgheadlap tmpmqmsghead(&cur->msghead, basic_type::pShmBase); + if(msgid == tmpmqmsghead.GetMsgId()){ + return (is_abs ? &cur->msghead : CHM_REL(basic_type::pShmBase, &cur->msghead, PMQMSGHEAD)); + } + } + return NULL; +} + +// +// This method calls Count() and rand(), then it is not good performance. +// Should use chminfo_lap::GetRandomMsgId() instead of this. +// +template +msgid_t mqmsgheadlist_lap::GetRandomMsgId(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return CHM_INVALID_MSGID; + } + + // for random position. + // using rand() which calls random() for linux, so thread-safe.(maybe) + // If thread unsafe, but we dont care for it.:-p + // + long msgidcnt = Count(); + if(0L >= msgidcnt){ + return CHM_INVALID_MSGID; + } + long randcnt = static_cast(rand()) % msgidcnt; // random position. + + ToFirst(); + + mqmsgheadlap mqmsghead; + st_ptr_type pos; + for(pos = basic_type::pAbsPtr; pos; pos = CHM_ABS(basic_type::pShmBase, pos->next, st_ptr_type)){ + mqmsghead.Reset(&pos->msghead, basic_type::pShmBase); + if(randcnt <= 0L){ + return mqmsghead.GetMsgId(); + } + --randcnt; + } + return CHM_INVALID_MSGID; +} + +// +// This method is not good performance. +// +template +bool mqmsgheadlist_lap::GetMsgidListByPid(pid_t pid, msgidlist_t& list, bool is_clear_list) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return NULL; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + if(is_clear_list){ + list.clear(); + } + + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + mqmsgheadlap tmpmqmsghead(&cur->msghead, basic_type::pShmBase); + if(pid == tmpmqmsghead.GetPid()){ + list.push_back(tmpmqmsghead.GetMsgId()); + } + } + return true; +} + +// +// This method is not good performance. +// +template +bool mqmsgheadlist_lap::GetMsgidListByUniqPid(msgidlist_t& list, bool is_clear_list) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PMQMSGHEADLIST does not set."); + return NULL; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + if(is_clear_list){ + list.clear(); + } + + pidbmap_t tmpmap; // pid map for temporary + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + mqmsgheadlap tmpmqmsghead(&cur->msghead, basic_type::pShmBase); + pid_t pid = tmpmqmsghead.GetPid(); + if(CHM_INVALID_PID == pid){ + continue; + } + if(tmpmap.end() != tmpmap.find(pid)){ + // already set + continue; + } + list.push_back(tmpmqmsghead.GetMsgId()); + tmpmap[pid] = true; // do not care for value. + } + return true; +} + +typedef mqmsgheadlist_lap mqmsgheadlistlap; + +//--------------------------------------------------------- +// For CHMLOGRAW +//--------------------------------------------------------- +template +class chmlograw_lap : public structure_lap +{ + public: + typedef T st_type; + typedef T* st_ptr_type; + typedef structure_lap basic_type; + + public: + chmlograw_lap(st_ptr_type ptr = NULL, const void* shmbase = NULL, bool is_abs = true); + + virtual bool Initialize(void); + virtual bool Dump(std::stringstream& sstream, const char* spacer) const; + virtual bool Clear(void); + bool Copy(st_ptr_type ptr) const; + + bool Set(logtype_t logtype, size_t length, const struct timespec& start, const struct timespec& fin); + bool Get(st_ptr_type plograw) const; +}; + +template +chmlograw_lap::chmlograw_lap(st_ptr_type ptr, const void* shmbase, bool is_abs) +{ + structure_lap::Reset(ptr, shmbase, is_abs); +} + +template +bool chmlograw_lap::Initialize(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOGRAW does not set."); + return false; + } + basic_type::pAbsPtr->log_type = CHMLOG_TYPE_NOTASSIGNED; + basic_type::pAbsPtr->length = 0L; + INIT_TIMESPEC(&(basic_type::pAbsPtr->start_time)); + INIT_TIMESPEC(&(basic_type::pAbsPtr->fin_time)); + + return true; +} + +template +bool chmlograw_lap::Dump(std::stringstream& sstream, const char* spacer) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOGRAW does not set."); + return false; + } + sstream << (spacer ? spacer : "") << "log_type = " << STR_CHMLOG_TYPE(basic_type::pAbsPtr->log_type) << std::endl; + sstream << (spacer ? spacer : "") << "length = " << basic_type::pAbsPtr->length << std::endl; + sstream << (spacer ? spacer : "") << "start_time = " << basic_type::pAbsPtr->start_time.tv_sec << "s " << basic_type::pAbsPtr->start_time.tv_nsec << "ns" << std::endl; + sstream << (spacer ? spacer : "") << "fin_time = " << basic_type::pAbsPtr->fin_time.tv_sec << "s " << basic_type::pAbsPtr->fin_time.tv_nsec << "ns" << std::endl; + + return true; +} + +template +bool chmlograw_lap::Copy(st_ptr_type ptr) const +{ + if(!ptr){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOGRAW does not set."); + return false; + } + ptr->log_type = basic_type::pAbsPtr->log_type; + ptr->length = basic_type::pAbsPtr->length; + ptr->start_time.tv_sec = basic_type::pAbsPtr->start_time.tv_sec; + ptr->start_time.tv_nsec = basic_type::pAbsPtr->start_time.tv_nsec; + ptr->fin_time.tv_sec = basic_type::pAbsPtr->fin_time.tv_sec; + ptr->fin_time.tv_nsec = basic_type::pAbsPtr->fin_time.tv_nsec; + + return true; +} + +template +bool chmlograw_lap::Clear(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOGRAW does not set."); + return false; + } + basic_type::pAbsPtr->length = 0L; + INIT_TIMESPEC(&(basic_type::pAbsPtr->start_time)); + INIT_TIMESPEC(&(basic_type::pAbsPtr->fin_time)); + + return true; +} + +template +bool chmlograw_lap::Set(logtype_t logtype, size_t length, const struct timespec& start, const struct timespec& fin) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOGRAW does not set."); + return false; + } + if(!IS_SAFE_CHMLOG_TYPE(logtype)){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + basic_type::pAbsPtr->log_type = logtype; + basic_type::pAbsPtr->length = length; + COPY_TIMESPEC(&(basic_type::pAbsPtr->start_time), &start); + COPY_TIMESPEC(&(basic_type::pAbsPtr->fin_time), &fin); + + return true; +} + +template +bool chmlograw_lap::Get(st_ptr_type plograw) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOGRAW does not set."); + return false; + } + if(!plograw){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + plograw->log_type = basic_type::pAbsPtr->log_type; + plograw->length = basic_type::pAbsPtr->length; + COPY_TIMESPEC(&(plograw->start_time), &(basic_type::pAbsPtr->start_time)); + COPY_TIMESPEC(&(plograw->fin_time), &(basic_type::pAbsPtr->fin_time)); + + return true; +} + +typedef chmlograw_lap chmlograwlap; + +//--------------------------------------------------------- +// For CHMLOG +//--------------------------------------------------------- +template +class chmlog_lap : public structure_lap +{ + public: + typedef T st_type; + typedef T* st_ptr_type; + typedef structure_lap basic_type; + + public: + chmlog_lap(st_ptr_type ptr = NULL, const void* shmbase = NULL, bool is_abs = true); + + virtual bool Initialize(void); + virtual bool Dump(std::stringstream& sstream, const char* spacer) const; + virtual bool Clear(void); + bool Copy(st_ptr_type ptr) const; + + bool Initialize(PCHMLOGRAW rel_lograwarea, long max_log_count); + + long GetHistoryCount(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->max_log_count : 0L); } + bool IsEnable(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->enable : false); } + bool Enable(void); + bool Disable(void); + bool Add(logtype_t logtype, size_t length, const struct timespec& start, const struct timespec& fin); + bool Get(PCHMLOGRAW plograwarr, long& arrsize, logtype_t dirmask, logtype_t devmask) const; +}; + +template +chmlog_lap::chmlog_lap(st_ptr_type ptr, const void* shmbase, bool is_abs) +{ + structure_lap::Reset(ptr, shmbase, is_abs); +} + +template +bool chmlog_lap::Initialize(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOG does not set."); + return false; + } + basic_type::pAbsPtr->enable = false; + basic_type::pAbsPtr->start_time = 0L; + basic_type::pAbsPtr->stop_time = 0L; + basic_type::pAbsPtr->max_log_count = 0L; + basic_type::pAbsPtr->next_pos = 0L; + basic_type::pAbsPtr->start_log_rel_area = NULL; + + return true; +} + +template +bool chmlog_lap::Dump(std::stringstream& sstream, const char* spacer) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOG does not set."); + return false; + } + sstream << (spacer ? spacer : "") << "enable = " << (basic_type::pAbsPtr->enable ? "true" : "false") << std::endl; + sstream << (spacer ? spacer : "") << "start_time = " << basic_type::pAbsPtr->start_time << std::endl; + sstream << (spacer ? spacer : "") << "stop_time = " << basic_type::pAbsPtr->stop_time << std::endl; + sstream << (spacer ? spacer : "") << "max_log_count = " << basic_type::pAbsPtr->max_log_count << std::endl; + sstream << (spacer ? spacer : "") << "next_pos = " << basic_type::pAbsPtr->next_pos << std::endl; + sstream << (spacer ? spacer : "") << "start_log_rel_area = " << basic_type::pAbsPtr->start_log_rel_area << std::endl; + + return true; +} + +// [NOTICE] +// This method does not copy(duplicate) start_log_rel_area member. +// Thus this method name is Copy instead of Dup. +// +template +bool chmlog_lap::Copy(st_ptr_type ptr) const +{ + if(!ptr){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOG does not set."); + return false; + } + ptr->enable = basic_type::pAbsPtr->enable; + ptr->start_time = basic_type::pAbsPtr->start_time; + ptr->stop_time = basic_type::pAbsPtr->stop_time; + ptr->max_log_count = basic_type::pAbsPtr->max_log_count; + ptr->next_pos = basic_type::pAbsPtr->next_pos; + ptr->start_log_rel_area = NULL; // Always NULL + + return true; +} + +template +bool chmlog_lap::Initialize(PCHMLOGRAW rel_lograwarea, long max_log_count) +{ + if(!rel_lograwarea){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOG does not set."); + return false; + } + basic_type::pAbsPtr->enable = false; + basic_type::pAbsPtr->start_time = time(NULL); + basic_type::pAbsPtr->stop_time = time(NULL); + basic_type::pAbsPtr->max_log_count = max_log_count; + basic_type::pAbsPtr->next_pos = 0L; + basic_type::pAbsPtr->start_log_rel_area = rel_lograwarea; + + return true; +} + +template +bool chmlog_lap::Clear(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOG does not set."); + return false; + } + basic_type::pAbsPtr->start_time = 0L; + basic_type::pAbsPtr->stop_time = 0L; + basic_type::pAbsPtr->max_log_count = 0L; + basic_type::pAbsPtr->next_pos = 0L; + basic_type::pAbsPtr->start_log_rel_area = NULL; + + return true; +} + +template +bool chmlog_lap::Enable(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOG does not set."); + return false; + } + if(IsEnable()){ + MSG_CHMPRN("History logging is already enabled."); + return true; + } + if(!basic_type::pAbsPtr->start_log_rel_area || 0L == basic_type::pAbsPtr->max_log_count){ + ERR_CHMPRN("History logging size is invalid."); + return false; + } + + // set terminal data + PCHMLOGRAW abs_lograw = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->start_log_rel_area, PCHMLOGRAW); + chmlograwlap lograw(&(abs_lograw[basic_type::pAbsPtr->max_log_count - 1L]), basic_type::pShmBase, true); // From abs + lograw.Initialize(); + + // set enable flag + basic_type::pAbsPtr->start_time = time(NULL); + basic_type::pAbsPtr->stop_time = time(NULL); + basic_type::pAbsPtr->next_pos = 0L; + basic_type::pAbsPtr->enable = true; + + return true; +} + +template +bool chmlog_lap::Disable(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOG does not set."); + return false; + } + if(!IsEnable()){ + MSG_CHMPRN("History logging is already disabled."); + return true; + } + + // set disable flag + basic_type::pAbsPtr->enable = false; + basic_type::pAbsPtr->stop_time = time(NULL); + + return true; +} + +template +bool chmlog_lap::Add(logtype_t logtype, size_t length, const struct timespec& start, const struct timespec& fin) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOG does not set."); + return false; + } + if(!IsEnable()){ + //MSG_CHMPRN("Now history logging is desabled."); + return true; + } + if(!basic_type::pAbsPtr->start_log_rel_area || 0L == basic_type::pAbsPtr->max_log_count){ + WAN_CHMPRN("History logging size is invalid."); + return false; + } + + long selfpos = basic_type::pAbsPtr->next_pos; + if(basic_type::pAbsPtr->max_log_count <= (basic_type::pAbsPtr->next_pos + 1)){ + // set next pos + basic_type::pAbsPtr->next_pos = 0L; + }else{ + ++(basic_type::pAbsPtr->next_pos); + } + + PCHMLOGRAW abs_lograw = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->start_log_rel_area, PCHMLOGRAW); + chmlograwlap lograw(&(abs_lograw[selfpos]), basic_type::pShmBase, true); // From abs + return lograw.Set(logtype, length, start, fin); +} + +template +bool chmlog_lap::Get(PCHMLOGRAW plograwarr, long& arrsize, logtype_t dirmask, logtype_t devmask) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMLOG does not set."); + return false; + } + if(!plograwarr || !IS_SAFE_CHMLOG_MASK(dirmask) || !IS_SAFE_CHMLOG_MASK(devmask)){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + + // set arrsize is maximum history count if it too big. + if(basic_type::pAbsPtr->max_log_count < arrsize){ + arrsize = basic_type::pAbsPtr->max_log_count; + } + PCHMLOGRAW abs_lograw = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->start_log_rel_area, PCHMLOGRAW); + + // set start pos + bool is_circled; + long cur_pos; + if(CHMLOG_TYPE_NOTASSIGNED == abs_lograw[basic_type::pAbsPtr->max_log_count - 1L].log_type){ + is_circled = true; + cur_pos = 0L; + }else{ + is_circled = false; + cur_pos = basic_type::pAbsPtr->next_pos; + } + + // Loop + long set_pos; + for(set_pos = 0L; set_pos < arrsize; ++cur_pos){ + if(is_circled && basic_type::pAbsPtr->next_pos <= cur_pos){ + break; + } + + if(basic_type::pAbsPtr->max_log_count <= cur_pos){ + if(!is_circled){ + is_circled = true; + cur_pos = 0L; + }else{ + break; // for safe + } + } + if(!IS_CHMLOG_ASSIGNED(abs_lograw[cur_pos].log_type)){ + continue; + } + if(0 == (abs_lograw[cur_pos].log_type & dirmask) || 0 == (abs_lograw[cur_pos].log_type & devmask)){ + continue; + } + plograwarr[set_pos].log_type = abs_lograw[cur_pos].log_type; + plograwarr[set_pos].length = abs_lograw[cur_pos].length; + COPY_TIMESPEC(&(plograwarr[set_pos].start_time), &(abs_lograw[cur_pos].start_time)); + COPY_TIMESPEC(&(plograwarr[set_pos].fin_time), &(abs_lograw[cur_pos].fin_time)); + ++set_pos; + } + arrsize = set_pos; + + return true; +} + +typedef chmlog_lap chmloglap; + +//--------------------------------------------------------- +// For CLTPROCLIST +//--------------------------------------------------------- +template +class cltproclist_lap : public structure_lap +{ + public: + typedef T st_type; + typedef T* st_ptr_type; + typedef structure_lap basic_type; + + public: + cltproclist_lap(st_ptr_type ptr = NULL, const void* shmbase = NULL, bool is_abs = true); + + virtual bool Initialize(void); + bool Initialize(st_ptr_type prev, st_ptr_type next, bool is_abs = true); + bool Initialize(pid_t pid); + bool Clear(void); + virtual bool Dump(std::stringstream& sstream, const char* spacer) const; + st_ptr_type Dup(void); + void Free(st_ptr_type ptr) const; + + st_ptr_type GetFirstPtr(bool is_abs = true); + bool ToFirst(void); + bool ToLast(void); + bool ToNext(void); + long Count(void); + bool Insert(st_ptr_type ptr, bool is_abs); + st_ptr_type Retrive(pid_t pid); + st_ptr_type Retrive(void); + st_ptr_type Find(pid_t pid, bool is_abs); + + bool GetAllPids(pidlist_t& list); +}; + +template +cltproclist_lap::cltproclist_lap(st_ptr_type ptr, const void* shmbase, bool is_abs) +{ + structure_lap::Reset(ptr, shmbase, is_abs); +} + +template +bool cltproclist_lap::Initialize(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return false; + } + basic_type::pAbsPtr->prev = NULL; + basic_type::pAbsPtr->next = NULL; + basic_type::pAbsPtr->pid = CHM_INVALID_PID; + return true; +} + +template +bool cltproclist_lap::Initialize(st_ptr_type prev, st_ptr_type next, bool is_abs) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return false; + } + basic_type::pAbsPtr->prev = is_abs ? CHM_REL(basic_type::pShmBase, prev, st_ptr_type) : prev; + basic_type::pAbsPtr->next = is_abs ? CHM_REL(basic_type::pShmBase, next, st_ptr_type) : next; + basic_type::pAbsPtr->pid = CHM_INVALID_PID; + return true; +} + +template +bool cltproclist_lap::Initialize(pid_t pid) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return false; + } + basic_type::pAbsPtr->pid = pid; + return true; +} + +template +bool cltproclist_lap::Clear(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return false; + } + basic_type::pAbsPtr->prev = NULL; + basic_type::pAbsPtr->next = NULL; + basic_type::pAbsPtr->pid = CHM_INVALID_PID; + return true; +} + +template +bool cltproclist_lap::Dump(std::stringstream& sstream, const char* spacer) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return false; + } + std::string tmpspacer1 = spacer ? spacer : ""; + std::string tmpspacer2; + tmpspacer1 += " "; + tmpspacer2 += tmpspacer1 + " "; + + // To first + st_ptr_type cur; + for(cur = const_cast(basic_type::pAbsPtr); cur->prev; cur = CHM_ABS(basic_type::pShmBase, cur->prev, st_ptr_type)); + + for(long count = 0L; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type), count++){ + sstream << (spacer ? spacer : "") << "[" << count << "] = {" << std::endl; + + sstream << tmpspacer1 << "prev = " << cur->prev << std::endl; + sstream << tmpspacer1 << "next = " << cur->next << std::endl; + sstream << tmpspacer1 << "pid = " << cur->pid << std::endl; + + sstream << (spacer ? spacer : "") << "}" << std::endl; + } + return true; +} + +template +typename cltproclist_lap::st_ptr_type cltproclist_lap::Dup(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return NULL; + } + + long count = Count(); + if(0 == count){ + return NULL; + } + + // To first + st_ptr_type cur; + for(cur = const_cast(basic_type::pAbsPtr); cur->prev; cur = CHM_ABS(basic_type::pShmBase, cur->prev, st_ptr_type)); + + // duplicate + st_ptr_type pdst; + if(NULL == (pdst = reinterpret_cast(calloc(count, sizeof(st_type))))){ + ERR_CHMPRN("Could not allocation memory."); + return NULL; + } + for(st_ptr_type pdstcur = pdst, pdstprev = NULL; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type), pdstprev = pdstcur++){ + if(pdstprev){ + pdstprev->next = pdstcur; + } + pdstcur->prev = pdstprev; + pdstcur->pid = cur->pid; + } + return pdst; +} + +template +void cltproclist_lap::Free(st_ptr_type ptr) const +{ + K2H_Free(ptr); +} + +template +typename cltproclist_lap::st_ptr_type cltproclist_lap::GetFirstPtr(bool is_abs) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + //MSG_CHMPRN("PCLTPROCLIST does not set."); + return NULL; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + return (is_abs ? cltproclist_lap::GetAbsPtr() : cltproclist_lap::GetRelPtr()); + +} + +template +bool cltproclist_lap::ToFirst(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return false; + } + for(; basic_type::pAbsPtr->prev; basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->prev, st_ptr_type)); + return true; +} + +template +bool cltproclist_lap::ToLast(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return false; + } + for(; basic_type::pAbsPtr->next; basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type)); + return true; +} + +template +bool cltproclist_lap::ToNext(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return false; + } + if(!basic_type::pAbsPtr->next){ + MSG_CHMPRN("Reached end of client process id list."); + return false; + } + basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type); + return true; +} + +template +long cltproclist_lap::Count(void) +{ + if(!basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return 0L; + } + if(!basic_type::pAbsPtr){ + //MSG_CHMPRN("PCLTPROCLIST does not set."); + return 0L; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + long count; + st_ptr_type pos; + for(count = 1L, pos = basic_type::pAbsPtr; pos->next; pos = CHM_ABS(basic_type::pShmBase, pos->next, st_ptr_type), count++); + + return count; +} + +// +// This method uses liner searching, so not good performance. +// +template +bool cltproclist_lap::Insert(st_ptr_type ptr, bool is_abs) +{ + if(!ptr){ + ERR_CHMPRN("ptr is null."); + return false; + } + if(!basic_type::pShmBase){ // allow basic_type::pAbsPtr is NULL + ERR_CHMPRN("PCLTPROCLIST does not set."); + return false; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + + st_ptr_type relptr = is_abs ? CHM_REL(basic_type::pShmBase, ptr, st_ptr_type) : ptr; + st_ptr_type absptr = is_abs ? ptr : CHM_ABS(basic_type::pShmBase, ptr, st_ptr_type); + + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + if(CHM_INVALID_PID != absptr->pid && cur->pid == absptr->pid){ + ERR_CHMPRN("Found same pid in list."); + return false; + }else if(cur->pid >= absptr->pid){ + st_ptr_type prevptr = CHM_ABS(basic_type::pShmBase, cur->prev, st_ptr_type); + if(prevptr){ + prevptr->next = relptr; + } + absptr->prev = cur->prev; + absptr->next = CHM_REL(basic_type::pShmBase, cur, st_ptr_type); + cur->prev = relptr; + basic_type::pAbsPtr = absptr; + break; + }else if(!cur->next){ + // add into end of list + cur->next = relptr; + absptr->prev = CHM_REL(basic_type::pShmBase, cur, st_ptr_type); + absptr->next = NULL; + basic_type::pAbsPtr = absptr; + break; + } + } + return true; +} + +// +// [CAREFUL] +// After calling this method, be careful for pAbsPtr is NULL. +// And this method is very slow, should use Retrieve(void) +// +template +typename cltproclist_lap::st_ptr_type cltproclist_lap::Retrive(pid_t pid) +{ + if(CHM_INVALID_PID == pid){ + ERR_CHMPRN("Parameter is wrong."); + return NULL; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return NULL; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + if(cur->pid == pid){ + // found + if(cur == basic_type::pAbsPtr){ + // basic_type::pAbsPtr is first object in list. + if(basic_type::pAbsPtr->next){ + basic_type::pAbsPtr = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type); + }else{ + basic_type::pAbsPtr = NULL; + } + } + st_ptr_type prevlist = CHM_ABS(basic_type::pShmBase, cur->prev, st_ptr_type); + st_ptr_type nextlist = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type); + if(prevlist){ + prevlist->next = cur->next; + } + if(nextlist){ + nextlist->prev = cur->prev; + } + cur->prev = NULL; + cur->next = NULL; + return cur; + } + } + + MSG_CHMPRN("Could not find pid(%d).", pid); + return NULL; +} + +// +// [CAREFUL] +// After calling this method, be careful for pAbsPtr is NULL. +// +template +typename cltproclist_lap::st_ptr_type cltproclist_lap::Retrive(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return NULL; + } + + st_ptr_type abs_cur = basic_type::pAbsPtr; + st_ptr_type abs_prev = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->prev, st_ptr_type); + st_ptr_type abs_next = CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->next, st_ptr_type); + + if(abs_prev){ + abs_prev->next = basic_type::pAbsPtr->next; + if(abs_next){ + abs_next->prev = basic_type::pAbsPtr->prev; + basic_type::pAbsPtr = abs_next; + }else{ + basic_type::pAbsPtr = abs_prev; + } + }else{ + if(abs_next){ + abs_next->prev = basic_type::pAbsPtr->prev; + basic_type::pAbsPtr = abs_next; + }else{ + basic_type::pAbsPtr = NULL; + } + } + abs_cur->prev = NULL; + abs_cur->next = NULL; + + return abs_cur; +} + +// +// This method is not good performance. +// +template +typename cltproclist_lap::st_ptr_type cltproclist_lap::Find(pid_t pid, bool is_abs) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return NULL; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + if(cur->pid == pid){ + return (is_abs ? cur : CHM_REL(basic_type::pShmBase, cur, st_ptr_type)); + } + } + return NULL; +} + +template +bool cltproclist_lap::GetAllPids(pidlist_t& list) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return false; + } + if(basic_type::pAbsPtr->prev){ + ToFirst(); + } + list.clear(); + + for(st_ptr_type cur = basic_type::pAbsPtr; cur; cur = CHM_ABS(basic_type::pShmBase, cur->next, st_ptr_type)){ + list.push_back(cur->pid); + } + return true; +} + +typedef cltproclist_lap cltproclistlap; + +//--------------------------------------------------------- +// For CHMPXMAN +//--------------------------------------------------------- +template +class chmpxman_lap : public structure_lap +{ + public: + typedef T st_type; + typedef T* st_ptr_type; + typedef structure_lap basic_type; + + protected: + long GetChmpxListCount(bool is_server) const; + PCHMPX* AbsBaseArr(void) const { return (basic_type::pAbsPtr ? CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->chmpx_base_srvs, PCHMPX*) : NULL); } + PCHMPX* AbsPendArr(void) const { return (basic_type::pAbsPtr ? CHM_ABS(basic_type::pShmBase, basic_type::pAbsPtr->chmpx_pend_srvs, PCHMPX*) : NULL); } + long* AbsSockFreeCnt(void) const { return (basic_type::pAbsPtr ? &(basic_type::pAbsPtr->sock_free_count) : NULL); } + PCHMSOCKLIST* AbsSockFrees(void) const { return (basic_type::pAbsPtr ? &(basic_type::pAbsPtr->sock_frees) : NULL); } + + public: + chmpxman_lap(st_ptr_type ptr = NULL, const void* shmbase = NULL, bool is_abs = true); + + virtual bool Initialize(void); + virtual bool Dump(std::stringstream& sstream, const char* spacer) const; + virtual bool Clear(void); + bool Copy(st_ptr_type ptr); + chmpxlap::st_ptr_type DupSelfChmpxSvr(void); + void Free(st_ptr_type ptr) const; + + bool Close(int eqfd, int type = CLOSETG_BOTH); + bool Initialize(const CHMCFGINFO* pchmcfg, const CHMNODE_CFGINFO* pselfnode, PCHMPXLIST relchmpxlist, PCHMSOCKLIST relchmsockarea, PCHMPX* rel_pchmpxarrbase, PCHMPX* rel_pchmpxarrpend); + bool ReloadConfigration(const CHMCFGINFO* pchmcfg); + + bool GetSelfChmpxSvr(PCHMPXSVR chmpxsvr) const; + bool GetChmpxSvr(chmpxid_t chmpxid, PCHMPXSVR chmpxsvr) const; + bool GetChmpxSvrs(PCHMPXSVR* ppchmpxsvrs, long& count) const; + bool MergeChmpxSvrs(PCHMPXSVR pchmpxsvrs, long count, bool is_client_join, bool is_remove = true, bool is_init_process = false, int eqfd = CHM_INVALID_HANDLE); + + bool GetGroup(std::string& group) const; + bool IsServerMode(chmpxid_t chmpxid = CHM_INVALID_CHMPXID) const; + bool IsSlaveMode(chmpxid_t chmpxid = CHM_INVALID_CHMPXID) const; + long GetReplicaCount(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->replica_count : DEFAULT_REPLICA_COUNT); } + chmpxid_t GetSelfChmpxId(void) const; + chmpxid_t GetNextRingChmpxId(chmpxid_t chmpxid = CHM_INVALID_CHMPXID) const; + + long GetServerCount(void) const { return GetChmpxListCount(true); } + long GetSlaveCount(void) const { return GetChmpxListCount(false); } + long GetUpServerCount(void) const; + chmpxpos_t GetSelfServerPos(void) const; + chmpxpos_t GetNextServerPos(chmpxpos_t startpos, chmpxpos_t nowpos, bool is_skip_self, bool is_cycle) const; + bool GetAllServerName(hnamesslmap_t& info) const; + + bool IsOperating(void); + bool IsServerChmpxId(chmpxid_t chmpxid) const; + chmpxid_t GetChmpxIdBySock(int sock, int type = CLOSETG_BOTH) const; + chmpxid_t GetChmpxIdByToServerName(const char* hostname, short ctlport) const; + chmpxid_t GetChmpxIdByStatus(chmpxsts_t status, bool part_match = false) const; + chmpxid_t GetRandomServerChmpxId(bool is_up_servers = false, bool without_suspend = false); + chmpxid_t GetServerChmpxIdByHash(chmhash_t hash) const; + bool GetServerChmpxIdAndBaseHashByHashs(chmhash_t hash, chmpxidlist_t& chmpxids, chmhashlist_t& basehashs, bool with_pending, bool without_down = true, bool without_suspend = true); + bool GetServerChmHashsByHashs(chmhash_t hash, chmhashlist_t& basehashs, bool with_pending, bool without_down = true, bool without_suspend = true); + bool GetServerChmpxIdByHashs(chmhash_t hash, chmpxidlist_t& chmpxids, bool with_pending, bool without_down = true, bool without_suspend = true); + long GetServerChmpxIds(chmpxidlist_t& list, bool with_pending, bool without_down = true, bool without_suspend = true) const; + bool GetServerBase(chmpxpos_t pos, std::string& name, chmpxid_t& chmpxid, short& port, short& ctlport) const; + bool GetServerBase(chmpxid_t chmpxid, std::string& name, short& port, short& ctlport) const; + bool GetServerBase(chmpxid_t chmpxid, CHMPXSSL& ssl) const; + bool GetServerSocks(chmpxid_t chmpxid, socklist_t& socklist, int& ctlsock) const; + bool GetServerHash(chmpxid_t chmpxid, chmhash_t& base, chmhash_t& pending) const; + bool GetMaxHashCount(chmhash_t& basehash, chmhash_t& pengindhash) const; + chmpxsts_t GetServerStatus(chmpxid_t chmpxid) const; + bool GetSelfPorts(short& port, short& ctlport) const; + bool GetSelfSocks(int& sock, int& ctlsock) const; + bool GetSelfHash(chmhash_t& base, chmhash_t& pending) const; + chmpxsts_t GetSelfStatus(void) const; + bool GetSelfSsl(CHMPXSSL& ssl) const; + long GetSlaveChmpxIds(chmpxidlist_t& list) const; + bool GetSlaveBase(chmpxid_t chmpxid, std::string& name, short& ctlport) const; + bool GetSlaveSock(chmpxid_t chmpxid, socklist_t& socklist) const; + chmpxsts_t GetSlaveStatus(chmpxid_t chmpxid) const; + + bool SetServerSocks(chmpxid_t chmpxid, int sock, int ctlsock, int type); + bool SetServerHash(chmpxid_t chmpxid, chmhash_t base, chmhash_t pending, int type); + bool SetServerStatus(chmpxid_t chmpxid, chmpxsts_t status, bool is_client_join); + bool RemoveServerSock(chmpxid_t chmpxid, int sock); + bool UpdatePendingHash(bool is_allow_operating = true); + bool SetSelfSocks(int sock, int ctlsock); + bool SetSelfHash(chmhash_t base, chmhash_t pending, int type); + bool SetSelfStatus(chmpxsts_t status, bool is_client_join); + + bool SetSlaveBase(chmpxid_t chmpxid, const char* hostname, short ctlport, const PCHMPXSSL pssl); + bool SetSlaveSock(chmpxid_t chmpxid, int sock); + bool SetSlaveStatus(chmpxid_t chmpxid, chmpxsts_t status); + bool RemoveSlaveSock(chmpxid_t chmpxid, int sock); + bool RemoveSlave(chmpxid_t chmpxid, int eqfd); + + bool AddStat(chmpxid_t chmpxid, bool is_sent, size_t length, const struct timespec& elapsed_time); + bool GetStat(PCHMSTAT pserver, PCHMSTAT pslave) const; +}; + +template +chmpxman_lap::chmpxman_lap(st_ptr_type ptr, const void* shmbase, bool is_abs) +{ + structure_lap::Reset(ptr, shmbase, is_abs); +} + +template +bool chmpxman_lap::Initialize(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + basic_type::pAbsPtr->group[0] = '\0'; + basic_type::pAbsPtr->cfg_revision = 0; + basic_type::pAbsPtr->cfg_date = 0L; + basic_type::pAbsPtr->replica_count = DEFAULT_REPLICA_COUNT; + basic_type::pAbsPtr->chmpx_count = 0; + basic_type::pAbsPtr->chmpx_base_srvs = NULL; + basic_type::pAbsPtr->chmpx_pend_srvs = NULL; + basic_type::pAbsPtr->chmpx_self = NULL; + basic_type::pAbsPtr->chmpx_bhash_count = 0; + basic_type::pAbsPtr->chmpx_server_count = 0; + basic_type::pAbsPtr->chmpx_servers = NULL; + basic_type::pAbsPtr->chmpx_slave_count = 0; + basic_type::pAbsPtr->chmpx_slaves = NULL; + basic_type::pAbsPtr->chmpx_free_count = 0; + basic_type::pAbsPtr->chmpx_frees = NULL; + basic_type::pAbsPtr->sock_free_count = 0; + basic_type::pAbsPtr->sock_frees = NULL; + basic_type::pAbsPtr->is_operating = false; + basic_type::pAbsPtr->last_chmpxid = CHM_INVALID_CHMPXID; + + for(int cnt = 0; cnt < CHMPXID_MAP_SIZE; ++cnt){ + basic_type::pAbsPtr->chmpxid_map[cnt] = NULL; + } + + chmstatlap tmpchmstat; + tmpchmstat.Reset(&basic_type::pAbsPtr->server_stat, basic_type::pShmBase); + if(!tmpchmstat.Initialize()){ + ERR_CHMPRN("Failed to initialize CHMSTAT."); + return false; + } + tmpchmstat.Reset(&basic_type::pAbsPtr->slave_stat, basic_type::pShmBase); + if(!tmpchmstat.Initialize()){ + ERR_CHMPRN("Failed to initialize CHMSTAT."); + return false; + } + + return true; +} + +template +bool chmpxman_lap::Dump(std::stringstream& sstream, const char* spacer) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + + chmstatlap tmpchmstat; + std::string tmpspacer = spacer ? spacer : ""; + tmpspacer += " "; + + sstream << (spacer ? spacer : "") << "group = " << basic_type::pAbsPtr->group << std::endl; + sstream << (spacer ? spacer : "") << "cfg_revision = " << basic_type::pAbsPtr->cfg_revision << std::endl; + sstream << (spacer ? spacer : "") << "cfg_date = " << basic_type::pAbsPtr->cfg_date << std::endl; + sstream << (spacer ? spacer : "") << "replica_count = " << basic_type::pAbsPtr->replica_count << std::endl; + sstream << (spacer ? spacer : "") << "chmpx_count = " << basic_type::pAbsPtr->chmpx_count << std::endl; + sstream << (spacer ? spacer : "") << "chmpxid_map[" << to_string(CHMPXID_MAP_SIZE) << "]" << std::endl; + + sstream << (spacer ? spacer : "") << "chmpx_base_srvs = " << basic_type::pAbsPtr->chmpx_base_srvs << std::endl; + if(basic_type::pAbsPtr->chmpx_base_srvs){ + PCHMPX* abs_base_arr = AbsBaseArr(); + PCHMPX* abs_pend_arr = AbsPendArr(); + long* abs_sock_free_cnt = AbsSockFreeCnt(); + PCHMSOCKLIST* abs_sock_frees = AbsSockFrees(); + chmpxlap tmpchmpx; + sstream << (spacer ? spacer : "") << "[" << basic_type::pAbsPtr->chmpx_bhash_count << "] {" << std::endl; + for(long cnt = 0; cnt < basic_type::pAbsPtr->chmpx_bhash_count; cnt++){ + tmpchmpx.Reset(abs_base_arr[cnt], abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase, false); // From rel + sstream << (spacer ? spacer : "") << " [" << cnt << "] = 0x" << to_hexstring(tmpchmpx.GetChmpxId()) << std::endl; + } + sstream << (spacer ? spacer : "") << "}" << std::endl; + } + + sstream << (spacer ? spacer : "") << "chmpx_pend_srvs = " << basic_type::pAbsPtr->chmpx_pend_srvs << std::endl; + if(basic_type::pAbsPtr->chmpx_pend_srvs){ + PCHMPX* abs_base_arr = AbsBaseArr(); + PCHMPX* abs_pend_arr = AbsPendArr(); + long* abs_sock_free_cnt = AbsSockFreeCnt(); + PCHMSOCKLIST* abs_sock_frees = AbsSockFrees(); + chmpxlap tmpchmpx; + sstream << (spacer ? spacer : "") << "[" << basic_type::pAbsPtr->chmpx_server_count << "] {" << std::endl; + for(long cnt = 0; cnt < basic_type::pAbsPtr->chmpx_server_count; cnt++){ + tmpchmpx.Reset(abs_pend_arr[cnt], abs_base_arr, abs_pend_arr, abs_sock_free_cnt, abs_sock_frees, basic_type::pShmBase, false); // From rel + sstream << (spacer ? spacer : "") << " [" << cnt << "] = 0x" << to_hexstring(tmpchmpx.GetChmpxId()) << std::endl; + } + sstream << (spacer ? spacer : "") << "}" << std::endl; + } + + sstream << (spacer ? spacer : "") << "chmpx_self = " << basic_type::pAbsPtr->chmpx_self << std::endl; + + sstream << (spacer ? spacer : "") << "{" << std::endl; + chmpxlistlap selfchmpxlist(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false);// Get CHMPXLIST from Relative + chmpxlap selfchmpx(selfchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + + selfchmpx.Dump(sstream, tmpspacer.c_str()); + sstream << (spacer ? spacer : "") << "}" << std::endl; + + sstream << (spacer ? spacer : "") << "chmpx_bhash_count = " << basic_type::pAbsPtr->chmpx_bhash_count << std::endl; + sstream << (spacer ? spacer : "") << "chmpx_server_count = " << basic_type::pAbsPtr->chmpx_server_count << std::endl; + sstream << (spacer ? spacer : "") << "chmpx_servers = " << basic_type::pAbsPtr->chmpx_servers << std::endl; + + if(0L < basic_type::pAbsPtr->chmpx_server_count){ + sstream << (spacer ? spacer : "") << "{" << std::endl; + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + svrchmpxlist.Dump(sstream, tmpspacer.c_str()); + sstream << (spacer ? spacer : "") << "}" << std::endl; + } + + sstream << (spacer ? spacer : "") << "chmpx_slave_count = " << basic_type::pAbsPtr->chmpx_slave_count << std::endl; + sstream << (spacer ? spacer : "") << "chmpx_slaves = " << basic_type::pAbsPtr->chmpx_slaves << std::endl; + + if(0L < basic_type::pAbsPtr->chmpx_slave_count){ + sstream << (spacer ? spacer : "") << "{" << std::endl; + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_slaves, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + svrchmpxlist.Dump(sstream, tmpspacer.c_str()); + sstream << (spacer ? spacer : "") << "}" << std::endl; + } + + sstream << (spacer ? spacer : "") << "chmpx_free_count = " << basic_type::pAbsPtr->chmpx_free_count << std::endl; + sstream << (spacer ? spacer : "") << "chmpx_frees = " << basic_type::pAbsPtr->chmpx_frees << std::endl; + + sstream << (spacer ? spacer : "") << "sock_free_count = " << basic_type::pAbsPtr->sock_free_count << std::endl; + sstream << (spacer ? spacer : "") << "sock_frees = " << basic_type::pAbsPtr->sock_frees << std::endl; + + sstream << (spacer ? spacer : "") << "is_operating = " << (basic_type::pAbsPtr->is_operating ? "yes" : "no") << std::endl; + sstream << (spacer ? spacer : "") << "last_chmpxid = 0x" << to_hexstring(basic_type::pAbsPtr->last_chmpxid) << std::endl; + + sstream << (spacer ? spacer : "") << "server_stat{" << std::endl; + tmpchmstat.Reset(&basic_type::pAbsPtr->server_stat, basic_type::pShmBase); + tmpchmstat.Dump(sstream, tmpspacer.c_str()); + sstream << (spacer ? spacer : "") << "}" << std::endl; + + sstream << (spacer ? spacer : "") << "slave_stat{" << std::endl; + tmpchmstat.Reset(&basic_type::pAbsPtr->slave_stat, basic_type::pShmBase); + tmpchmstat.Dump(sstream, tmpspacer.c_str()); + sstream << (spacer ? spacer : "") << "}" << std::endl; + + return true; +} + +template +bool chmpxman_lap::Copy(st_ptr_type ptr) +{ + if(!ptr){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + + ptr->cfg_revision = basic_type::pAbsPtr->cfg_revision; + ptr->cfg_date = basic_type::pAbsPtr->cfg_date; + ptr->replica_count = basic_type::pAbsPtr->replica_count; + ptr->chmpx_count = basic_type::pAbsPtr->chmpx_count; + ptr->chmpx_base_srvs = NULL; // Always NULL + ptr->chmpx_pend_srvs = NULL; // Always NULL + ptr->chmpx_bhash_count = basic_type::pAbsPtr->chmpx_bhash_count; + ptr->chmpx_server_count = basic_type::pAbsPtr->chmpx_server_count; + ptr->chmpx_slave_count = basic_type::pAbsPtr->chmpx_slave_count; + ptr->chmpx_free_count = basic_type::pAbsPtr->chmpx_free_count; + ptr->chmpx_frees = NULL; // Always NULL + ptr->sock_free_count = basic_type::pAbsPtr->sock_free_count; + ptr->sock_frees = NULL; // Always NULL + ptr->is_operating = basic_type::pAbsPtr->is_operating; + ptr->last_chmpxid = basic_type::pAbsPtr->last_chmpxid; + + strcpy(ptr->group, basic_type::pAbsPtr->group); + memset(ptr->chmpxid_map, 0, sizeof(PCHMPXLIST) * CHMPXID_MAP_SIZE); // [NOTE] clear all for pointer array + + chmpxlistlap selfchmpxlist(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + ptr->chmpx_self = selfchmpxlist.Dup(true); // [NOTE] top of list is self chmpx + + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + ptr->chmpx_servers = svrchmpxlist.Dup(); + + chmpxlistlap slvchmpxlist(basic_type::pAbsPtr->chmpx_slaves, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + ptr->chmpx_slaves = slvchmpxlist.Dup(); + + chmstatlap chmstat; + chmstat.Reset(&basic_type::pAbsPtr->server_stat, basic_type::pShmBase); + if(!chmstat.Copy(&ptr->server_stat)){ + WAN_CHMPRN("Failed to copy server_stat structure, but continue..."); + } + + chmstat.Reset(&basic_type::pAbsPtr->slave_stat, basic_type::pShmBase); + if(!chmstat.Copy(&ptr->slave_stat)){ + WAN_CHMPRN("Failed to copy slave_stat structure, but continue..."); + } + + return true; +} + +template +chmpxlap::st_ptr_type chmpxman_lap::DupSelfChmpxSvr(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return NULL; + } + chmpxlap::st_ptr_type pselfchmpx; + if(NULL == (pselfchmpx = reinterpret_cast(calloc(1, sizeof(chmpxlap::st_type))))){ + ERR_CHMPRN("Could not allocation memory."); + return NULL; + } + chmpxlistlap selfchmpxlist(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // Get CHMPXLIST from Relative + chmpxlap selfchmpx(selfchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + if(!selfchmpx.Copy(pselfchmpx)){ + ERR_CHMPRN("Could not get self chmpx structure."); + K2H_Free(pselfchmpx); + return NULL; + } + return pselfchmpx; +} + +template +void chmpxman_lap::Free(st_ptr_type ptr) const +{ + if(ptr){ + chmpxlistlap tmpchmpxlist; // [NOTE] pointer is not on Shm, but we use chmpxlistlap object. thus using only Free method. + tmpchmpxlist.Free(ptr->chmpx_self); + tmpchmpxlist.Free(ptr->chmpx_servers); + tmpchmpxlist.Free(ptr->chmpx_slaves); + } +} + +template +bool chmpxman_lap::Initialize(const CHMCFGINFO* pchmcfg, const CHMNODE_CFGINFO* pselfnode, PCHMPXLIST relchmpxlist, PCHMSOCKLIST relchmsockarea, PCHMPX* rel_pchmpxarrbase, PCHMPX* rel_pchmpxarrpend) +{ + if(!pchmcfg || !relchmpxlist || !pselfnode || !rel_pchmpxarrbase || !rel_pchmpxarrpend){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(MAX_GROUP_LENGTH <= pchmcfg->groupname.length() || MAX_CHMPX_COUNT < pchmcfg->max_chmpx_count){ + ERR_CHMPRN("Configuration information are wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + + strcpy(basic_type::pAbsPtr->group, pchmcfg->groupname.c_str()); + + basic_type::pAbsPtr->cfg_revision = pchmcfg->revision; + basic_type::pAbsPtr->cfg_date = pchmcfg->date; + basic_type::pAbsPtr->replica_count = pchmcfg->replica_count; + basic_type::pAbsPtr->chmpx_count = pchmcfg->max_chmpx_count; + basic_type::pAbsPtr->chmpx_base_srvs = rel_pchmpxarrbase; + basic_type::pAbsPtr->chmpx_pend_srvs = rel_pchmpxarrpend; + + // Remaining list is free sock list + chmsocklistlap freesocklist(relchmsockarea, basic_type::pShmBase, false); // From Relative + basic_type::pAbsPtr->sock_free_count = freesocklist.Count(); // To first of list + basic_type::pAbsPtr->sock_frees = freesocklist.GetFirstPtr(false); // Get Relative + + // Free PCHMPXLIST + chmpxlistlap freechmpxlist(relchmpxlist, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From Relative + + // initialize servers/slaves list + basic_type::pAbsPtr->chmpx_bhash_count = 0; + basic_type::pAbsPtr->chmpx_server_count = 0; + basic_type::pAbsPtr->chmpx_servers = NULL; + basic_type::pAbsPtr->chmpx_slave_count = 0; + basic_type::pAbsPtr->chmpx_slaves = NULL; + + // self + { + // Get one PCHMPXLIST for self from front of free list + // Get self PCHMPX + chmpxlistlap selfchmpxlist(freechmpxlist.PopFront(), basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get one CHMPXLIST from Absolute + chmpxlap selfchmpx(selfchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + CHMPXSSL ssl; + CVT_SSL_STRUCTURE(ssl, *pselfnode); + + // Inilitalize self PCHMPX for server in self list + if(pchmcfg->is_server_mode){ + selfchmpx.InitializeServer(pselfnode->name.c_str(), pchmcfg->groupname.c_str(), pselfnode->port, pselfnode->ctlport, ssl); + basic_type::pAbsPtr->chmpx_servers = selfchmpxlist.GetRelPtr(); // Set Self chmpxlist into servers + basic_type::pAbsPtr->chmpx_server_count = 1; + + // [NOTE] + // chmpx_bhash_count is 0, because InitializeServer sets status DOWN. + }else{ + selfchmpx.InitializeSlave(pselfnode->name.c_str(), pchmcfg->groupname.c_str(), pselfnode->ctlport, ssl); + } + selfchmpxlist.SaveChmpxIdMap(); // Set chmpxid map + basic_type::pAbsPtr->chmpx_self = selfchmpxlist.GetRelPtr(); // Set Self chmpxlist + } + + // Set servers list + chmpxlistlap cursvrlist; + chmpxlap cursvrchmpx; + for(chmnode_cfginfos_t::const_iterator iter = pchmcfg->servers.begin(); iter != pchmcfg->servers.end(); ++iter){ + if(pchmcfg->is_server_mode){ + // If server mode, already set self chmpx into server list. + // So skip it.(server name & ctlport are same) + if(iter->name == pselfnode->name && iter->ctlport == pselfnode->ctlport){ + continue; + } + } + + // set CHMPX + CHMPXSSL ssl; + CVT_SSL_STRUCTURE(ssl, *iter); + cursvrlist.Reset(freechmpxlist.PopFront(), basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get one CHMPXLIST from Absolute + cursvrchmpx.Reset(cursvrlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + cursvrchmpx.InitializeServer(iter->name.c_str(), pchmcfg->groupname.c_str(), iter->port, iter->ctlport, ssl); + + // inter CHMPX into servers list + if(basic_type::pAbsPtr->chmpx_servers){ + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + svrchmpxlist.Insert(cursvrlist.GetAbsPtr(), true); // From abs + + basic_type::pAbsPtr->chmpx_server_count++; + basic_type::pAbsPtr->chmpx_servers = svrchmpxlist.GetFirstPtr(false); + }else{ + cursvrlist.SaveChmpxIdMap(); // Set chmpxid map + basic_type::pAbsPtr->chmpx_server_count = 1; + basic_type::pAbsPtr->chmpx_servers = cursvrlist.GetRelPtr(); + } + // [NOTE] + // chmpx_bhash_count is 0, because InitializeServer sets status DOWN. + } + + // Remaining list is free chmpx list + basic_type::pAbsPtr->chmpx_free_count = freechmpxlist.Count(); // To first of list + basic_type::pAbsPtr->chmpx_frees = freechmpxlist.GetFirstPtr(false); // Get Relative + + // operating flag/last chmpxid + basic_type::pAbsPtr->is_operating = false; + basic_type::pAbsPtr->last_chmpxid = CHM_INVALID_CHMPXID; + + chmstatlap tmpchmstat; + tmpchmstat.Reset(&basic_type::pAbsPtr->server_stat, basic_type::pShmBase); + if(!tmpchmstat.Initialize()){ + ERR_CHMPRN("Failed to initialize CHMSTAT."); + return false; + } + tmpchmstat.Reset(&basic_type::pAbsPtr->slave_stat, basic_type::pShmBase); + if(!tmpchmstat.Initialize()){ + ERR_CHMPRN("Failed to initialize CHMSTAT."); + return false; + } + return true; +} + +template +bool chmpxman_lap::ReloadConfigration(const CHMCFGINFO* pchmcfg) +{ + if(!pchmcfg){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + // check + if(MAX_GROUP_LENGTH <= pchmcfg->groupname.length() || MAX_CHMPX_COUNT < pchmcfg->max_chmpx_count){ + ERR_CHMPRN("Configuration information are wrong."); + return false; + } + if(0 != strcmp(basic_type::pAbsPtr->group, pchmcfg->groupname.c_str())){ + ERR_CHMPRN("Could not change groupname(%s -> %s).", basic_type::pAbsPtr->group, pchmcfg->groupname.c_str()); + return false; + } + + // reset + basic_type::pAbsPtr->cfg_revision = pchmcfg->revision; + basic_type::pAbsPtr->cfg_date = pchmcfg->date; + basic_type::pAbsPtr->replica_count = pchmcfg->replica_count; + basic_type::pAbsPtr->chmpx_count = pchmcfg->max_chmpx_count; + + // [NOTE] + // now server list is not update. + // + return true; +} + +template +bool chmpxman_lap::GetSelfChmpxSvr(PCHMPXSVR chmpxsvr) const +{ + if(!chmpxsvr){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap selfchmpxlist(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // Get CHMPXLIST from Relative + chmpxlap selfchmpx(selfchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return selfchmpx.GetChmpxSvr(chmpxsvr); +} + +template +bool chmpxman_lap::GetChmpxSvr(chmpxid_t chmpxid, PCHMPXSVR chmpxsvr) const +{ + if(CHM_INVALID_CHMPXID == chmpxid || !chmpxsvr){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false);// From rel + if(!svrchmpxlist.Find(chmpxid)){ + MSG_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ") in servers list.", chmpxid); + return false; + } + chmpxlap selfchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return selfchmpx.GetChmpxSvr(chmpxsvr); +} + +template +bool chmpxman_lap::GetChmpxSvrs(PCHMPXSVR* ppchmpxsvrs, long& count) const +{ + if(!ppchmpxsvrs){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + + if(0 == (count = basic_type::pAbsPtr->chmpx_server_count)){ + *ppchmpxsvrs = NULL; + return false; + } + if(NULL == ((*ppchmpxsvrs) = reinterpret_cast(malloc(sizeof(CHMPXSVR) * count)))){ + ERR_CHMPRN("Could not allocation memory."); + return false; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + chmpxlap svrchmpx; + long setpos = 0; + do{ + svrchmpx.Reset(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + svrchmpx.GetChmpxSvr(&((*ppchmpxsvrs)[setpos++])); + }while(svrchmpxlist.ToNext(false, false, false, false)); // not cycle, all server(not only base hash servers, up/down servers), (not same server) + + return true; +} + +template +bool chmpxman_lap::MergeChmpxSvrs(PCHMPXSVR pchmpxsvrs, long count, bool is_client_join, bool is_remove, bool is_init_process, int eqfd) +{ + if(!pchmpxsvrs || count <= 0L){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + chmpxid_t selfchmpxid = CHM_INVALID_CHMPXID; + if(IsServerMode()){ + selfchmpxid = GetSelfChmpxId(); + } + + // First over write/insert + for(long cnt = 0; cnt < count; cnt++){ + if(svrchmpxlist.GetAbsPtr() && svrchmpxlist.Find(pchmpxsvrs[cnt].chmpxid)){ + // Found -> merge + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + + // check self status + if(selfchmpxid == pchmpxsvrs[cnt].chmpxid){ + if(svrchmpx.GetStatus() != pchmpxsvrs[cnt].status){ + // check self status changing + if(IS_CHMPXSTS_DOWN(pchmpxsvrs[cnt].status) && (is_init_process || CHM_INVALID_SOCK != svrchmpx.GetSock(SOCKTG_SELFSOCK))){ + // This case is that the server is up after the server have been down. + // + // [NOTICE] + // You should service out the down server, before you up it. + // + if(CHMPXSTS_SRVIN_DOWN_NORMAL == pchmpxsvrs[cnt].status){ + if(!is_client_join){ + WAN_CHMPRN("Self new chmpx status(0x%016" PRIx64 ":%s), so new status is CHMPXSTS_SRVIN_UP_NORMAL(suspend).", pchmpxsvrs[cnt].status, STR_CHMPXSTS_FULL(pchmpxsvrs[cnt].status).c_str()); + pchmpxsvrs[cnt].status = CHMPXSTS_SRVIN_UP_NORMAL; // suspend (set later) + }else{ + WAN_CHMPRN("Self new chmpx status(0x%016" PRIx64 ":%s), so new status is CHMPXSTS_SRVIN_UP_MERGING.", pchmpxsvrs[cnt].status, STR_CHMPXSTS_FULL(pchmpxsvrs[cnt].status).c_str()); + pchmpxsvrs[cnt].status = CHMPXSTS_SRVIN_UP_MERGING; // nosuspend + } + + }else if(CHMPXSTS_SRVIN_DOWN_DELPENDING == pchmpxsvrs[cnt].status){ + if(!is_client_join){ + WAN_CHMPRN("Self new chmpx status(0x%016" PRIx64 ":%s), so new status is CHMPXSTS_SRVIN_UP_NORMAL(suspend).", pchmpxsvrs[cnt].status, STR_CHMPXSTS_FULL(pchmpxsvrs[cnt].status).c_str()); + pchmpxsvrs[cnt].status = CHMPXSTS_SRVIN_UP_NORMAL; // suspend (set later) + }else{ + WAN_CHMPRN("Self new chmpx status(0x%016" PRIx64 ":%s), so new status is CHMPXSTS_SRVIN_UP_MERGING.", pchmpxsvrs[cnt].status, STR_CHMPXSTS_FULL(pchmpxsvrs[cnt].status).c_str()); + pchmpxsvrs[cnt].status = CHMPXSTS_SRVIN_UP_MERGING; // nosuspend(should be CHMPXSTS_SRVIN_UP_NORMAL?) + } + + }else if(CHMPXSTS_SRVIN_DOWN_DELETED == pchmpxsvrs[cnt].status){ + WAN_CHMPRN("Self new chmpx status(0x%016" PRIx64 ":%s), so new status is CHMPXSTS_SRVOUT_UP_NORMAL.", pchmpxsvrs[cnt].status, STR_CHMPXSTS_FULL(pchmpxsvrs[cnt].status).c_str()); + pchmpxsvrs[cnt].status = CHMPXSTS_SRVOUT_UP_NORMAL; + + }else{ // CHMPXSTS_SRVOUT_DOWN_NORMAL, CHMPXSTS_SLAVE_DOWN_NORMAL + WAN_CHMPRN("Self new chmpx status(0x%016" PRIx64 ":%s), so new status is CHMPXSTS_SRVOUT(SLAVE)_UP_NORMAL.", pchmpxsvrs[cnt].status, STR_CHMPXSTS_FULL(pchmpxsvrs[cnt].status).c_str()); + SET_CHMPXSTS_UP(pchmpxsvrs[cnt].status); + } + // suspend + CHANGE_CHMPXSTS_SUSPEND_FLAG(pchmpxsvrs[cnt].status, !is_client_join); + + }else if(IS_CHMPXSTS_UP(pchmpxsvrs[cnt].status) && !is_init_process && CHM_INVALID_SOCK == svrchmpx.GetSock(SOCKTG_SELFSOCK)){ + WAN_CHMPRN("Self new chmpx status(0x%016" PRIx64 ":%s) is wrong, because self server is down, so status is arranged.", pchmpxsvrs[cnt].status, STR_CHMPXSTS_FULL(pchmpxsvrs[cnt].status).c_str()); + CHANGE_CHMPXSTS_TO_DOWN(pchmpxsvrs[cnt].status); + + // not suspend + SET_CHMPXSTS_NOSUP(pchmpxsvrs[cnt].status); + } + } + } + // merge + if(!svrchmpx.MergeChmpxSvr(&pchmpxsvrs[cnt], false, eqfd)){ // not force + WAN_CHMPRN("Failed to merge CHMPX(%s: 0x%016" PRIx64 ") from CHMPXSVR, but continue...", pchmpxsvrs[cnt].name, pchmpxsvrs[cnt].chmpxid); + } + + }else{ + // Not found -> Insert new chmpx + if(basic_type::pAbsPtr->chmpx_free_count <= 0L){ + ERR_CHMPRN("No more free CHMPXLIST."); + + // [NOTICE] + // care for bash hash count. + // + basic_type::pAbsPtr->chmpx_bhash_count = svrchmpxlist.BaseHashCount(true); + basic_type::pAbsPtr->is_operating = svrchmpxlist.IsOperating(true); + return false; + } + // make new chmpx(list) from free chmpx list + chmpxlistlap freechmpxlist(basic_type::pAbsPtr->chmpx_frees, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From Relative + chmpxlistlap newchmpxlist(freechmpxlist.PopAny(), basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get one CHMPXLIST from Absolute + chmpxlap newchmpx(newchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + + basic_type::pAbsPtr->chmpx_free_count--; + basic_type::pAbsPtr->chmpx_frees = freechmpxlist.GetFirstPtr(false); + + newchmpxlist.Clear(); + if(!newchmpx.InitializeServer(&pchmpxsvrs[cnt], basic_type::pAbsPtr->group)){ + WAN_CHMPRN("Failed to initialize CHMPX(%s: 0x%016" PRIx64 ") from CHMPXSVR, but continue...", pchmpxsvrs[cnt].name, pchmpxsvrs[cnt].chmpxid); + } + + if(svrchmpxlist.GetAbsPtr()){ + // insert + svrchmpxlist.Insert(newchmpxlist.GetAbsPtr(), true); // From abs + basic_type::pAbsPtr->chmpx_server_count++; + basic_type::pAbsPtr->chmpx_servers = svrchmpxlist.GetFirstPtr(false); + }else{ + // insert new + newchmpxlist.SaveChmpxIdMap(); // Set chmpxid map + basic_type::pAbsPtr->chmpx_slave_count = 1; + basic_type::pAbsPtr->chmpx_servers = newchmpxlist.GetRelPtr(); + } + } + } + + // Next, remove chmpx server from list + if(is_remove){ + for(bool result = svrchmpxlist.ToFirst(); result; ){ + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + chmpxid_t chmpxid = svrchmpx.GetChmpxId(); + if(chmpxid == selfchmpxid){ + // self chmpxid, skip this. + result = svrchmpxlist.ToNext(false, false, false, false); // not cycle, all server(not only base hash servers, up/down servers), (not same server) + continue; + } + + // check chmpxid + long chkcnt; + for(chkcnt = 0; chkcnt < count; chkcnt++){ + if(chmpxid == pchmpxsvrs[chkcnt].chmpxid){ + break; + } + } + if(chkcnt < count){ + // Found -> not remove + result = svrchmpxlist.ToNext(false, false, false, false); // not cycle, all server(not only base hash servers, up/down servers), (not same server) + continue; + } + + // Not found chmpxid -> remove this chmpxlist + PCHMPXLIST retrivelist; + if(NULL == (retrivelist = svrchmpxlist.Retrive())){ + // Failed to remove it, continue next. + WAN_CHMPRN("Failed to remove CHMPX(0x%016" PRIx64 ") from CHMPXSVR, but continue...", chmpxid); + result = svrchmpxlist.ToNext(false, false, false, false); // not cycle, all server(not only base hash servers, up/down servers), (not same server) + continue; + } + // [NOTICE] + // If Retrive() is succeed, list current in list is set next point(or end of list) + // + result = true; + + // Remove Success + chmpxlistlap retrivechmpxlist(retrivelist, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, true); // From Abs + retrivechmpxlist.Clear(eqfd); + + chmpxlistlap freechmpxlist(basic_type::pAbsPtr->chmpx_frees, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From Relative(allow NULL) + if(!freechmpxlist.Push(retrivelist, true)){ + WAN_CHMPRN("Failed to push back new free CHMPXLIST to freelist, but continue..."); + }else{ + basic_type::pAbsPtr->chmpx_free_count++; + basic_type::pAbsPtr->chmpx_frees = freechmpxlist.GetFirstPtr(false); + } + // [NOTICE] + // decreament here, but setting pointer is after this loop. + // + if(0L < basic_type::pAbsPtr->chmpx_server_count){ + basic_type::pAbsPtr->chmpx_server_count--; + } + } + + // [NOTICE] + // Set only pointer. + // + if(basic_type::pAbsPtr->chmpx_server_count <= 0L){ + basic_type::pAbsPtr->chmpx_servers = NULL; + }else{ + basic_type::pAbsPtr->chmpx_servers = svrchmpxlist.GetFirstPtr(false); + } + } + + // set base hash server count + basic_type::pAbsPtr->chmpx_bhash_count = svrchmpxlist.BaseHashCount(true); + // operating flag + basic_type::pAbsPtr->is_operating = svrchmpxlist.IsOperating(true); + + return true; +} + +template +bool chmpxman_lap::Clear(void) +{ + return Initialize(); +} + +template +bool chmpxman_lap::Close(int eqfd, int type) +{ + if(IS_CLOSETG_SERVERS(type) && basic_type::pAbsPtr->chmpx_servers){ + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false);// From rel + for(bool result = svrchmpxlist.ToFirst(); result; result = svrchmpxlist.ToNext(false, false, false, false)){ // not cycle, all server(not only base hash servers, up/down servers), (not same server) + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + svrchmpx.Close(eqfd); + } + } + if(IS_CLOSETG_SLAVES(type) && basic_type::pAbsPtr->chmpx_slaves){ + chmpxlistlap slvchmpxlist(basic_type::pAbsPtr->chmpx_slaves, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + for(bool result = slvchmpxlist.ToFirst(); result; result = slvchmpxlist.ToNext(false, false, false, false)){ // not cycle, all server(not only base hash servers, up/down servers), (not same server) + chmpxlap slvchmpx(slvchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + slvchmpx.Close(eqfd); + } + } + return true; +} + +template +bool chmpxman_lap::GetGroup(std::string& group) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + group = basic_type::pAbsPtr->group; + return true; +} + +template +bool chmpxman_lap::IsServerMode(chmpxid_t chmpxid) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap svrchmpxlist; + if(CHM_INVALID_CHMPXID == chmpxid){ + svrchmpxlist.Reset(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // Get CHMPXLIST from Relative + chmpxlap selfchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return selfchmpx.IsServerMode(); + }else{ + svrchmpxlist.Reset(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false);// From rel + if(svrchmpxlist.Find(chmpxid)){ + // foud in server list + return true; + } + } + return false; +} + +template +bool chmpxman_lap::IsSlaveMode(chmpxid_t chmpxid) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap svrchmpxlist; + if(CHM_INVALID_CHMPXID == chmpxid){ + svrchmpxlist.Reset(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // Get CHMPXLIST from Relative + chmpxlap selfchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return selfchmpx.IsSlaveMode(); + }else{ + svrchmpxlist.Reset(basic_type::pAbsPtr->chmpx_slaves, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(svrchmpxlist.Find(chmpxid)){ + // foud in slave list + return true; + } + } + return false; +} + +template +chmpxid_t chmpxman_lap::GetSelfChmpxId(void) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return CHM_INVALID_CHMPXID; + } + chmpxlistlap selfchmpxlist(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false);// Get CHMPXLIST from Relative + chmpxlap selfchmpx(selfchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return selfchmpx.GetChmpxId(); +} + +template +chmpxid_t chmpxman_lap::GetNextRingChmpxId(chmpxid_t chmpxid) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return CHM_INVALID_CHMPXID; + } + if(!IsServerMode()){ + ERR_CHMPRN("Self chmpx does not server mode."); + return CHM_INVALID_CHMPXID; + } + + // set start position. + chmpxlistlap svrchmpxlist; + if(CHM_INVALID_CHMPXID == chmpxid){ + // start self + // [NOTICE] self is in servers list. + svrchmpxlist.Reset(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From Relative + chmpxid = GetSelfChmpxId(); + }else{ + svrchmpxlist.Reset(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false);// From Relative + if(!svrchmpxlist.Find(chmpxid)){ + ERR_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return CHM_INVALID_CHMPXID; + } + } + + if(svrchmpxlist.ToNext(true, false, true, false)){ // cycle, not only base hash servers, up servers, not allow same server + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return svrchmpx.GetChmpxId(); + } + + MSG_CHMPRN("Could not get next server in server list."); + return CHM_INVALID_CHMPXID; +} + +template +long chmpxman_lap::GetChmpxListCount(bool is_server) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return 0L; + } + return (is_server ? basic_type::pAbsPtr->chmpx_server_count : basic_type::pAbsPtr->chmpx_slave_count); +} + +template +long chmpxman_lap::GetUpServerCount(void) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return 0L; + } + if(basic_type::pAbsPtr->chmpx_server_count <= 0 || !basic_type::pAbsPtr->chmpx_servers){ + // there is no server list. + return 0L; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + chmpxidlist_t list; + return svrchmpxlist.GetChmpxIds(list, static_cast(CHMPXSTS_VAL_UP), false); +} + +template +chmpxpos_t chmpxman_lap::GetSelfServerPos(void) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return CHM_INVALID_CHMPXLISTPOS; + } + return reinterpret_cast(basic_type::pAbsPtr->chmpx_self); // result is rel +} + +// +// If startpos is CHM_INVALID_CHMPXLISTPOS, get first pos in list. +// +template +chmpxpos_t chmpxman_lap::GetNextServerPos(chmpxpos_t startpos, chmpxpos_t nowpos, bool is_skip_self, bool is_cycle) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return CHM_INVALID_CHMPXLISTPOS; + } + if(basic_type::pAbsPtr->chmpx_server_count <= 0 || !basic_type::pAbsPtr->chmpx_servers){ + ERR_CHMPRN("There is no server in list."); + return CHM_INVALID_CHMPXLISTPOS; + } + + if(CHM_INVALID_CHMPXLISTPOS == startpos && CHM_INVALID_CHMPXLISTPOS != nowpos){ + startpos = reinterpret_cast(basic_type::pAbsPtr->chmpx_servers); + } + PCHMPXLIST pStartPos = reinterpret_cast(startpos); + PCHMPXLIST pNowPos = reinterpret_cast(nowpos); + + // normalize + chmpxlistlap svrchmpxlist; + if(CHM_INVALID_CHMPXLISTPOS == nowpos){ + svrchmpxlist.Reset(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false);// From rel + }else{ + svrchmpxlist.Reset(pNowPos, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + // next + if(!svrchmpxlist.ToNext(is_cycle, false, false, true)){ // cycle(or not cycle), all server(not only base hash servers, up/down servers), allow same server + MSG_CHMPRN("Reached end of server chmpx list."); + return CHM_INVALID_CHMPXLISTPOS; + } + } + if(pNowPos == svrchmpxlist.GetRelPtr() || pStartPos == svrchmpxlist.GetRelPtr()){ + MSG_CHMPRN("There is no more chmpx object in list."); + return CHM_INVALID_CHMPXLISTPOS; + } + + if(is_skip_self){ + // check self + if(basic_type::pAbsPtr->chmpx_self == svrchmpxlist.GetRelPtr()){ + // found, skip self + if(!svrchmpxlist.ToNext(is_cycle, false, false, true)){ // cycle(or not cycle), all server(not only base hash servers, up/down servers), allow same server + MSG_CHMPRN("Reached end of server chmpx list."); + return CHM_INVALID_CHMPXLISTPOS; + } + + if(pNowPos == svrchmpxlist.GetRelPtr() || pStartPos == svrchmpxlist.GetRelPtr() || basic_type::pAbsPtr->chmpx_self == svrchmpxlist.GetRelPtr()){ + MSG_CHMPRN("There is no more chmpx object in list."); + return CHM_INVALID_CHMPXLISTPOS; + } + } + } + return reinterpret_cast(svrchmpxlist.GetRelPtr()); +} + +template +bool chmpxman_lap::GetAllServerName(hnamesslmap_t& info) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + info.clear(); + + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From Relative + bool result; + for(result = svrchmpxlist.ToFirst(); result; result = svrchmpxlist.ToNext(false, false, false, false)){ // not cycle, all server(not only base hash servers, up/down servers), not allow same server + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + std::string name; + chmpxid_t chmpxid; + short port; + short ctlport; + PCHMPXSSL pssl; + + if(!svrchmpx.Get(name, chmpxid, port, ctlport)){ + WAN_CHMPRN("Could not get name/chmpxid/port/ctlport."); + continue; + } + if(info.end() == info.find(name)){ + info[name] = NULL; // "hostanme" = NULL + } + name += ":"; + name += to_string(ctlport); + + pssl = new CHMPXSSL; + svrchmpx.GetSslStructure(*pssl); + info[name] = pssl; // "hostname:ctlport" = PCHMPXSSL + } + return true; +} + +template +bool chmpxman_lap::IsServerChmpxId(chmpxid_t chmpxid) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + return svrchmpxlist.Find(chmpxid); +} + +// +// This method searches liner in list, so it is not good performance. +// +template +chmpxid_t chmpxman_lap::GetChmpxIdBySock(int sock, int type) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return CHM_INVALID_CHMPXID; + } + + PCHMPX abschmpx = NULL; + if(IS_CLOSETG_SERVERS(type)){ + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false);// From rel + if(svrchmpxlist.Find(sock)){ + abschmpx = svrchmpxlist.GetAbsChmpxPtr(); + } + } + if(!abschmpx && IS_CLOSETG_SLAVES(type)){ + chmpxlistlap slvchmpxlist(basic_type::pAbsPtr->chmpx_slaves, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(slvchmpxlist.Find(sock)){ + abschmpx = slvchmpxlist.GetAbsChmpxPtr(); + } + } + if(!abschmpx){ + return CHM_INVALID_CHMPXID; + } + chmpxlap svrchmpx(abschmpx, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return svrchmpx.GetChmpxId(); +} + +template +chmpxid_t chmpxman_lap::GetChmpxIdByToServerName(const char* hostname, short ctlport) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return CHM_INVALID_CHMPXID; + } + + chmpxid_t chmpxid = CHM_INVALID_CHMPXID; + if(CHMEMPTYSTR(hostname) || CHM_INVALID_CHMPXID == (chmpxid = MakeChmpxId(basic_type::pAbsPtr->group, hostname, ctlport))){ + ERR_CHMPRN("Parameter are wrong."); + return CHM_INVALID_CHMPXID; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!svrchmpxlist.Find(chmpxid)){ + return CHM_INVALID_CHMPXID; + } + return chmpxid; +} + +// +// This method is linear search in list, then it is not good performance. +// +template +chmpxid_t chmpxman_lap::GetChmpxIdByStatus(chmpxsts_t status, bool part_match) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return CHM_INVALID_CHMPXID; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + return svrchmpxlist.FindByStatus(status, part_match); +} + +template +chmpxid_t chmpxman_lap::GetRandomServerChmpxId(bool is_up_servers, bool without_suspend) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return CHM_INVALID_CHMPXID; + } + if(0 == basic_type::pAbsPtr->chmpx_bhash_count){ + ERR_CHMPRN("There is no up servers."); + return CHM_INVALID_CHMPXID; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + + if(CHM_INVALID_CHMPXID == basic_type::pAbsPtr->last_chmpxid){ + MSG_CHMPRN("last random chmpxid is not initialized by valid id, so using first chmpxid."); + }else{ + if(!svrchmpxlist.Find(basic_type::pAbsPtr->last_chmpxid)){ + WAN_CHMPRN("last random chmpxid(0x%016" PRIx64 ") is not found, so using first chmpxid.", basic_type::pAbsPtr->last_chmpxid); + }else{ + if(!svrchmpxlist.ToNext(true, true, is_up_servers, true, without_suspend)){ + ERR_CHMPRN("Failed to get next chmpxid by last random chmpxid(0x%016" PRIx64 "), so using first chmpxid.", basic_type::pAbsPtr->last_chmpxid); + } + } + } + + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + + // Check + chmpxsts_t status = svrchmpx.GetStatus(); + if(IS_CHMPXSTS_BASEHASH(status) && (!is_up_servers || IS_CHMPXSTS_UP(status)) && (!without_suspend || IS_CHMPXSTS_NOSUP(status))){ + // OK + basic_type::pAbsPtr->last_chmpxid = svrchmpx.GetChmpxId(); + }else{ + // NG(Maybe, no server is up.) + basic_type::pAbsPtr->last_chmpxid = CHM_INVALID_CHMPXID; + } + return basic_type::pAbsPtr->last_chmpxid; +} + +// +// This method returns only MAIN chmpxid. +// +template +chmpxid_t chmpxman_lap::GetServerChmpxIdByHash(chmhash_t hash) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return CHM_INVALID_CHMPXID; + } + if(0 == basic_type::pAbsPtr->chmpx_bhash_count){ + ERR_CHMPRN("There is no up servers."); + return CHM_INVALID_CHMPXID; + } + PCHMPX* abs_base_arr = AbsBaseArr(); + PCHMPX* abs_pend_arr = AbsPendArr(); + + if(!abs_base_arr || !abs_pend_arr){ + ERR_CHMPRN("based(pending) hash array pointer is NULL."); + return CHM_INVALID_CHMPXID; + } + + // normalize for base hash + chmpxlap svrchmpx; + chmhash_t base_hash = hash % static_cast(basic_type::pAbsPtr->chmpx_bhash_count); + + if(abs_base_arr[base_hash]){ + svrchmpx.Reset(abs_base_arr[base_hash], abs_base_arr, abs_pend_arr, AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + }else{ + // Somthing wrong, but try to search liner.(not good performace) + WAN_CHMPRN("There is no chmpxid in base hashed array for hash(0x%016" PRIx64 ") - base hash(0x%016" PRIx64 "), but retry to do by liner searching.", hash, base_hash); + + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, abs_base_arr, abs_pend_arr, AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!svrchmpxlist.FindByHash(base_hash, true)){ + ERR_CHMPRN("There is no chmpxid in base hashed array and list for hash(0x%016" PRIx64 ") - base hash(0x%016" PRIx64 ").", hash, base_hash); + return CHM_INVALID_CHMPXID; + } + svrchmpx.Reset(svrchmpxlist.GetAbsChmpxPtr(), abs_base_arr, abs_pend_arr, AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + } + + basic_type::pAbsPtr->last_chmpxid = svrchmpx.GetChmpxId(); // for random + + return basic_type::pAbsPtr->last_chmpxid; +} + +// +// Return true / false(no target) +// chmpxids Set all chmpxid( for replication ) +// If operating and with_pending is ture, this value includes other chmpxid by pending hash. +// basehashs Set all chmhash( for replication by manual ) +// If operating and with_pending is ture, this value includes other chmpx's chmhash. +// +template +bool chmpxman_lap::GetServerChmpxIdAndBaseHashByHashs(chmhash_t hash, chmpxidlist_t& chmpxids, chmhashlist_t& basehashs, bool with_pending, bool without_down, bool without_suspend) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + if(0 == basic_type::pAbsPtr->chmpx_bhash_count){ + ERR_CHMPRN("There is no up servers."); + return false; + } + PCHMPX* abs_base_arr = AbsBaseArr(); + PCHMPX* abs_pend_arr = AbsPendArr(); + + if(!abs_base_arr || !abs_pend_arr){ + ERR_CHMPRN("based(pending) hash array pointer is NULL."); + return false; + } + chmpxids.clear(); + basehashs.clear(); + + // normalize for target hash + chmpxid_t last_chmpxid = CHM_INVALID_CHMPXID; + chmhash_t common_hash = hash % static_cast(basic_type::pAbsPtr->chmpx_bhash_count); + chmhash_t target_hash = CHM_INVALID_HASHVAL; + chmhash_t target_base_hash = CHM_INVALID_HASHVAL; + chmhash_t target_pending_hash = CHM_INVALID_HASHVAL; // not use + chmpxidmap_t chmpxidmap; // for the prevention of duplicate registration + chmhashmap_t chmhashmap; // for the prevention of duplicate registration + + for(long cnt = 0; cnt < (basic_type::pAbsPtr->replica_count + 1); cnt++){ + chmpxlap svrchmpx; + + target_hash = common_hash + static_cast(cnt); + if(static_cast(basic_type::pAbsPtr->chmpx_bhash_count) <= target_hash){ + target_hash = target_hash % static_cast(basic_type::pAbsPtr->chmpx_bhash_count); + } + + if(abs_base_arr[target_hash]){ + svrchmpx.Reset(abs_base_arr[target_hash], abs_base_arr, abs_pend_arr, AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + }else{ + // Somthing wrong, but try to search liner.(not good performace) + WAN_CHMPRN("There is no chmpxid in base hashed array for hash(0x%016" PRIx64 ") - base hash(0x%016" PRIx64 "), but retry to do by liner searching.", hash, target_hash); + + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, abs_base_arr, abs_pend_arr, AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!svrchmpxlist.FindByHash(target_hash, true)){ + ERR_CHMPRN("There is no chmpxid in base hashed array and list for hash(0x%016" PRIx64 ") - base hash(0x%016" PRIx64 ").", hash, target_hash); + continue; + } + svrchmpx.Reset(svrchmpxlist.GetAbsChmpxPtr(), abs_base_arr, abs_pend_arr, AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + } + + chmpxid_t tmpchmpxid; + if(CHM_INVALID_CHMPXID == (tmpchmpxid = svrchmpx.GetChmpxId())){ + ERR_CHMPRN("Base hash(0x%016" PRIx64 ") object does not have chmpxid(not initialized).", target_hash); + continue; + } + chmpxsts_t status = svrchmpx.GetStatus(); + if(without_down && IS_CHMPXSTS_DOWN(status)){ + MSG_CHMPRN("Base hash(0x%016" PRIx64 ") object is chmpxid(0x%016" PRIx64 "), but this server is DOWN.", target_hash, tmpchmpxid); + continue; + } + if(without_suspend && IS_CHMPXSTS_SUSPEND(status)){ + MSG_CHMPRN("Base hash(0x%016" PRIx64 ") object is chmpxid(0x%016" PRIx64 "), but this server is SUSPEND.", target_hash, tmpchmpxid); + continue; + } + if(!svrchmpx.Get(target_base_hash, target_pending_hash)){ + ERR_CHMPRN("Failed to get base and pending hash value for Base hash(0x%016" PRIx64 ") object.", target_hash); + continue; + } + + // check duplicate registration + if(chmpxidmap.end() != chmpxidmap.find(tmpchmpxid)){ + //MSG_CHMPRN("chmpx(0x%016" PRIx64 ") is already registered.", tmpchmpxid); + continue; + } + last_chmpxid = tmpchmpxid; + chmpxidmap[tmpchmpxid] = true; + chmpxids.push_back(tmpchmpxid); + + // check invalid hash and check duplicate registration + if(CHM_INVALID_HASHVAL == target_base_hash || chmhashmap.end() != chmhashmap.find(target_base_hash)){ + //MSG_CHMPRN("chmhash(0x%016" PRIx64 ") is already registered.", target_base_hash); + continue; + } + chmhashmap[target_base_hash] = true; + basehashs.push_back(target_base_hash); + } + if(CHM_INVALID_CHMPXID != last_chmpxid){ + basic_type::pAbsPtr->last_chmpxid = last_chmpxid; // for random + } + + // If operating, adding other chmpxids + if(with_pending && chmpxman_lap::IsOperating()){ + // update common_hash for pending hash count + { + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + chmhash_t chmpx_phash_count = static_cast(svrchmpxlist.PendingHashCount()); // svrchmpxlist is top of list + common_hash = hash % chmpx_phash_count; + } + for(long cnt = 0; cnt < (basic_type::pAbsPtr->replica_count + 1); cnt++){ + chmpxlap svrchmpx; + + target_hash = common_hash + static_cast(cnt); + if(static_cast(basic_type::pAbsPtr->chmpx_bhash_count) <= target_hash){ + target_hash = target_hash % static_cast(basic_type::pAbsPtr->chmpx_bhash_count); + } + + if(abs_pend_arr[target_hash]){ + svrchmpx.Reset(abs_pend_arr[target_hash], abs_base_arr, abs_pend_arr, AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + }else{ + // Somthing wrong, but try to search liner.(not good performace) + WAN_CHMPRN("There is no chmpxid in pending hashed array for hash(0x%016" PRIx64 ") - pending hash(0x%016" PRIx64 "), but retry to do by liner searching.", hash, target_hash); + + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, abs_base_arr, abs_pend_arr, AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!svrchmpxlist.FindByHash(target_hash, false)){ + ERR_CHMPRN("There is no chmpxid in pending hashed array and list for hash(0x%016" PRIx64 ") - pending hash(0x%016" PRIx64 ").", hash, target_hash); + continue; + } + svrchmpx.Reset(svrchmpxlist.GetAbsChmpxPtr(), abs_base_arr, abs_pend_arr, AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + } + + chmpxid_t tmpchmpxid; + if(CHM_INVALID_CHMPXID == (tmpchmpxid = svrchmpx.GetChmpxId())){ + ERR_CHMPRN("Pending hash(0x%016" PRIx64 ") object does not have chmpxid(not initialized).", target_hash); + continue; + } + chmpxsts_t status = svrchmpx.GetStatus(); + if(without_down && IS_CHMPXSTS_DOWN(status)){ + MSG_CHMPRN("Pending hash(0x%016" PRIx64 ") object is chmpxid(0x%016" PRIx64 "), but this server is DOWN.", target_hash, tmpchmpxid); + continue; + } + if(without_suspend && IS_CHMPXSTS_SUSPEND(status)){ + MSG_CHMPRN("Pending hash(0x%016" PRIx64 ") object is chmpxid(0x%016" PRIx64 "), but this server is SUSPEND.", target_hash, tmpchmpxid); + continue; + } + if(!svrchmpx.Get(target_base_hash, target_pending_hash)){ + ERR_CHMPRN("Failed to get base and pending hash value for Base hash(0x%016" PRIx64 ") object.", target_hash); + continue; + } + + // check duplicate registration + if(chmpxidmap.end() != chmpxidmap.find(tmpchmpxid)){ + //MSG_CHMPRN("chmpx(0x%016" PRIx64 ") is already registered.", tmpchmpxid); + continue; + } + chmpxidmap[tmpchmpxid] = true; + chmpxids.push_back(tmpchmpxid); + + // check invalid hash and check duplicate registration + if(CHM_INVALID_HASHVAL == target_base_hash || chmhashmap.end() != chmhashmap.find(target_base_hash)){ + //MSG_CHMPRN("chmhash(0x%016" PRIx64 ") is already registered.", target_base_hash); + continue; + } + chmhashmap[target_base_hash] = true; + basehashs.push_back(target_base_hash); + } + } + return (0 != chmpxids.size()); +} + +// +// Return true / false(no target) +// basehashs Set all chmhash( for replication by manual ) +// If operating and with_pending is ture, this value includes other chmpx's chmhash. +// +template +bool chmpxman_lap::GetServerChmHashsByHashs(chmhash_t hash, chmhashlist_t& basehashs, bool with_pending, bool without_down, bool without_suspend) +{ + chmpxidlist_t chmpxids; // not use + return GetServerChmpxIdAndBaseHashByHashs(hash, chmpxids, basehashs, with_pending, without_down, without_suspend); +} + +// +// Return true / false(no target) +// chmpxids Set all chmpxid( for replication ) +// If operating and with_pending is ture, this value includes other chmpxid by pending hash. +// +template +bool chmpxman_lap::GetServerChmpxIdByHashs(chmhash_t hash, chmpxidlist_t& chmpxids, bool with_pending, bool without_down, bool without_suspend) +{ + chmhashlist_t basehashs; // not use + return GetServerChmpxIdAndBaseHashByHashs(hash, chmpxids, basehashs, with_pending, without_down, without_suspend); +} + +template +long chmpxman_lap::GetServerChmpxIds(chmpxidlist_t& list, bool with_pending, bool without_suspend, bool without_down) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return 0L; + } + if(!basic_type::pAbsPtr->chmpx_servers){ + return 0L; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + return svrchmpxlist.GetChmpxIds(list, with_pending, without_suspend, without_down); +} + +template +bool chmpxman_lap::GetServerBase(chmpxpos_t pos, std::string& name, chmpxid_t& chmpxid, short& port, short& ctlport) const +{ + if(CHM_INVALID_CHMPXLISTPOS == pos){ + ERR_CHMPRN("pos is invalid."); + return false; + } + PCHMPXLIST pchmpxlist = reinterpret_cast(pos); + chmpxlistlap svrchmpxlist(pchmpxlist, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return svrchmpx.Get(name, chmpxid, port, ctlport); +} + +template +bool chmpxman_lap::GetServerBase(chmpxid_t chmpxid, std::string& name, short& port, short& ctlport) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + if(!basic_type::pAbsPtr->chmpx_servers){ + return false; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!svrchmpxlist.Find(chmpxid)){ + ERR_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return svrchmpx.Get(name, chmpxid, port, ctlport); +} + +template +bool chmpxman_lap::GetServerBase(chmpxid_t chmpxid, CHMPXSSL& ssl) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + if(!basic_type::pAbsPtr->chmpx_servers){ + return false; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!svrchmpxlist.Find(chmpxid)){ + ERR_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return svrchmpx.GetSslStructure(ssl); +} + +template +bool chmpxman_lap::GetServerSocks(chmpxid_t chmpxid, socklist_t& socklist, int& ctlsock) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + if(!basic_type::pAbsPtr->chmpx_servers){ + return false; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!svrchmpxlist.Find(chmpxid)){ + //MSG_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + int selfsock; + int selfctlsock; + return svrchmpx.Get(socklist, ctlsock, selfsock, selfctlsock); +} + +template +bool chmpxman_lap::GetServerHash(chmpxid_t chmpxid, chmhash_t& base, chmhash_t& pending) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + if(!basic_type::pAbsPtr->chmpx_servers){ + return false; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!svrchmpxlist.Find(chmpxid)){ + ERR_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return svrchmpx.Get(base, pending); +} + +template +bool chmpxman_lap::GetMaxHashCount(chmhash_t& basehash, chmhash_t& pengindhash) const +{ + basehash = 0; + pengindhash = 0; + + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + basehash = static_cast(basic_type::pAbsPtr->chmpx_bhash_count); + pengindhash = static_cast(svrchmpxlist.PendingHashCount()); // svrchmpxlist is top of list + return true; +} + +template +chmpxsts_t chmpxman_lap::GetServerStatus(chmpxid_t chmpxid) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return CHMPXSTS_SRVOUT_DOWN_NORMAL; + } + if(!basic_type::pAbsPtr->chmpx_servers){ + return false; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!svrchmpxlist.Find(chmpxid)){ + ERR_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return CHMPXSTS_SRVOUT_DOWN_NORMAL; + } + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return svrchmpx.GetStatus(); +} + +template +bool chmpxman_lap::GetSelfPorts(short& port, short& ctlport) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap selfchmpxlist(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // Get CHMPXLIST from Relative + chmpxlap selfchmpx(selfchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + std::string name; // tmp + chmpxid_t chmpxid; // tmp + return selfchmpx.Get(name, chmpxid, port, ctlport); +} + +template +bool chmpxman_lap::GetSelfSocks(int& sock, int& ctlsock) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap selfchmpxlist(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // Get CHMPXLIST from Relative + chmpxlap selfchmpx(selfchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + socklist_t tmpsocklist; + int tmpctlsock; + return selfchmpx.Get(tmpsocklist, tmpctlsock, sock, ctlsock); +} + +template +bool chmpxman_lap::GetSelfHash(chmhash_t& base, chmhash_t& pending) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap selfchmpxlist(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // Get CHMPXLIST from Relative + chmpxlap selfchmpx(selfchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return selfchmpx.Get(base, pending); +} + +template +chmpxsts_t chmpxman_lap::GetSelfStatus(void) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return CHMPXSTS_SLAVE_DOWN_NORMAL; + } + chmpxlistlap selfchmpxlist(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // Get CHMPXLIST from Relative + chmpxlap selfchmpx(selfchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return selfchmpx.GetStatus(); +} + +template +bool chmpxman_lap::GetSelfSsl(CHMPXSSL& ssl) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap selfchmpxlist(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // Get CHMPXLIST from Relative + chmpxlap selfchmpx(selfchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return selfchmpx.GetSslStructure(ssl); +} + +template +long chmpxman_lap::GetSlaveChmpxIds(chmpxidlist_t& list) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return 0L; + } + if(!basic_type::pAbsPtr->chmpx_slaves){ + return 0L; + } + chmpxlistlap slvchmpxlist(basic_type::pAbsPtr->chmpx_slaves, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + return slvchmpxlist.GetChmpxIds(list); // All +} + +template +bool chmpxman_lap::GetSlaveBase(chmpxid_t chmpxid, std::string& name, short& ctlport) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + if(!basic_type::pAbsPtr->chmpx_slaves){ + return false; + } + chmpxlistlap slvchmpxlist(basic_type::pAbsPtr->chmpx_slaves, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!slvchmpxlist.Find(chmpxid)){ + ERR_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + chmpxlap slvchmpx(slvchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + short port; + return slvchmpx.Get(name, chmpxid, port, ctlport); +} + +template +bool chmpxman_lap::GetSlaveSock(chmpxid_t chmpxid, socklist_t& socklist) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + if(!basic_type::pAbsPtr->chmpx_slaves){ + return false; + } + chmpxlistlap slvchmpxlist(basic_type::pAbsPtr->chmpx_slaves, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!slvchmpxlist.Find(chmpxid)){ + MSG_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + chmpxlap slvchmpx(slvchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + int ctlsock; + int selfsock; + int selfctlsock; + return slvchmpx.Get(socklist, ctlsock, selfsock, selfctlsock); +} + +template +chmpxsts_t chmpxman_lap::GetSlaveStatus(chmpxid_t chmpxid) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return CHMPXSTS_SLAVE_DOWN_NORMAL; + } + if(!basic_type::pAbsPtr->chmpx_slaves){ + return false; + } + chmpxlistlap slvchmpxlist(basic_type::pAbsPtr->chmpx_slaves, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!slvchmpxlist.Find(chmpxid)){ + ERR_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return CHMPXSTS_SLAVE_DOWN_NORMAL; + } + chmpxlap slvchmpx(slvchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return slvchmpx.GetStatus(); +} + +template +bool chmpxman_lap::SetServerSocks(chmpxid_t chmpxid, int sock, int ctlsock, int type) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + if(!basic_type::pAbsPtr->chmpx_servers){ + return false; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!svrchmpxlist.Find(chmpxid)){ + ERR_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return svrchmpx.Set(sock, ctlsock, CHM_INVALID_SOCK, CHM_INVALID_SOCK, (type & SOCKTG_BOTH)); +} + +template +bool chmpxman_lap::RemoveServerSock(chmpxid_t chmpxid, int sock) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + if(!basic_type::pAbsPtr->chmpx_servers){ + return false; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!svrchmpxlist.Find(chmpxid)){ + ERR_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return svrchmpx.Remove(sock); +} + +template +bool chmpxman_lap::SetServerHash(chmpxid_t chmpxid, chmhash_t base, chmhash_t pending, int type) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + + // If server mode & self chmpxid, set value to self chmpx + if(IsServerMode() && GetSelfChmpxId() == chmpxid){ + chmpxlistlap selfchmpxlist(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // Get CHMPXLIST from Relative + chmpxlap selfchmpx(selfchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + if(!selfchmpx.Set(base, pending, type)){ + ERR_CHMPRN("Could not set hash value to self."); + return false; + } + } + + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!svrchmpxlist.Find(chmpxid)){ + ERR_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + chmpxlap svrchmpx(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return svrchmpx.Set(base, pending, type); +} + +template +bool chmpxman_lap::SetServerStatus(chmpxid_t chmpxid, chmpxsts_t status, bool is_client_join) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + + chmpxlistlap svrchmpxlist; + chmpxlap svrchmpx; + + // check self status + if(IsServerMode() && GetSelfChmpxId() == chmpxid){ + svrchmpxlist.Reset(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // Get CHMPXLIST from Relative + svrchmpx.Reset(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + + // check self status changing + if(IS_CHMPXSTS_DOWN(status) && CHM_INVALID_SOCK != svrchmpx.GetSock(SOCKTG_SELFSOCK)){ + ERR_CHMPRN("Self new chmpx status(0x%016" PRIx64 ":%s) is wrong, because self server is up.", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + }else if(IS_CHMPXSTS_UP(status) && CHM_INVALID_SOCK == svrchmpx.GetSock(SOCKTG_SELFSOCK)){ + WAN_CHMPRN("Self new chmpx status(0x%016" PRIx64 ":%s) is wrong, because self server is down.", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + // Check suspend flag + CHANGE_CHMPXSTS_SUSPEND_FLAG(status, !is_client_join); + + }else{ + svrchmpxlist.Reset(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!svrchmpxlist.Find(chmpxid)){ + ERR_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + svrchmpx.Reset(svrchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + } + + // set status + if(!svrchmpx.SetStatus(status)){ + return false; + } + + // set base hash server count + basic_type::pAbsPtr->chmpx_bhash_count = svrchmpxlist.BaseHashCount(true); + + // operating flag + if(basic_type::pAbsPtr->is_operating && !IS_CHMPXSTS_OPERATING(status)){ + // check all server status. + basic_type::pAbsPtr->is_operating = svrchmpxlist.IsOperating(true); + }else if(!basic_type::pAbsPtr->is_operating && IS_CHMPXSTS_OPERATING(status)){ + basic_type::pAbsPtr->is_operating = true; + }else{ + // nothing to do because not changed. + } + return true; +} + +template +bool chmpxman_lap::IsOperating(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + return basic_type::pAbsPtr->is_operating; +} + +// +// Calculate and Update all server pending hash. Servers which are set pending hash must be +// SERVICE ON/OUT on RING. Thus the server which is DOWN and SERVICE OUT is not a target for +// calicurating hash. +// +template +bool chmpxman_lap::UpdatePendingHash(bool is_allow_operating) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + if(!IsServerMode()){ + ERR_CHMPRN("Updating pengind hash must be on Server mode."); + return false; + } + if(!is_allow_operating && basic_type::pAbsPtr->is_operating){ + ERR_CHMPRN("Failed to request updateing pending hash, but blocks it because of operating now."); + return false; + } + chmpxlistlap svrchmpxlist(basic_type::pAbsPtr->chmpx_servers, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!svrchmpxlist.SetPendingHash(false)){ + ERR_CHMPRN("Failed to set pending hash value to servers."); + return false; + } + return true; +} + +template +bool chmpxman_lap::SetSelfSocks(int sock, int ctlsock) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap selfchmpxlist(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // Get CHMPXLIST from Relative + chmpxlap selfchmpx(selfchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return selfchmpx.Set(CHM_INVALID_SOCK, CHM_INVALID_SOCK, sock, ctlsock, SOCKTG_BOTHSELF); +} + +template +bool chmpxman_lap::SetSelfHash(chmhash_t base, chmhash_t pending, int type) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap selfchmpxlist(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // Get CHMPXLIST from Relative + chmpxlap selfchmpx(selfchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + if(!selfchmpx.Set(base, pending, type)){ + ERR_CHMPRN("Could not set hash value to self."); + return false; + } + return true; +} + +template +bool chmpxman_lap::SetSelfStatus(chmpxsts_t status, bool is_client_join) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap selfchmpxlist(basic_type::pAbsPtr->chmpx_self, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // Get CHMPXLIST from Relative + chmpxlap selfchmpx(selfchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + + // check status + if(IsServerMode()){ + // check self status changing + if(IS_CHMPXSTS_UP(status) && CHM_INVALID_SOCK == selfchmpx.GetSock(SOCKTG_SELFSOCK)){ + WAN_CHMPRN("Self new chmpx status(0x%016" PRIx64 ":%s) is wrong, because self server is down.", status, STR_CHMPXSTS_FULL(status).c_str()); + return false; + } + // Check suspend flag + CHANGE_CHMPXSTS_SUSPEND_FLAG(status, !is_client_join); + } + if(!selfchmpx.SetStatus(status)){ + ERR_CHMPRN("Could not set status value to self."); + return false; + } + + if(IsServerMode()){ + // set base hash server count + basic_type::pAbsPtr->chmpx_bhash_count = selfchmpxlist.BaseHashCount(true); + + // operating flag + if(basic_type::pAbsPtr->is_operating && !IS_CHMPXSTS_OPERATING(status)){ + // check all server status. + basic_type::pAbsPtr->is_operating = selfchmpxlist.IsOperating(true); + }else if(!basic_type::pAbsPtr->is_operating && IS_CHMPXSTS_OPERATING(status)){ + basic_type::pAbsPtr->is_operating = true; + }else{ + // nothing to do because not changed. + } + } + return true; +} + +template +bool chmpxman_lap::SetSlaveBase(chmpxid_t chmpxid, const char* hostname, short ctlport, const PCHMPXSSL pssl) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + if(CHMEMPTYSTR(hostname) || !pssl || chmpxid != MakeChmpxId(basic_type::pAbsPtr->group, hostname, ctlport)){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + + bool is_new = true; + chmpxlistlap slvchmpxlist; + if(basic_type::pAbsPtr->chmpx_slaves){ + slvchmpxlist.Reset(basic_type::pAbsPtr->chmpx_slaves, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(slvchmpxlist.Find(chmpxid)){ + is_new = false; + } + } + if(is_new){ + // add new slave chmpx + if(basic_type::pAbsPtr->chmpx_free_count <= 0L){ + ERR_CHMPRN("No more free CHMPXLIST."); + return false; + } + // Get one PCHMPXLIST for self from front of free list + chmpxlistlap freechmpxlist(basic_type::pAbsPtr->chmpx_frees, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From Relative + chmpxlistlap newchmpxlist(freechmpxlist.PopAny(), basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get one CHMPXLIST from Absolute + chmpxlap newchmpx(newchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + + basic_type::pAbsPtr->chmpx_free_count--; + basic_type::pAbsPtr->chmpx_frees = freechmpxlist.GetFirstPtr(false); + + // Add + newchmpx.InitializeSlave(hostname, basic_type::pAbsPtr->group, ctlport, *pssl); + if(basic_type::pAbsPtr->chmpx_slaves){ + slvchmpxlist.Insert(newchmpxlist.GetAbsPtr(), true); // From abs + basic_type::pAbsPtr->chmpx_slave_count++; + basic_type::pAbsPtr->chmpx_slaves = slvchmpxlist.GetFirstPtr(false); + }else{ + newchmpxlist.SaveChmpxIdMap(); // Set chmpxid map + basic_type::pAbsPtr->chmpx_slave_count = 1; + basic_type::pAbsPtr->chmpx_slaves = newchmpxlist.GetRelPtr(); + } + }else{ + // overwrite slave chmpx + chmpxlap slvchmpx(slvchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + slvchmpx.InitializeSlave(hostname, basic_type::pAbsPtr->group, ctlport, *pssl); + } + return true; +} + +template +bool chmpxman_lap::SetSlaveSock(chmpxid_t chmpxid, int sock) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + if(CHM_INVALID_SOCK == sock){ + ERR_CHMPRN("sock is invalid."); + return false; + } + chmpxlistlap slvchmpxlist(basic_type::pAbsPtr->chmpx_slaves, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!slvchmpxlist.Find(chmpxid)){ + ERR_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + chmpxlap slvchmpx(slvchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + return slvchmpx.Set(sock, CHM_INVALID_SOCK, CHM_INVALID_SOCK, CHM_INVALID_SOCK, SOCKTG_SOCK); +} + +template +bool chmpxman_lap::RemoveSlaveSock(chmpxid_t chmpxid, int sock) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap slvchmpxlist(basic_type::pAbsPtr->chmpx_slaves, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!slvchmpxlist.Find(chmpxid)){ + ERR_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + chmpxlap slvchmpx(slvchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + + return slvchmpx.Remove(sock); +} + +template +bool chmpxman_lap::SetSlaveStatus(chmpxid_t chmpxid, chmpxsts_t status) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + chmpxlistlap slvchmpxlist(basic_type::pAbsPtr->chmpx_slaves, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!slvchmpxlist.Find(chmpxid)){ + ERR_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return false; + } + chmpxlap slvchmpx(slvchmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + + // Check suspend flag + if(IS_CHMPXSTS_SUSPEND(status)){ + WAN_CHMPRN("Slave new chmpx status(0x%016" PRIx64 ":%s) is wrong, because slave server could not have SUSPEND status. so remove SUSPEND flag.", status, STR_CHMPXSTS_FULL(status).c_str()); + SET_CHMPXSTS_NOSUP(status); + } + return slvchmpx.SetStatus(status); +} + +template +bool chmpxman_lap::RemoveSlave(chmpxid_t chmpxid, int eqfd) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + // Find + chmpxlistlap slvchmpxlist(basic_type::pAbsPtr->chmpx_slaves, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From rel + if(!slvchmpxlist.Find(chmpxid)){ + WAN_CHMPRN("Could not find chmpxid(0x%016" PRIx64 ").", chmpxid); + return true; + } + + // Remove from slave list + PCHMPXLIST retrivelist; + if(NULL == (retrivelist = slvchmpxlist.Retrive())){ + // Failed to remove it + ERR_CHMPRN("Failed to remove CHMPX(0x%016" PRIx64 ") from CHMPXSLV.", chmpxid); + return false; + } + + // Rechain slave list + // + // Be careful about slvchmpxlist after Retrive(). + // If there is no object in list, this list is invalid. + // + if(slvchmpxlist.GetAbsPtr()){ + basic_type::pAbsPtr->chmpx_slave_count--; + basic_type::pAbsPtr->chmpx_slaves = slvchmpxlist.GetFirstPtr(false); + }else{ + basic_type::pAbsPtr->chmpx_slave_count = 0; + basic_type::pAbsPtr->chmpx_slaves = NULL; + } + + // If has opened socket, close it. + chmpxlistlap retrivechmpxlist(retrivelist, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, true); // From Abs + chmpxlap retrivechmpx(retrivechmpxlist.GetAbsChmpxPtr(), AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase); // Get CHMPX from Absolute + retrivechmpxlist.Clear(eqfd); + + // Add free list + chmpxlistlap freechmpxlist(basic_type::pAbsPtr->chmpx_frees, basic_type::pAbsPtr->chmpxid_map, AbsBaseArr(), AbsPendArr(), AbsSockFreeCnt(), AbsSockFrees(), basic_type::pShmBase, false); // From Relative(allow NULL) + if(!freechmpxlist.Push(retrivelist, true)){ + WAN_CHMPRN("Failed to push back new free CHMPXLIST to freelist, but continue..."); + }else{ + basic_type::pAbsPtr->chmpx_free_count++; + basic_type::pAbsPtr->chmpx_frees = freechmpxlist.GetFirstPtr(false); + } + return true; +} + +template +bool chmpxman_lap::AddStat(chmpxid_t chmpxid, bool is_sent, size_t length, const struct timespec& elapsed_time) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + if(CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + + chmstatlap statlap; + if(IsServerMode(chmpxid)){ + statlap.Reset(&(basic_type::pAbsPtr->server_stat), basic_type::pShmBase, true); // From abs + }else{ + statlap.Reset(&(basic_type::pAbsPtr->slave_stat), basic_type::pShmBase, true); // From abs + } + return statlap.Add(is_sent, length, elapsed_time); +} + +template +bool chmpxman_lap::GetStat(PCHMSTAT pserver, PCHMSTAT pslave) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMPXMAN does not set."); + return false; + } + if(!pserver && !pslave){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + + chmstatlap statlap; + if(pserver){ + statlap.Reset(&(basic_type::pAbsPtr->server_stat), basic_type::pShmBase, true); // From abs + if(!statlap.Get(pserver)){ + ERR_CHMPRN("Could not get server stat."); + return false; + } + } + if(pslave){ + statlap.Reset(&(basic_type::pAbsPtr->slave_stat), basic_type::pShmBase, true); // From abs + if(!statlap.Get(pslave)){ + ERR_CHMPRN("Could not get slave stat."); + return false; + } + } + return true; +} + +typedef chmpxman_lap chmpxmanlap; + +//--------------------------------------------------------- +// For CHMINFO +//--------------------------------------------------------- +template +class chminfo_lap : public structure_lap +{ + public: + typedef T st_type; + typedef T* st_ptr_type; + typedef structure_lap basic_type; + + protected: + bool SetMqFlagStatus(msgid_t msgid, bool is_assigned, bool is_activated); + bool SetMergingClinetPid(pid_t pid); + + public: + chminfo_lap(st_ptr_type ptr = NULL, const void* shmbase = NULL, bool is_abs = true); + + virtual bool Initialize(void); + virtual bool Dump(std::stringstream& sstream, const char* spacer) const; + virtual bool Clear(void); + st_ptr_type Dup(void); + chmpxlap::st_ptr_type DupSelfChmpxSvr(void); + void Free(st_ptr_type ptr) const; + + bool Close(int eqfd, int type = CLOSETG_BOTH); + bool Initialize(const CHMCFGINFO* pchmcfg, PMQMSGHEADLIST rel_chmpxmsgarea, const CHMNODE_CFGINFO* pselfnode, PCHMPXLIST relchmpxlist, PCLTPROCLIST relcltproclist, PCHMSOCKLIST relchmsockarea, PCHMPX* pchmpxarrbase, PCHMPX* pchmpxarrpend); + bool ReloadConfigration(const CHMCFGINFO* pchmcfg); + + msgid_t GetBaseMsgId(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->base_msgid : 0L ); } + msgid_t GetRandomMsgId(bool is_chmpx, bool is_activated = true); // get msgid from assigned list + msgid_t AssignMsg(bool is_chmpx, bool is_activated); // Assign msgid + bool SetMqActivated(msgid_t msgid) { return SetMqFlagStatus(msgid, true, true); } // change msgid status to activate + bool SetMqDisactivated(msgid_t msgid) { return SetMqFlagStatus(msgid, true, false); } // change msgid status to disactivate + bool FreeMsg(msgid_t msgid); // Free msgid + bool IsMsgidActivated(msgid_t msgid) const; + bool GetMsgidListByPid(pid_t pid, msgidlist_t& list, bool is_clear_list = false); // Get msgid list by PID(from assigned and activated list) + bool GetMsgidListByUniqPid(msgidlist_t& list, bool is_clear_list = false); // Get msgid list for all uniq PID(from activated list) + + pid_t GetChmpxSvrPid(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->pid : CHM_INVALID_PID); } + pid_t* GetChmpxSvrPidAddr(bool is_abs) { return (!basic_type::pAbsPtr ? NULL : is_abs ? &(basic_type::pAbsPtr->pid) : CHM_REL(basic_type::pShmBase, &(basic_type::pAbsPtr->pid), pid_t*)); } + void* GetClientPidListOffset(void) { return (!basic_type::pAbsPtr ? NULL : CHM_REL(basic_type::pShmBase, &(basic_type::pAbsPtr->client_pids), void*)); } + + bool GetSelfChmpxSvr(PCHMPXSVR chmpxsvr) const; + bool GetChmpxSvr(chmpxid_t chmpxid, PCHMPXSVR chmpxsvr) const; + bool GetChmpxSvrs(PCHMPXSVR* ppchmpxsvrs, long& count) const; + bool MergeChmpxSvrs(PCHMPXSVR pchmpxsvrs, long count, bool is_remove = true, bool is_init_process = false, int eqfd = CHM_INVALID_HANDLE); + + bool GetGroup(std::string& group) const; + bool IsRandomDeliver(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->is_random_deliver : false); } + bool IsAutoMergeConf(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->is_auto_merge : false); } + bool IsDoMergeConf(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->is_do_merge : false); } + int GetSocketThreadCount(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->evsock_thread_cnt : 0); } + int GetMQThreadCount(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->evmq_thread_cnt : 0); } + long GetMaxMQCount(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->max_mqueue : -1); } + long GetChmpxMQCount(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->chmpx_mqueue : -1); } + long GetMaxQueuePerChmpxMQ(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->max_q_per_chmpxmq : -1); } + long GetMaxQueuePerClientMQ(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->max_q_per_cltmq : -1); } + long GetMQPerClient(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->max_mq_per_client : -1); } + long GetMQPerAttach(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->mq_per_attach : -1); } + bool IsAckMQ(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->mq_ack : true); } + int GetMaxSockPool(void) const { return (basic_type::pAbsPtr ? (0 < basic_type::pAbsPtr->max_sock_pool ? basic_type::pAbsPtr->max_sock_pool : 1) : 1); } + time_t GetSockPoolTimeout(void) const { return (basic_type::pAbsPtr ? (0 <= basic_type::pAbsPtr->sock_pool_timeout ? basic_type::pAbsPtr->sock_pool_timeout : 0) : 0); } + int GetSockRetryCnt(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->sock_retrycnt : -1); } + suseconds_t GetSockTimeout(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->timeout_wait_socket : 0); } + suseconds_t GetConnectTimeout(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->timeout_wait_connect : 0); } + int GetMQRetryCnt(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->mq_retrycnt : -1); } + long GetMQTimeout(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->timeout_wait_mq : 0); } + time_t GetMergeTimeout(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->timeout_merge : 0); } + bool IsServerMode(void) const; + bool IsSlaveMode(void) const; + long GetReplicaCount(void) const; + chmpxid_t GetSelfChmpxId(void) const; + chmpxid_t GetNextRingChmpxId(chmpxid_t chmpxid = CHM_INVALID_CHMPXID) const; + + long GetServerCount(void) const; + long GetSlaveCount(void) const; + long GetUpServerCount(void) const; + chmpxpos_t GetSelfServerPos(void) const; + chmpxpos_t GetNextServerPos(chmpxpos_t startpos, chmpxpos_t nowpos, bool is_skip_self, bool is_cycle) const; + bool GetAllServerName(hnamesslmap_t& info) const; + + bool IsOperating(void); + bool IsServerChmpxId(chmpxid_t chmpxid) const; + chmpxid_t GetChmpxIdBySock(int sock, int type = CLOSETG_BOTH) const; + chmpxid_t GetChmpxIdByToServerName(const char* hostname, short ctlport) const; + chmpxid_t GetChmpxIdByStatus(chmpxsts_t status, bool part_match = false) const; + chmpxid_t GetRandomServerChmpxId(bool is_up_servers = false, bool without_suspend = false); + chmpxid_t GetServerChmpxIdByHash(chmhash_t hash) const; + bool GetServerChmHashsByHashs(chmhash_t hash, chmhashlist_t& basehashs, bool with_pending, bool without_down = true, bool without_suspend = true); + bool GetServerChmpxIdByHashs(chmhash_t hash, chmpxidlist_t& chmpxids, bool with_pending, bool without_down = true, bool without_suspend = true); + long GetServerChmpxIds(chmpxidlist_t& list, bool with_pending, bool without_down = true, bool without_suspend = true) const; + bool GetServerBase(chmpxpos_t pos, std::string& name, chmpxid_t& chmpxid, short& port, short& ctlport) const; + bool GetServerBase(chmpxid_t chmpxid, std::string& name, short& port, short& ctlport) const; + bool GetServerBase(chmpxid_t chmpxid, CHMPXSSL& ssl) const; + bool GetServerSocks(chmpxid_t chmpxid, socklist_t& socklist, int& ctlsock) const; + bool GetServerHash(chmpxid_t chmpxid, chmhash_t& base, chmhash_t& pending) const; + bool GetMaxHashCount(chmhash_t& basehash, chmhash_t& pengindhash) const; + chmpxsts_t GetServerStatus(chmpxid_t chmpxid) const; + bool GetSelfPorts(short& port, short& ctlport) const; + bool GetSelfSocks(int& sock, int& ctlsock) const; + bool GetSelfHash(chmhash_t& base, chmhash_t& pending) const; + chmpxsts_t GetSelfStatus(void) const; + bool GetSelfSsl(CHMPXSSL& ssl) const; + long GetSlaveChmpxIds(chmpxidlist_t& list) const; + bool GetSlaveBase(chmpxid_t chmpxid, std::string& name, short& ctlport) const; + bool GetSlaveSock(chmpxid_t chmpxid, socklist_t& socklist) const; + chmpxsts_t GetSlaveStatus(chmpxid_t chmpxid) const; + + bool SetServerSocks(chmpxid_t chmpxid, int sock, int ctlsock, int type); + bool SetServerHash(chmpxid_t chmpxid, chmhash_t base, chmhash_t pending, int type); + bool SetServerStatus(chmpxid_t chmpxid, chmpxsts_t status); + bool RemoveServerSock(chmpxid_t chmpxid, int sock); + bool UpdatePendingHash(bool is_allow_operating = true); + bool SetSelfSocks(int sock, int ctlsock); + bool SetSelfHash(chmhash_t base, chmhash_t pending, int type); + bool SetSelfStatus(chmpxsts_t status); + bool SetSlaveBase(chmpxid_t chmpxid, const char* hostname, short ctlport, const PCHMPXSSL pssl); + bool SetSlaveSock(chmpxid_t chmpxid, int sock); + bool SetSlaveStatus(chmpxid_t chmpxid, chmpxsts_t status); + bool RemoveSlaveSock(chmpxid_t chmpxid, int sock, bool is_remove_empty = true); + bool RemoveSlave(chmpxid_t chmpxid, int eqfd); + + bool AddStat(chmpxid_t chmpxid, bool is_sent, size_t length, const struct timespec& elapsed_time); + bool GetStat(PCHMSTAT pserver, PCHMSTAT pslave) const; + + bool RetriveClientPid(pid_t pid); + bool AddClientPid(pid_t pid); + bool GetAllPids(pidlist_t& list); + bool IsClientPids(void) const; + + long GetMaxHistoryLogCount(void) const { return (basic_type::pAbsPtr ? basic_type::pAbsPtr->histlog_count : -1); } +}; + +template +chminfo_lap::chminfo_lap(st_ptr_type ptr, const void* shmbase, bool is_abs) +{ + structure_lap::Reset(ptr, shmbase, is_abs); +} + +template +bool chminfo_lap::Initialize(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + basic_type::pAbsPtr->pid = CHM_INVALID_PID; + basic_type::pAbsPtr->start_time = 0; + basic_type::pAbsPtr->is_random_deliver = false; + basic_type::pAbsPtr->is_auto_merge = false; + basic_type::pAbsPtr->is_do_merge = false; + basic_type::pAbsPtr->evsock_thread_cnt = 0; + basic_type::pAbsPtr->evmq_thread_cnt = 0; + basic_type::pAbsPtr->max_mqueue = 0L; + basic_type::pAbsPtr->chmpx_mqueue = 0L; + basic_type::pAbsPtr->max_q_per_chmpxmq = 0L; + basic_type::pAbsPtr->max_q_per_cltmq = 0L; + basic_type::pAbsPtr->max_mq_per_client = 0L; + basic_type::pAbsPtr->mq_per_attach = 0L; + basic_type::pAbsPtr->mq_ack = true; + basic_type::pAbsPtr->max_sock_pool = 0; + basic_type::pAbsPtr->sock_pool_timeout = 0; + basic_type::pAbsPtr->sock_retrycnt = 0; + basic_type::pAbsPtr->timeout_wait_socket = 0L; + basic_type::pAbsPtr->timeout_wait_connect = 0L; + basic_type::pAbsPtr->mq_retrycnt = 0; + basic_type::pAbsPtr->timeout_wait_mq = 0; + basic_type::pAbsPtr->timeout_merge = 0; + basic_type::pAbsPtr->base_msgid = 0L; + basic_type::pAbsPtr->chmpx_msg_count = 0L; + basic_type::pAbsPtr->chmpx_msgs = NULL; + basic_type::pAbsPtr->activated_msg_count = 0L; + basic_type::pAbsPtr->activated_msgs = NULL; + basic_type::pAbsPtr->assigned_msg_count = 0L; + basic_type::pAbsPtr->assigned_msgs = NULL; + basic_type::pAbsPtr->free_msg_count = 0L; + basic_type::pAbsPtr->free_msgs = NULL; + basic_type::pAbsPtr->last_msgid_chmpx = CHM_INVALID_MSGID; + basic_type::pAbsPtr->last_msgid_activated = CHM_INVALID_MSGID; + basic_type::pAbsPtr->last_msgid_assigned = CHM_INVALID_MSGID; + basic_type::pAbsPtr->rel_chmpxmsgarea = NULL; + basic_type::pAbsPtr->client_pids = NULL; + basic_type::pAbsPtr->free_pids = NULL; + basic_type::pAbsPtr->k2h_fullmap = true; + basic_type::pAbsPtr->k2h_mask_bitcnt = 0; + basic_type::pAbsPtr->k2h_cmask_bitcnt = 0; + basic_type::pAbsPtr->k2h_max_element = 0; + basic_type::pAbsPtr->histlog_count = 0L; + + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + if(!tmpchmpxman.Initialize()){ + ERR_CHMPRN("Failed to initialize CHMPXMAN."); + return false; + } + return true; +} + +template +bool chminfo_lap::Dump(std::stringstream& sstream, const char* spacer) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + std::string tmpspacer = spacer ? spacer : ""; + tmpspacer += " "; + + sstream << (spacer ? spacer : "") << "pid = " << basic_type::pAbsPtr->pid << std::endl; + sstream << (spacer ? spacer : "") << "start_time = " << basic_type::pAbsPtr->start_time << std::endl; + sstream << (spacer ? spacer : "") << "is_random_deliver = " << (basic_type::pAbsPtr->is_random_deliver ? "true" : "false") << std::endl; + sstream << (spacer ? spacer : "") << "automerge in conf = " << (basic_type::pAbsPtr->is_auto_merge ? "yes" : "no") << std::endl; + sstream << (spacer ? spacer : "") << "domerge in conf = " << (basic_type::pAbsPtr->is_do_merge ? "yes" : "no") << std::endl; + sstream << (spacer ? spacer : "") << "socket thread count = " << basic_type::pAbsPtr->evsock_thread_cnt << std::endl; + sstream << (spacer ? spacer : "") << "MQ thread count = " << basic_type::pAbsPtr->evmq_thread_cnt << std::endl; + sstream << (spacer ? spacer : "") << "max_mqueue = " << basic_type::pAbsPtr->max_mqueue << std::endl; + sstream << (spacer ? spacer : "") << "chmpx_mqueue = " << basic_type::pAbsPtr->chmpx_mqueue << std::endl; + sstream << (spacer ? spacer : "") << "max_q_per_chmpxmq = " << basic_type::pAbsPtr->max_q_per_chmpxmq << std::endl; + sstream << (spacer ? spacer : "") << "max_q_per_cltmq = " << basic_type::pAbsPtr->max_q_per_cltmq << std::endl; + sstream << (spacer ? spacer : "") << "max_mq_per_client = " << basic_type::pAbsPtr->max_mq_per_client << std::endl; + sstream << (spacer ? spacer : "") << "mq_per_attach = " << basic_type::pAbsPtr->mq_per_attach << std::endl; + sstream << (spacer ? spacer : "") << "mq_ack = " << (basic_type::pAbsPtr->mq_ack ? "true" : "false") << std::endl; + sstream << (spacer ? spacer : "") << "Max socket pool = " << basic_type::pAbsPtr->max_sock_pool << std::endl; + sstream << (spacer ? spacer : "") << "sock pool timeout = " << basic_type::pAbsPtr->sock_pool_timeout << std::endl; + sstream << (spacer ? spacer : "") << "sock retry count = " << basic_type::pAbsPtr->sock_retrycnt << std::endl; + sstream << (spacer ? spacer : "") << "sock timeout = " << basic_type::pAbsPtr->timeout_wait_socket << std::endl; + sstream << (spacer ? spacer : "") << "connect timeout = " << basic_type::pAbsPtr->timeout_wait_connect << std::endl; + sstream << (spacer ? spacer : "") << "MQ retry count = " << basic_type::pAbsPtr->mq_retrycnt << std::endl; + sstream << (spacer ? spacer : "") << "MQ timeout = " << basic_type::pAbsPtr->timeout_wait_mq << std::endl; + sstream << (spacer ? spacer : "") << "merge timeout = " << basic_type::pAbsPtr->timeout_merge << std::endl; + sstream << (spacer ? spacer : "") << "base_msgid = " << basic_type::pAbsPtr->base_msgid << std::endl; + sstream << (spacer ? spacer : "") << "chmpx_msg_count = " << basic_type::pAbsPtr->chmpx_msg_count << std::endl; + sstream << (spacer ? spacer : "") << "chmpx_msgs = " << basic_type::pAbsPtr->chmpx_msgs << std::endl; + + if(0L < basic_type::pAbsPtr->chmpx_msg_count){ + sstream << (spacer ? spacer : "") << "{" << std::endl; + mqmsgheadlistlap chmpx_msgs(basic_type::pAbsPtr->chmpx_msgs, basic_type::pShmBase, false); // From rel + chmpx_msgs.Dump(sstream, tmpspacer.c_str()); + sstream << (spacer ? spacer : "") << "}" << std::endl; + } + + sstream << (spacer ? spacer : "") << "activated_msg_count = " << basic_type::pAbsPtr->activated_msg_count << std::endl; + sstream << (spacer ? spacer : "") << "activated_msgs = " << basic_type::pAbsPtr->activated_msgs << std::endl; + + if(0L < basic_type::pAbsPtr->activated_msg_count){ + sstream << (spacer ? spacer : "") << "{" << std::endl; + mqmsgheadlistlap activated_msgs(basic_type::pAbsPtr->activated_msgs, basic_type::pShmBase, false); // From rel + activated_msgs.Dump(sstream, tmpspacer.c_str()); + sstream << (spacer ? spacer : "") << "}" << std::endl; + } + + sstream << (spacer ? spacer : "") << "assigned_msg_count = " << basic_type::pAbsPtr->assigned_msg_count << std::endl; + sstream << (spacer ? spacer : "") << "assigned_msgs = " << basic_type::pAbsPtr->assigned_msgs << std::endl; + + if(0L < basic_type::pAbsPtr->assigned_msg_count){ + sstream << (spacer ? spacer : "") << "{" << std::endl; + mqmsgheadlistlap assigned_msgs(basic_type::pAbsPtr->assigned_msgs, basic_type::pShmBase, false); // From rel + assigned_msgs.Dump(sstream, tmpspacer.c_str()); + sstream << (spacer ? spacer : "") << "}" << std::endl; + } + + sstream << (spacer ? spacer : "") << "free_msg_count = " << basic_type::pAbsPtr->free_msg_count << std::endl; + sstream << (spacer ? spacer : "") << "free_msgs = " << basic_type::pAbsPtr->free_msgs << std::endl; + sstream << (spacer ? spacer : "") << "last_msgid_chmpx = 0x" << to_hexstring(basic_type::pAbsPtr->last_msgid_chmpx) << std::endl; + sstream << (spacer ? spacer : "") << "last_msgid_activated = 0x" << to_hexstring(basic_type::pAbsPtr->last_msgid_activated) << std::endl; + sstream << (spacer ? spacer : "") << "last_msgid_assigned = 0x" << to_hexstring(basic_type::pAbsPtr->last_msgid_assigned) << std::endl; + + sstream << (spacer ? spacer : "") << "chmpx_man{" << std::endl; + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + tmpchmpxman.Dump(sstream, tmpspacer.c_str()); + sstream << (spacer ? spacer : "") << "}" << std::endl; + + sstream << (spacer ? spacer : "") << "rel_chmpxmsgarea = " << basic_type::pAbsPtr->rel_chmpxmsgarea << std::endl; + + sstream << (spacer ? spacer : "") << "client_pids = " << basic_type::pAbsPtr->client_pids << std::endl; + if(basic_type::pAbsPtr->client_pids){ + sstream << (spacer ? spacer : "") << "{" << std::endl; + cltproclistlap cltproc_pids(basic_type::pAbsPtr->client_pids, basic_type::pShmBase, false); // From rel + cltproc_pids.Dump(sstream, tmpspacer.c_str()); + sstream << (spacer ? spacer : "") << "}" << std::endl; + } + sstream << (spacer ? spacer : "") << "free_pids = " << basic_type::pAbsPtr->free_pids << std::endl; + + sstream << (spacer ? spacer : "") << "k2hash full map = " << (basic_type::pAbsPtr->k2h_fullmap ? "yes" : "no") << std::endl; + sstream << (spacer ? spacer : "") << "init k2h mask bit = " << basic_type::pAbsPtr->k2h_mask_bitcnt << std::endl; + sstream << (spacer ? spacer : "") << "init k2h cmask bit = " << basic_type::pAbsPtr->k2h_cmask_bitcnt << std::endl; + sstream << (spacer ? spacer : "") << "k2hash max element = " << basic_type::pAbsPtr->k2h_max_element << std::endl; + sstream << (spacer ? spacer : "") << "history log count = " << basic_type::pAbsPtr->histlog_count << std::endl; + + return true; +} + +template +typename chminfo_lap::st_ptr_type chminfo_lap::Dup(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return NULL; + } + + // duplicate + st_ptr_type pdst; + if(NULL == (pdst = reinterpret_cast(calloc(1, sizeof(st_type))))){ + ERR_CHMPRN("Could not allocation memory."); + return NULL; + } + pdst->pid = basic_type::pAbsPtr->pid; + pdst->start_time = basic_type::pAbsPtr->start_time; + pdst->evsock_thread_cnt = basic_type::pAbsPtr->evsock_thread_cnt; + pdst->evmq_thread_cnt = basic_type::pAbsPtr->evmq_thread_cnt; + pdst->is_random_deliver = basic_type::pAbsPtr->is_random_deliver; + pdst->is_auto_merge = basic_type::pAbsPtr->is_auto_merge; + pdst->is_do_merge = basic_type::pAbsPtr->is_do_merge; + pdst->max_mqueue = basic_type::pAbsPtr->max_mqueue; + pdst->chmpx_mqueue = basic_type::pAbsPtr->chmpx_mqueue; + pdst->max_q_per_chmpxmq = basic_type::pAbsPtr->max_q_per_chmpxmq; + pdst->max_q_per_cltmq = basic_type::pAbsPtr->max_q_per_cltmq; + pdst->max_mq_per_client = basic_type::pAbsPtr->max_mq_per_client; + pdst->mq_per_attach = basic_type::pAbsPtr->mq_per_attach; + pdst->mq_ack = basic_type::pAbsPtr->mq_ack; + pdst->max_sock_pool = basic_type::pAbsPtr->max_sock_pool; + pdst->sock_pool_timeout = basic_type::pAbsPtr->sock_pool_timeout; + pdst->sock_retrycnt = basic_type::pAbsPtr->sock_retrycnt; + pdst->timeout_wait_socket = basic_type::pAbsPtr->timeout_wait_socket; + pdst->timeout_wait_connect = basic_type::pAbsPtr->timeout_wait_connect; + pdst->mq_retrycnt = basic_type::pAbsPtr->mq_retrycnt; + pdst->timeout_wait_mq = basic_type::pAbsPtr->timeout_wait_mq; + pdst->timeout_merge = basic_type::pAbsPtr->timeout_merge; + pdst->base_msgid = basic_type::pAbsPtr->base_msgid; + pdst->chmpx_msg_count = basic_type::pAbsPtr->chmpx_msg_count; + pdst->activated_msg_count = basic_type::pAbsPtr->activated_msg_count; + pdst->assigned_msg_count = basic_type::pAbsPtr->assigned_msg_count; + pdst->free_msg_count = basic_type::pAbsPtr->free_msg_count; + pdst->free_msgs = NULL; // Allways NULL + pdst->last_msgid_chmpx = basic_type::pAbsPtr->last_msgid_chmpx; + pdst->last_msgid_activated = basic_type::pAbsPtr->last_msgid_activated; + pdst->last_msgid_assigned = basic_type::pAbsPtr->last_msgid_assigned; + pdst->rel_chmpxmsgarea = NULL; // Allways NULL + pdst->free_pids = NULL; // Allways NULL + pdst->k2h_fullmap = basic_type::pAbsPtr->k2h_fullmap; + pdst->k2h_mask_bitcnt = basic_type::pAbsPtr->k2h_mask_bitcnt; + pdst->k2h_cmask_bitcnt = basic_type::pAbsPtr->k2h_cmask_bitcnt; + pdst->k2h_max_element = basic_type::pAbsPtr->k2h_max_element; + pdst->histlog_count = basic_type::pAbsPtr->histlog_count; + + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + if(!tmpchmpxman.Copy(&pdst->chmpx_man)){ + WAN_CHMPRN("Failed to copy chmpx_man structure, but continue..."); + } + + mqmsgheadlistlap chmpx_msgs(basic_type::pAbsPtr->chmpx_msgs, basic_type::pShmBase, false); // From rel + pdst->chmpx_msgs = chmpx_msgs.Dup(); + + mqmsgheadlistlap activated_msgs(basic_type::pAbsPtr->activated_msgs, basic_type::pShmBase, false); // From rel + pdst->activated_msgs = activated_msgs.Dup(); + + mqmsgheadlistlap assigned_msgs(basic_type::pAbsPtr->assigned_msgs, basic_type::pShmBase, false); // From rel + pdst->assigned_msgs = assigned_msgs.Dup(); + + cltproclistlap cltproc_pids(basic_type::pAbsPtr->client_pids, basic_type::pShmBase, false); // From rel + pdst->client_pids = cltproc_pids.Dup(); + + return pdst; +} + +template +chmpxlap::st_ptr_type chminfo_lap::DupSelfChmpxSvr(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCLTPROCLIST does not set."); + return NULL; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.DupSelfChmpxSvr(); +} + +template +void chminfo_lap::Free(st_ptr_type ptr) const +{ + if(ptr){ + chmpxmanlap tmpchmpxman; // [NOTE] pointer is not on Shm, but we use chmpxmanlap object. thus using only Free method. + tmpchmpxman.Free(&ptr->chmpx_man); + + mqmsgheadlistlap tmpchmpxmsgs; // [NOTE] pointer is not on Shm, but we use mqmsgheadlistlap object. thus using only Free method. + tmpchmpxmsgs.Free(ptr->chmpx_msgs); + tmpchmpxmsgs.Free(ptr->activated_msgs); + tmpchmpxmsgs.Free(ptr->assigned_msgs); + + cltproclistlap tmpcltprocpids; // [NOTE] pointer is not on Shm, but we use cltproclistlap object. thus using only Free method. + tmpcltprocpids.Free(ptr->client_pids); + + K2H_Free(ptr); + } +} + +template +bool chminfo_lap::Initialize(const CHMCFGINFO* pchmcfg, PMQMSGHEADLIST rel_chmpxmsgarea, const CHMNODE_CFGINFO* pselfnode, PCHMPXLIST relchmpxlist, PCLTPROCLIST relcltproclist, PCHMSOCKLIST relchmsockarea, PCHMPX* pchmpxarrbase, PCHMPX* pchmpxarrpend) +{ + if(!pchmcfg || !rel_chmpxmsgarea || !relchmpxlist || !pselfnode || !relcltproclist || !relchmsockarea || !pchmpxarrbase || !pchmpxarrpend){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(MAX_MQUEUE_COUNT < (pchmcfg->max_server_mq_cnt + pchmcfg->max_client_mq_cnt)){ + ERR_CHMPRN("Configuration information are wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + + basic_type::pAbsPtr->pid = getpid(); + basic_type::pAbsPtr->start_time = time(NULL); + basic_type::pAbsPtr->is_random_deliver = pchmcfg->is_random_mode; + basic_type::pAbsPtr->is_auto_merge = pchmcfg->is_auto_merge; + basic_type::pAbsPtr->is_do_merge = pchmcfg->is_do_merge; + basic_type::pAbsPtr->evsock_thread_cnt = pchmcfg->sock_thread_cnt; + basic_type::pAbsPtr->evmq_thread_cnt = pchmcfg->mq_thread_cnt; + basic_type::pAbsPtr->max_mqueue = pchmcfg->max_server_mq_cnt + pchmcfg->max_client_mq_cnt; + basic_type::pAbsPtr->chmpx_mqueue = pchmcfg->max_server_mq_cnt; + basic_type::pAbsPtr->max_q_per_chmpxmq = pchmcfg->max_q_per_servermq; + basic_type::pAbsPtr->max_q_per_cltmq = pchmcfg->max_q_per_clientmq; + basic_type::pAbsPtr->max_mq_per_client = pchmcfg->max_mq_per_client; + basic_type::pAbsPtr->mq_per_attach = pchmcfg->mqcnt_per_attach; + basic_type::pAbsPtr->mq_ack = pchmcfg->mq_ack; + basic_type::pAbsPtr->max_sock_pool = pchmcfg->max_sock_pool; + basic_type::pAbsPtr->sock_pool_timeout = pchmcfg->sock_pool_timeout; + basic_type::pAbsPtr->sock_retrycnt = pchmcfg->retrycnt; + basic_type::pAbsPtr->timeout_wait_socket = static_cast(pchmcfg->timeout_wait_socket); + basic_type::pAbsPtr->timeout_wait_connect = static_cast(pchmcfg->timeout_wait_connect); + basic_type::pAbsPtr->mq_retrycnt = pchmcfg->mq_retrycnt; + basic_type::pAbsPtr->timeout_wait_mq = pchmcfg->timeout_wait_mq; + basic_type::pAbsPtr->timeout_merge = pchmcfg->timeout_merge; + basic_type::pAbsPtr->base_msgid = MakeBaseMsgId(pchmcfg->groupname.c_str(), pselfnode->name.c_str(), pselfnode->ctlport); + basic_type::pAbsPtr->chmpx_msg_count = 0L; + basic_type::pAbsPtr->chmpx_msgs = NULL; + basic_type::pAbsPtr->activated_msg_count = 0L; + basic_type::pAbsPtr->activated_msgs = NULL; + basic_type::pAbsPtr->assigned_msg_count = 0L; + basic_type::pAbsPtr->assigned_msgs = NULL; + basic_type::pAbsPtr->free_msg_count = pchmcfg->max_server_mq_cnt + pchmcfg->max_client_mq_cnt; + basic_type::pAbsPtr->free_msgs = rel_chmpxmsgarea; + basic_type::pAbsPtr->last_msgid_chmpx = CHM_INVALID_MSGID; + basic_type::pAbsPtr->last_msgid_activated = CHM_INVALID_MSGID; + basic_type::pAbsPtr->last_msgid_assigned = CHM_INVALID_MSGID; + basic_type::pAbsPtr->rel_chmpxmsgarea = rel_chmpxmsgarea; + basic_type::pAbsPtr->client_pids = NULL; + basic_type::pAbsPtr->free_pids = relcltproclist; + basic_type::pAbsPtr->k2h_fullmap = pchmcfg->k2h_fullmap; + basic_type::pAbsPtr->k2h_mask_bitcnt = pchmcfg->k2h_mask_bitcnt; + basic_type::pAbsPtr->k2h_cmask_bitcnt = pchmcfg->k2h_cmask_bitcnt; + basic_type::pAbsPtr->k2h_max_element = pchmcfg->k2h_max_element; + basic_type::pAbsPtr->histlog_count = pchmcfg->max_histlog_count; + + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + if(!tmpchmpxman.Initialize(pchmcfg, pselfnode, relchmpxlist, relchmsockarea, pchmpxarrbase, pchmpxarrpend)){ + ERR_CHMPRN("Failed to initialize CHMPXMAN."); + return false; + } + + // Fill msgid + mqmsgheadarrlap mqmsgheadarr(basic_type::pAbsPtr->rel_chmpxmsgarea, basic_type::pShmBase, basic_type::pAbsPtr->max_mqueue, false); // From Relative + if(!mqmsgheadarr.FillMsgId(basic_type::pAbsPtr->base_msgid)){ + ERR_CHMPRN("Failed to fill msgid into MQMSGHEADLIST."); + return false; + } + return true; +} + +template +bool chminfo_lap::ReloadConfigration(const CHMCFGINFO* pchmcfg) +{ + if(!pchmcfg){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + + // check + if(MAX_MQUEUE_COUNT < (pchmcfg->max_server_mq_cnt + pchmcfg->max_client_mq_cnt)){ + ERR_CHMPRN("Configuration information are wrong."); + return false; + } + if(basic_type::pAbsPtr->is_random_deliver != pchmcfg->is_random_mode){ + WAN_CHMPRN("chmpx mode(random/hash) could not be changed."); + } + + // reset + basic_type::pAbsPtr->is_auto_merge = pchmcfg->is_auto_merge; + basic_type::pAbsPtr->is_do_merge = pchmcfg->is_do_merge; + basic_type::pAbsPtr->evsock_thread_cnt = pchmcfg->sock_thread_cnt; + basic_type::pAbsPtr->evmq_thread_cnt = pchmcfg->mq_thread_cnt; + basic_type::pAbsPtr->max_mqueue = pchmcfg->max_server_mq_cnt + pchmcfg->max_client_mq_cnt; + basic_type::pAbsPtr->chmpx_mqueue = pchmcfg->max_server_mq_cnt; + basic_type::pAbsPtr->max_q_per_chmpxmq = pchmcfg->max_q_per_servermq; + basic_type::pAbsPtr->max_q_per_cltmq = pchmcfg->max_q_per_clientmq; + basic_type::pAbsPtr->max_mq_per_client = pchmcfg->max_mq_per_client; + basic_type::pAbsPtr->mq_per_attach = pchmcfg->mqcnt_per_attach; + basic_type::pAbsPtr->mq_ack = pchmcfg->mq_ack; + basic_type::pAbsPtr->max_sock_pool = pchmcfg->max_sock_pool; + basic_type::pAbsPtr->sock_pool_timeout = pchmcfg->sock_pool_timeout; + basic_type::pAbsPtr->sock_retrycnt = pchmcfg->retrycnt; + basic_type::pAbsPtr->timeout_wait_socket = static_cast(pchmcfg->timeout_wait_socket); + basic_type::pAbsPtr->timeout_wait_connect = static_cast(pchmcfg->timeout_wait_connect); + basic_type::pAbsPtr->mq_retrycnt = pchmcfg->mq_retrycnt; + basic_type::pAbsPtr->timeout_wait_mq = pchmcfg->timeout_wait_mq; + basic_type::pAbsPtr->timeout_merge = pchmcfg->timeout_merge; + basic_type::pAbsPtr->histlog_count = pchmcfg->max_histlog_count; + + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + if(!tmpchmpxman.ReloadConfigration(pchmcfg)){ + ERR_CHMPRN("Failed to initialize CHMPXMAN."); + return false; + } + return true; +} + +template +bool chminfo_lap::Clear(void) +{ + return Initialize(); +} + +template +bool chminfo_lap::Close(int eqfd, int type) +{ + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.Close(eqfd, type); +} + +template +bool chminfo_lap::FreeMsg(msgid_t msgid) +{ + if(CHM_INVALID_MSGID == msgid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + + // Check last msgid cache and clean it if hits. + if(msgid == basic_type::pAbsPtr->last_msgid_chmpx){ + basic_type::pAbsPtr->last_msgid_chmpx = CHM_INVALID_MSGID; + } + if(msgid == basic_type::pAbsPtr->last_msgid_activated){ + basic_type::pAbsPtr->last_msgid_activated = CHM_INVALID_MSGID; + } + if(msgid == basic_type::pAbsPtr->last_msgid_assigned){ + basic_type::pAbsPtr->last_msgid_assigned = CHM_INVALID_MSGID; + } + + // Find + mqmsgheadarrlap msgheadarr(basic_type::pAbsPtr->rel_chmpxmsgarea, basic_type::pShmBase, basic_type::pAbsPtr->max_mqueue, false); // From Relative + PMQMSGHEADLIST retrived_list_ptr = msgheadarr.Find(msgid, true); // To abs + if(!retrived_list_ptr){ + ERR_CHMPRN("Could not find msgid(0x%016" PRIx64 ").", msgid); + return false; + } + + // build list + mqmsgheadlistlap retrived_list(retrived_list_ptr, basic_type::pShmBase, true); // From abs + mqmsgheadlap retrieved_msghead(retrived_list.GetAbsMqMsgHeadPtr(), basic_type::pShmBase); // From abs + + // check flag + if(retrieved_msghead.IsNotAssigned()){ + WAN_CHMPRN("Already not assigned by msgid(0x%016" PRIx64 ").", msgid); + return true; + } + + // Retrive + if(retrieved_msghead.IsChmpxProc()){ + if(NULL == (retrived_list_ptr = retrived_list.Retrive())){ + ERR_CHMPRN("Failed to remove msgid(0x%016" PRIx64 ") from chmpxlist.", msgid); + return false; + } + if(0 < basic_type::pAbsPtr->chmpx_msg_count){ + basic_type::pAbsPtr->chmpx_msg_count--; + } + basic_type::pAbsPtr->chmpx_msgs = retrived_list.GetFirstPtr(false); + + }else if(retrieved_msghead.IsClientProc()){ + if(retrieved_msghead.IsAssigned() && !retrieved_msghead.IsActivated()){ + if(NULL == (retrived_list_ptr = retrived_list.Retrive())){ + ERR_CHMPRN("Failed to remove msgid(0x%016" PRIx64 ") from client assigned list.", msgid); + return false; + } + if(0 < basic_type::pAbsPtr->assigned_msg_count){ + basic_type::pAbsPtr->assigned_msg_count--; + } + basic_type::pAbsPtr->assigned_msgs = retrived_list.GetFirstPtr(false); + + + }else if(retrieved_msghead.IsAssigned() && retrieved_msghead.IsActivated()){ + if(NULL == (retrived_list_ptr = retrived_list.Retrive())){ + ERR_CHMPRN("Failed to remove msgid(0x%016" PRIx64 ") from client activated list.", msgid); + return false; + } + if(0 < basic_type::pAbsPtr->activated_msg_count){ + basic_type::pAbsPtr->activated_msg_count--; + } + basic_type::pAbsPtr->activated_msgs = retrived_list.GetFirstPtr(false); + + }else{ + ERR_CHMPRN("msgid(0x%016" PRIx64 ") status is soemthing wrong.", msgid); + return false; + } + }else{ + WAN_CHMPRN("msgid(0x%016" PRIx64 ") is already freed.", msgid); + return true; + } + + // Add freemsglist + retrived_list.Reset(retrived_list_ptr, basic_type::pShmBase, true); // From abs + retrieved_msghead.Reset(retrived_list.GetAbsMqMsgHeadPtr(), basic_type::pShmBase); // From abs + + retrieved_msghead.NotAccountMqFlag(); + + mqmsgheadlistlap freed_msgs(basic_type::pAbsPtr->free_msgs, basic_type::pShmBase, false); // From rel(allow NULL) + if(!freed_msgs.Push(retrived_list_ptr, true)){ + ERR_CHMPRN("Failed to add freed PMQMSGHEADLIST %p.", retrived_list_ptr); + return false; + } + basic_type::pAbsPtr->free_msg_count++; + basic_type::pAbsPtr->free_msgs = freed_msgs.GetFirstPtr(false); + + return true; +} + +template +bool chminfo_lap::IsMsgidActivated(msgid_t msgid) const +{ + if(CHM_INVALID_MSGID == msgid){ + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + + // get msghead + mqmsgheadarrlap msgheadarr(basic_type::pAbsPtr->rel_chmpxmsgarea, basic_type::pShmBase, basic_type::pAbsPtr->max_mqueue, false); // From Relative + PMQMSGHEADLIST msg_list_ptr = msgheadarr.Find(msgid, true); // To abs + if(!msg_list_ptr){ + MSG_CHMPRN("Could not find msgid(0x%016" PRIx64 ").", msgid); + return false; + } + mqmsgheadlistlap msg_list(msg_list_ptr, basic_type::pShmBase, true); // From abs + mqmsgheadlap msghead(msg_list.GetAbsMqMsgHeadPtr(), basic_type::pShmBase); // From abs + + return msghead.IsActivated(); +} + +template +msgid_t chminfo_lap::AssignMsg(bool is_chmpx, bool is_activated) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return CHM_INVALID_MSGID; + } + + // freed list + mqmsgheadlistlap freed_msgs(basic_type::pAbsPtr->free_msgs, basic_type::pShmBase, false); // From rel + PMQMSGHEADLIST new_msghlst_ptr = freed_msgs.PopFront(); // Get abs + if(!new_msghlst_ptr){ + ERR_CHMPRN("Could not get new msgheadlist."); + return CHM_INVALID_MSGID; + } + // Re-set freed list + // + basic_type::pAbsPtr->free_msg_count--; + basic_type::pAbsPtr->free_msgs = freed_msgs.GetFirstPtr(false); + + // Initialize new msghead list + mqmsgheadlistlap new_msghlist(new_msghlst_ptr, basic_type::pShmBase); // From abs + if(!new_msghlist.Clear()){ + ERR_CHMPRN("Failed to clear new PMQMSGHEADLIST %p.", new_msghlst_ptr); + return CHM_INVALID_MSGID; + } + // keep msgid + mqmsgheadlap new_mqmsghead(new_msghlist.GetAbsMqMsgHeadPtr(), basic_type::pShmBase); + msgid_t new_msgid = new_mqmsghead.GetMsgId(); + + // change status(Assigned) + if(!new_mqmsghead.InitializeMqFlag(is_chmpx, is_activated)){ + ERR_CHMPRN("Could not initialize msghead flag to Assigned."); + + // For recovering, add freed list + new_mqmsghead.NotAccountMqFlag(); + mqmsgheadlistlap freed_msgs(basic_type::pAbsPtr->free_msgs, basic_type::pShmBase, false); // From rel(allow NULL) + if(!freed_msgs.Push(new_msghlst_ptr, true)){ + ERR_CHMPRN("Failed to add freed PMQMSGHEADLIST %p.", new_msghlst_ptr); + }else{ + basic_type::pAbsPtr->free_msg_count++; + basic_type::pAbsPtr->free_msgs = freed_msgs.GetFirstPtr(false); + } + return CHM_INVALID_MSGID; + } + + // Add assigned list + mqmsgheadlistlap base_msgs((is_chmpx ? basic_type::pAbsPtr->chmpx_msgs : !is_activated ? basic_type::pAbsPtr->assigned_msgs : basic_type::pAbsPtr->activated_msgs), basic_type::pShmBase, false); // From rel(allow NULL) + if(!base_msgs.Push(new_msghlst_ptr, true)){ + ERR_CHMPRN("Failed to add freed PMQMSGHEADLIST %p.", new_msghlst_ptr); + + // For recovering, add freed list + new_mqmsghead.NotAccountMqFlag(); + mqmsgheadlistlap freed_msgs(basic_type::pAbsPtr->free_msgs, basic_type::pShmBase, false); // From rel(allow NULL) + if(!freed_msgs.Push(new_msghlst_ptr, true)){ + ERR_CHMPRN("Failed to add freed PMQMSGHEADLIST %p.", new_msghlst_ptr); + }else{ + basic_type::pAbsPtr->free_msg_count++; + basic_type::pAbsPtr->free_msgs = freed_msgs.GetFirstPtr(false); + } + return CHM_INVALID_MSGID; + } + if(is_chmpx){ + basic_type::pAbsPtr->chmpx_msg_count++; + basic_type::pAbsPtr->chmpx_msgs = base_msgs.GetFirstPtr(false); + }else if(!is_activated){ + basic_type::pAbsPtr->assigned_msg_count++; + basic_type::pAbsPtr->assigned_msgs = base_msgs.GetFirstPtr(false); + }else{ // is_activated + basic_type::pAbsPtr->activated_msg_count++; + basic_type::pAbsPtr->activated_msgs = base_msgs.GetFirstPtr(false); + } + return new_msgid; +} + +template +msgid_t chminfo_lap::GetRandomMsgId(bool is_chmpx, bool is_activated) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return CHM_INVALID_MSGID; + } + + // first: lastest msgid + msgid_t msgid = CHM_INVALID_MSGID; + if(is_chmpx){ + msgid = basic_type::pAbsPtr->last_msgid_chmpx; + }else{ // !is_chmpx + if(is_activated){ + msgid = basic_type::pAbsPtr->last_msgid_activated; + }else{ // !is_activated + if(CHM_INVALID_MSGID == basic_type::pAbsPtr->last_msgid_activated || basic_type::pAbsPtr->last_msgid_assigned < basic_type::pAbsPtr->last_msgid_activated){ + msgid = basic_type::pAbsPtr->last_msgid_assigned; + }else{ + msgid = basic_type::pAbsPtr->last_msgid_activated; + } + } + } + + // find lastest msgid and get next msgid + if(CHM_INVALID_MSGID != msgid){ + // Find + mqmsgheadarrlap msgheadarr(basic_type::pAbsPtr->rel_chmpxmsgarea, basic_type::pShmBase, basic_type::pAbsPtr->max_mqueue, false); // From Relative + PMQMSGHEADLIST list_ptr = msgheadarr.Find(msgid, true); // To abs + if(!list_ptr){ + ERR_CHMPRN("Could not find msgid(0x%016" PRIx64 "), but try to get first msgid in list.", msgid); + msgid = CHM_INVALID_MSGID; + }else{ + // build list + mqmsgheadlistlap msglist(list_ptr, basic_type::pShmBase, true); // From abs + if(!msglist.ToNext(true)){ + ERR_CHMPRN("Could not get next msgid by msgid(0x%016" PRIx64 "), but try to get first msgid in list.", msgid); + msgid = CHM_INVALID_MSGID; + }else{ + mqmsgheadlap msghead(msglist.GetAbsMqMsgHeadPtr(), basic_type::pShmBase); // From abs + msgid = msghead.GetMsgId(); + } + } + } + + // when could not get next msgid yet. + if(CHM_INVALID_MSGID == msgid){ + PMQMSGHEADLIST base_ptr = NULL; + if(is_chmpx){ + if(!basic_type::pAbsPtr->chmpx_msgs || 0L == basic_type::pAbsPtr->chmpx_msg_count){ + ERR_CHMPRN("There is no msgid in chmpx msg list."); + return CHM_INVALID_MSGID; + } + base_ptr = basic_type::pAbsPtr->chmpx_msgs; + + }else{ // !is_chmpx + if(is_activated){ + if(!basic_type::pAbsPtr->activated_msgs || 0L == basic_type::pAbsPtr->activated_msg_count){ + ERR_CHMPRN("There is no msgid in activated msg list."); + return CHM_INVALID_MSGID; + } + base_ptr = basic_type::pAbsPtr->activated_msgs; + + }else{ // !is_activated + if(!basic_type::pAbsPtr->assigned_msgs || 0L == basic_type::pAbsPtr->assigned_msg_count){ + if(!basic_type::pAbsPtr->activated_msgs || 0L == basic_type::pAbsPtr->activated_msg_count){ + ERR_CHMPRN("There is no msgid in activated/assigned msg list."); + return CHM_INVALID_MSGID; + } + base_ptr = basic_type::pAbsPtr->activated_msgs; + }else{ + base_ptr = basic_type::pAbsPtr->assigned_msgs; + } + } + } + mqmsgheadlistlap msglist(base_ptr, basic_type::pShmBase, false); // From Relative + mqmsgheadlap msghead(msglist.GetAbsMqMsgHeadPtr(), basic_type::pShmBase); // From abs + msgid = msghead.GetMsgId(); + } + + // set last msgid into cache + if(is_chmpx){ + basic_type::pAbsPtr->last_msgid_chmpx = msgid; + }else{ // !is_chmpx + if(is_activated){ + basic_type::pAbsPtr->last_msgid_activated = msgid; + }else{ // !is_activated + basic_type::pAbsPtr->last_msgid_assigned = msgid; + } + } + return msgid; +} + +template +bool chminfo_lap::SetMqFlagStatus(msgid_t msgid, bool is_assigned, bool is_activated) +{ + if(CHM_INVALID_MSGID == msgid || (!is_assigned && is_activated)){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + + // If not assigned(and activated), it means to free msg. + if(!is_assigned && !is_activated){ + return FreeMsg(msgid); + } + // After here, assigned msg. + // + + // get msghead + mqmsgheadarrlap msgheadarr(basic_type::pAbsPtr->rel_chmpxmsgarea, basic_type::pShmBase, basic_type::pAbsPtr->max_mqueue, false); // From Relative + PMQMSGHEADLIST msg_list_ptr = msgheadarr.Find(msgid, true); // To abs + if(!msg_list_ptr){ + ERR_CHMPRN("Could not find msgid(0x%016" PRIx64 ").", msgid); + return false; + } + mqmsgheadlistlap msg_list(msg_list_ptr, basic_type::pShmBase, true); // From abs + mqmsgheadlap msghead(msg_list.GetAbsMqMsgHeadPtr(), basic_type::pShmBase); // From abs + + // Set + if(msghead.IsChmpxProc()){ + if(is_assigned && !is_activated){ + ERR_CHMPRN("msgid(0x%016" PRIx64 ") is used chmpx process, but specified invalid status Assigned and NOT activated.", msgid); + return false; + } + if(!msghead.SetMqFlagStatus(is_assigned, is_activated)){ + ERR_CHMPRN("Failed to set status Assigned and Activated to msgid(0x%016" PRIx64 ").", msgid); + return false; + } + + }else if(msghead.IsClientProc()){ + // Client msg + if(is_assigned && !is_activated){ + // Activated -> NOT activated + if(!msghead.IsActivated()){ + ERR_CHMPRN("msgid(0x%016" PRIx64 ") is already not activated.", msgid); + return true; + } + + // Set status + if(!msghead.SetMqFlagStatus(true, false)){ + ERR_CHMPRN("Failed to set status Assigned and NOT activated to msgid(0x%016" PRIx64 ").", msgid); + return false; + } + + // retrive msg from now(activated) list. + if(NULL == (msg_list_ptr = msg_list.Retrive())){ + WAN_CHMPRN("Failed to remove msgid(0x%016" PRIx64 ") from activated list.", msgid); + return false; + } + if(0L < basic_type::pAbsPtr->activated_msg_count){ + basic_type::pAbsPtr->activated_msg_count--; + } + if(0L < basic_type::pAbsPtr->activated_msg_count){ + basic_type::pAbsPtr->activated_msgs = msg_list.GetFirstPtr(false); + }else{ + basic_type::pAbsPtr->activated_msgs = NULL; + } + + // add msg to assigned list + msg_list.Reset(basic_type::pAbsPtr->assigned_msgs, basic_type::pShmBase, false); // From Relative(allow NULL) + if(!msg_list.Push(msg_list_ptr, true)){ + ERR_CHMPRN("Failed to add msgid(0x%016" PRIx64 ") asigned msgs.", msgid); + return false; + } + basic_type::pAbsPtr->assigned_msg_count++; + basic_type::pAbsPtr->assigned_msgs = msg_list.GetFirstPtr(false); + + }else{ // is_assigned && is_activated + // NOT activated -> Activated + if(msghead.IsActivated()){ + ERR_CHMPRN("msgid(0x%016" PRIx64 ") is already activated.", msgid); + return true; + } + + // Set status + if(!msghead.SetMqFlagStatus(true, true)){ + ERR_CHMPRN("Failed to set status Assigned and Activated to msgid(0x%016" PRIx64 ").", msgid); + return false; + } + + // retrive msg from now(assigned) list. + if(NULL == (msg_list_ptr = msg_list.Retrive())){ + WAN_CHMPRN("Failed to remove msgid(0x%016" PRIx64 ") from assigned list.", msgid); + return false; + } + if(0L < basic_type::pAbsPtr->assigned_msg_count){ + basic_type::pAbsPtr->assigned_msg_count--; + } + if(0L < basic_type::pAbsPtr->assigned_msg_count){ + basic_type::pAbsPtr->assigned_msgs = msg_list.GetFirstPtr(false); + }else{ + basic_type::pAbsPtr->assigned_msgs = NULL; + } + + // add msg to activated list + msg_list.Reset(basic_type::pAbsPtr->activated_msgs, basic_type::pShmBase, false); // From Relative(allow NULL) + if(!msg_list.Push(msg_list_ptr, true)){ + ERR_CHMPRN("Failed to add msgid(0x%016" PRIx64 ") asigned msgs.", msgid); + return false; + } + basic_type::pAbsPtr->activated_msg_count++; + basic_type::pAbsPtr->activated_msgs = msg_list.GetFirstPtr(false); + } + }else{ + ERR_CHMPRN("msgid(0x%016" PRIx64 ") is not accounted.", msgid); + return false; + } + return true; +} + +// +// Get all msgid from Assigned, and Activated list by PID. +// (not get from chmpx list) +// +template +bool chminfo_lap::GetMsgidListByPid(pid_t pid, msgidlist_t& list, bool is_clear_list) +{ + if(CHM_INVALID_PID == pid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + if(is_clear_list){ + list.clear(); + } + + // get + mqmsgheadlistlap msg_list; + if(basic_type::pAbsPtr->activated_msgs){ + msg_list.Reset(basic_type::pAbsPtr->activated_msgs, basic_type::pShmBase, false); // From rel + if(!msg_list.GetMsgidListByPid(pid, list, false)){ + ERR_CHMPRN("Something error occured during getting pid list from activated list."); + return false; + } + } + if(basic_type::pAbsPtr->assigned_msgs){ + msg_list.Reset(basic_type::pAbsPtr->assigned_msgs, basic_type::pShmBase, false); // From rel + if(!msg_list.GetMsgidListByPid(pid, list, false)){ + ERR_CHMPRN("Something error occured during getting pid list from assigned list."); + return false; + } + } + return true; +} + +// +// Get all msgid from Activated list for all PID. +// (not get from chmpx list) +// +template +bool chminfo_lap::GetMsgidListByUniqPid(msgidlist_t& list, bool is_clear_list) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + if(is_clear_list){ + list.clear(); + } + + // get + mqmsgheadlistlap msg_list; + if(basic_type::pAbsPtr->activated_msgs){ + msg_list.Reset(basic_type::pAbsPtr->activated_msgs, basic_type::pShmBase, false); // From rel + if(!msg_list.GetMsgidListByUniqPid(list, false)){ + ERR_CHMPRN("Something error occured during getting pid list from activated list."); + return false; + } + } + return true; +} + +template +bool chminfo_lap::GetSelfChmpxSvr(PCHMPXSVR chmpxsvr) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetSelfChmpxSvr(chmpxsvr); +} + +template +bool chminfo_lap::GetChmpxSvr(chmpxid_t chmpxid, PCHMPXSVR chmpxsvr) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetChmpxSvr(chmpxid, chmpxsvr); +} + +template +bool chminfo_lap::GetChmpxSvrs(PCHMPXSVR* ppchmpxsvrs, long& count) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetChmpxSvrs(ppchmpxsvrs, count); +} + +template +bool chminfo_lap::MergeChmpxSvrs(PCHMPXSVR pchmpxsvrs, long count, bool is_remove, bool is_init_process, int eqfd) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.MergeChmpxSvrs(pchmpxsvrs, count, (NULL != basic_type::pAbsPtr->client_pids), is_remove, is_init_process, eqfd); +} + +template +bool chminfo_lap::GetGroup(std::string& group) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetGroup(group); +} + +template +bool chminfo_lap::IsServerMode(void) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.IsServerMode(); +} + +template +bool chminfo_lap::IsSlaveMode(void) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.IsSlaveMode(); +} + +template +long chminfo_lap::GetReplicaCount(void) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return DEFAULT_REPLICA_COUNT; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetReplicaCount(); +} + +template +chmpxid_t chminfo_lap::GetSelfChmpxId(void) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return CHM_INVALID_CHMPXID; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetSelfChmpxId(); +} + +template +chmpxid_t chminfo_lap::GetNextRingChmpxId(chmpxid_t chmpxid) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return CHM_INVALID_CHMPXID; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetNextRingChmpxId(chmpxid); +} + +template +long chminfo_lap::GetServerCount(void) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return 0L; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetServerCount(); +} + +template +long chminfo_lap::GetSlaveCount(void) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return 0L; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetSlaveCount(); +} + +template +long chminfo_lap::GetUpServerCount(void) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return 0L; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetUpServerCount(); +} + +template +chmpxpos_t chminfo_lap::GetSelfServerPos(void) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return CHM_INVALID_CHMPXLISTPOS; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetSelfServerPos(); +} + +template +chmpxpos_t chminfo_lap::GetNextServerPos(chmpxpos_t startpos, chmpxpos_t nowpos, bool is_skip_self, bool is_cycle) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return CHM_INVALID_CHMPXLISTPOS; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetNextServerPos(startpos, nowpos, is_skip_self, is_cycle); +} + +template +bool chminfo_lap::GetAllServerName(hnamesslmap_t& info) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetAllServerName(info); +} + +template +bool chminfo_lap::IsServerChmpxId(chmpxid_t chmpxid) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.IsServerChmpxId(chmpxid); +} + +template + +chmpxid_t chminfo_lap::GetChmpxIdBySock(int sock, int type) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return CHM_INVALID_CHMPXID; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetChmpxIdBySock(sock, type); +} + +template +chmpxid_t chminfo_lap::GetChmpxIdByToServerName(const char* hostname, short ctlport) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return CHM_INVALID_CHMPXID; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetChmpxIdByToServerName(hostname, ctlport); +} + +// +// This method is linear search in list, then it is not good performance. +// +template +chmpxid_t chminfo_lap::GetChmpxIdByStatus(chmpxsts_t status, bool part_match) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return CHM_INVALID_CHMPXID; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetChmpxIdByStatus(status, part_match); +} + +template +chmpxid_t chminfo_lap::GetRandomServerChmpxId(bool is_up_servers, bool without_suspend) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return CHM_INVALID_CHMPXID; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetRandomServerChmpxId(is_up_servers, without_suspend); +} + +template +chmpxid_t chminfo_lap::GetServerChmpxIdByHash(chmhash_t hash) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return CHM_INVALID_CHMPXID; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetServerChmpxIdByHash(hash); +} + +template +bool chminfo_lap::GetServerChmHashsByHashs(chmhash_t hash, chmhashlist_t& basehashs, bool with_pending, bool without_down, bool without_suspend) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetServerChmHashsByHashs(hash, basehashs, with_pending, without_down, without_suspend); +} + +template +bool chminfo_lap::GetServerChmpxIdByHashs(chmhash_t hash, chmpxidlist_t& chmpxids, bool with_pending, bool without_down, bool without_suspend) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetServerChmpxIdByHashs(hash, chmpxids, with_pending, without_down, without_suspend); +} + +template +long chminfo_lap::GetServerChmpxIds(chmpxidlist_t& list, bool with_pending, bool without_down, bool without_suspend) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return 0L; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetServerChmpxIds(list, with_pending, without_down, without_suspend); +} + +template +bool chminfo_lap::GetServerBase(chmpxpos_t pos, std::string& name, chmpxid_t& chmpxid, short& port, short& ctlport) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetServerBase(pos, name, chmpxid, port, ctlport); +} + +template +bool chminfo_lap::GetServerBase(chmpxid_t chmpxid, std::string& name, short& port, short& ctlport) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetServerBase(chmpxid, name, port, ctlport); +} + +template +bool chminfo_lap::GetServerBase(chmpxid_t chmpxid, CHMPXSSL& ssl) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetServerBase(chmpxid, ssl); +} + +template +bool chminfo_lap::GetServerSocks(chmpxid_t chmpxid, socklist_t& socklist, int& ctlsock) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetServerSocks(chmpxid, socklist, ctlsock); +} + +template +bool chminfo_lap::GetServerHash(chmpxid_t chmpxid, chmhash_t& base, chmhash_t& pending) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetServerHash(chmpxid, base, pending); +} + +template +bool chminfo_lap::GetMaxHashCount(chmhash_t& basehash, chmhash_t& pengindhash) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetMaxHashCount(basehash, pengindhash); +} + +template +chmpxsts_t chminfo_lap::GetServerStatus(chmpxid_t chmpxid) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return CHMPXSTS_SRVOUT_DOWN_NORMAL; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetServerStatus(chmpxid); +} + +template +bool chminfo_lap::GetSelfPorts(short& port, short& ctlport) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetSelfPorts(port, ctlport); +} + +template +bool chminfo_lap::GetSelfSocks(int& sock, int& ctlsock) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetSelfSocks(sock, ctlsock); +} + +template +bool chminfo_lap::GetSelfHash(chmhash_t& base, chmhash_t& pending) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetSelfHash(base, pending); +} + +template +chmpxsts_t chminfo_lap::GetSelfStatus(void) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return CHMPXSTS_SLAVE_DOWN_NORMAL; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetSelfStatus(); +} + +template +bool chminfo_lap::GetSelfSsl(CHMPXSSL& ssl) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetSelfSsl(ssl); +} + +template +long chminfo_lap::GetSlaveChmpxIds(chmpxidlist_t& list) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return 0L; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetSlaveChmpxIds(list); +} + +template +bool chminfo_lap::GetSlaveBase(chmpxid_t chmpxid, std::string& name, short& ctlport) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetSlaveBase(chmpxid, name, ctlport); +} + +template +bool chminfo_lap::GetSlaveSock(chmpxid_t chmpxid, socklist_t& socklist) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetSlaveSock(chmpxid, socklist); +} + +template +chmpxsts_t chminfo_lap::GetSlaveStatus(chmpxid_t chmpxid) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return CHMPXSTS_SLAVE_DOWN_NORMAL; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetSlaveStatus(chmpxid); +} + +template +bool chminfo_lap::SetServerSocks(chmpxid_t chmpxid, int sock, int ctlsock, int type) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.SetServerSocks(chmpxid, sock, ctlsock, type); +} + +template +bool chminfo_lap::RemoveServerSock(chmpxid_t chmpxid, int sock) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.RemoveServerSock(chmpxid, sock); +} + +template +bool chminfo_lap::SetServerHash(chmpxid_t chmpxid, chmhash_t base, chmhash_t pending, int type) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.SetServerHash(chmpxid, base, pending, type); +} + +template +bool chminfo_lap::SetServerStatus(chmpxid_t chmpxid, chmpxsts_t status) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.SetServerStatus(chmpxid, status, (NULL != basic_type::pAbsPtr->client_pids)); +} + +template +bool chminfo_lap::IsOperating(void) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.IsOperating(); +} + +template +bool chminfo_lap::UpdatePendingHash(bool is_allow_operating) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.UpdatePendingHash(is_allow_operating); +} + +template +bool chminfo_lap::SetSelfSocks(int sock, int ctlsock) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.SetSelfSocks(sock, ctlsock); +} + +template +bool chminfo_lap::SetSelfHash(chmhash_t base, chmhash_t pending, int type) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.SetSelfHash(base, pending, type); +} + +template +bool chminfo_lap::SetSelfStatus(chmpxsts_t status) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.SetSelfStatus(status, (NULL != basic_type::pAbsPtr->client_pids)); +} + +template +bool chminfo_lap::SetSlaveBase(chmpxid_t chmpxid, const char* hostname, short ctlport, const PCHMPXSSL pssl) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.SetSlaveBase(chmpxid, hostname, ctlport, pssl); +} + +template +bool chminfo_lap::SetSlaveSock(chmpxid_t chmpxid, int sock) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.SetSlaveSock(chmpxid, sock); +} + +template +bool chminfo_lap::SetSlaveStatus(chmpxid_t chmpxid, chmpxsts_t status) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.SetSlaveStatus(chmpxid, status); +} + +template +bool chminfo_lap::RemoveSlaveSock(chmpxid_t chmpxid, int sock, bool is_remove_empty) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + if(tmpchmpxman.RemoveSlaveSock(chmpxid, sock)){ + WAN_CHMPRN("Failed to remove the sock(%d) from slave.", sock); + } + + socklist_t socklist; + if(is_remove_empty && tmpchmpxman.GetSlaveSock(chmpxid, socklist) && socklist.empty()){ + // slave has no socket, so remove slave + return tmpchmpxman.RemoveSlave(chmpxid, CHM_INVALID_HANDLE); // do not need eqfd + } + return true; +} + +// +// This method forces to close all socket. +// +template +bool chminfo_lap::RemoveSlave(chmpxid_t chmpxid, int eqfd) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.RemoveSlave(chmpxid, eqfd); +} + +template +bool chminfo_lap::AddStat(chmpxid_t chmpxid, bool is_sent, size_t length, const struct timespec& elapsed_time) +{ + if(CHM_INVALID_CHMPXID == chmpxid){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.AddStat(chmpxid, is_sent, length, elapsed_time); +} + +template +bool chminfo_lap::GetStat(PCHMSTAT pserver, PCHMSTAT pslave) const +{ + if(!pserver || !pslave){ + ERR_CHMPRN("Parameter are wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + chmpxmanlap tmpchmpxman(&basic_type::pAbsPtr->chmpx_man, basic_type::pShmBase); + return tmpchmpxman.GetStat(pserver, pslave); +} + +template +bool chminfo_lap::RetriveClientPid(pid_t pid) +{ + if(CHM_INVALID_PID == pid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + if(!basic_type::pAbsPtr->client_pids){ + ERR_CHMPRN("Could not free pid(%d), because not found it.", pid); + return false; + } + + // Retrive + cltproclistlap cltproc_pids(basic_type::pAbsPtr->client_pids, basic_type::pShmBase, false); // From Relative + PCLTPROCLIST ptgcltproc; + if(NULL == (ptgcltproc = cltproc_pids.Retrive(pid))){ + ERR_CHMPRN("Could not free pid(%d), maybe not found it.", pid); + return false; + } + basic_type::pAbsPtr->client_pids = cltproc_pids.GetFirstPtr(false); // To Rel + + // clear + cltproclistlap tgcltproc(ptgcltproc, basic_type::pShmBase, true); // From abs + tgcltproc.Clear(); + + // set into free list + if(basic_type::pAbsPtr->free_pids){ + cltproclistlap free_pids(basic_type::pAbsPtr->free_pids, basic_type::pShmBase, false); // From Relative + free_pids.Insert(ptgcltproc, true); + }else{ + basic_type::pAbsPtr->free_pids = tgcltproc.GetFirstPtr(false); + } + return true; +} + +template +bool chminfo_lap::AddClientPid(pid_t pid) +{ + if(CHM_INVALID_PID == pid){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + + // Check + // if client_pids is NULL, but following logic works good. + // + cltproclistlap cltproc_pids(basic_type::pAbsPtr->client_pids, basic_type::pShmBase, false); // From Relative + PCLTPROCLIST ptgcltproc; + if(basic_type::pAbsPtr->client_pids && (NULL != (ptgcltproc = cltproc_pids.Find(pid, true)))){ // To Abs + MSG_CHMPRN("pid(%d) already set in pid list.", pid); + return true; + } + + // get one object from free list + if(!basic_type::pAbsPtr->free_pids){ + ERR_CHMPRN("There is no space for set pid list."); + return false; + } + cltproclistlap free_pids(basic_type::pAbsPtr->free_pids, basic_type::pShmBase, false); // From Relative + ptgcltproc = free_pids.Retrive(); + basic_type::pAbsPtr->free_pids = free_pids.GetFirstPtr(false); // To Rel + + // initialize + cltproclistlap tgcltproc(ptgcltproc, basic_type::pShmBase, true); // From abs + tgcltproc.Initialize(pid); + + // set + if(basic_type::pAbsPtr->client_pids){ + // set into list + if(!cltproc_pids.Insert(ptgcltproc, true)){ // from abs + ERR_CHMPRN("Failed to insert pid object to list."); + // recover... + tgcltproc.Clear(); + free_pids.Insert(ptgcltproc, true); // do not check error + basic_type::pAbsPtr->free_pids = free_pids.GetFirstPtr(false); // to rel + return false; + } + }else{ + basic_type::pAbsPtr->client_pids = tgcltproc.GetFirstPtr(false); // to rel + } + return true; +} + +template +bool chminfo_lap::GetAllPids(pidlist_t& list) +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + + if(basic_type::pAbsPtr->client_pids){ + cltproclistlap cltproc_pids(basic_type::pAbsPtr->client_pids, basic_type::pShmBase, false); // From Relative + if(!cltproc_pids.GetAllPids(list)){ + ERR_CHMPRN("Something error occured during getting all pid list."); + return false; + } + }else{ + list.clear(); + } + return true; +} + +template +bool chminfo_lap::IsClientPids(void) const +{ + if(!basic_type::pAbsPtr || !basic_type::pShmBase){ + ERR_CHMPRN("PCHMINFO does not set."); + return false; + } + return (NULL != basic_type::pAbsPtr->client_pids); +} + +typedef chminfo_lap chminfolap; + +#endif // CHMSTRUCTURE_TCC + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmthread.cc b/lib/chmthread.cc new file mode 100644 index 0000000..4e7e6d2 --- /dev/null +++ b/lib/chmthread.cc @@ -0,0 +1,662 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include +#include + +#include "chmcommon.h" +#include "chmutil.h" +#include "chmthread.h" +#include "chmdbg.h" + +using namespace std; + +//--------------------------------------------------------- +// Utilities +//--------------------------------------------------------- +inline bool IS_CHMTHREAD_EXIT(const volatile chmthsts_t& status) +{ + return (ChmThread::CHMTHCOM_EXIT == status); +} + +inline bool IS_CHMTHREAD_SLEEP(const volatile chmthsts_t& status) +{ + return (ChmThread::CHMTHCOM_SLEEP == status); +} + +inline bool IS_CHMTHREAD_WORKING(const volatile chmthsts_t& status) +{ + return (ChmThread::CHMTHCOM_WORK <= status); +} + +inline void SET_CHMTHREAD_STATUS(volatile chmthsts_t& status, chmthsts_t newstatus, pthread_mutex_t& mutex) +{ + pthread_mutex_lock(&mutex); + status = newstatus; + pthread_mutex_unlock(&mutex); +} + +inline void DECREMENT_CHMTHREAD_STATUS(volatile chmthsts_t& status, pthread_mutex_t& mutex) +{ + pthread_mutex_lock(&mutex); + --status; + pthread_mutex_unlock(&mutex); +} + +inline void SET_CHMTHREAD_EXIT(volatile chmthsts_t& status, pthread_mutex_t& mutex) +{ + SET_CHMTHREAD_STATUS(status, ChmThread::CHMTHCOM_EXIT, mutex); +} + +inline void SET_CHMTHREAD_SLEEP(volatile chmthsts_t& status, pthread_mutex_t& mutex) +{ + SET_CHMTHREAD_STATUS(status, ChmThread::CHMTHCOM_SLEEP, mutex); +} + +inline void SET_CHMTHREAD_WORK(volatile chmthsts_t& status, pthread_mutex_t& mutex) +{ + SET_CHMTHREAD_STATUS(status, ChmThread::CHMTHCOM_WORK, mutex); +} + +//--------------------------------------------------------- +// Class Variable +//--------------------------------------------------------- +const chmthsts_t ChmThread::CHMTHCOM_EXIT; +const chmthsts_t ChmThread::CHMTHCOM_SLEEP; +const chmthsts_t ChmThread::CHMTHCOM_WORK; + +//--------------------------------------------------------- +// Class Method +//--------------------------------------------------------- +void* ChmThread::WorkerProc(void* param) +{ + PCHMTHWP_PARAM pchmthparam = reinterpret_cast(param); + + // check + if(!pchmthparam || !pchmthparam->pchmthread || !pchmthparam->work_proc){ + ERR_CHMPRN("Could not run worker thread(%lu), some parameter is wrong.", pthread_self()); + pthread_exit(NULL); + } + MSG_CHMPRN("Worker thread(%s: %ld) start up now.", pchmthparam->thread_name.c_str(), pthread_self()); + + // loop + while(true){ + if(IS_CHMTHREAD_EXIT(pchmthparam->status)){ + pthread_mutex_unlock(&(pchmthparam->cond_mutex)); + break; + + }else if(IS_CHMTHREAD_SLEEP(pchmthparam->status)){ + pthread_mutex_lock(&(pchmthparam->cond_mutex)); + + // recheck status with locking. + if(!IS_CHMTHREAD_SLEEP(pchmthparam->status)){ + pthread_mutex_unlock(&(pchmthparam->cond_mutex)); + continue; + } + + // wait cond + pthread_cond_wait(&(pchmthparam->cond_val), &(pchmthparam->cond_mutex)); + pthread_mutex_unlock(&(pchmthparam->cond_mutex)); + + if(IS_CHMTHREAD_EXIT(pchmthparam->status)){ + break; + } + } + + // do work + if(!pchmthparam->work_proc(pchmthparam->common_param, pchmthparam->wp_param)){ + // exit thread + while(!fullock::flck_trylock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval))); // LOCK LIST + + // remove from working map + chmthmap_t::iterator miter = pchmthparam->pchmthread->chmthworkmap.find(pchmthparam->wp_param); + if(pchmthparam->pchmthread->chmthworkmap.end() != miter){ + pchmthparam->pchmthread->chmthworkmap.erase(miter); + } + + // change status to exit + SET_CHMTHREAD_EXIT(pchmthparam->status, pchmthparam->cond_mutex); + + // [NOTE] + // do not need to remove sleeping list and all list. + + fullock::flck_unlock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval)); // UNLOCK LIST + + }else{ + // finish to work normal + if(pchmthparam->is_onece){ + // finish to work, so exit thread + while(!fullock::flck_trylock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval))); // LOCK LIST + + // remove from working map + chmthmap_t::iterator miter = pchmthparam->pchmthread->chmthworkmap.find(pchmthparam->wp_param); + if(pchmthparam->pchmthread->chmthworkmap.end() != miter){ + pchmthparam->pchmthread->chmthworkmap.erase(miter); + } + + // change status to exit + SET_CHMTHREAD_EXIT(pchmthparam->status, pchmthparam->cond_mutex); + + // [NOTE] + // do not need to remove sleeping list and all list. + + fullock::flck_unlock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval)); // UNLOCK LIST + + }else if(pchmthparam->is_nosleep){ + // next to work assp, so set work status + if(IS_CHMTHREAD_EXIT(pchmthparam->status)){ + // exit status, so remove from working map + while(!fullock::flck_trylock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval))); // LOCK LIST + + // remove from working map + chmthmap_t::iterator miter = pchmthparam->pchmthread->chmthworkmap.find(pchmthparam->wp_param); + if(pchmthparam->pchmthread->chmthworkmap.end() != miter){ + pchmthparam->pchmthread->chmthworkmap.erase(miter); + } + + fullock::flck_unlock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval)); // UNLOCK LIST + + }else{ + // keep working + SET_CHMTHREAD_WORK(pchmthparam->status, pchmthparam->cond_mutex); + } + + }else if(!pchmthparam->is_keep_evcnt){ + // if working count is over 1, set work status(keep working) + if(IS_CHMTHREAD_WORKING(pchmthparam->status)){ // nolocking + // check working count + if(ChmThread::CHMTHCOM_WORK == pchmthparam->status){ // nolocking + // not stack working count, so next to sleep + while(!fullock::flck_trylock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval))); // LOCK LIST + + // remove from working map + chmthmap_t::iterator miter = pchmthparam->pchmthread->chmthworkmap.find(pchmthparam->wp_param); + if(pchmthparam->pchmthread->chmthworkmap.end() != miter){ + pchmthparam->pchmthread->chmthworkmap.erase(miter); + } + // set status + SET_CHMTHREAD_SLEEP(pchmthparam->status, pchmthparam->cond_mutex); + + // push sleeping list + pchmthparam->pchmthread->chmthsleeps.push_back(pchmthparam); + + fullock::flck_unlock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval)); // UNLOCK LIST + + }else{ + // stacked working count, so next to work + SET_CHMTHREAD_WORK(pchmthparam->status, pchmthparam->cond_mutex); + } + + }else if(IS_CHMTHREAD_EXIT(pchmthparam->status)){ // nolocking + // exit status, so remove from working map + while(!fullock::flck_trylock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval))); // LOCK LIST + + // remove from working map + chmthmap_t::iterator miter = pchmthparam->pchmthread->chmthworkmap.find(pchmthparam->wp_param); + if(pchmthparam->pchmthread->chmthworkmap.end() != miter){ + pchmthparam->pchmthread->chmthworkmap.erase(miter); + } + + fullock::flck_unlock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval)); // UNLOCK LIST + + }else{ + // WHY? (already sleep) + // next to sleep, so remove from working map + while(!fullock::flck_trylock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval))); // LOCK LIST + + // remove from working map + chmthmap_t::iterator miter = pchmthparam->pchmthread->chmthworkmap.find(pchmthparam->wp_param); + if(pchmthparam->pchmthread->chmthworkmap.end() != miter){ + pchmthparam->pchmthread->chmthworkmap.erase(miter); + } + + // check sleeping list + bool is_need_push = true; + for(chmthlist_t::iterator siter = pchmthparam->pchmthread->chmthsleeps.begin(); siter != pchmthparam->pchmthread->chmthsleeps.begin(); ++siter){ + if((*siter) == pchmthparam){ + // already set + is_need_push = false; + } + } + // push sleeping list + if(is_need_push){ + pchmthparam->pchmthread->chmthsleeps.push_back(pchmthparam); + } + + fullock::flck_unlock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval)); // UNLOCK LIST + } + + }else{ + // normal decrement working count + if(ChmThread::CHMTHCOM_WORK == pchmthparam->status){ // nolocking + while(!fullock::flck_trylock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval))); // LOCK LIST + + // next to sleep, so remove from working map + chmthmap_t::iterator miter = pchmthparam->pchmthread->chmthworkmap.find(pchmthparam->wp_param); + if(pchmthparam->pchmthread->chmthworkmap.end() != miter){ + pchmthparam->pchmthread->chmthworkmap.erase(miter); + } + // set status + SET_CHMTHREAD_SLEEP(pchmthparam->status, pchmthparam->cond_mutex); + + // push sleeping list + pchmthparam->pchmthread->chmthsleeps.push_back(pchmthparam); + + fullock::flck_unlock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval)); // UNLOCK LIST + + }else if(IS_CHMTHREAD_WORKING(pchmthparam->status)){ // nolocking + // next to work, nothing to do + DECREMENT_CHMTHREAD_STATUS(pchmthparam->status, pchmthparam->cond_mutex); + + }else if(IS_CHMTHREAD_EXIT(pchmthparam->status)){ + // exit status, so remove from working map + while(!fullock::flck_trylock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval))); // LOCK LIST + + chmthmap_t::iterator miter = pchmthparam->pchmthread->chmthworkmap.find(pchmthparam->wp_param); + if(pchmthparam->pchmthread->chmthworkmap.end() != miter){ + pchmthparam->pchmthread->chmthworkmap.erase(miter); + } + + fullock::flck_unlock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval)); // UNLOCK LIST + + }else{ + // already sleep, nothing to do + } + } + } + } + // if mapped in working, remove from working map + while(!fullock::flck_trylock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval))); // LOCK LIST + + chmthmap_t::iterator miter = pchmthparam->pchmthread->chmthworkmap.find(pchmthparam->wp_param); + if(pchmthparam->pchmthread->chmthworkmap.end() != miter){ + pchmthparam->pchmthread->chmthworkmap.erase(miter); + } + fullock::flck_unlock_noshared_mutex(&(pchmthparam->pchmthread->list_lockval)); // UNLOCK LIST + + MSG_CHMPRN("Worker thread(%s: %ld) exit now.", pchmthparam->thread_name.c_str(), pthread_self()); + pthread_exit(NULL); + + return NULL; +} + +//--------------------------------------------------------- +// Methods +//--------------------------------------------------------- +ChmThread::ChmThread(const char* pname) : thread_name(CHMEMPTYSTR(pname) ? "" : pname), free_proc(NULL), list_lockval(FLCK_NOSHARED_MUTEX_VAL_UNLOCKED) +{ +} + +ChmThread::~ChmThread() +{ + ExitAllThreads(); +} + +int ChmThread::CreateThreads(int thread_cnt, Tfp_Chm_WorkerProc work_func, Tfp_Chm_FreeParameter free_func, void* common_param, chmthparam_t wp_param, bool is_sleep_at_start, bool is_onece, bool is_nosleep, bool is_keep_evcnt) +{ + if(thread_cnt <= 0 || !work_func || (is_onece && is_nosleep)){ + ERR_CHMPRN("Parameter are wrong."); + return 0; + } + + // set common function + free_proc = free_func; + + fullock::flck_lock_noshared_mutex(&list_lockval); // LOCK + + // create thread loop + for(int cnt = 0; cnt < thread_cnt; ++cnt){ + // thread parameter + PCHMTHWP_PARAM pthparam = new CHMTHWP_PARAM; + pthparam->thread_name = thread_name + string("(") + to_string(cnt) + string(")"); + pthparam->pchmthread = this; + pthparam->status = is_sleep_at_start ? ChmThread::CHMTHCOM_SLEEP : ChmThread::CHMTHCOM_WORK; + pthparam->is_onece = is_onece; + pthparam->is_nosleep = is_nosleep; + pthparam->is_keep_evcnt = is_keep_evcnt; + pthparam->work_proc = work_func; + pthparam->common_param = common_param; + pthparam->wp_param = wp_param; + + // create thread + int result; + if(0 != (result = pthread_create(&(pthparam->threadid), NULL, ChmThread::WorkerProc, pthparam))){ + ERR_CHMPRN("Failed to create thread. return code(error) = %d", result); + CHM_Delete(pthparam); + fullock::flck_unlock_noshared_mutex(&list_lockval); // UNLOCK + return cnt; // break loop + } + + // add list + chmthlist.push_back(pthparam); + if(is_sleep_at_start){ + chmthsleeps.push_back(pthparam); + }else{ + chmthworkmap[wp_param] = pthparam; + } + } + fullock::flck_unlock_noshared_mutex(&list_lockval); // UNLOCK + + return thread_cnt; +} + +int ChmThread::ExitThreads(int thread_cnt, bool is_wait) +{ + if(thread_cnt <= 0){ + ERR_CHMPRN("Parameter is wrong."); + return 0; + } + + PCHMTHWP_PARAM exit_pthparam; + int exited_cnt; + for(exited_cnt = 0; exited_cnt < thread_cnt; ){ + fullock::flck_lock_noshared_mutex(&list_lockval); // LOCK + + // search sleep thread. + exit_pthparam = NULL; + for(chmthlist_t::iterator iter = chmthlist.begin(); iter != chmthlist.end(); ++iter){ + PCHMTHWP_PARAM pthparam = *iter; + + // success to lock thread param + pthread_mutex_lock(&(pthparam->cond_mutex)); + + if(IS_CHMTHREAD_SLEEP(pthparam->status)){ + pthparam->status= ChmThread::CHMTHCOM_EXIT; + exit_pthparam = pthparam; + + // cond signal + pthread_cond_signal(&(pthparam->cond_val)); + pthread_mutex_unlock(&(pthparam->cond_mutex)); + break; + + }else if(IS_CHMTHREAD_EXIT(pthparam->status)){ + // already exited... + exit_pthparam = pthparam; + + // cond signal + pthread_cond_signal(&(pthparam->cond_val)); + pthread_mutex_unlock(&(pthparam->cond_mutex)); + break; + } + pthread_mutex_unlock(&(pthparam->cond_mutex)); + } + fullock::flck_unlock_noshared_mutex(&list_lockval); // UNLOCK + + // exit thread and cleanup + if(exit_pthparam){ + if(!WaitExitThread(exit_pthparam)){ + ERR_CHMPRN("Could not stop sub thread, but continue..."); + } + ++exited_cnt; + }else if(!is_wait){ + break; + } + } + return exited_cnt; +} + +bool ChmThread::WaitExitThread(PCHMTHWP_PARAM thread_param) +{ + if(!thread_param){ + ERR_CHMPRN("Parameter is wrong."); + return false; + } + + // wait for thread exiting + // + // [NOTICE] + // If you have pthread_tryjoin_np function, should be instead of pthread_join. + // (But pthread_tryjoin_np is not unsupport on some system.) + // + void* pretval = NULL; + int result; + if(0 != (result = pthread_join(thread_param->threadid, &pretval))){ + ERR_CHMPRN("Failed to wait exiting thread. return code(error) = %d", result); + return false; + } + MSG_CHMPRN("Succeed to wait exiting thread. return value ptr = %p(expect=NULL)", pretval); + + // remove from list + fullock::flck_lock_noshared_mutex(&list_lockval); // LOCK + + // check working map + chmthmap_t::iterator miter = chmthworkmap.find(thread_param->wp_param); + if(chmthworkmap.end() != miter){ + // why? + WAN_CHMPRN("Why is thread(param = 0x%016" PRIx64 " ) not removed from mapping yet, do remove here.", thread_param->wp_param); + chmthworkmap.erase(miter); + } + // check sleeping list + for(chmthlist_t::iterator iter = chmthsleeps.begin(); iter != chmthsleeps.end(); ++iter){ + if(thread_param == (*iter)){ + chmthsleeps.erase(iter); + break; + } + } + // remove from all list + for(chmthlist_t::iterator iter = chmthlist.begin(); iter != chmthlist.end(); ++iter){ + if(thread_param == (*iter)){ + chmthlist.erase(iter); + break; + } + } + fullock::flck_unlock_noshared_mutex(&list_lockval); // UNLOCK + + // clear parameter + if(free_proc){ + if(!free_proc(thread_param->common_param, thread_param->wp_param)){ + MSG_CHMPRN("Failed to free parameter for thread, but continue..."); + } + thread_param->common_param = NULL; + thread_param->wp_param = 0; + } + CHM_Delete(thread_param); + + return true; +} + +bool ChmThread::ExitAllThreads(void) +{ + int req_cnt; + int exited_cnt; + + fullock::flck_lock_noshared_mutex(&list_lockval); // LOCK + req_cnt = static_cast(chmthlist.size()); + fullock::flck_unlock_noshared_mutex(&list_lockval); // UNLOCK + + if(0 == req_cnt){ + return true; + } + if(req_cnt != (exited_cnt = ExitThreads(req_cnt, true))){ + ERR_CHMPRN("Could not stop all sub thread(%d), exited only %d thread.", req_cnt, exited_cnt); + return false; + } + return true; +} + +bool ChmThread::IsThreadRun(void) +{ + PCHMTHWP_PARAM exit_pthparam; + bool result = false; + while(true){ + while(!fullock::flck_trylock_noshared_mutex(&list_lockval));// LOCK + + // search exit thread and wait for exiting it. + exit_pthparam = NULL; + for(chmthlist_t::iterator iter = chmthlist.begin(); iter != chmthlist.end(); ++iter){ + PCHMTHWP_PARAM pthparam = *iter; + if(IS_CHMTHREAD_EXIT(pthparam->status)){ + exit_pthparam = pthparam; + break; + } + } + if(!exit_pthparam){ + // there is no exit status thread. + result = (0 < chmthlist.size()); + fullock::flck_unlock_noshared_mutex(&list_lockval); // UNLOCK + break; + } + fullock::flck_unlock_noshared_mutex(&list_lockval); // UNLOCK + + // exit thread and cleanup + if(!WaitExitThread(exit_pthparam)){ + ERR_CHMPRN("Could not stop sub thread, but continue..."); + } + } + return result; +} + +bool ChmThread::DoWorkThread(chmthparam_t req_param) +{ + return (NULL != DispatchThread(req_param, true, NULL)); +} + +PCHMTHWP_PARAM ChmThread::DispatchThread(chmthparam_t req_param, bool is_wait, bool* pis_already_working) +{ + static struct timespec sleeptime = {0, 1}; // minimum value = 1ns + + if(pis_already_working){ + *pis_already_working = false; + } + + // thread search loop(if needs) + do{ + while(!fullock::flck_trylock_noshared_mutex(&list_lockval)); // LOCK + + // check working thread + chmthmap_t::iterator miter = chmthworkmap.find(req_param); + if(chmthworkmap.end() != miter){ + // same parameter thread is working now. + PCHMTHWP_PARAM thread_param = miter->second; + + // check status + if(IS_CHMTHREAD_WORKING(thread_param->status)){ + pthread_mutex_lock(&(thread_param->cond_mutex)); + + // count up for continue to work + thread_param->status++; + + if(pis_already_working){ + *pis_already_working = true; + } + fullock::flck_unlock_noshared_mutex(&list_lockval); // UNLOCK + + // cond signal(just in case) + pthread_cond_signal(&(thread_param->cond_val)); + pthread_mutex_unlock(&(thread_param->cond_mutex)); + + return thread_param; + } + } + + // check sleep thread + for(chmthlist_t::iterator iter = chmthsleeps.begin(); iter != chmthsleeps.end(); ++iter){ + PCHMTHWP_PARAM thread_param = *iter; + + // check status + if(IS_CHMTHREAD_SLEEP(thread_param->status)){ + // remove + chmthsleeps.erase(iter); + + // lock before status changing + pthread_mutex_lock(&(thread_param->cond_mutex)); + + // set status to work + thread_param->wp_param = req_param; + thread_param->status = ChmThread::CHMTHCOM_WORK; // for blocking under flow + + // add working map + chmthworkmap[thread_param->wp_param] = thread_param; + + fullock::flck_unlock_noshared_mutex(&list_lockval); // UNLOCK + + // cond signal + pthread_cond_signal(&(thread_param->cond_val)); + pthread_mutex_unlock(&(thread_param->cond_mutex)); + + return thread_param; + } + } + fullock::flck_unlock_noshared_mutex(&list_lockval); // UNLOCK + + if(is_wait){ + nanosleep(&sleeptime, NULL); // minimum sleep + } + }while(is_wait); + + MSG_CHMPRN("Could not find sleep or same thread."); + return NULL; +} + +bool ChmThread::ClearSelfWorkerStatus(void) +{ + pthread_t threadid= pthread_self(); + bool result = false; + + while(!fullock::flck_trylock_noshared_mutex(&list_lockval)); // LOCK + + // At first, search working thread with same parameter + for(chmthlist_t::iterator iter = chmthlist.begin(); iter != chmthlist.end(); ++iter){ + PCHMTHWP_PARAM thread_param = *iter; + + if(pthread_equal(thread_param->threadid, threadid)){ + // found same thread + if(IS_CHMTHREAD_WORKING(thread_param->status)){ + thread_param->status = ChmThread::CHMTHCOM_SLEEP; // status to SLEEP + } + result = true; + break; + } + } + fullock::flck_unlock_noshared_mutex(&list_lockval); // UNLOCK + + return result; +} + +chmthparam_t ChmThread::GetSelfWorkerParam(void) +{ + pthread_t threadid= pthread_self(); + + while(!fullock::flck_trylock_noshared_mutex(&list_lockval)); // LOCK + + // At first, search working thread with same parameter + for(chmthlist_t::iterator iter = chmthlist.begin(); iter != chmthlist.end(); ++iter){ + PCHMTHWP_PARAM thread_param = *iter; + + if(pthread_equal(thread_param->threadid, threadid)){ + // found same thread + fullock::flck_unlock_noshared_mutex(&list_lockval); // UNLOCK + return thread_param->wp_param; + } + } + fullock::flck_unlock_noshared_mutex(&list_lockval); // UNLOCK + + return 0; +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmthread.h b/lib/chmthread.h new file mode 100644 index 0000000..1eb2810 --- /dev/null +++ b/lib/chmthread.h @@ -0,0 +1,141 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMTHREAD_H +#define CHMTHREAD_H + +#include +#include + +#include +#include + +class ChmThread; + +//--------------------------------------------------------- +// Utility inline function +//--------------------------------------------------------- +inline bool CHMTHREAD_INITIALIZE_CONDVAL(pthread_mutex_t& cond_mutex, pthread_cond_t& cond_val) +{ + pthread_mutex_init(&cond_mutex, NULL); + if(0 != pthread_cond_init(&cond_val, NULL)){ + return false; + } + return true; +} + +inline bool CHMTHREAD_DESTROY_CONDVAL(pthread_mutex_t& cond_mutex, pthread_cond_t& cond_val) +{ + bool result = true; + if(0 != pthread_cond_destroy(&cond_val)){ + result = false; + } + if(0 != pthread_mutex_destroy(&cond_mutex)){ + result = false; + } + return result; +} + +//--------------------------------------------------------- +// Typedefs +//--------------------------------------------------------- +typedef int chmthsts_t; +typedef uint64_t chmthparam_t; + +typedef bool (*Tfp_Chm_WorkerProc)(void*, chmthparam_t); // result: true -> continue, false -> exit thread +typedef bool (*Tfp_Chm_FreeParameter)(void*, chmthparam_t); + +typedef struct chmth_wp_param{ + std::string thread_name; + ChmThread* pchmthread; // pointer to ChmThread + volatile chmthsts_t status; // exit, sleep, working... + bool is_onece; // if true, thread exits after working onece + bool is_nosleep; // if true, thread never sleep + bool is_keep_evcnt; // if false, no event stacking + Tfp_Chm_WorkerProc work_proc; + void* common_param; + chmthparam_t wp_param; + pthread_mutex_t cond_mutex; + pthread_cond_t cond_val; + pthread_t threadid; // thread id + + chmth_wp_param() : thread_name(""), pchmthread(NULL), status(0), is_onece(false), is_nosleep(false), is_keep_evcnt(true), work_proc(NULL), common_param(NULL), wp_param(0) // status = CHMTHCOM_SLEEP + { + CHMTHREAD_INITIALIZE_CONDVAL(cond_mutex, cond_val); + } + + ~chmth_wp_param() + { + CHMTHREAD_DESTROY_CONDVAL(cond_mutex, cond_val); + } +}CHMTHWP_PARAM, *PCHMTHWP_PARAM; + +typedef std::vector chmthlist_t; +typedef std::map chmthmap_t; + +//--------------------------------------------------------- +// ChmThread Class +//--------------------------------------------------------- +class ChmThread +{ + public: + static const chmthsts_t CHMTHCOM_EXIT = -1; + static const chmthsts_t CHMTHCOM_SLEEP = 0; + static const chmthsts_t CHMTHCOM_WORK = 1; // 1 and over 1 + + protected: + std::string thread_name; + Tfp_Chm_FreeParameter free_proc; + volatile int list_lockval; // like mutex + chmthlist_t chmthlist; // all threads + chmthlist_t chmthsleeps; // sleeping threads + chmthmap_t chmthworkmap; // working threads + + protected: + static void* WorkerProc(void* param); + + PCHMTHWP_PARAM DispatchThread(chmthparam_t req_param, bool is_wait, bool* pis_already_working); + bool WaitExitThread(PCHMTHWP_PARAM thread_param); + + public: + ChmThread(const char* pname = NULL); + virtual ~ChmThread(); + + bool HasThread(void) const { return (0 < chmthlist.size()); } + int GetThreadCount(void) const { return chmthlist.size(); } + int CreateThreads(int thread_cnt, Tfp_Chm_WorkerProc work_func, Tfp_Chm_FreeParameter free_func = NULL, void* common_param = NULL, chmthparam_t wp_param = 0, bool is_sleep_at_start = true, bool is_onece = false, bool is_nosleep = false, bool is_keep_evcnt = true); + int ExitThreads(int thread_cnt, bool is_wait = true); + bool ExitAllThreads(void); + bool IsThreadRun(void); + bool DoWorkThread(chmthparam_t req_param = 0); + bool ClearSelfWorkerStatus(void); + chmthparam_t GetSelfWorkerParam(void); +}; + +#endif // CHMTHREAD_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ + diff --git a/lib/chmutil.cc b/lib/chmutil.cc new file mode 100644 index 0000000..d8cb99d --- /dev/null +++ b/lib/chmutil.cc @@ -0,0 +1,428 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "chmutil.h" +#include "chmcommon.h" +#include "chmdbg.h" + +using namespace std; + +//--------------------------------------------------------- +// String Utilities +//--------------------------------------------------------- +// RFC 2822 +string str_rfcdate(time_t date) +{ + if(-1 == date){ + date = time(NULL); + } + char* oldlocale = setlocale(LC_TIME, "C"); + + char buff[128]; + strftime(buff, sizeof(buff), "%a, %d %b %Y %T %z", gmtime(&date)); + + setlocale(LC_TIME, oldlocale); + return buff; +} + +// RFC 2822 +time_t rfcdate_time(const char* rfcdate) +{ + if(CHMEMPTYSTR(rfcdate)){ + return 0L; // ... + } + char* oldlocale = setlocale(LC_TIME, "C"); + + struct tm tmdate; + strptime(rfcdate, "%a, %d %b %Y %T %z", &tmdate); + time_t tdate = mktime(&tmdate); + + setlocale(LC_TIME, oldlocale); + return tdate; +} + +bool sorted_insert_strmaparr(strmaparr_t& sorted_smaps, strmap_t& smap, const char* pSortKey1, const char* pSortKey2) +{ + if(CHMEMPTYSTR(pSortKey1)){ + return false; + } + if(smap.end() == smap.find(pSortKey1)){ + // not found key, it is wrong smap data. + return false; + } + + bool is_name2 = !CHMEMPTYSTR(pSortKey2); + string strname1 = smap[pSortKey1]; + string strname2 = !is_name2 ? "" : (smap.end() == smap.find(pSortKey2) ? "" : smap[pSortKey2]); + + for(strmaparr_t::iterator iter = sorted_smaps.begin(); iter != sorted_smaps.end(); ++iter){ + if(strname1 == (*iter)[pSortKey1]){ + if(!is_name2){ + // found same key, merge data + merge_strmap((*iter), smap); + return true; + }else{ + // check strname2 + for(; iter != sorted_smaps.end(); ++iter){ + if(strname1 == (*iter)[pSortKey1]){ + + string sorted_strname2 = iter->end() == iter->find(pSortKey2) ? "" : (*iter)[pSortKey2]; + if(strname2 == sorted_strname2){ + // found same key, merge data + merge_strmap((*iter), smap); + return true; + }else if(strname2 < sorted_strname2){ + // over posission, insert data + sorted_smaps.insert(iter, smap); + return true; + } + }else{ + // over posission, insert data + sorted_smaps.insert(iter, smap); + return true; + } + } + } + break; + + }else if(strname1 < (*iter)[pSortKey1]){ + // over posission, insert data + sorted_smaps.insert(iter, smap); + return true; + } + } + // not found, insert to lastest posision + sorted_smaps.push_back(smap); + + return true; +} + +bool is_string_alpha(const char* str) +{ + if(CHMEMPTYSTR(str)){ + return false; + } + for(; '\0' != *str; ++str){ + if(0 == isalpha(*str)){ + return false; + } + } + return true; +} + +bool is_string_number(const char* str) +{ + if(CHMEMPTYSTR(str)){ + return false; + } + for(; '\0' != *str; ++str){ + if(0 == isdigit(*str)){ + return false; + } + } + return true; +} + +bool str_paeser(const char* pbase, strlst_t& strarr, const char* psep, bool istrim) +{ + strarr.clear(); + + if(CHMEMPTYSTR(pbase)){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + strarr.push_back(string(pbase)); + + if(CHMEMPTYSTR(psep)){ + psep = " \t\r\n"; + } + for(; psep && *psep; psep++){ + for(strlst_t::iterator iter = strarr.begin(); iter != strarr.end(); ){ + strlst_t tmparr; + tmparr.clear(); + if(str_split(iter->c_str(), tmparr, *psep, istrim)){ + iter = strarr.erase(iter); + if(iter != strarr.end()){ + for(strlst_t::iterator iter2 = tmparr.begin(); iter2 != tmparr.end(); ++iter2){ + iter = strarr.insert(iter, *iter2); + ++iter; + } + }else{ + for(strlst_t::iterator iter2 = tmparr.begin(); iter2 != tmparr.end(); ++iter2){ + strarr.push_back(*iter2); + } + break; + } + }else{ + ++iter; + } + } + } + return true; +} + +bool str_split(const char* pbase, strlst_t& strarr, char sep, bool istrim) +{ + strarr.clear(); + + if(CHMEMPTYSTR(pbase)){ + ERR_CHMPRN("Parameters are wrong."); + return false; + } + stringstream ss(pbase); + string one; + while(getline(ss, one, sep)){ + if(istrim){ + one = trim(one); + } + strarr.push_back(one); + } + return true; +} + +//--------------------------------------------------------- +// Utilities +//--------------------------------------------------------- +#define DEFAULT_READ_BUFF_SIZE 4096 + +ssize_t chm_pread(int fd, void *buf, size_t count, off_t offset) +{ + ssize_t read_cnt; + ssize_t one_read; + for(read_cnt = 0L, one_read = 0L; static_cast(read_cnt) < count; read_cnt += one_read){ + if(-1 == (one_read = pread(fd, &(static_cast(buf))[read_cnt], (count - static_cast(read_cnt)), (offset + read_cnt)))){ + WAN_CHMPRN("Failed to read from fd(%d:%zd:%zu), errno = %d", fd, static_cast(offset) + read_cnt, count - static_cast(read_cnt), errno); + return -1; + } + if(0 == one_read){ + break; + } + } + return read_cnt; +} + +ssize_t chm_pwrite(int fd, const void *buf, size_t count, off_t offset) +{ + ssize_t write_cnt; + ssize_t one_write; + for(write_cnt = 0L, one_write = 0L; static_cast(write_cnt) < count; write_cnt += one_write){ + if(-1 == (one_write = pwrite(fd, &(static_cast(buf))[write_cnt], (count - static_cast(write_cnt)), (offset + write_cnt)))){ + WAN_CHMPRN("Failed to write from fd(%d:%zd:%zu), errno = %d", fd, static_cast(offset) + write_cnt, count - static_cast(write_cnt), errno); + return -1; + } + } + return write_cnt; +} + +unsigned char* chm_read(int fd, size_t* psize) +{ + if(CHM_INVALID_HANDLE == fd || !psize){ + ERR_CHMPRN("Parameter fd or psize is wrong."); + return NULL; + } + + size_t buffsize = DEFAULT_READ_BUFF_SIZE; + unsigned char* pbuff; + if(NULL == (pbuff = reinterpret_cast(malloc(buffsize * sizeof(unsigned char))))){ + ERR_CHMPRN("Could not allocation memory."); + return NULL; + } + + for(ssize_t pos = 0, readsize = 0; true; pos += readsize){ + if(-1 == (readsize = read(fd, &pbuff[pos], DEFAULT_READ_BUFF_SIZE))){ + if(EAGAIN == errno){ + MSG_CHMPRN("reading fd reached end(EAGAIN)"); + break; + }else if(EINTR == errno){ + MSG_CHMPRN("break reading fd by signal, so retry to read."); + readsize = 0; + continue; + } + ERR_CHMPRN("Failed to read from fd(%d: %zd: %d). errno=%d", fd, pos, DEFAULT_READ_BUFF_SIZE, errno); + CHM_Free(pbuff); + return NULL; + } + if(0 <= readsize && readsize < DEFAULT_READ_BUFF_SIZE){ + buffsize = static_cast(pos + readsize); + break; + } + + if(buffsize < static_cast(pos + readsize + DEFAULT_READ_BUFF_SIZE)){ + // reallocate + buffsize = static_cast(pos + readsize + DEFAULT_READ_BUFF_SIZE); // => DEFAULT_READ_BUFF_SIZE * X + + unsigned char* ptmp; + if(NULL == (ptmp = reinterpret_cast(realloc(pbuff, buffsize)))){ + ERR_CHMPRN("Could not allocation memory."); + CHM_Free(pbuff); + return NULL; + } + pbuff = ptmp; + } + } + *psize = buffsize; + return pbuff; +} + +bool is_file_exist(const char* file) +{ + if(CHMEMPTYSTR(file)){ + ERR_CHMPRN("Parameter is NULL."); + return false; + } + struct stat st; + if(-1 == stat(file, &st)){ + MSG_CHMPRN("Could not get stat for %s(errno:%d)", file, errno); + return false; + } + return true; +} + +bool is_file_safe_exist(const char* file) +{ + if(CHMEMPTYSTR(file)){ + ERR_CHMPRN("Parameter is NULL."); + return false; + } + struct stat st; + if(-1 == stat(file, &st)){ + MSG_CHMPRN("Could not get stat for %s(errno:%d)", file, errno); + return false; + } + if(!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)){ + MSG_CHMPRN("file %s is not regular nor symbolic file", file); + return false; + } + return true; +} + +bool is_dir_exist(const char* path) +{ + if(CHMEMPTYSTR(path)){ + ERR_CHMPRN("Parameter is NULL."); + return false; + } + struct stat st; + if(-1 == stat(path, &st)){ + MSG_CHMPRN("Could not get stat for %s(errno:%d)", path, errno); + return false; + } + if(!S_ISDIR(st.st_mode)){ + MSG_CHMPRN("path %s is not directory", path); + return false; + } + return true; +} + +bool get_file_size(const char* file, size_t& length) +{ + if(CHMEMPTYSTR(file)){ + ERR_CHMPRN("Parameter is NULL."); + return false; + } + struct stat st; + if(-1 == stat(file, &st)){ + MSG_CHMPRN("Could not get stat for %s(errno:%d)", file, errno); + return false; + } + length = static_cast(st.st_size); + return true; +} + +bool move_file_to_backup(const char* file, const char* pext) +{ + if(CHMEMPTYSTR(file) || CHMEMPTYSTR(pext)){ + ERR_CHMPRN("Parameter is NULL."); + return false; + } + if(!is_file_exist(file)){ + ERR_CHMPRN("file %s does not exist.", file); + return false; + } + + string oldpath = file; + string tmppath = oldpath + "." + pext; + string newpath; + for(int cnt = -1; true; cnt++){ + newpath = tmppath; + if(0 <= cnt){ + newpath += "." + to_string(cnt); + } + if(!is_file_exist(newpath.c_str())){ + break; + } + MSG_CHMPRN("backup file %s exists, change file extension.", newpath.c_str()); + } + + if(-1 == link(oldpath.c_str(), newpath.c_str())){ + ERR_CHMPRN("Failed to link from file %s to %s, errno=%d", oldpath.c_str(), newpath.c_str(), errno); + return false; + } + if(-1 == unlink(oldpath.c_str())){ + ERR_CHMPRN("Failed to unlink file %s, errno=%d", oldpath.c_str(), errno); + unlink(newpath.c_str()); // try to recover + return false; + } + return true; +} + +bool truncate_filling_zero(int fd, size_t length, int pagesize) +{ + if(CHM_INVALID_HANDLE == fd || 0 == length || 0 >= pagesize){ + ERR_CHMPRN("parameters are wrong."); + return false; + } + // truncate + if(0 != ftruncate(fd, length)){ + ERR_CHMPRN("Could not truncate file(fd=%d) to %zu bytes, errno = %d", fd, length, errno); + return false; + } + // for buffer + unsigned char szBuff[pagesize]; + memset(szBuff, 0, pagesize); + + // initialize(fill zero) + for(size_t wrote = 0, onewrote = 0; wrote < length; wrote += onewrote){ + onewrote = min(static_cast(sizeof(unsigned char) * pagesize), (length - wrote)); + if(-1 == chm_pwrite(fd, szBuff, onewrote, static_cast(wrote))){ + ERR_CHMPRN("Failed to write file(fd=%d), errno = %d", fd, errno); + return false; + } + } + return true; +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/chmutil.h b/lib/chmutil.h new file mode 100644 index 0000000..76591c5 --- /dev/null +++ b/lib/chmutil.h @@ -0,0 +1,302 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#ifndef CHMUTIL_H +#define CHMUTIL_H + +#include +#include +#include +#include +#include +#include + +#include "chmcommon.h" + +//--------------------------------------------------------- +// String Utilities +//--------------------------------------------------------- +// RFC date +std::string str_rfcdate(time_t date = -1); +time_t rfcdate_time(const char* rfcdate); + +// is_xxx +bool is_string_alpha(const char* str); +bool is_string_number(const char* str); + +// array / mapping +typedef std::list strlst_t; +typedef std::map strmap_t; +typedef std::vector strmaparr_t; +typedef std::map strlstmap_t; + +struct strarr_sort +{ + bool operator()(const std::string& lstr, const std::string& rstr) const + { + return lstr < rstr; + } +}; + +template +struct merge_map +{ + typedef typename std::map merge_map_t; + typedef typename std::map::const_iterator merge_map_iterator; + + merge_map(merge_map_t& map1, merge_map_t& map2) + { + for(merge_map_iterator iter = map2.begin(); iter != map2.end(); ++iter){ + map1[iter->first] = iter->second; + } + } +}; +typedef merge_map merge_strmap; + +// sepalate +bool str_paeser(const char* pbase, strlst_t& strarr, const char* psep = NULL, bool istrim = true); +bool str_split(const char* pbase, strlst_t& strarr, char sep, bool istrim = true); + +// others +bool sorted_insert_strmaparr(strmaparr_t& sorted_smaps, strmap_t& smap, const char* pSortKey1, const char* pSortKey2 = NULL); + +//--------------------------------------------------------- +// Utility Macros +//--------------------------------------------------------- +#define CHM_Free(ptr) \ + { \ + if(ptr){ \ + free(ptr); \ + ptr = NULL; \ + } \ + } + +#define CHM_Delete(ptr) \ + { \ + if(ptr){ \ + delete ptr; \ + ptr = NULL; \ + } \ + } + +#define CHM_CLOSE(fd) \ + { \ + if(CHM_INVALID_HANDLE != fd){ \ + close(fd); \ + fd = CHM_INVALID_HANDLE; \ + } \ + } + +#define CHM_CLOSESOCK(sock) \ + { \ + if(CHM_INVALID_SOCK != sock){ \ + close(sock); \ + sock = CHM_INVALID_SOCK; \ + } \ + } + +#define CHM_MUMMAP(fd, base, length) \ + { \ + if(NULL != base){ \ + munmap(base, length); \ + base = NULL; \ + } \ + CHM_CLOSE(fd); \ + } + +//--------------------------------------------------------- +// timespec +//--------------------------------------------------------- +#if defined(__cplusplus) +template inline void SET_TIMESPEC(T* ptr, time_t sec, long nsec) +{ + (ptr)->tv_sec = sec; + if(nsec >= (1000 * 1000 * 1000)){ + (ptr)->tv_sec += (nsec / (1000 * 1000 * 1000)); + nsec = (nsec % (1000 * 1000 * 1000)); + } + (ptr)->tv_nsec = nsec; +} +template inline void COPY_TIMESPEC(T* dest, const T* src) +{ + (dest)->tv_sec = (src)->tv_sec; + (dest)->tv_nsec = (src)->tv_nsec; +} +template inline int COMPARE_TIMESPEC(const T* data1, const T* data2) +{ + if((data1)->tv_sec > (data2)->tv_sec){ + return 1; + }else if((data1)->tv_sec < (data2)->tv_sec){ + return -1; + }else if((data1)->tv_nsec > (data2)->tv_nsec){ + return 1; + }else if((data1)->tv_nsec < (data2)->tv_nsec){ + return -1; + } + return 0; +} +template inline void ADD_TIMESPEC(T* base, const T* val) +{ + (base)->tv_sec += (val)->tv_sec; + (base)->tv_nsec += (val)->tv_nsec; + if(1 <= (((base)->tv_nsec) / (1000 * 1000 * 1000))){ + ++((base)->tv_sec); + ((base)->tv_nsec) = ((base)->tv_nsec) % (1000 * 1000 * 1000); + } +} +template inline void SUB_TIMESPEC(T* base, const T* val) +{ + // Do not care down flow. + // + if((base)->tv_nsec < (val)->tv_nsec){ + --((base)->tv_sec); + (base)->tv_nsec += 1000 * 1000 * 1000; + } + (base)->tv_nsec -= (val)->tv_nsec; + (base)->tv_sec -= (val)->tv_sec; +} +template inline void INIT_TIMESPEC(T* ptr) +{ + SET_TIMESPEC(ptr, 0, 0L); +} +template inline bool RT_TIMESPEC(T* ptr) +{ + if(-1 == clock_gettime(CLOCK_REALTIME_COARSE, ptr)){ + return false; + } + return true; +} +template inline void STR_MATE_TIMESPEC(T* ptr) +{ + if(!RT_TIMESPEC(ptr)){ + INIT_TIMESPEC(ptr); + } +} +template inline void FIN_MATE_TIMESPEC(T* ptr) +{ + T ts; + if(!RT_TIMESPEC(&ts)){ + INIT_TIMESPEC(&ts); + } + SUB_TIMESPEC(&ts, ptr); + COPY_TIMESPEC(ptr, &ts); +} +template inline void FIN_MATE_TIMESPEC2(T* start, T* fin, T* elapsed) +{ + if(!RT_TIMESPEC(fin)){ + INIT_TIMESPEC(fin); + } + COPY_TIMESPEC(elapsed, fin); + SUB_TIMESPEC(elapsed, start); +} + +#else // __cplusplus + +#define SET_TIMESPEC(ptr, sec, nsec) \ + { \ + (ptr)->tv_sec = sec; \ + if(nsec >= (1000 * 1000 * 1000)){ \ + (ptr)->tv_sec += (nsec / (1000 * 1000 * 1000)); \ + nsec = (nsec % (1000 * 1000 * 1000)); \ + } \ + (ptr)->tv_nsec = nsec; \ + } +#define COPY_TIMESPEC(dest, src) \ + { \ + (dest)->tv_sec = (src)->tv_sec; \ + (dest)->tv_nsec = (src)->tv_nsec; \ + } +#define COMPARE_TIMESPEC(data1, data2) ( (data1)->tv_sec > (data2)->tv_sec ? 1 : \ + (data1)->tv_sec < (data2)->tv_sec ? -1 : \ + (data1)->tv_nsec > (data2)->tv_nsec ? 1 : \ + (data1)->tv_nsec < (data2)->tv_nsec ? -1 : 0 ) +#define ADD_TIMESPEC(base, val) \ + { \ + (base)->tv_sec = (val)->tv_sec; \ + (base)->tv_nsec = (val)->tv_nsec; \ + if(1 <= (((base)->tv_nsec) / (1000 * 1000 * 1000))){ \ + ((base)->tv_sec)++; \ + ((base)->tv_nsec) = ((base)->tv_nsec) % (1000 * 1000 * 1000); \ + } \ + } +#define SUB_TIMESPEC(base, val) \ + { \ + if((base)->tv_nsec < (val)->tv_nsec){ \ + --((base)->tv_sec); \ + (base)->tv_nsec += 1000 * 1000 * 1000; \ + } \ + (base)->tv_nsec -= (val)->tv_nsec; \ + (base)->tv_sec -= (val)->tv_sec; \ + } +#define INIT_TIMESPEC(ptr) SET_TIMESPEC(ptr, 0, 0L) +#define RT_TIMESPEC(ptr) (0 == clock_gettime(CLOCK_REALTIME_COARSE, ptr)) +#define STR_MATE_TIMESPEC(ptr) \ + { \ + if(!RT_TIMESPEC(ptr)){ \ + INIT_TIMESPEC(ptr); \ + } \ + } +#define FIN_MATE_TIMESPEC(ptr) \ + { \ + struct timespec ts; \ + if(!RT_TIMESPEC(&ts)){ \ + INIT_TIMESPEC(&ts); \ + } \ + SUB_TIMESPEC(&ts, ptr); \ + COPY_TIMESPEC(ptr, &ts); \ + } +#define FIN_MATE_TIMESPEC2(start, fin, elapsed) \ + { \ + if(!RT_TIMESPEC(fin)){ \ + INIT_TIMESPEC(fin); \ + } \ + COPY_TIMESPEC(elapsed, fin); \ + SUB_TIMESPEC(elapsed, start); \ + } + +#endif // __cplusplus + +//--------------------------------------------------------- +// Utility Functions +//--------------------------------------------------------- +DECL_EXTERN_C_START + +// File +ssize_t chm_pread(int fd, void *buf, size_t count, off_t offset); +ssize_t chm_pwrite(int fd, const void *buf, size_t count, off_t offset); +unsigned char* chm_read(int fd, size_t* psize); +bool is_file_exist(const char* file); +bool is_file_safe_exist(const char* file); +bool is_dir_exist(const char* path); +bool get_file_size(const char* file, size_t& length); +bool move_file_to_backup(const char* file, const char* pext); +bool truncate_filling_zero(int fd, size_t length, int pagesize); + +DECL_EXTERN_C_END + +#endif // CHMUTIL_H + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/lib/libchmpx.pc.in b/lib/libchmpx.pc.in new file mode 100644 index 0000000..9ebbbcc --- /dev/null +++ b/lib/libchmpx.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: chmpx +Description: chmpx C/C++ library +Version: @VERSION@ +Libs: -L${libdir} -lchmpx +Cflags: -I${includedir}/chmpx diff --git a/make_release_version_file.sh b/make_release_version_file.sh new file mode 100755 index 0000000..97375e1 --- /dev/null +++ b/make_release_version_file.sh @@ -0,0 +1,240 @@ +#!/bin/sh +# +# Make RELEASE_VERSION file - make_release_version_file.sh +# +# Copyright 2016 Yahoo! JAPAN corporation. +# +# Templates for customizing screwdriver CPP and autotools. +# This template files are provided by yjcore team. +# +# This script makes RELEASE_VERSION from github release tag +# or ChangeLog file, and check version. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Thu, Jun 2 2016 +# REVISION: +# + +##################################################################### +# +# Usage: make_release_version_file.sh [-not_use_git] [-no_check_ver_diff] [-f changelog file path] +# +PROGRAM_NAME=`basename $0` +SRCTOP=`dirname $0` + +##################################################################### +# +# Parameter +# +NOGIT="no" +NOCHECKVERDIFF="no" +CHANGELOGFILE="ChangeLog" +while [ $# -ne 0 ]; do + if [ "X$1" = "X-not_use_git" ]; then + NOGIT="yes" + + elif [ "X$1" = "X-no_check_ver_diff" ]; then + NOCHECKVERDIFF="yes" + + elif [ "X$1" = "X-f" ]; then + shift + if [ $# -eq 0 ]; then + echo "ERROR: Must set changelog file name after -f option." + echo "Usage: ${PROGRAM_NAME} [-not_use_git] [-no_check_ver_diff] [-f changefile path]" + exit 1 + fi + if [ ! -f ${SRCTOP}/$1 ]; then + echo "ERROR: Not found changelog($1) file " + exit 1 + fi + CHANGELOGFILE=$1 + + elif [ "X$1" = "X-h" -o "X$1" = "X--help" ]; then + echo "Usage: ${PROGRAM_NAME} [-not_use_git] [-no_check_ver_diff] [-f changefile path]" + exit 1 + else + echo "ERROR: Unkown option $1" + echo "Usage: ${PROGRAM_NAME} [-not_use_git] [-no_check_ver_diff] [-f changefile path]" + exit 1 + fi + + shift +done + +##################################################################### +# +# Version number from Github +# +# get version number from git release tag formatted following: +# "v10", "v 10", "ver10", "ver-10", "version10", "version,10" +# "v10.0.0", "v 10.0", "ver 10.0.0a", "v10.0.0-1", etc +# +# and the last build number is cut.(ex, "v10.0.1-1" -> "10.0.1") +# +if [ "X${NOGIT}" = "Xno" ]; then + GIT_RELEASE_VERSION=`git tag | grep '^[v|V]\([e|E][r|R]\([s|S][i|I][o|O][n|N]\)\{0,1\}\)\{0,1\}' | sed 's/^[v|V]\([e|E][r|R]\([s|S][i|I][o|O][n|N]\)\{0,1\}\)\{0,1\}//' | grep -o '[0-9]\+\([\.]\([0-9]\)\+\)\+\(.\)*$' | sed 's/-\(.\)*$//' | sort -t . -n -k 1,1 -k 2,2 -k 3,3 -k 4,4 | uniq | tail -1 | tr -d '\n'` + + if [ "X${GIT_RELEASE_VERSION}" = "X" ]; then + echo "WARNING: Could not get latest release tag from github release tag" + GIT_RELEASE_VERSION= + fi +else + GIT_RELEASE_VERSION= +fi + +##################################################################### +# +# Version number from ChangeLog +# +# get version number from ChangeLog file formatted like debian. +# and the last build number is cut.(ex, "10.0.1-1" -> "10.0.1") +# +if [ -f ${SRCTOP}/${CHANGELOGFILE} ]; then + CH_RELEASE_VERSION=`grep -o '^.*[(].*[)].*[;].*$' ${SRCTOP}/${CHANGELOGFILE} | grep -o '[(].*[)]' | head -1 | sed 's/[(|)]//g'` + + if [ "X${CH_RELEASE_VERSION}" = "X" ]; then + echo "WARNING: Could not get latest release tag from ChangeLog file ( ${SRCTOP}/${CHANGELOGFILE} )" + CH_RELEASE_VERSION= + fi +else + echo "MESSAGE: not found ChangeLog file ( ${SRCTOP}/${CHANGELOGFILE} )" + CH_RELEASE_VERSION= +fi + +##################################################################### +# +# Check version number between github release tag and ChangeLog file +# +# If version number from Github is later than one from ChangeLog, +# this script puts error and exits. +# The other case, this script continue to work and puts version number +# to RELEASE_VERION file. +# If there are no version number from Github and ChangeLog, this script +# checks RELEASE_VERION file existing. +# +IS_PUT_RELEASE_VERSION_FILE=yes + +if [ "X${GIT_RELEASE_VERSION}" != "X" -a "X${CH_RELEASE_VERSION}" != "X" ]; then + if [ "X${NOCHECKVERDIFF}" = "Xno" ]; then + # + # Check latest version + # + GIT_VERS=`echo ${GIT_RELEASE_VERSION} | sed 's/\./ /g'` + CH_VERS=`echo ${CH_RELEASE_VERSION} | sed 's/\./ /g'` + + GIT_VER_PART_CNT=0 + LATEST_VER_TYPE= + for git_ver_part in ${GIT_VERS}; do + ch_ver_part= + + CH_VER_PART_CNT=0 + for ver_tmp in ${CH_VERS}; do + ch_ver_part=`echo ${ver_tmp}` + CH_VER_PART_CNT=`expr ${CH_VER_PART_CNT} + 1` + + if [ ${GIT_VER_PART_CNT} -lt ${CH_VER_PART_CNT} ]; then + break + fi + done + + if [ "X${ch_ver_part}" != "X" ]; then + if [ ${git_ver_part} -gt ${ch_ver_part} ]; then + LATEST_VER_TYPE=gitver + break + elif [ ${git_ver_part} -lt ${ch_ver_part} ]; then + LATEST_VER_TYPE=chver + break + fi + else + LATEST_VER_TYPE=gitver + break + fi + + GIT_VER_PART_CNT=`expr ${GIT_VER_PART_CNT} + 1` + done + + if [ "X${LATEST_VER_TYPE}" = "X" ]; then + GIT_VER_PART_CNT=0 + for git_ver_part in ${GIT_VERS}; do + GIT_VER_PART_CNT=`expr ${GIT_VER_PART_CNT} + 1` + done + + CH_VER_PART_CNT=0 + for ver_tmp in ${CH_VERS}; do + CH_VER_PART_CNT=`expr ${CH_VER_PART_CNT} + 1` + done + + if [ ${GIT_VER_PART_CNT} -lt ${CH_VER_PART_CNT} ]; then + LATEST_VER_TYPE=chver + fi + fi + + if [ "X${LATEST_VER_TYPE}" = "Xgitver" ]; then + echo "ERROR: Github release tag ( ${GIT_RELEASE_VERSION} ) is later than ChangeLog file ( ${CHANGELOGFILE} ) version ( ${CH_RELEASE_VERSION} )." + exit 1 + + elif [ "X${LATEST_VER_TYPE}" = "Xchver" ]; then + echo "WARNING: ChangeLog file ( ${CHANGELOGFILE} ) version ( ${CH_RELEASE_VERSION} ) is later than Github release tag ( ${GIT_RELEASE_VERSION} )." + echo " Then RELEASE_VERSION file is put Github release tag ( ${GIT_RELEASE_VERSION} )" + + RELEASE_VERSION=${GIT_RELEASE_VERSION} + + else + # LATEST_VER_TYPE is not set, this means same version. + + RELEASE_VERSION=${GIT_RELEASE_VERSION} + fi + + else + # + # Not check version number, so only use it from Github + # + RELEASE_VERSION=${GIT_RELEASE_VERSION} + fi + +elif [ "X${GIT_RELEASE_VERSION}" != "X" ]; then + RELEASE_VERSION=${GIT_RELEASE_VERSION} + +elif [ "X${CH_RELEASE_VERSION}" != "X" ]; then + RELEASE_VERSION=${CH_RELEASE_VERSION} + +elif [ -f ${SRCTOP}/RELEASE_VERSION ]; then + RELEASE_VERSION=`cat ${SRCTOP}/RELEASE_VERSION` + IS_PUT_RELEASE_VERSION_FILE=no + +else + echo "ERROR: There is no version number information." + echo " The version number must be given by Github release tag" + echo " or ChangeLog file or RELEASE_VERSION file." + exit 1 +fi + +##################################################################### +# +# Make RELEASE_VERSION file +# +if [ "X${IS_PUT_RELEASE_VERSION_FILE}" = "Xyes" ]; then + echo "MESSAGE: Put version number ${RELEASE_VERSION} to RELEASE_VERSION file" + + echo -n ${RELEASE_VERSION} > ${SRCTOP}/RELEASE_VERSION +fi + + +##################################################################### +# +# finish +# +echo "SUCCEED: ${PROGRAM_NAME}" +echo " Result - Version number is ${RELEASE_VERSION}" +echo "" + +exit 0 + +# +# VIM modelines +# +# vim:set ts=4 fenc=utf-8: +# diff --git a/make_rev.sh b/make_rev.sh new file mode 100755 index 0000000..74c2515 --- /dev/null +++ b/make_rev.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# +# CHMPX +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue July 1 2014 +# REVISION: +# + +# +# This script puts git commit hash string to C header file. +# ex: static const char version[] = "...."; +# +# Usage: make_rev.sh +# + +PRGNAME=`basename $0` + +if [ $# -ne 2 ]; then + echo "${PRGNAME}: Error - parameter is not found." + exit 1 +fi +FILEPATH=$1 +VALUENAME=$2 + +REVISION=`git rev-parse --short HEAD` +if [ $? -ne 0 ]; then + echo "${PRGNAME}: Warning - git commit hash code is not found, so set to \"unknown\"." + REVISION="unknown" +fi +#echo ${REVISION} + +NEWCODES="char ${VALUENAME}[] = \"${REVISION}\";" +#echo ${NEWCODES} + +if [ -f ${FILEPATH} ]; then + FILECODES=`cat ${FILEPATH}` + #echo ${FILECODES} + + if [ "X${FILECODES}" = "X${NEWCODES}" ]; then + #echo "${PRGNAME}: ${FILEPATH} is not updated." + exit 0 + fi +fi + +echo ${NEWCODES} > ${FILEPATH} +echo "${PRGNAME}: ${FILEPATH} is updated." + +exit 0 + +# +# VIM modelines +# +# vim:set ts=4 fenc=utf-8: +# diff --git a/make_valiables.sh b/make_valiables.sh new file mode 100755 index 0000000..93793a2 --- /dev/null +++ b/make_valiables.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# +# Puts project variables for building package - make_valiables.sh +# +# Copyright 2016 Yahoo! JAPAN corporation. +# +# Templates for customizing screwdriver CPP and autotools. +# This template files are provided by yjcore team. +# +# This script file puts some formatted variables for building +# packages. For building package, the project needs common +# valiables for example package name, version, etc. +# This script puts those variables from project files. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Thu, Jun 2 2016 +# REVISION: +# + +# +# Usage make_valiables.sh [-pkg_version | -lib_version_info | -major_number] +# +PROGRAM_NAME=`basename $0` +PROGRAM_DIR=`dirname $0` +TOP_DIR=`cd ${PROGRAM_DIR}; pwd` + +if [ $# -ne 1 ]; then + echo "ERROR: ${PROGRAM_NAME} needs parameter." + echo "Usage: ${PROGRAM_NAME} [-pkg_version | -lib_version_info | -major_number]" + exit 1 +fi + +# +# Make result +# +if [ "X$1" = "X-h" -o "X$1" = "X--help" ]; then + echo "Usage: ${PROGRAM_NAME} [-pkg_version | -lib_version_info | -major_number]" + exit 0 + +elif [ "X$1" = "X-pkg_version" ]; then + RESULT=`cat ${TOP_DIR}/RELEASE_VERSION` + +elif [ "X$1" = "X-lib_version_info" ]; then + RESULT=`cat ${TOP_DIR}/RELEASE_VERSION | sed 's/["|\.]/ /g' | awk '{print $1":"$3":0"}'` + +elif [ "X$1" = "X-major_number" ]; then + RESULT=`cat ${TOP_DIR}/RELEASE_VERSION | sed 's/["|\.]/ /g' | awk '{print $1}'` + +else + echo "ERROR: unkown parameter $1" + echo "Usage: ${PROGRAM_NAME} [-pkg_version | -lib_version_info | -major_number]" + exit 1 +fi + +# +# Output result +# +echo -n $RESULT + +exit 0 + +# +# VIM modelines +# +# vim:set ts=4 fenc=utf-8: +# diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..5f8ebf3 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,39 @@ +# +# CHMPX +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue July 1 2014 +# REVISION: +# + +bin_PROGRAMS = chmpx + +# +# -lrt: for mqueue +# +chmpx_SOURCES = chmmain.cc +chmpx_LDADD = -L../lib/.libs -lchmpx $(k2hash_LIBS) $(fullock_LIBS) + +ACLOCAL_AMFLAGS = -I m4 +AM_CFLAGS = -I../lib $(k2hash_CFLAGS) $(fullock_CFLAGS) +AM_CPPFLAGS = -I../lib $(k2hash_CFLAGS) $(fullock_CFLAGS) + +# +# VIM modelines +# +# vim:set ts=4 fenc=utf-8: +# diff --git a/src/chmmain.cc b/src/chmmain.cc new file mode 100644 index 0000000..26ded81 --- /dev/null +++ b/src/chmmain.cc @@ -0,0 +1,298 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include +#include +#include +#include + +#include + +#include "chmcommon.h" +#include "chmcntrl.h" +#include "chmsigcntrl.h" +#include "chmutil.h" +#include "chmopts.h" +#include "chmpx.h" +#include "chmconfutil.h" +#include "chmdbg.h" + +using namespace std; + +//--------------------------------------------------------- +// Symbols +//--------------------------------------------------------- +#define OPT_HELP1 "H" +#define OPT_HELP2 "HELP" +#define OPT_VERSION1 "V" +#define OPT_VERSION2 "VER" +#define OPT_VERSION3 "VERSION" +#define OPT_CONFPATH "CONF" +#define OPT_CONFPATH2 "F" +#define OPT_JSONCONF "JSON" +#define OPT_CTLPORT "CTLPORT" +#define OPT_CTLPORT2 "CNTLPORT" +#define OPT_CTLPORT3 "CNTRLPORT" +#define OPT_DBG "D" +#define OPT_DBG2 "G" +#define OPT_DBGFILEPATH "DFILE" +#define OPT_DBGFILEPATH2 "GFILE" +#define OPT_DBG_PARAM_SLT "SLT" +#define OPT_DBG_PARAM_SLIENT "SILENT" +#define OPT_DBG_PARAM_ERR "ERR" +#define OPT_DBG_PARAM_ERROR "ERROR" +#define OPT_DBG_PARAM_WAN "WAN" +#define OPT_DBG_PARAM_WARNING "WARNING" +#define OPT_DBG_PARAM_MSG "MSG" +#define OPT_DBG_PARAM_MESSAGE "MESSAGE" +#define OPT_DBG_PARAM_INFO "INFO" +#define OPT_DBG_PARAM_DUMP "DUMP" +#define OPT_DBG_PARAM_ALLOW_SC "ALLOWSELFCERT" // [Hidden option] Only SSL debugging + +//--------------------------------------------------------- +// Signal handler +//--------------------------------------------------------- +static void SigUsr1handler(int signum) +{ + if(SIGUSR1 == signum){ + // Bumpup debug level. + BumpupChmDbgMode(); + + ChmDbgMode chmdbgmode = GetChmDbgMode(); + string strmode = ChmDbgMode_STR(chmdbgmode); + cout << "MESSAGE: Caught signal SIGUSR1, bumpup the logging level to " << strmode << "." << endl; + + // If debug level is not same chmpx and k2hash, then it should be + // set same level. + if(CHMDBG_SILENT == chmdbgmode){ + k2h_set_debug_level_silent(); + }else if(CHMDBG_ERR == chmdbgmode){ + k2h_set_debug_level_error(); + }else if(CHMDBG_WARN == chmdbgmode){ + k2h_set_debug_level_warning(); + }else if(CHMDBG_MSG == chmdbgmode){ + k2h_set_debug_level_message(); + }else if(CHMDBG_DUMP == chmdbgmode){ + k2h_set_debug_level_message(); // k2hash does not have dump mode + } + } +} + +//--------------------------------------------------------- +// Functions +//--------------------------------------------------------- +// [Usage] +// prgname -conf [-d [slient|err|wan|msg]] +// +static bool PrintUsage(const char* prgname) +{ + cout << "[Usage]" << endl; + cout << (prgname ? prgname : "prgname") << " [-conf | -json ] [-ctlport ] [-d [slient|err|wan|msg|dump]] [-dfile ]" << endl; + cout << (prgname ? prgname : "prgname") << " [ -h | -v ]" << endl; + cout << endl; + cout << "[option]" << endl; + cout << " -conf specify the configration file(.ini .yaml .json) path" << endl; + cout << " -json specify the configration json string" << endl; + cout << " -ctlport specify the self contrl port(*)" << endl; + cout << " -d specify the debugging output mode:" << endl; + cout << " silent - no output" << endl; + cout << " err - output error level" << endl; + cout << " wan - output warning level" << endl; + cout << " msg - output debug(message) level" << endl; + cout << " dump - output communication debug level" << endl; + cout << " -dfile specify the file path which is put debug output" << endl; + cout << " -h(help) display this usage." << endl; + cout << " -v(version) display version." << endl; + cout << endl; + cout << "[environments]" << endl; + cout << " CHMDBGMODE debugging mode like \"-d\" option." << endl; + cout << " CHMDBGFILE the file path for debugging output like \"-dfile\" option." << endl; + cout << " CHMCONFFILE configuration file path like \"-conf\" option" << endl; + cout << " CHMJSONCONF configuration json string like \"-json\" option" << endl; + cout << endl; + cout << "(*) if ctlport option is specified, chmpx searches same ctlport in configuration" << endl; + cout << " file and ignores \"CTLPORT\" directive in \"GLOBAL\" section. and chmpx will" << endl; + cout << " start in the mode indicated by the server entry that has beed detected." << endl; + cout << endl; + + return true; +} + +//--------------------------------------------------------- +// Main +//--------------------------------------------------------- +int main(int argc, char** argv) +{ + if(0 == argc || !argv){ + exit(EXIT_FAILURE); + } + string prgname = basename(argv[0]); + if(0 == strncmp(prgname.c_str(), "lt-", strlen("lt-"))){ + prgname = prgname.substr(3); + } + + // limit + struct rlimit rl; + getrlimit(RLIMIT_NOFILE, &rl); + if(rl.rlim_cur < rl.rlim_max){ + rl.rlim_cur = rl.rlim_max; + if(0 != setrlimit(RLIMIT_NOFILE, &rl)){ + cout << "ERROR: Could not set RLIMIT_NOFILE from " << rl.rlim_max << " to " << rl.rlim_cur << ", but continue..." << endl; + } + } + + ChmOpts opts((argc - 1), &argv[1]); + + // help + if(opts.Find(OPT_HELP1) || opts.Find(OPT_HELP2)){ + PrintUsage(prgname.c_str()); + exit(EXIT_SUCCESS); + } + + // help + if(opts.Find(OPT_VERSION1) || opts.Find(OPT_VERSION2) || opts.Find(OPT_VERSION3)){ + chmpx_print_version(stdout); + exit(EXIT_SUCCESS); + } + + // parameter - configration path + string config; + if(!opts.Get(OPT_CONFPATH, config) && !opts.Get(OPT_CONFPATH2, config) && !opts.Get(OPT_JSONCONF, config)){ + if(!have_env_chm_conf()){ + cout << "ERROR: Must specify option \"-conf\" or \"-json\"" << endl; + PrintUsage(prgname.c_str()); + exit(EXIT_FAILURE); + } + // Has configuration environment. + } + + // parameter - control port + short ctlport = CHM_INVALID_PORT; + string strctlport; + if(opts.Get(OPT_CTLPORT, strctlport) || opts.Get(OPT_CTLPORT2, strctlport) || opts.Get(OPT_CTLPORT3, strctlport)){ + ctlport = static_cast(atoi(strctlport.c_str())); + if(CHM_INVALID_PORT == ctlport || 0 == ctlport){ + cout << "ERROR: option \"-ctlport\" is specified with invalid value." << endl; + exit(EXIT_FAILURE); + } + } + + // parameter - debug + bool is_dbgopt = false; + ChmDbgMode dbgmode = CHMDBG_SILENT; + string dbgfile = ""; + { + string strDbgMode; + if(opts.Get(OPT_DBG, strDbgMode) || opts.Get(OPT_DBG2, strDbgMode)){ + if(strDbgMode.empty()){ + cout << "ERROR: \"-d\" option must be specified with parameter." << endl; + PrintUsage(prgname.c_str()); + exit(EXIT_FAILURE); + } + strDbgMode = upper(strDbgMode); + + if(OPT_DBG_PARAM_SLT == strDbgMode || OPT_DBG_PARAM_SLIENT == strDbgMode){ + dbgmode = CHMDBG_SILENT; + }else if(OPT_DBG_PARAM_ERR == strDbgMode || OPT_DBG_PARAM_ERROR == strDbgMode){ + dbgmode = CHMDBG_ERR; + }else if(OPT_DBG_PARAM_WAN == strDbgMode || OPT_DBG_PARAM_WARNING == strDbgMode){ + dbgmode = CHMDBG_WARN; + }else if(OPT_DBG_PARAM_MSG == strDbgMode || OPT_DBG_PARAM_MESSAGE == strDbgMode || OPT_DBG_PARAM_INFO == strDbgMode){ + dbgmode = CHMDBG_MSG; + }else if(OPT_DBG_PARAM_DUMP == strDbgMode){ + dbgmode = CHMDBG_DUMP; + }else{ + cout << "ERROR: Unknown \"-d\" option parameter(" << strDbgMode << ") is specified." << endl; + PrintUsage(prgname.c_str()); + exit(EXIT_FAILURE); + } + is_dbgopt = true; + } + if(!opts.Get(OPT_DBGFILEPATH, dbgfile)){ + opts.Get(OPT_DBGFILEPATH2, dbgfile); + } + } + + // Set debug mode + if(!LoadChmDbgEnv()){ + cout << "WARNING: Something error occured while loading debugging mode/file from environment." << endl; + } + if(!dbgfile.empty()){ + if(!SetChmDbgFile(dbgfile.c_str())){ + cout << "WARNING: Something error occured while dispatching debugging file(" << dbgfile << ")." << endl; + } + } + if(is_dbgopt){ + SetChmDbgMode(dbgmode); + } + + // signal mask + ChmSigCntrl sigcntrl; + int sigunmask[] = {SIGUSR1, SIGHUP, SIGINT}; // we have only USR1/HUP/INT handler now. + if( !sigcntrl.Initialize(sigunmask, (sizeof(sigunmask) / sizeof(int))) || + !sigcntrl.SetSignalProcMask() || + !sigcntrl.SetHandler(SIGUSR1, SigUsr1handler) || + !sigcntrl.SetHandler(SIGHUP, ChmCntrl::LoopBreakHandler) || + !sigcntrl.SetHandler(SIGINT, ChmCntrl::LoopBreakHandler) ) + { + cout << "ERROR: Could not set signal procmask, handler, etc..." << endl; + exit(EXIT_SUCCESS); + } + + // Initialize + ChmCntrl chmobj; + if(!chmobj.InitializeOnChmpx(config.c_str(), ctlport)){ + cout << "ERROR: Could not initialize process." << endl; + cout << "You can see detail about error, execute with \"-d\" option." << endl; + exit(EXIT_FAILURE); + } + if(opts.Find(OPT_DBG_PARAM_ALLOW_SC)){ + // Hidden option. + chmobj.AllowSelfCert(); + } + + // print process information. + stringstream sstream; + if(!chmobj.DumpSelfChmpxSvr(sstream)){ + cout << "ERROR: Could not get chmpx process information, maybe failed to initialize." << endl; + exit(EXIT_FAILURE); + } + cout << sstream.str() << endl; + + // main loop + if(!chmobj.EventLoop()){ + cout << "ERROR: Something error occured in main event loop." << endl; + chmobj.Clean(); + exit(EXIT_FAILURE); + } + chmobj.Clean(); + + exit(EXIT_SUCCESS); +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..e7ae9c6 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,53 @@ +# +# CHMPX +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue July 1 2014 +# REVISION: +# + +bin_PROGRAMS = chmpxbench chmpxstatus +noinst_PROGRAMS = chmconftest chmstreamtest + +# +# -lrt: for mqueue +# +chmconftest_SOURCES = chmconftest.cc +chmconftest_LDADD = -L../lib/.libs -lchmpx $(k2hash_LIBS) $(fullock_LIBS) + +chmpxbench_SOURCES = chmpxbench.cc +chmpxbench_LDADD = -L../lib/.libs -lchmpx $(k2hash_LIBS) $(fullock_LIBS) -lpthread + +chmstreamtest_SOURCES = chmstreamtest.cc +chmstreamtest_LDADD = -L../lib/.libs -lchmpx $(k2hash_LIBS) $(fullock_LIBS) + +chmpxstatus_SOURCES = chmpxstatus.cc +chmpxstatus_LDADD = -L../lib/.libs -lchmpx $(k2hash_LIBS) $(fullock_LIBS) + +ACLOCAL_AMFLAGS = -I m4 +AM_CFLAGS = -I../lib $(k2hash_CFLAGS) $(fullock_CFLAGS) +AM_CPPFLAGS = -I../lib $(k2hash_CFLAGS) $(fullock_CFLAGS) + +TESTS = test.sh + +EXTRA_DIST = test.sh test_server.ini test_slave.ini test_slave.yaml test_server.yaml test_server.json test_slave.json test_json_string.data + +# +# VIM modelines +# +# vim:set ts=4 fenc=utf-8: +# diff --git a/tests/chmconftest.cc b/tests/chmconftest.cc new file mode 100644 index 0000000..a3f7685 --- /dev/null +++ b/tests/chmconftest.cc @@ -0,0 +1,311 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "chmcommon.h" +#include "chmstructure.h" +#include "chmutil.h" +#include "chmdbg.h" +#include "chmconf.h" +#include "chmopts.h" + +using namespace std; + +//--------------------------------------------------------- +// Functions +//--------------------------------------------------------- +// Parse parameters +// +static void Help(char* progname) +{ + printf("Usage: %s [options]\n", progname ? progname : "program"); + printf("Option -conf [file name] Configuration file( .ini / .json / .yaml ) path\n"); + printf(" -json [json string] Configuration JSON string\n"); + printf(" -print_default print default datas\n"); + printf(" -d [debug level] \"ERR\" / \"WAN\" / \"INF\"\n"); + printf(" -h display help\n"); +} + +static bool LoadConfTest(CHMConf* pconfobj) +{ + if(!pconfobj){ + ERR_CHMPRN("parameter error."); + return false; + } + + const CHMCFGINFO* pchmcfginfo = pconfobj->GetConfiguration(true); + if(!pchmcfginfo){ + ERR_CHMPRN("Failed load configuration."); + return false; + } + + // Dump + printf("configuration{\n"); + printf("\tGROUP = %s\n", pchmcfginfo->groupname.c_str()); + printf("\tREVISION = %ld\n", pchmcfginfo->revision); + printf("\tMAXCHMPX = %ld\n", pchmcfginfo->max_chmpx_count); + printf("\tREPLICA = %ld\n", pchmcfginfo->replica_count); + printf("\tMAXMQSERVER = %ld\n", pchmcfginfo->max_server_mq_cnt); + printf("\tMAXMQCLIENT = %ld\n", pchmcfginfo->max_client_mq_cnt); + printf("\tMQPERATTACH = %ld\n", pchmcfginfo->mqcnt_per_attach); + printf("\tMAXQPERSERVERMQ = %ld\n", pchmcfginfo->max_q_per_servermq); + printf("\tMAXQPERCLIENTMQ = %ld\n", pchmcfginfo->max_q_per_clientmq); + printf("\tMAXMQPERCLIENT = %ld\n", pchmcfginfo->max_mq_per_client); + printf("\tMAXHISTLOG = %ld\n", pchmcfginfo->max_histlog_count); + printf("\tDATE = %jd\n", static_cast(pchmcfginfo->date)); + + int count = 1; + chmnode_cfginfos_t::const_iterator iter; + for(iter = pchmcfginfo->servers.begin(); iter != pchmcfginfo->servers.end(); ++iter, ++count){ + printf("\tserver[%d]{\n", count); + printf("\t\tNAME = %s\n", iter->name.c_str()); + printf("\t\tPORT = %d\n", iter->port); + printf("\t\tCTLPORT = %d\n", iter->ctlport); + printf("\t\tSSL = %s\n", iter->is_ssl ? "yes" : "no"); + if(iter->is_ssl){ + printf("\t\tVERIFY_PEER = %s\n", iter->verify_peer ? "yes" : "no"); + printf("\t\tCA PATH TYPE = %s\n", iter->is_ca_file ? "file" : "dir"); + printf("\t\tCA PATH = %s\n", iter->capath.c_str()); + printf("\t\tSERVER CERT = %s\n", iter->server_cert.c_str()); + printf("\t\tSERVER PRIKEY = %s\n", iter->server_prikey.c_str()); + printf("\t\tSLAVE CERT = %s\n", iter->slave_cert.c_str()); + printf("\t\tSLAVE PRIKEY = %s\n", iter->slave_prikey.c_str()); + } + printf("\t}\n"); + } + + count = 1; + for(iter = pchmcfginfo->slaves.begin(); iter != pchmcfginfo->slaves.end(); ++iter, ++count){ + printf("\tserver[%d]{\n", count); + printf("\t\tNAME = %s\n", iter->name.c_str()); + printf("\t\tPORT = %d\n", iter->port); + printf("\t\tCTLPORT = %d\n", iter->ctlport); + printf("\t\tSSL = %s\n", iter->is_ssl ? "yes" : "no"); + if(iter->is_ssl){ + printf("\t\tVERIFY_PEER = %s\n", iter->verify_peer ? "yes" : "no"); + printf("\t\tCA PATH TYPE = %s\n", iter->is_ca_file ? "file" : "dir"); + printf("\t\tCA PATH = %s\n", iter->capath.c_str()); + printf("\t\tSERVER CERT = %s\n", iter->server_cert.c_str()); + printf("\t\tSERVER PRIKEY = %s\n", iter->server_prikey.c_str()); + printf("\t\tSLAVE CERT = %s\n", iter->slave_cert.c_str()); + printf("\t\tSLAVE PRIKEY = %s\n", iter->slave_prikey.c_str()); + } + printf("\t}\n"); + } + printf("}\n"); + + return true; +} + +// +// For Checking for structure size +// +void print_initial_datas(void) +{ + printf("================================================================\n"); + printf(" Default datas and size for SHM\n"); + printf("----------------------------------------------------------------\n"); + printf("CHMPX %zd bytes\n", sizeof(CHMPX)); + printf("CHMPXLIST %zd bytes\n", sizeof(CHMPXLIST)); + printf("CHMSTAT %zd bytes\n", sizeof(CHMSTAT)); + printf("CHMPXMAN %zd bytes\n", sizeof(CHMPXMAN)); + printf("MQMSGHEAD %zd bytes\n", sizeof(MQMSGHEAD)); + printf("MQMSGHEADLIST %zd bytes\n", sizeof(MQMSGHEADLIST)); + printf("CHMINFO %zd bytes\n", sizeof(CHMINFO)); + printf("CHMLOGRAW %zd bytes\n", sizeof(CHMLOGRAW)); + printf("CHMLOG %zd bytes\n", sizeof(CHMLOG)); + printf("CHMSHM %zd bytes\n", sizeof(CHMSHM)); + printf("\n"); + printf("CHMPXLIST[def] %zd bytes\n", sizeof(CHMPXLIST) * DEFAULT_CHMPX_COUNT); + printf("MQMSGHEADLIST[def] %zd bytes\n", sizeof(MQMSGHEADLIST) * DEFAULT_CLIENT_MQ_CNT); + printf("CHMLOGRAW[def] %zd bytes\n", sizeof(CHMLOGRAW) * DEFAULT_HISTLOG_COUNT); + printf("\n"); + printf("CHMPXLIST[max] %zd bytes\n", sizeof(CHMPXLIST) * MAX_CHMPX_COUNT); + printf("MQMSGHEADLIST[max] %zd bytes\n", sizeof(MQMSGHEADLIST) * MAX_CLIENT_MQ_CNT); + printf("CHMLOGRAW[max] %zd bytes\n", sizeof(CHMLOGRAW) * MAX_HISTLOG_COUNT); + printf("\n"); + printf("total[def] %zd bytes\n", sizeof(CHMSHM) + sizeof(CHMPXLIST) * DEFAULT_CHMPX_COUNT + sizeof(MQMSGHEADLIST) * DEFAULT_CLIENT_MQ_CNT + sizeof(CHMLOGRAW) * DEFAULT_HISTLOG_COUNT); + printf("total[max] %zd bytes\n", sizeof(CHMSHM) + sizeof(CHMPXLIST) * MAX_CHMPX_COUNT + sizeof(MQMSGHEADLIST) * MAX_CLIENT_MQ_CNT + sizeof(CHMLOGRAW) * MAX_HISTLOG_COUNT); + printf("\n"); + + PCHMSHM shm = new CHMSHM; + + printf("----------------------------------------------------------------\n"); + printf("CHMSHM %p\n", shm); + printf(" CHMINFO %p\n", &(shm->info)); + printf(" pid %p\n", &(shm->info.pid)); + printf(" start_time %jd\n", static_cast(shm->info.start_time)); + printf(" chmpx_man %p\n", &(shm->info.chmpx_man)); + printf(" max_mqueue %s\n", to_string(shm->info.max_mqueue).c_str()); + printf(" chmpx_mqueue %s\n", to_string(shm->info.chmpx_mqueue).c_str()); + printf(" max_q_per_chmpxmq %s\n", to_string(shm->info.max_q_per_chmpxmq).c_str()); + printf(" max_q_per_cltmq %s\n", to_string(shm->info.max_q_per_cltmq).c_str()); + printf(" base_msgid %s\n", to_hexstring(shm->info.base_msgid).c_str()); + printf(" activated_msg_count %s\n", to_string(shm->info.activated_msg_count).c_str()); + printf(" activated_msgs %p\n", &(shm->info.activated_msgs)); + printf(" assigned_msg_count %s\n", to_string(shm->info.assigned_msg_count).c_str()); + printf(" assigned_msgs %p\n", &(shm->info.assigned_msgs)); + printf(" free_msg_count %s\n", to_string(shm->info.free_msg_count).c_str()); + printf(" free_msgs %p\n", &(shm->info.free_msgs)); + printf(" rel_chmpxmsgarea %p\n", &(shm->info.rel_chmpxmsgarea)); + printf(" PCHMPXLIST %p\n", &(shm->rel_chmpxarea)); + printf(" PCHMPX* %p\n", &(shm->rel_pchmpxarrarea)); + printf(" PMQMSGHEADLIST %p\n", &(shm->rel_chmpxmsgarea)); + printf(" LOGRAW %p\n", &(shm->chmpxlog)); + printf(" enable %s\n", shm->chmpxlog.enable ? "true" : "false"); + printf(" start_time %jd\n", static_cast(shm->chmpxlog.start_time)); + printf(" stop_time %jd\n", static_cast(shm->chmpxlog.stop_time)); + printf(" max_log_count %s\n", to_string(shm->chmpxlog.max_log_count).c_str()); + printf(" next_pos %s\n", to_string(shm->chmpxlog.next_pos).c_str()); + printf(" start_log_rel_area %p\n", &(shm->chmpxlog.start_log_rel_area)); + printf("----------------------------------------------------------------\n"); + printf("\n"); + + delete shm; +} + +//--------------------------------------------------------- +// Main +//--------------------------------------------------------- +int main(int argc, char** argv) +{ + ChmOpts opts((argc - 1), &argv[1]); + + // help + if(opts.Find("h") || opts.Find("help")){ + char* pprgname = basename(argv[0]); + Help(pprgname); + exit(EXIT_SUCCESS); + } + + // DBG Mode + string dbgmode; + if(opts.Get("g", dbgmode) || opts.Get("d", dbgmode)){ + if(0 == strcasecmp(dbgmode.c_str(), "ERR")){ + SetChmDbgMode(CHMDBG_ERR); + }else if(0 == strcasecmp(dbgmode.c_str(), "WAN")){ + SetChmDbgMode(CHMDBG_WARN); + }else if(0 == strcasecmp(dbgmode.c_str(), "INF")){ + SetChmDbgMode(CHMDBG_MSG); + }else{ + ERR_CHMPRN("Wrong parameter value \"-d\"(\"-g\") %s.", dbgmode.c_str()); + exit(EXIT_FAILURE); + } + } + + // print default data + if(opts.Find("print_default")){ + print_initial_datas(); + exit(EXIT_SUCCESS); + } + + // configuration option + string config(""); + if(opts.Get("conf", config) || opts.Get("f", config)){ + MSG_CHMPRN("Configuration file is %s.", config.c_str()); + }else if(opts.Get("json", config)){ + MSG_CHMPRN("Configuration JSON is \"%s\"", config.c_str()); + } + + // create epoll event fd + int eventfd; + if(CHM_INVALID_HANDLE == (eventfd = epoll_create1(EPOLL_CLOEXEC))){ // EPOLL_CLOEXEC is OK? + ERR_CHMPRN("epoll_create: error %d", errno); + exit(EXIT_FAILURE); + } + + // get conf object + CHMConf* pconfobj = CHMConf::GetCHMConf(eventfd, NULL, config.c_str(), CHM_INVALID_PORT, true, &config); + if(!pconfobj){ + ERR_CHMPRN("Could not build configuration object."); + close(eventfd); + exit(EXIT_FAILURE); + } + + // conf + if(!LoadConfTest(pconfobj)){ + WAN_CHMPRN("Failed dump configuration file."); + } + + // inotify set + if(!pconfobj->SetEventQueueFd(eventfd) || pconfobj->SetEventQueue()){ + ERR_CHMPRN("Failed to set eventfd for inotify."); + pconfobj->UnsetEventQueue(); + delete pconfobj; + close(eventfd); + exit(EXIT_FAILURE); + } + + // Signal block test + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGUSR1); + sigprocmask(SIG_SETMASK, &sigset, NULL); + + // Loop + bool isloop = true; + while(isloop){ + struct epoll_event events[32]; + int max_events = 32; + int timeout = 1000; // 1s (ex, shmproxy is 100ms) + + int evcount = epoll_pwait(eventfd, events, max_events, timeout, &sigset); + if(-1 == evcount){ + ERR_CHMPRN("epoll_pwait: error %d", errno); + break; + }else if(0 == evcount){ + MSG_CHMPRN("epoll_pwait timeouted."); + }else{ + for(int ecnt = 0; ecnt < evcount; ecnt++){ + if(pconfobj->IsEventQueueFd(events[ecnt].data.fd)){ + if(!pconfobj->Receive(events[ecnt].data.fd)){ + ERR_CHMPRN("Failed to check inotify fd."); + break; + } + }else{ + ERR_CHMPRN("unkown event for fd(%d).\n", events[ecnt].data.fd); + } + } + } + } + pconfobj->UnsetEventQueue(); + delete pconfobj; + close(eventfd); + + exit(EXIT_SUCCESS); +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ + diff --git a/tests/chmpxbench.cc b/tests/chmpxbench.cc new file mode 100644 index 0000000..a47b16e --- /dev/null +++ b/tests/chmpxbench.cc @@ -0,0 +1,1130 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "chmcommon.h" +#include "chmstructure.h" +#include "chmutil.h" +#include "chmconfutil.h" +#include "chmdbg.h" +#include "chmcntrl.h" +#include "chmopts.h" + +using namespace std; + +//--------------------------------------------------------- +// Symbol & Macros +//--------------------------------------------------------- +#define MINIMUM_DATA_LENGTH 64 +#define FILL_CHARACTOR '@' +#define BENCH_TMP_FILE_FORM "/tmp/chmpxbench-%d.dat" +#define BENCH_CONF_LENGTH (1024 * 64) // 64K + +//--------------------------------------------------------- +// Structure +//--------------------------------------------------------- +// +// All option +// +typedef struct bench_opts{ + bool isServerMode; + char szConfig[BENCH_CONF_LENGTH]; + int LoopCnt; + int proccnt; + int threadcnt; + bool isPrint; + bool isTrunAround; + size_t DataLength; + bool isBroadcast; +}BOPTS, *PBOPTS; + +// +// For child process(thread) +// +typedef struct child_control{ + int procid; // process id for child + int threadid; // thread id(gettid) + pthread_t pthreadid; // pthead id(pthread_create) + bool is_ready; // + bool is_exit; // + struct timespec ts; // result time + int errorcnt; // error count +}CHLDCNTL, *PCHLDCNTL; + +// +// For parent(main) process, common data +// +typedef struct exec_control{ + int procid; // = parent id + BOPTS opt; + bool is_suspend; + bool is_wait_doing; + bool is_exit; +}EXECCNTL, *PEXECCNTL; + +// +// For thread function +// +typedef struct thread_param{ + ChmCntrl* pCntlObj; + PEXECCNTL pexeccntl; + PCHLDCNTL pmycntl; +}THPARAM, *PTHPARAM; + +//--------------------------------------------------------- +// Utility Functions +//--------------------------------------------------------- +static inline void PRN(const char* format, ...) +{ + if(format){ + va_list ap; + va_start(ap, format); + vfprintf(stdout, format, ap); + va_end(ap); + } + fprintf(stdout, "\n"); +} + +#define PRNFLAG(flag, ...) if(flag){ PRN(__VA_ARGS__); } + +static inline void ERR(const char* format, ...) +{ + fprintf(stderr, "[ERR] "); + if(format){ + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + } + fprintf(stderr, "\n"); +} + +static inline char* programname(char* prgpath) +{ + if(!prgpath){ + return NULL; + } + char* pprgname = basename(prgpath); + if(0 == strncmp(pprgname, "lt-", strlen("lt-"))){ + pprgname = &pprgname[strlen("lt-")]; + } + return pprgname; +} + +static bool SetStringBytes(string& str, size_t totallength, char ch) +{ + if(totallength <= 0){ + return false; + } + if((str.length() + 1) == totallength){ + // nothing to do + }else if((str.length() + 1) < totallength){ + for(size_t cnt = totallength - (str.length() + 1); 0 < cnt; cnt--){ + str += ch; + } + }else{ // (str.length() + 1) > totallength + str = str.substr(0, (totallength - 1)); + } + return true; +} + +inline string to_short(string& base) +{ + return base.substr(0, MINIMUM_DATA_LENGTH); +} + +static inline void init_bench_opts(BOPTS& benchopts) +{ + memset(&benchopts.szConfig[0], 0, BENCH_CONF_LENGTH); + + benchopts.isServerMode = false; + benchopts.LoopCnt = 1; + benchopts.proccnt = 1; + benchopts.threadcnt = 1; + benchopts.isPrint = true; + benchopts.isTrunAround = true; + benchopts.DataLength = MINIMUM_DATA_LENGTH; + benchopts.isBroadcast = false; +} + +static inline void copy_bench_opts(BOPTS& dest, const BOPTS& src) +{ + memcpy(&dest.szConfig[0], &src.szConfig[0], BENCH_CONF_LENGTH); + + dest.isServerMode = src.isServerMode; + dest.LoopCnt = src.LoopCnt; + dest.proccnt = src.proccnt; + dest.threadcnt = src.threadcnt; + dest.isPrint = src.isPrint; + dest.isTrunAround = src.isTrunAround; + dest.DataLength = src.DataLength; + dest.isBroadcast = src.isBroadcast; +} + +static inline void get_nomotonic_time(struct timespec& ts) +{ + if(-1 == clock_gettime(CLOCK_MONOTONIC, &ts)){ + ERR("Could not get CLOCK_MONOTONIC timespec, but continue...(errno=%d)", errno); + ts.tv_sec = 0; + ts.tv_nsec = 0; + } +} + +static inline void get_nomotonic_time(struct timespec& diffts, const struct timespec& startts) +{ + struct timespec endts; + if(-1 == clock_gettime(CLOCK_MONOTONIC, &endts)){ + ERR("Could not get CLOCK_MONOTONIC timespec, but continue...(errno=%d)", errno); + endts.tv_sec = 0; + endts.tv_nsec = 0; + } + + if(endts.tv_nsec < startts.tv_nsec){ + if(0 < endts.tv_sec){ + endts.tv_sec--; + endts.tv_nsec += 1000 * 1000 * 1000; + }else{ + ERR("Start time > End time, so result is ZERO."); + diffts.tv_sec = 0; + diffts.tv_nsec = 0; + return; + } + } + if(endts.tv_sec < startts.tv_sec){ + ERR("Start time > End time, so result is ZERO."); + diffts.tv_sec = 0; + diffts.tv_nsec = 0; + return; + } + diffts.tv_nsec = endts.tv_nsec - startts.tv_nsec; + diffts.tv_sec = endts.tv_sec - startts.tv_sec; +} + +//--------------------------------------------------------- +// Utility Functions +//--------------------------------------------------------- +// Parse parameters +// +static void Help(char* progname) +{ + PRN(""); + PRN("Usage: %s [ -s | -c ] [options]", progname ? programname(progname) : "program"); + PRN(""); + PRN("Option [ -s | -c ] process mode: server or client"); + PRN(" -conf [file name] Configuration file( .ini / .json / .yaml ) path"); + PRN(" -json [json] Configuration JSON string\n"); + PRN(" -l [loop count] Loop count(default 1), 0 means no limit."); + PRN(" -proccnt process count."); + PRN(" -threadcnt thread count per one process."); + PRN(" -ta Turn around mode.(message is terned around on server to slave.)"); + PRN(" -b Broadcast message(only client mode)"); + PRN(" -dl [bytes] send message size.(minimum bytes is %d)", MINIMUM_DATA_LENGTH); + PRN(" -pr print sending/recieving message."); + PRN(" -mergedump display merge request/data."); + PRN(" -d [debug level] \"ERR\" / \"WAN\" / \"INF\" / \"DUMP\""); + PRN(" -h display help"); + PRN(""); +} + +//--------------------------------------------------------- +// Parameter +//--------------------------------------------------------- +static bool SetBenchOptions(ChmOpts& opts, BOPTS& benchopts) +{ + // configuration option + string config(""); + if(opts.Get("conf", config) || opts.Get("f", config)){ + MSG_CHMPRN("Configuration file is %s.", config.c_str()); + }else if(opts.Get("json", config)){ + MSG_CHMPRN("Configuration JSON is \"%s\"", config.c_str()); + }else{ + // check environment + if(!getenv_chm_conffile(config) || !getenv_chm_jsonconf(config)){ + ERR("ERROR: There is no parameter \"-conf\"(\"-f\") or \"-json\"."); + exit(EXIT_FAILURE); + } + } + strncpy(benchopts.szConfig, config.c_str(), BENCH_CONF_LENGTH - 1); + + // loop count + benchopts.LoopCnt = 1; + string strloop; + if(opts.Get("l", strloop)){ + if(0 > (benchopts.LoopCnt = atoi(strloop.c_str()))){ + ERR("ERROR: \"-l\" option value is wrong, must set 0 - xxxx."); + exit(EXIT_FAILURE); + } + } + + // process mode. + if(opts.Find("s")){ + benchopts.isServerMode = true; + }else if(opts.Find("c")){ + benchopts.isServerMode = false; + }else{ + ERR("ERROR: There is no parameter \"-s\" or \"-c\"."); + exit(EXIT_FAILURE); + } + + // parallel count. + benchopts.proccnt = 1; + benchopts.threadcnt = 1; + string strproccnt; + string strthreadcnt; + if(opts.Get("proccnt", strproccnt)){ + if(1 > (benchopts.proccnt = atoi(strproccnt.c_str()))){ + ERR("ERROR: \"-proccnt\" option value is wrong, must set 1 - xxxx."); + exit(EXIT_FAILURE); + } + } + if(opts.Get("threadcnt", strthreadcnt)){ + if(1 > (benchopts.threadcnt = atoi(strthreadcnt.c_str()))){ + ERR("ERROR: \"-threadcnt\" option value is wrong, must set 1 - xxxx."); + exit(EXIT_FAILURE); + } + } + + // turn-around mode. + if(opts.Find("ta")){ + benchopts.isTrunAround = true; + }else{ + benchopts.isTrunAround = false; + } + + // print mode. + if(opts.Find("pr")){ + benchopts.isPrint = true; + }else{ + benchopts.isPrint = false; + } + + // data length. + string strdatalen; + if(opts.Get("dl", strdatalen)){ + int tmp; + if(MINIMUM_DATA_LENGTH > (tmp = atoi(strdatalen.c_str()))){ + ERR("ERROR: \"-dl\" option value is wrong, must set %d - xxxx.", MINIMUM_DATA_LENGTH); + exit(EXIT_FAILURE); + } + benchopts.DataLength = static_cast(tmp); + }else{ + benchopts.DataLength = MINIMUM_DATA_LENGTH; + } + + // broadcast mode. + if(opts.Find("b")){ + if(benchopts.isServerMode){ + ERR("ERROR: Could not specify \"-b(broadcast)\" option with \"-s\" server mode."); + exit(EXIT_FAILURE); + } + benchopts.isBroadcast = true; + }else{ + benchopts.isBroadcast = false; + } + return true; +} + +//--------------------------------------------------------- +// Common Mmap file +//--------------------------------------------------------- +static int OpenBenchFile(const string filepath, size_t& totalsize, bool is_create) +{ + int fd; + + if(is_create){ + // remove exist file + struct stat st; + if(0 == stat(filepath.c_str(), &st)){ + // file exists + if(0 != unlink(filepath.c_str())){ + ERR("Could not remove exist file(%s), errno=%d", filepath.c_str(), errno); + return -1; + } + } + + // create file + if(-1 == (fd = open(filepath.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH))){ + ERR("Could not make file(%s), errno=%d", filepath.c_str(), errno); + return -1; + } + + // truncate & clean up + if(0 != ftruncate(fd, totalsize)){ + ERR("Could not truncate file(%s) to %zu, errno = %d", filepath.c_str(), totalsize, errno); + CHM_CLOSE(fd); + return -1; + } + unsigned char szBuff[1024]; + memset(szBuff, 0, sizeof(szBuff)); + for(ssize_t wrote = 0, onewrote = 0; static_cast(wrote) < totalsize; wrote += onewrote){ + onewrote = min(static_cast(sizeof(unsigned char) * 1024), (totalsize - static_cast(wrote))); + if(-1 == (onewrote = pwrite(fd, szBuff, static_cast(onewrote), static_cast(wrote)))){ + ERR("Failed to write initializing file(%s), errno = %d", filepath.c_str(), errno); + CHM_CLOSE(fd); + return -1; + } + } + }else{ + struct stat st; + if(0 != stat(filepath.c_str(), &st)){ + // file does not exist + ERR("Could not find file(%s)", filepath.c_str()); + return -1; + } + if(-1 == (fd = open(filepath.c_str(), O_RDWR))){ + ERR("Could not open file(%s), errno = %d", filepath.c_str(), errno); + return -1; + } + totalsize = static_cast(st.st_size); + } + return fd; +} + +static void* MmapBenchFile(bool is_create, int proccnt, int threadcnt, string& filepath, int& fd, PEXECCNTL& pexeccntl, PCHLDCNTL& pchldcntl, size_t& totalsize) +{ + // file path + if(is_create){ + char szBuff[32]; + sprintf(szBuff, BENCH_TMP_FILE_FORM, gettid()); + filepath = szBuff; + totalsize = sizeof(EXECCNTL) + (sizeof(CHLDCNTL) * proccnt * threadcnt); + }else{ + totalsize = 0; + } + + // open + if(-1 == (fd = OpenBenchFile(filepath, totalsize, is_create))){ + ERR("Could not open(initialize) file(%s)", filepath.c_str()); + return NULL; + } + + // mmap + void* pShm; + if(MAP_FAILED == (pShm = mmap(NULL, totalsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0))){ + ERR("Could not mmap to file(%s), errno = %d", filepath.c_str(), errno); + CHM_CLOSE(fd); + return NULL; + } + + // set pointer + pexeccntl = reinterpret_cast(pShm); + pchldcntl = reinterpret_cast(reinterpret_cast(pShm) + sizeof(EXECCNTL)); + + return pShm; +} + +//--------------------------------------------------------- +// Merge test functions +//--------------------------------------------------------- +#define MERGE_DUMMY_DATA_FORM "%d - MERGE DATA" + +static bool IsMergeDump = false; + +static bool MergeGetLastTimeCallback(chmpx_h handle, struct timespec* pts) +{ + if(!pts){ + ERR("Merge MergeGetLastTimeCallback parameter is wrong."); + return false; + } + pts->tv_sec = 0; + pts->tv_nsec= 0; + + if(IsMergeDump){ + PRN("CALLBACK: MergeGetLastTimeCallback(chmpx_h(0x%016" PRIx64 "), timespec*(%p))", handle, pts); + } + return true; +} + +static bool MergeGetCallback(chmpx_h handle, const PCHM_MERGE_GETPARAM pparam, chmhash_t* pnexthash, PCHMBIN* ppdatas, size_t* pdatacnt) +{ + if(!pparam || !pnexthash || !ppdatas || !pdatacnt){ + ERR("Merge MergeGetCallback parameter is wrong."); + return false; + } + + if(IsMergeDump){ + PRN("CALLBACK: MergeGetCallback(chmpx_h(0x%016" PRIx64 "), pparam(%p), chmhash_t(%p), PCHMBIN(%p), size_t(%p))", handle, pparam, pnexthash, ppdatas, pdatacnt); + PRN(" PARAM = {"); + PRN(" starthash = 0x%016" PRIx64 ",", pparam->starthash); + PRN(" startts.tv_sec = %zu.", pparam->startts.tv_sec); + PRN(" startts.tv_nsec = %ld.", pparam->startts.tv_nsec); + PRN(" endts.tv_sec = %zu.", pparam->endts.tv_sec); + PRN(" endts.tv_nsec = %ld.", pparam->endts.tv_nsec); + PRN(" target_hash = 0x%016" PRIx64 ",", pparam->target_hash); + PRN(" target_max_hash = 0x%016" PRIx64 ",", pparam->target_max_hash); + PRN(" target_hash_range = 0x%016" PRIx64 , pparam->target_hash_range); + PRN(" }"); + } + + if(0x5555 == pparam->starthash){ + // second call -> end + *pnexthash = CHM_INVALID_HASHVAL; + *pdatacnt = 0; + *ppdatas = NULL; + + if(IsMergeDump){ + PRN(" RESULT(END OF MERGE) = {"); + PRN(" nexthash = -1"); + PRN(" datacnt = 0"); + PRN(" pdatas = NULL"); + PRN(" pdatas->byptr = NULL"); + PRN(" pdatas->lengfth = 0"); + PRN(" }"); + } + }else{ + // first call + char szBuff[256]; + sprintf(szBuff, MERGE_DUMMY_DATA_FORM, getpid()); + + *pnexthash = 0x5555; + *pdatacnt = 1; + *ppdatas = reinterpret_cast(malloc(sizeof(CHMBIN))); + (*ppdatas)->byptr = reinterpret_cast(malloc(strlen(szBuff) + 1)); + (*ppdatas)->length = strlen(szBuff) + 1; + strcpy(reinterpret_cast((*ppdatas)->byptr), szBuff); + + if(IsMergeDump){ + PRN(" RESULT(DO MERGE DATA) = {"); + PRN(" nexthash = 0x5555(dummy hash)"); + PRN(" datacnt = 1"); + PRN(" pdatas = %p", *ppdatas); + PRN(" pdatas->byptr = %p(%s)", (*ppdatas ? (*ppdatas)->byptr : NULL), ((*ppdatas && (*ppdatas)->byptr) ? reinterpret_cast((*ppdatas)->byptr) : "null")); + PRN(" pdatas->lengfth = %zu", (*ppdatas ? (*ppdatas)->length : 0)); + PRN(" }"); + } + } + return true; +} + +static bool MergeSetCallback(chmpx_h handle, size_t length, const unsigned char* pdata, const struct timespec* pts) +{ + if(!pdata || !pts){ + ERR("Merge MergeSetCallback parameter is wrong."); + return false; + } + + if(IsMergeDump){ + PRN("CALLBACK: MergeSetCallback(chmpx_h(0x%016" PRIx64 "), length(%zu), pdata(%p: \"%s\"), pts(sec=%zu, nsec=%ld))", handle, length, pdata, reinterpret_cast(pdata), pts->tv_sec, pts->tv_nsec); + } + return true; +} + +//--------------------------------------------------------- +// Execute Functions +//--------------------------------------------------------- +static void* RunServerModeThread(void* param) +{ + PTHPARAM pThParam = reinterpret_cast(param); + if(!pThParam || !pThParam->pmycntl){ + ERR("Parameter for thread is wrong."); + pthread_exit(NULL); + } + + pThParam->pmycntl->threadid = gettid(); + if(!pThParam->pCntlObj || !pThParam->pexeccntl){ + ERR("Parameter for thread is wrong."); + pThParam->pmycntl->is_ready = true; + pThParam->pmycntl->is_exit = true; + pthread_exit(NULL); + } + + // wait for suspend flag off + while(pThParam->pexeccntl->is_suspend && !pThParam->pexeccntl->is_exit){ + struct timespec sleeptime = {0L, 10 * 1000 * 1000}; // 10ms + nanosleep(&sleeptime, NULL); + } + if(pThParam->pexeccntl->is_exit){ + ERR("Exit thread ASSAP."); + pThParam->pmycntl->is_ready = true; + pThParam->pmycntl->is_exit = true; + pthread_exit(NULL); + } + + //--------------------------- + // Initialize internal datas. + //--------------------------- + string strbase = to_hexstring(gettid()) + "-"; + + // wait for start + pThParam->pmycntl->is_ready = true; + while(pThParam->pexeccntl->is_wait_doing && !pThParam->pexeccntl->is_exit){ + struct timespec sleeptime = {0L, 1 * 1000 * 1000}; // 1ms + nanosleep(&sleeptime, NULL); + } + if(pThParam->pexeccntl->is_exit){ + ERR("Exit thread ASSAP."); + pThParam->pmycntl->is_exit = true; + pthread_exit(NULL); + } + + // set start timespec + struct timespec start; + get_nomotonic_time(start); + + //--------------------------- + // Loop: Do read/write to k2hash + //--------------------------- + for(int cnt = 0; 0 == pThParam->pexeccntl->opt.LoopCnt || cnt < pThParam->pexeccntl->opt.LoopCnt; ++cnt){ + PCOMPKT pComPkt = NULL; + unsigned char* pbody = NULL; + size_t length = 0L; + + if(!pThParam->pCntlObj->Receive(&pComPkt, &pbody, &length, 1000, true)){ // timeout 1s, auto rejoin + ERR_CHMPRN("Something error occured at waiting message."); + pThParam->pmycntl->errorcnt += (0 == pThParam->pexeccntl->opt.LoopCnt ? 0 : (pThParam->pexeccntl->opt.LoopCnt - cnt)); + pthread_exit(NULL); + } + if(!pComPkt){ + MSG_CHMPRN("Receiving message is timeouted(1s)."); + pThParam->pmycntl->errorcnt++; + }else{ + MSG_CHMPRN("Succeed to receive message."); + + string strReceive = pbody ? reinterpret_cast(pbody) : ""; + PRNFLAG(pThParam->pexeccntl->opt.isPrint, "Receive : %s", to_short(strReceive).c_str()); + + if(pThParam->pexeccntl->opt.isTrunAround){ + strReceive += ":"; + strReceive += strbase; + strReceive += to_hexstring(cnt); // max "ffffffff-ffffffff:ffffffff-ffffffff" 18 + 17 + 1 bytes + SetStringBytes(strReceive, pThParam->pexeccntl->opt.DataLength, FILL_CHARACTOR); + + // reply + if(!pThParam->pCntlObj->Reply(pComPkt, reinterpret_cast(strReceive.c_str()), strReceive.length() + 1)){ + if(!pThParam->pCntlObj->IsChmpxExit()){ + ERR_CHMPRN("Something error occured at replying message."); + pThParam->pmycntl->errorcnt += (0 == pThParam->pexeccntl->opt.LoopCnt ? 0 : (pThParam->pexeccntl->opt.LoopCnt - cnt)); + CHM_Free(pComPkt); + CHM_Free(pbody); + pthread_exit(NULL); + } + pThParam->pmycntl->errorcnt++; + } + } + CHM_Free(pComPkt); + CHM_Free(pbody); + } + } + + // set timespec and exit flag + get_nomotonic_time(pThParam->pmycntl->ts, start); + pThParam->pmycntl->is_exit = true; + + pthread_exit(NULL); + return NULL; +} + +static void* RunSlaveModeThread(void* param) +{ + PTHPARAM pThParam = reinterpret_cast(param); + if(!pThParam || !pThParam->pmycntl){ + ERR("Parameter for thread is wrong."); + pthread_exit(NULL); + } + + pThParam->pmycntl->threadid = gettid(); + if(!pThParam->pCntlObj || !pThParam->pexeccntl){ + ERR("Parameter for thread is wrong."); + pThParam->pmycntl->is_ready = true; + pThParam->pmycntl->is_exit = true; + pthread_exit(NULL); + } + + // wait for suspend flag off + while(pThParam->pexeccntl->is_suspend && !pThParam->pexeccntl->is_exit){ + struct timespec sleeptime = {0L, 10 * 1000 * 1000}; // 10ms + nanosleep(&sleeptime, NULL); + } + if(pThParam->pexeccntl->is_exit){ + ERR("Exit thread ASSAP."); + pThParam->pmycntl->is_ready = true; + pThParam->pmycntl->is_exit = true; + pthread_exit(NULL); + } + + //--------------------------- + // Initialize internal datas. + //--------------------------- + // open msgid + msgid_t msgid = pThParam->pCntlObj->Open(true); + if(CHM_INVALID_MSGID == msgid){ + ERR("Could not open message queue id."); + pThParam->pmycntl->errorcnt += pThParam->pexeccntl->opt.LoopCnt; + pthread_exit(NULL); + } + string strbase = to_hexstring(gettid()) + "-"; + string strmessage; + + // wait for start + pThParam->pmycntl->is_ready = true; + while(pThParam->pexeccntl->is_wait_doing && !pThParam->pexeccntl->is_exit){ + struct timespec sleeptime = {0L, 1 * 1000 * 1000}; // 1ms + nanosleep(&sleeptime, NULL); + } + if(pThParam->pexeccntl->is_exit){ + ERR("Exit thread ASSAP."); + pThParam->pmycntl->is_exit = true; + pthread_exit(NULL); + } + + // set start timespec + struct timespec start; + get_nomotonic_time(start); + + //--------------------------- + // Loop: Do read/write to k2hash + //--------------------------- + for(int cnt = 0; 0 == pThParam->pexeccntl->opt.LoopCnt || cnt < pThParam->pexeccntl->opt.LoopCnt; ++cnt){ + PCOMPKT pComPkt = NULL; + unsigned char* pbody = NULL; + size_t length = 0L; + long receiver= 0L; + bool result; + + strmessage = strbase; + strmessage += to_hexstring(cnt); // max "ffffffff-ffffffff" 17+1 bytes + SetStringBytes(strmessage, pThParam->pexeccntl->opt.DataLength, FILL_CHARACTOR); + + if(pThParam->pexeccntl->opt.isBroadcast){ + result = pThParam->pCntlObj->Broadcast(msgid, reinterpret_cast(strmessage.c_str()), strmessage.length() + 1, static_cast(cnt), &receiver); + }else{ + result = pThParam->pCntlObj->Send(msgid, reinterpret_cast(strmessage.c_str()), strmessage.length() + 1, static_cast(cnt), &receiver); + } + if(!result){ + if(!pThParam->pCntlObj->IsChmpxExit()){ + PRNFLAG(pThParam->pexeccntl->opt.isPrint, "Send : %s -> Failed", to_short(strmessage).c_str()); + ERR_CHMPRN("Something error occured at sending message."); + }else{ + PRNFLAG(pThParam->pexeccntl->opt.isPrint, "Send : %s -> Failed(chmpx process down)", to_short(strmessage).c_str()); + // rejoin + msgid = pThParam->pCntlObj->Open(true); + } + pThParam->pmycntl->errorcnt++; + continue; + } + PRNFLAG(pThParam->pexeccntl->opt.isPrint, "Send : %s -> Succeed(receiver count:%ld)", to_short(strmessage).c_str(), receiver); + + if(pThParam->pexeccntl->opt.isTrunAround){ + for(long rcnt = 0; ((0 >= receiver) ? (rcnt < 1) : (rcnt < receiver)); ++rcnt){ + if(!pThParam->pCntlObj->Receive(msgid, &pComPkt, &pbody, &length, 1000)){ // timeout 1s + if(!pThParam->pCntlObj->IsChmpxExit()){ + PRNFLAG(pThParam->pexeccntl->opt.isPrint, "Receive reply : -> 1 Failed"); + ERR_CHMPRN("Something error occured at waiting reply message."); + }else{ + PRNFLAG(pThParam->pexeccntl->opt.isPrint, "Receive reply : -> 1 Failed(chmpx process down)"); + // rejoin + msgid = pThParam->pCntlObj->Open(true); + } + pThParam->pmycntl->errorcnt++; + break; + } + + if(!pComPkt){ + PRNFLAG(pThParam->pexeccntl->opt.isPrint, "Receive reply : -> 1 Failed(timeouted - 1s)"); + MSG_CHMPRN("Receiving message is timeouted(1s)."); + pThParam->pmycntl->errorcnt++; + }else{ + string strReply = pbody ? reinterpret_cast(pbody) : ""; + PRNFLAG(pThParam->pexeccntl->opt.isPrint, "Receive reply : -> 1 Succeed(%s)", to_short(strReply).c_str()); + MSG_CHMPRN("Succeed to receive message."); + CHM_Free(pComPkt); + CHM_Free(pbody); + } + } + } + } + + // set timespec and exit flag + get_nomotonic_time(pThParam->pmycntl->ts, start); + pThParam->pmycntl->is_exit = true; + + pthread_exit(NULL); + return NULL; +} + +static int RunChild(string cntlfile) +{ + pid_t pid = getpid(); + + // mmap(attach) control file + int cntlfd = -1; + void* pShmBase = NULL; + PEXECCNTL pexeccntl = NULL; + PCHLDCNTL pchldcntl = NULL; + size_t totalsize = 0; + if(NULL == (pShmBase = MmapBenchFile(false, 0, 0, cntlfile, cntlfd, pexeccntl, pchldcntl, totalsize))){ + ERR("Could not mmap to file(%s), errno = %d", cntlfile.c_str(), errno); + return EXIT_FAILURE; + } + + // wait for suspend flag off + while(pexeccntl->is_suspend && !pexeccntl->is_exit){ + struct timespec sleeptime = {0L, 10 * 1000 * 1000}; // 10ms + nanosleep(&sleeptime, NULL); + } + if(pexeccntl->is_exit){ + ERR("Exit process ASSAP."); + munmap(pShmBase, totalsize); + CHM_CLOSE(cntlfd); + return EXIT_FAILURE; + } + + // search my start pos of PCHLDCNTL. + PCHLDCNTL pmycntl = NULL; + for(int cnt = 0; cnt < pexeccntl->opt.proccnt * pexeccntl->opt.threadcnt; cnt += pexeccntl->opt.threadcnt){ + if(pchldcntl[cnt].procid == pid){ + pmycntl = &pchldcntl[cnt]; + break; + } + } + if(!pmycntl){ + ERR("Could not find my procid in PCHLDCNTL."); + munmap(pShmBase, totalsize); + CHM_CLOSE(cntlfd); + return EXIT_FAILURE; + } + + // Control Object + ChmCntrl* pCntlObj= new ChmCntrl(); + bool bresult; + if(pexeccntl->opt.isServerMode){ + bresult = pCntlObj->InitializeOnServer(&pexeccntl->opt.szConfig[0], true, MergeGetCallback, MergeSetCallback, MergeGetLastTimeCallback); // auto rejoin + }else{ + bresult = pCntlObj->InitializeOnSlave(&pexeccntl->opt.szConfig[0], true); // auto rejoin + } + if(!bresult){ + ERR("Could not initialize internal object/data/etc."); + CHM_Delete(pCntlObj); + munmap(pShmBase, totalsize); + CHM_CLOSE(cntlfd); + return EXIT_FAILURE; + } + + // create threads + PTHPARAM pthparam = new THPARAM[pexeccntl->opt.threadcnt]; + for(int cnt = 0; cnt < pexeccntl->opt.threadcnt; ++cnt){ + // initialize + pmycntl[cnt].procid = pid; + pmycntl[cnt].threadid = 0; + pmycntl[cnt].pthreadid = 0; + pmycntl[cnt].is_ready = false; + pmycntl[cnt].is_exit = false; // *1 + pmycntl[cnt].ts.tv_sec = 0; + pmycntl[cnt].ts.tv_nsec = 0; + pmycntl[cnt].errorcnt = 0; + + // create thread + pthparam[cnt].pCntlObj = pCntlObj; + pthparam[cnt].pexeccntl = pexeccntl; + pthparam[cnt].pmycntl = &pmycntl[cnt]; + if(0 != pthread_create(&(pmycntl[cnt].pthreadid), NULL, pexeccntl->opt.isServerMode ? RunServerModeThread : RunSlaveModeThread, &(pthparam[cnt]))){ + ERR("Could not create thread."); + pmycntl[cnt].is_exit = true; // *1 + break; + } + } + + // wait all thread exit + for(int cnt = 0; cnt < pexeccntl->opt.threadcnt; ++cnt){ + if(0 == pmycntl[cnt].pthreadid){ + continue; + } + void* pretval = NULL; + int result; + if(0 != (result = pthread_join(pmycntl[cnt].pthreadid, &pretval))){ + ERR("Failed to wait thread exit. return code(error) = %d", result); + continue; + } + } + K2H_Delete(pthparam); + + // exit + return EXIT_SUCCESS; +} + +//--------------------------------------------------------- +// Print Result +//--------------------------------------------------------- +static void PrintResult(const PEXECCNTL pexeccntl, const PCHLDCNTL pchldcntl, struct timespec& realtime) +{ + if(!pexeccntl || !pchldcntl){ + ERR("Parameters are wrong."); + return; + } + + // calc total + struct timespec addtotalts = {0, 0}; + int totalerr = 0; + for(int cnt = 0; cnt < pexeccntl->opt.proccnt * pexeccntl->opt.threadcnt; ++cnt){ + totalerr += pchldcntl[cnt].errorcnt; + addtotalts.tv_sec += pchldcntl[cnt].ts.tv_sec; + addtotalts.tv_nsec += pchldcntl[cnt].ts.tv_nsec; + if((1000 * 1000 * 1000) < addtotalts.tv_nsec){ + addtotalts.tv_nsec -= 1000 * 1000 * 1000; + addtotalts.tv_sec += 1; + } + } + // additional measurement total time + long addtotalfns = (addtotalts.tv_sec * 1000 * 1000 * 1000) + addtotalts.tv_nsec; + long addtotalsec = static_cast(addtotalts.tv_sec); + long addtotalms = (addtotalfns / (1000 * 1000)) % 1000; + long addtotalus = (addtotalfns / 1000) % 1000; + long addtotalns = addtotalfns % 1000; + + // additional measurement average time + long addavrgfns = addtotalfns / static_cast(pexeccntl->opt.proccnt * pexeccntl->opt.threadcnt * (0 == pexeccntl->opt.LoopCnt ? 1 : pexeccntl->opt.LoopCnt)); // Loop count + long addavrgsec = addavrgfns / (1000 * 1000 * 1000); + long addavrgms = (addavrgfns / (1000 * 1000)) % 1000; + long addavrgus = (addavrgfns / 1000) % 1000; + long addavrgns = addavrgfns % 1000; + + // additional measurement total time + long realtotalfns= (realtime.tv_sec * 1000 * 1000 * 1000) + realtime.tv_nsec; + long realtotalsec= static_cast(realtime.tv_sec); + long realtotalms = (realtotalfns / (1000 * 1000)) % 1000; + long realtotalus = (realtotalfns / 1000) % 1000; + long realtotalns = realtotalfns % 1000; + + // additional measurement average time + long realavrgfns = realtotalfns / static_cast(pexeccntl->opt.proccnt * pexeccntl->opt.threadcnt * (0 == pexeccntl->opt.LoopCnt ? 1 : pexeccntl->opt.LoopCnt)); // Loop count + long realavrgsec = realavrgfns / (1000 * 1000 * 1000); + long realavrgms = (realavrgfns / (1000 * 1000)) % 1000; + long realavrgus = (realavrgfns / 1000) % 1000; + long realavrgns = realavrgfns % 1000; + + PRN("==========================================================="); + PRN("CHMPX bench mark"); + PRN("-----------------------------------------------------------"); + PRN("CHMPX mode %s", pexeccntl->opt.isServerMode ? "Server mode" : "Slave mode"); + PRN("CHMPX configration %s", pexeccntl->opt.szConfig); + PRN("-----------------------------------------------------------"); + PRN("Total loop count %ld", 0 == pexeccntl->opt.LoopCnt ? 0 : pexeccntl->opt.proccnt * pexeccntl->opt.threadcnt * pexeccntl->opt.LoopCnt); + PRN(" Process count %ld", pexeccntl->opt.proccnt); + PRN(" Thread count %ld", pexeccntl->opt.threadcnt); + PRN(" Loop count %ld", pexeccntl->opt.LoopCnt); + PRN("Data length %zu", pexeccntl->opt.DataLength); + PRN("Turn Around mode %s", pexeccntl->opt.isTrunAround ? "yes" : "no"); + PRN("Broadcast mode %s", pexeccntl->opt.isBroadcast ? "yes" : "no"); + if(0 == pexeccntl->opt.LoopCnt){ + PRN(""); + PRN("* If loop count is 0, it means no limited loop. So average times is calculated by 1 loop."); + PRN(" Then average times can not be the correct value. So not display those."); + } + PRN("-----------------------------------------------------------"); + PRN("Total error count %ld", totalerr); + PRN("Total time(addition thread) %03lds %03ldms %03ldus %03ldns (%ldns)", addtotalsec, addtotalms, addtotalus, addtotalns, addtotalfns); + if(0 != pexeccntl->opt.LoopCnt){ + PRN("Average time(addition thread) %03lds %03ldms %03ldus %03ldns (%ldns)", addavrgsec, addavrgms, addavrgus, addavrgns, addavrgfns); + } + PRN("Total time(real) %03lds %03ldms %03ldus %03ldns (%ldns)", realtotalsec, realtotalms, realtotalus, realtotalns, realtotalfns); + if(0 != pexeccntl->opt.LoopCnt){ + PRN("Average time(real) %03lds %03ldms %03ldus %03ldns (%ldns)", realavrgsec, realavrgms, realavrgus, realavrgns, realavrgfns); + } + PRN("-----------------------------------------------------------"); +} + +//--------------------------------------------------------- +// Main +//--------------------------------------------------------- +int main(int argc, char** argv) +{ + // parse parameters + ChmOpts opts((argc - 1), &argv[1]); + + // help + if(opts.Find("h") || opts.Find("help")){ + Help(argv[0]); + exit(EXIT_SUCCESS); + } + + // DBG Mode + string dbgmode; + SetChmDbgMode(CHMDBG_SILENT); + if(opts.Get("g", dbgmode) || opts.Get("d", dbgmode)){ + if(0 == strcasecmp(dbgmode.c_str(), "ERR")){ + SetChmDbgMode(CHMDBG_ERR); + }else if(0 == strcasecmp(dbgmode.c_str(), "WAN")){ + SetChmDbgMode(CHMDBG_WARN); + }else if(0 == strcasecmp(dbgmode.c_str(), "INF") || 0 == strcasecmp(dbgmode.c_str(), "MSG")){ + SetChmDbgMode(CHMDBG_MSG); + }else if(0 == strcasecmp(dbgmode.c_str(), "DUMP")){ + SetChmDbgMode(CHMDBG_DUMP); + }else{ + ERR("ERROR: Wrong parameter value \"-d\"(\"-g\") %s.", dbgmode.c_str()); + exit(EXIT_FAILURE); + } + } + if(opts.Find("mergedump")){ + IsMergeDump = true; + } + + // set all option + BOPTS benchopts; + init_bench_opts(benchopts); + if(!SetBenchOptions(opts, benchopts)){ + exit(EXIT_FAILURE); + } + + // Initialize control file + string cntlfile; + int cntlfd = -1; + void* pShmBase = NULL; + PEXECCNTL pexeccntl = NULL; + PCHLDCNTL pchldcntl = NULL; + size_t totalsize = 0; + if(NULL == (pShmBase = MmapBenchFile(true, benchopts.proccnt, benchopts.threadcnt, cntlfile, cntlfd, pexeccntl, pchldcntl, totalsize))){ + ERR("Could not mmap to file(%s), errno = %d", cntlfile.c_str(), errno); + exit(EXIT_SUCCESS); + } + pexeccntl->procid = getpid(); + pexeccntl->is_suspend = true; + pexeccntl->is_wait_doing= true; + pexeccntl->is_exit = false; + copy_bench_opts(pexeccntl->opt, benchopts); + for(int cnt = 0; cnt < (benchopts.proccnt * benchopts.threadcnt); ++cnt){ + pchldcntl[cnt].procid = 0; + pchldcntl[cnt].threadid = 0; + pchldcntl[cnt].pthreadid = 0; + pchldcntl[cnt].is_ready = false; + pchldcntl[cnt].is_exit = true; + pchldcntl[cnt].ts.tv_sec = 0; + pchldcntl[cnt].ts.tv_nsec = 0; + pchldcntl[cnt].errorcnt = 0; + } + + // timespec for real time measurement + struct timespec realstart; + struct timespec realtime; + + //--------------------------------------- + // Run child process/threads + //--------------------------------------- + int childcnt = 0; + for(childcnt = 0; childcnt < benchopts.proccnt; ++childcnt){ + // initialize + for(int pcnt = 0; pcnt < benchopts.threadcnt; ++pcnt){ + pchldcntl[childcnt * benchopts.threadcnt + pcnt].procid = 0; + pchldcntl[childcnt * benchopts.threadcnt + pcnt].threadid = 0; + pchldcntl[childcnt * benchopts.threadcnt + pcnt].pthreadid = 0; + pchldcntl[childcnt * benchopts.threadcnt + pcnt].is_ready = false; + pchldcntl[childcnt * benchopts.threadcnt + pcnt].is_exit = false; // *1 + pchldcntl[childcnt * benchopts.threadcnt + pcnt].ts.tv_sec = 0; + pchldcntl[childcnt * benchopts.threadcnt + pcnt].ts.tv_nsec = 0; + pchldcntl[childcnt * benchopts.threadcnt + pcnt].errorcnt = 0; + } + + pid_t pid = fork(); + if(-1 == pid){ + ERR("Could not fork child process, errno = %d", errno); + pexeccntl->is_exit = true; + + for(int pcnt = 0; pcnt < benchopts.threadcnt; ++pcnt){ + pchldcntl[childcnt * benchopts.threadcnt + pcnt].is_exit = true; // *1 + } + break; + } + if(0 == pid){ + // child process + int result = RunChild(cntlfile); + exit(result); + + }else{ + // parent process + for(int pcnt = 0; pcnt < benchopts.threadcnt; ++pcnt){ + pchldcntl[childcnt * benchopts.threadcnt + pcnt].procid = pid; + } + } + } + + // start children initializing + pexeccntl->is_suspend = false; + + // wait all children ready + if(!pexeccntl->is_exit){ + // wait for all children is ready + for(bool is_all_initialized = false; !is_all_initialized; ){ + is_all_initialized = true; + for(int cnt = 0; cnt < (benchopts.proccnt * benchopts.threadcnt); ++cnt){ + if(!pchldcntl[cnt].is_exit && !pchldcntl[cnt].is_ready){ + is_all_initialized = false; + break; + } + } + if(!is_all_initialized){ + struct timespec sleeptime = {0L, 10 * 1000 * 1000}; // 10ms + nanosleep(&sleeptime, NULL); + } + } + } + + // set start time point + get_nomotonic_time(realstart); + + // start(no blocking) doing + pexeccntl->is_wait_doing = false; + + // wait all process exit + pid_t exitpid; + int status = 0; + while(0 < (exitpid = waitpid(-1, &status, 0))){ + --childcnt; + if(0 >= childcnt){ + break; + } + } + + // set measurement timespec + get_nomotonic_time(realtime, realstart); + + //--------------------------------------- + // End + //--------------------------------------- + + // display result + PrintResult(pexeccntl, pchldcntl, realtime); + + // cleanup + munmap(pShmBase, totalsize); + CHM_CLOSE(cntlfd); + unlink(cntlfile.c_str()); + + exit(EXIT_SUCCESS); +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/tests/chmpxstatus.cc b/tests/chmpxstatus.cc new file mode 100644 index 0000000..fac9960 --- /dev/null +++ b/tests/chmpxstatus.cc @@ -0,0 +1,537 @@ +/* + * CHMPX + * + * Copyright 2016 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Fri Sep 2 2016 + * REVISION: + * + */ +#include +#include + +#include "chmcommon.h" +#include "chmstructure.h" +#include "chmutil.h" +#include "chmdbg.h" +#include "chmcntrl.h" +#include "chmopts.h" + +using namespace std; + +//--------------------------------------------------------- +// Utility Functions +//--------------------------------------------------------- +static inline void PRN(const char* format, ...) +{ + if(format){ + va_list ap; + va_start(ap, format); + vfprintf(stdout, format, ap); + va_end(ap); + } + fprintf(stdout, "\n"); +} + +static inline void ERR(const char* format, ...) +{ + fprintf(stderr, "[ERR] "); + if(format){ + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + } + fprintf(stderr, "\n"); +} + +static inline char* programname(char* prgpath) +{ + if(!prgpath){ + return NULL; + } + char* pprgname = basename(prgpath); + if(0 == strncmp(pprgname, "lt-", strlen("lt-"))){ + pprgname = &pprgname[strlen("lt-")]; + } + return pprgname; +} + +inline std::string PRN_TIMESPEC(const timespec& ts) +{ + char szBuff[32]; + string strResult; + + if(0 < ts.tv_sec){ + sprintf(szBuff, "%zus ", ts.tv_sec); + strResult += szBuff; + } + sprintf(szBuff, "%ldms ", (ts.tv_nsec % (1000 * 1000))); + strResult += szBuff; + + sprintf(szBuff, "%ldus ", ((ts.tv_nsec % (1000 * 1000)) / 1000)); + strResult += szBuff; + + sprintf(szBuff, "%ldns ", (ts.tv_nsec % 1000)); + strResult += szBuff; + + if(0 < ts.tv_sec){ + sprintf(szBuff, "(%zu%09ldns)", ts.tv_sec, ts.tv_nsec); + }else{ + sprintf(szBuff, "(%ldns)", ts.tv_nsec); + } + strResult += szBuff; + + return strResult; +} + +//--------------------------------------------------------- +// Utility Functions +//--------------------------------------------------------- +static void Help(char* progname) +{ + PRN(""); + PRN("Usage: %s -conf [-ctlport ] [-self] [-d [slient|err|wan|msg|dump]] [-dfile ]", progname ? programname(progname) : "program"); + PRN("Usage: %s -h", progname ? programname(progname) : "program"); + PRN(""); + PRN("Option"); + PRN(" -conf configuration file( .ini / .json / .yaml ) path"); + PRN(" -json configuration JSON string\n"); + PRN(" -ctlport specify the self contrl port(*)"); + PRN(" -self print only self chmpx"); + PRN(" -d specify the debugging output mode:"); + PRN(" silent - no output"); + PRN(" err - output error level"); + PRN(" wan - output warning level"); + PRN(" msg - output debug(message) level"); + PRN(" dump - output communication debug level"); + PRN(" -dfile specify the file path which is put output"); + PRN(" -h(help) display this usage."); + PRN(""); + PRN("(*) if ctlport option is specified, chmpx searches same ctlport in configuration"); + PRN(" file and ignores \"CTLPORT\" directive in \"GLOBAL\" section. and chmpx will"); + PRN(" start in the mode indicated by the server entry that has beed detected."); + PRN(""); +} + +//--------------------------------------------------------- +// Print +//--------------------------------------------------------- +static bool PrintAllInfo(ChmCntrl* pchmobj) +{ + if(!pchmobj){ + return false; + } + // Get information + PCHMINFOEX pInfo = pchmobj->DupAllChmInfo(); + if(!pInfo){ + ERR("Something error occurred in getting information."); + return false; + } + if(!pInfo->pchminfo){ + ERR("Something error occurred in getting information."); + ChmCntrl::FreeDupAllChmInfo(pInfo); + return false; + } + + int counter; + PCHMPXLIST pchmpxlist; + PMQMSGHEADLIST pmsglisttmp; + PCLTPROCLIST pproclist; + + // chmpx info & info ex + PRN("chmpx process id = %d", pInfo->pchminfo->pid); + PRN("process start time = %zu (unix time)", pInfo->pchminfo->start_time); + PRN("chmpx shared memory file path = %s", pInfo->shmpath); + PRN("chmpx shared memory file size = %zu (byte)", pInfo->shmsize); + PRN("k2hash file path = %s", pInfo->k2hashpath); + PRN("k2hash full mapping = %s", pInfo->pchminfo->k2h_fullmap ? "yes" : "no"); + PRN("k2hash mask bit count = %d", pInfo->pchminfo->k2h_mask_bitcnt); + PRN("k2hash collision mask bit count = %d", pInfo->pchminfo->k2h_cmask_bitcnt); + PRN("k2hash maximum element count = %d", pInfo->pchminfo->k2h_max_element); + PRN("random mode = %s", pInfo->pchminfo->is_random_deliver ? "yes" : "no"); + PRN("communication history count = %ld", pInfo->pchminfo->histlog_count); + PRN("auto merge = %s", pInfo->pchminfo->is_auto_merge ? "yes" : "no"); + PRN("merge processing(do merge) = %s", pInfo->pchminfo->is_do_merge ? "yes" :"no"); + PRN("timeout for merge = %zd (s)", pInfo->pchminfo->timeout_merge); + PRN("thread count for socket = %d", pInfo->pchminfo->evsock_thread_cnt); + PRN("thread count for MQ = %d", pInfo->pchminfo->evmq_thread_cnt); + PRN("maximum socket count per chmpx(socket pool) = %d", pInfo->pchminfo->max_sock_pool); + PRN("timeout for socket pool = %zd (s)", pInfo->pchminfo->sock_pool_timeout); + PRN("retry count on socket = %d", pInfo->pchminfo->sock_retrycnt); + PRN("timeout for send/recieve on socket = %ld (us)", pInfo->pchminfo->timeout_wait_socket); + PRN("timeout for connect on socket = %ld (us)", pInfo->pchminfo->timeout_wait_connect); + PRN("timeout for send/recieve on socket = %ld (us)", pInfo->pchminfo->timeout_wait_mq); + PRN("maximum MQ count = %ld", pInfo->pchminfo->max_mqueue); + PRN("chmpx process MQ count = %ld", pInfo->pchminfo->chmpx_mqueue); + PRN("maximum queue per chmpx process MQ = %ld", pInfo->pchminfo->max_q_per_chmpxmq); + PRN("maximum queue per client process MQ = %ld", pInfo->pchminfo->max_q_per_cltmq); + PRN("maximum MQ per client process = %ld", pInfo->pchminfo->max_mq_per_client); + PRN("maximum MQ per attach = %ld", pInfo->pchminfo->mq_per_attach); + PRN("using MQ ACK = %s", pInfo->pchminfo->mq_ack ? "yes" : "no"); + PRN("retry count on MQ = %d", pInfo->pchminfo->mq_retrycnt); + PRN("base msgid = 0x%016" PRIx64 , pInfo->pchminfo->base_msgid); + PRN(""); + + // chmpx info & info ex --> chmpx manager + PRN("chmpx name = %s", pInfo->pchminfo->chmpx_man.group); + PRN("configration file version = %ld", pInfo->pchminfo->chmpx_man.cfg_revision); + PRN("configration file date = %zu (unix time)", pInfo->pchminfo->chmpx_man.cfg_date); + PRN("replication count = %ld", pInfo->pchminfo->chmpx_man.replica_count); + PRN("maximum chmpx count = %ld", pInfo->pchminfo->chmpx_man.chmpx_count); + PRN("base hash count = %ld", pInfo->pchminfo->chmpx_man.chmpx_bhash_count); + PRN("now operating = %s", pInfo->pchminfo->chmpx_man.is_operating ? "yes" : "no"); + PRN("last using random chmpxid = 0x%016" PRIx64 , pInfo->pchminfo->chmpx_man.last_chmpxid); + + // chmpx info & info ex --> chmpx manager --> self chmpx + if(pInfo->pchminfo->chmpx_man.chmpx_self){ + PRN("self chmpx = {"); + PRN(" chmpxid = 0x%016" PRIx64 ,pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.chmpxid); + PRN(" hostname = %s", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.name); + PRN(" mode = %s", STRCHMPXMODE(pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.mode)); + PRN(" base hash = 0x%016" PRIx64 ,pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.base_hash); + PRN(" pending hash = 0x%016" PRIx64 ,pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.pending_hash); + PRN(" port = %d", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.port); + PRN(" control port = %d", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.ctlport); + PRN(" ssl = %s", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.is_ssl ? "yes" : "no"); + + if(pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.is_ssl){ + PRN(" verify client peer = %s", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.verify_peer ? "yes" : "no"); + PRN(" CA path is = %s", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.is_ca_file ? "file" : "directory"); + PRN(" CA path = %s", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.capath); + PRN(" server cert path = %s", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.server_cert); + PRN(" server private key path = %s", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.server_prikey); + PRN(" slave cert path = %s", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.slave_cert); + PRN(" slave private key path = %s", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.slave_prikey); + } + + string socks; + for(PCHMSOCKLIST psocklist = pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.socklist; psocklist; psocklist = psocklist->next){ + char szBuff[32]; + sprintf(szBuff, "%d", psocklist->sock); + if(!socks.empty()){ + socks += ","; + } + socks += szBuff; + } + PRN(" sockets = %s", socks.c_str()); + PRN(" listen socket = %d", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.selfsock); + PRN(" control socket = %d", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.ctlsock); + PRN(" listen control socket = %d", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.selfctlsock); + PRN(" last status update time = %zu (unix time)", pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.last_status_time); + PRN(" status = %s", STR_CHMPXSTS_FULL(pInfo->pchminfo->chmpx_man.chmpx_self->chmpx.status).c_str()); + PRN("}"); + } + + // chmpx info & info ex --> chmpx manager --> server chmpxs + PRN("server chmpxs [ %ld ] = {", pInfo->pchminfo->chmpx_man.chmpx_server_count); + for(counter = 0, pchmpxlist = pInfo->pchminfo->chmpx_man.chmpx_servers; pchmpxlist; pchmpxlist = pchmpxlist->next, ++counter){ + PRN(" [%d] = {", counter); + PRN(" chmpxid = 0x%016" PRIx64 ,pchmpxlist->chmpx.chmpxid); + PRN(" hostname = %s", pchmpxlist->chmpx.name); + PRN(" mode = %s", STRCHMPXMODE(pchmpxlist->chmpx.mode)); + PRN(" base hash = 0x%016" PRIx64 ,pchmpxlist->chmpx.base_hash); + PRN(" pending hash = 0x%016" PRIx64 ,pchmpxlist->chmpx.pending_hash); + PRN(" port = %d", pchmpxlist->chmpx.port); + PRN(" control port = %d", pchmpxlist->chmpx.ctlport); + PRN(" ssl = %s", pchmpxlist->chmpx.is_ssl ? "yes" : "no"); + + if(pchmpxlist->chmpx.is_ssl){ + PRN(" verify client peer = %s", pchmpxlist->chmpx.verify_peer ? "yes" : "no"); + PRN(" CA path is = %s", pchmpxlist->chmpx.is_ca_file ? "file" : "directory"); + PRN(" CA path = %s", pchmpxlist->chmpx.capath); + PRN(" server cert path = %s", pchmpxlist->chmpx.server_cert); + PRN(" server private key path = %s", pchmpxlist->chmpx.server_prikey); + PRN(" slave cert path = %s", pchmpxlist->chmpx.slave_cert); + PRN(" slave private key path = %s", pchmpxlist->chmpx.slave_prikey); + } + + string socks; + for(PCHMSOCKLIST psocklist = pchmpxlist->chmpx.socklist; psocklist; psocklist = psocklist->next){ + char szBuff[32]; + sprintf(szBuff, "%d", psocklist->sock); + if(!socks.empty()){ + socks += ","; + } + socks += szBuff; + } + PRN(" sockets = %s", socks.c_str()); + PRN(" listen socket = %d", pchmpxlist->chmpx.selfsock); + PRN(" control socket = %d", pchmpxlist->chmpx.ctlsock); + PRN(" listen control socket = %d", pchmpxlist->chmpx.selfctlsock); + PRN(" last status update time = %zu (unix time)", pchmpxlist->chmpx.last_status_time); + PRN(" status = %s", STR_CHMPXSTS_FULL(pchmpxlist->chmpx.status).c_str()); + PRN(" }"); + } + PRN("}"); + + // chmpx info & info ex --> chmpx manager --> slave chmpxs + PRN("slave chmpxs [ %ld ] = {", pInfo->pchminfo->chmpx_man.chmpx_slave_count); + for(counter = 0, pchmpxlist = pInfo->pchminfo->chmpx_man.chmpx_slaves; pchmpxlist; pchmpxlist = pchmpxlist->next, ++counter){ + PRN(" [%d] = {", counter); + PRN(" chmpxid = 0x%016" PRIx64 ,pchmpxlist->chmpx.chmpxid); + PRN(" hostname = %s", pchmpxlist->chmpx.name); + PRN(" mode = %s", STRCHMPXMODE(pchmpxlist->chmpx.mode)); + PRN(" base hash = 0x%016" PRIx64 ,pchmpxlist->chmpx.base_hash); + PRN(" pending hash = 0x%016" PRIx64 ,pchmpxlist->chmpx.pending_hash); + PRN(" port = %d", pchmpxlist->chmpx.port); + PRN(" control port = %d", pchmpxlist->chmpx.ctlport); + PRN(" ssl = %s", pchmpxlist->chmpx.is_ssl ? "yes" : "no"); + + if(pchmpxlist->chmpx.is_ssl){ + PRN(" verify client peer = %s", pchmpxlist->chmpx.verify_peer ? "yes" : "no"); + PRN(" CA path is = %s", pchmpxlist->chmpx.is_ca_file ? "file" : "directory"); + PRN(" CA path = %s", pchmpxlist->chmpx.capath); + PRN(" server cert path = %s", pchmpxlist->chmpx.server_cert); + PRN(" server private key path = %s", pchmpxlist->chmpx.server_prikey); + PRN(" slave cert path = %s", pchmpxlist->chmpx.slave_cert); + PRN(" slave private key path = %s", pchmpxlist->chmpx.slave_prikey); + } + + string socks; + for(PCHMSOCKLIST psocklist = pchmpxlist->chmpx.socklist; psocklist; psocklist = psocklist->next){ + char szBuff[32]; + sprintf(szBuff, "%d", psocklist->sock); + if(!socks.empty()){ + socks += ","; + } + socks += szBuff; + } + PRN(" sockets = %s", socks.c_str()); + PRN(" listen socket = %d", pchmpxlist->chmpx.selfsock); + PRN(" control socket = %d", pchmpxlist->chmpx.ctlsock); + PRN(" listen control socket = %d", pchmpxlist->chmpx.selfctlsock); + PRN(" last status update time = %zu (unix time)", pchmpxlist->chmpx.last_status_time); + PRN(" status = %s", STR_CHMPXSTS_FULL(pchmpxlist->chmpx.status).c_str()); + PRN(" }"); + } + PRN("}"); + + // chmpx info & info ex --> chmpx manager --> stat + PRN("server stat = {"); + PRN(" total send message count = %ld", pInfo->pchminfo->chmpx_man.server_stat.total_sent_count); + PRN(" total receive message count = %ld", pInfo->pchminfo->chmpx_man.server_stat.total_received_count); + PRN(" total body size = %zu (bytes)", pInfo->pchminfo->chmpx_man.server_stat.total_body_bytes); + PRN(" minimum body size = %zu (bytes)", pInfo->pchminfo->chmpx_man.server_stat.min_body_bytes); + PRN(" maximum body size = %zu (bytes)", pInfo->pchminfo->chmpx_man.server_stat.max_body_bytes); + PRN(" total elapsed time = %s", PRN_TIMESPEC(pInfo->pchminfo->chmpx_man.server_stat.total_elapsed_time).c_str()); + PRN(" minimum elapsed time = %s", PRN_TIMESPEC(pInfo->pchminfo->chmpx_man.server_stat.min_elapsed_time).c_str()); + PRN(" maximum elapsed time = %s", PRN_TIMESPEC(pInfo->pchminfo->chmpx_man.server_stat.max_elapsed_time).c_str()); + PRN("}"); + + PRN("slave stat = {"); + PRN(" total send message count = %ld", pInfo->pchminfo->chmpx_man.slave_stat.total_sent_count); + PRN(" total receive message count = %ld", pInfo->pchminfo->chmpx_man.slave_stat.total_received_count); + PRN(" total body size = %zu (bytes)", pInfo->pchminfo->chmpx_man.slave_stat.total_body_bytes); + PRN(" minimum body size = %zu (bytes)", pInfo->pchminfo->chmpx_man.slave_stat.min_body_bytes); + PRN(" maximum body size = %zu (bytes)", pInfo->pchminfo->chmpx_man.slave_stat.max_body_bytes); + PRN(" total elapsed time = %s", PRN_TIMESPEC(pInfo->pchminfo->chmpx_man.slave_stat.total_elapsed_time).c_str()); + PRN(" minimum elapsed time = %s", PRN_TIMESPEC(pInfo->pchminfo->chmpx_man.slave_stat.min_elapsed_time).c_str()); + PRN(" maximum elapsed time = %s", PRN_TIMESPEC(pInfo->pchminfo->chmpx_man.slave_stat.max_elapsed_time).c_str()); + PRN("}"); + + // chmpx info & info ex --> chmpx manager --> free chmpx/sock + PRN("free chmpx area count = %ld", pInfo->pchminfo->chmpx_man.chmpx_free_count); + PRN("free sock area count = %ld", pInfo->pchminfo->chmpx_man.sock_free_count); + PRN(""); + + // chmpx info & info ex --> mq list + PRN("chmpx using MQ [ %ld ] = {", pInfo->pchminfo->chmpx_msg_count); + for(counter = 0, pmsglisttmp = pInfo->pchminfo->chmpx_msgs; pmsglisttmp; pmsglisttmp = pmsglisttmp->next, ++counter){ + PRN(" [%d] = {", counter); + PRN(" msgid = 0x%016" PRIx64 ,pmsglisttmp->msghead.msgid); + PRN(" flag = %s%s%s", STR_MQFLAG_ASSIGNED(pmsglisttmp->msghead.flag), STR_MQFLAG_KIND(pmsglisttmp->msghead.flag), STR_MQFLAG_ACTIVATED(pmsglisttmp->msghead.flag)); + PRN(" pid = %d", pmsglisttmp->msghead.pid); + PRN(" }"); + } + PRN("}"); + + // chmpx info & info ex --> mq list + PRN("client process using MQ [ %ld ] = {", pInfo->pchminfo->activated_msg_count); + for(counter = 0, pmsglisttmp = pInfo->pchminfo->activated_msgs; pmsglisttmp; pmsglisttmp = pmsglisttmp->next, ++counter){ + PRN(" [%d] = {", counter); + PRN(" msgid = 0x%016" PRIx64 ,pmsglisttmp->msghead.msgid); + PRN(" flag = %s%s%s", STR_MQFLAG_ASSIGNED(pmsglisttmp->msghead.flag), STR_MQFLAG_KIND(pmsglisttmp->msghead.flag), STR_MQFLAG_ACTIVATED(pmsglisttmp->msghead.flag)); + PRN(" pid = %d", pmsglisttmp->msghead.pid); + PRN(" }"); + } + PRN("}"); + + // chmpx info & info ex --> mq list + PRN("assigned MQ [ %ld ] = {", pInfo->pchminfo->assigned_msg_count); + for(counter = 0, pmsglisttmp = pInfo->pchminfo->assigned_msgs; pmsglisttmp; pmsglisttmp = pmsglisttmp->next, ++counter){ + PRN(" [%d] = {", counter); + PRN(" msgid = 0x%016" PRIx64 ,pmsglisttmp->msghead.msgid); + PRN(" flag = %s%s%s", STR_MQFLAG_ASSIGNED(pmsglisttmp->msghead.flag), STR_MQFLAG_KIND(pmsglisttmp->msghead.flag), STR_MQFLAG_ACTIVATED(pmsglisttmp->msghead.flag)); + PRN(" pid = %d", pmsglisttmp->msghead.pid); + PRN(" }"); + } + PRN("}"); + + // chmpx info & info ex + PRN("free MQ = %ld", pInfo->pchminfo->free_msg_count); + PRN("last random msgid used by chmpx process = 0x%016" PRIx64 , pInfo->pchminfo->last_msgid_chmpx); + PRN("last activated msgid = 0x%016" PRIx64 , pInfo->pchminfo->last_msgid_activated); + PRN("last assigned msgid = 0x%016" PRIx64 , pInfo->pchminfo->last_msgid_assigned); + + // chmpx info & info ex --> client process + PRN("joining client proces = {"); + for(counter = 0, pproclist = pInfo->pchminfo->client_pids; pproclist; pproclist = pproclist->next, ++counter){ + PRN(" [%d] pid = %d", counter, pproclist->pid); + } + PRN("}"); + + ChmCntrl::FreeDupAllChmInfo(pInfo); + + return true; +} + +static bool PrintSelfInfo(ChmCntrl* pchmobj) +{ + if(!pchmobj){ + return false; + } + // Get information + PCHMPX pInfo = pchmobj->DupSelfChmpxInfo(); + if(!pInfo){ + ERR("Something error occurred in getting information."); + return false; + } + + // chmpx info + PRN(" chmpxid = 0x%016" PRIx64 ,pInfo->chmpxid); + PRN(" hostname = %s", pInfo->name); + PRN(" mode = %s", STRCHMPXMODE(pInfo->mode)); + PRN(" base hash = 0x%016" PRIx64 ,pInfo->base_hash); + PRN(" pending hash = 0x%016" PRIx64 ,pInfo->pending_hash); + PRN(" port = %d", pInfo->port); + PRN(" control port = %d", pInfo->ctlport); + PRN(" ssl = %s", pInfo->is_ssl ? "yes" : "no"); + + if(pInfo->is_ssl){ + PRN(" verify client peer = %s", pInfo->verify_peer ? "yes" : "no"); + PRN(" CA path is = %s", pInfo->is_ca_file ? "file" : "directory"); + PRN(" CA path = %s", pInfo->capath); + PRN(" server cert path = %s", pInfo->server_cert); + PRN(" server private key path = %s", pInfo->server_prikey); + PRN(" slave cert path = %s", pInfo->slave_cert); + PRN(" slave private key path = %s", pInfo->slave_prikey); + } + + string socks; + for(PCHMSOCKLIST psocklist = pInfo->socklist; psocklist; psocklist = psocklist->next){ + char szBuff[32]; + sprintf(szBuff, "%d", psocklist->sock); + if(!socks.empty()){ + socks += ","; + } + socks += szBuff; + } + PRN(" sockets = %s", socks.c_str()); + PRN(" listen socket = %d", pInfo->selfsock); + PRN(" control socket = %d", pInfo->ctlsock); + PRN(" listen control socket = %d", pInfo->selfctlsock); + PRN(" last status update time = %zu (unix time)", pInfo->last_status_time); + PRN(" status = %s", STR_CHMPXSTS_FULL(pInfo->status).c_str()); + + ChmCntrl::FreeDupSelfChmpxInfo(pInfo); + + return true; +} + +//--------------------------------------------------------- +// Main +//--------------------------------------------------------- +int main(int argc, char** argv) +{ + // parse parameters + ChmOpts opts((argc - 1), &argv[1]); + + // help + if(opts.Find("h") || opts.Find("help")){ + Help(argv[0]); + exit(EXIT_SUCCESS); + } + + // DBG Mode + { + string dbgfile; + if(opts.Get("dfile", dbgfile) || opts.Get("gfile", dbgfile)){ + if(!SetChmDbgFile(dbgfile.c_str())){ + ERR("Could not set debugging log file(%s), but continue...", dbgfile.c_str()); + } + } + string dbgmode; + SetChmDbgMode(CHMDBG_SILENT); + if(opts.Get("d", dbgmode) || opts.Get("g", dbgmode)){ + if(0 == strcasecmp(dbgmode.c_str(), "ERR") || 0 == strcasecmp(dbgmode.c_str(), "ERROR")){ + SetChmDbgMode(CHMDBG_ERR); + }else if(0 == strcasecmp(dbgmode.c_str(), "WAN") || 0 == strcasecmp(dbgmode.c_str(), "WARNING")){ + SetChmDbgMode(CHMDBG_WARN); + }else if(0 == strcasecmp(dbgmode.c_str(), "INF") || 0 == strcasecmp(dbgmode.c_str(), "INFO") || 0 == strcasecmp(dbgmode.c_str(), "MSG")){ + SetChmDbgMode(CHMDBG_MSG); + }else if(0 == strcasecmp(dbgmode.c_str(), "DUMP")){ + SetChmDbgMode(CHMDBG_DUMP); + }else{ + ERR("Wrong parameter value \"-d\"(\"-g\") %s.", dbgmode.c_str()); + exit(EXIT_FAILURE); + } + } + } + + // configuration file or json + string config; + if(!opts.Get("conf", config) && !opts.Get("f", config) && !opts.Get("json", config)){ + //PRN("There is no -conf and -json option, thus check environment automatically."); + } + + // control port + short ctlport = CHM_INVALID_PORT; + { + string strtmp; + if(opts.Get("ctlport", strtmp) || opts.Get("cntlport", strtmp) || opts.Get("cntrlport", strtmp)){ + ctlport = static_cast(atoi(strtmp.c_str())); + } + } + + // only self + bool is_only_self = opts.Find("self"); + + // Initialize + ChmCntrl chmobj; + if(!chmobj.OnlyAttachInitialize(config.c_str(), ctlport)){ + ERR("Could not initialize(attach) chmpx shared memory."); + PRN("You can see detail about error, execute with \"-d\"(\"-g\") option."); + exit(EXIT_FAILURE); + } + + // Main processing + bool result; + if(!is_only_self){ + result = PrintAllInfo(&chmobj); + }else{ + result = PrintSelfInfo(&chmobj); + } + chmobj.Clean(); + + exit(result ? EXIT_SUCCESS : EXIT_FAILURE); +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/tests/chmstreamtest.cc b/tests/chmstreamtest.cc new file mode 100644 index 0000000..ae5b314 --- /dev/null +++ b/tests/chmstreamtest.cc @@ -0,0 +1,689 @@ +/* + * CHMPX + * + * Copyright 2014 Yahoo! JAPAN corporation. + * + * CHMPX is inprocess data exchange by MQ with consistent hashing. + * CHMPX is made for the purpose of the construction of + * original messaging system and the offer of the client + * library. + * CHMPX transfers messages between the client and the server/ + * slave. CHMPX based servers are dispersed by consistent + * hashing and are automatically layouted. As a result, it + * provides a high performance, a high scalability. + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + * + * AUTHOR: Takeshi Nakatani + * CREATE: Tue July 1 2014 + * REVISION: + * + */ +#include +#include +#include +#include +#include +#include + +#include "chmcommon.h" +#include "chmstructure.h" +#include "chmutil.h" +#include "chmconfutil.h" +#include "chmdbg.h" +#include "chmcntrl.h" +#include "chmopts.h" +#include "chmstream.h" + +using namespace std; + +//--------------------------------------------------------- +// Macros +//--------------------------------------------------------- +static inline void PRN(const char* format, ...) +{ + if(format){ + va_list ap; + va_start(ap, format); + vfprintf(stdout, format, ap); + va_end(ap); + } + fprintf(stdout, "\n"); +} + +static inline void ERR(const char* format, ...) +{ + fprintf(stderr, "[ERR] "); + if(format){ + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + } + fprintf(stderr, "\n"); +} + +//--------------------------------------------------------- +// Test functions +//--------------------------------------------------------- +#define OSTREAM_KEY_STR "noreply_ostream_key" +#define OSTREAM_KEY_STR2 "noreply_ostream_key2" +#define OSTREAM_DIR_KEY_STR "noreply_ostream_dir_key" +#define OSTREAM_DIR_KEY_STR2 "noreply_ostream_dir_key2" +#define OSTREAM_DIR_HASH_VAL 0 + +#define ISTREAM_DIR_KEY_STR "noreply_istream_dir_key" +#define ISTREAM_DIR_KEY_STR2 "noreply_istream_dir_key2" +#define ISTREAM_DIR_HASH_VAL 0 + +#define STREAM_KEY_STR "reply_stream_key" +#define STREAM_KEY_STR2 "reply_stream_key2" +#define STREAM_DIR_KEY_STR "reply_stream_dir_key" +#define STREAM_DIR_KEY_STR2 "reply_stream_dir_key2" +#define STREAM_DIR_HASH_VAL 1 + +#define REQUEST32_KEY_STR "reply_val32" +#define REQUEST8192_KEY_STR "reply_val8192" + +#define VALTYPE_TEST_KEY_CHR "chkval_char" +#define VALTYPE_TEST_KEY_INT "chkval_int" +#define VALTYPE_TEST_KEY_LNG "chkval_long" +#define VALTYPE_TEST_NKEY_SKP "chkval_seekp***** NG AREA ******" // last 20 word is NG +#define VALTYPE_TEST_TKEY_SKP "chkval_seekp" +#define VALTYPE_TEST_PKEY_SKP "chkval_s" +#define VALTYPE_TEST_KEY_SKG "chkval_seekg" + +#define VALTYPE_TEST_VAL_CHR 'Z' +#define VALTYPE_TEST_VAL_INT 1000 +#define VALTYPE_TEST_VAL_LNG 0xA5A5A5A5 + +#define STREAM_REPLY_HASHKEY_STR "reply_stream_hash" +#define VALTYPE_REPLY_VAL_SKG "*******************OK" // begin 20 word is NG +#define VALTYPE_REPLY_VAL_OK "OK" +#define VALTYPE_REPLY_VAL_NG "NG" + +#define STREAM_VAL32_STR "123456789+123456789+123456789+--" + +static bool TestServerSide(ChmCntrl& chmobj) +{ + PRN("-------------------------------------------------------"); + PRN("TEST Receiver side"); + PRN("-------------------------------------------------------"); + + chmstream strm(&chmobj); + string strKey; + string strVal; + string strRVal; + string strRVal8192; + char buff; + int ival; + long lval; + bool is_reply; + bool is_error; + for(int cnt = 0; cnt < 256; ++cnt){ + strRVal8192 += STREAM_VAL32_STR; + } + + while(true){ + is_error = false; + is_reply = false; + buff = '\0'; + ival = -1; + lval = -1; + + strKey.erase(); + strVal.erase(); + + // read key & value + strm.clear(); + strm >> strKey; + if(strm.eof() && 0 == strKey.length()){ + //ERR("Could not read key data from stream, maybe timeout."); + continue; + }else{ + if(strKey == VALTYPE_TEST_KEY_CHR){ + strm >> buff; + strVal = buff; + + }else if(strKey == VALTYPE_TEST_KEY_INT){ + strm >> ival; + strVal = to_string(ival); + + }else if(strKey == VALTYPE_TEST_KEY_LNG){ + strm >> lval; + strVal = to_hexstring(lval); + + }else{ + strm >> strVal; + if(strm.eof() && 0 == strVal.length()){ + // value is empty, so swap key and value. + strVal = strKey; + strKey = ""; + } + } + } + + // check + if(0 == strKey.length()){ + if(STREAM_DIR_HASH_VAL == strm.receivedhash()){ + is_reply = true; + strRVal = VALTYPE_REPLY_VAL_OK; + }else{ + is_reply = false; + } + + }else if(strKey == OSTREAM_DIR_KEY_STR || strKey == OSTREAM_DIR_KEY_STR2){ + is_reply = false; + + }else if(strKey == OSTREAM_KEY_STR || strKey == OSTREAM_KEY_STR2){ + is_reply = false; + + }else if(strKey == ISTREAM_DIR_KEY_STR || strKey == ISTREAM_DIR_KEY_STR2){ + is_reply = false; + + }else if(strKey == STREAM_DIR_KEY_STR || strKey == STREAM_DIR_KEY_STR2){ + is_reply = true; + strRVal = strVal; + + }else if(strKey == STREAM_KEY_STR || strKey == STREAM_KEY_STR2){ + is_reply = true; + strRVal = strVal; + + }else if(strKey == REQUEST32_KEY_STR){ + is_reply = true; + strRVal = STREAM_VAL32_STR; + + }else if(strKey == REQUEST8192_KEY_STR){ + is_reply = true; + strRVal = strRVal8192; + + }else if(strKey == VALTYPE_TEST_KEY_CHR){ + is_reply= true; + if(buff == VALTYPE_TEST_VAL_CHR){ + is_error = false; + strRVal = VALTYPE_REPLY_VAL_OK; + }else{ + is_error = true; + strRVal = VALTYPE_REPLY_VAL_NG; + } + + }else if(strKey == VALTYPE_TEST_KEY_INT){ + is_reply= true; + if(ival == VALTYPE_TEST_VAL_INT){ + is_error = false; + strRVal = VALTYPE_REPLY_VAL_OK; + }else{ + is_error = true; + strRVal = VALTYPE_REPLY_VAL_NG; + } + + }else if(strKey == VALTYPE_TEST_KEY_LNG){ + is_reply= true; + if(lval == VALTYPE_TEST_VAL_LNG){ + is_error = false; + strRVal = VALTYPE_REPLY_VAL_OK; + }else{ + is_error = true; + strRVal = VALTYPE_REPLY_VAL_NG; + } + + }else if(strKey == VALTYPE_TEST_KEY_SKG){ + is_reply = true; + strRVal = VALTYPE_REPLY_VAL_SKG; + + }else if(0 == strncmp(strKey.c_str(), VALTYPE_TEST_PKEY_SKP, strlen(VALTYPE_TEST_PKEY_SKP))){ + if(strKey == VALTYPE_TEST_TKEY_SKP){ + is_reply = true; + strRVal = VALTYPE_REPLY_VAL_OK; + }else{ + is_reply = true; + is_error = true; + strRVal = VALTYPE_REPLY_VAL_NG; + } + + }else{ + is_reply = false; + is_error = true; + } + + // report + if(is_error){ + PRN("receive(error): \"%s\" => \"%s\"", strKey.c_str(), strVal.c_str()); + }else{ + PRN("receive: \"%s\" => \"%s\"", strKey.c_str(), strVal.c_str()); + } + + // reply + if(is_reply){ + strm.clear(); + strm << strRVal << endl; + PRN("reply: \"value\" => \"%s\"", strRVal.c_str()); + }else{ + PRN("reply: no replying"); + } + } + return true; +} + +static bool TestSlaveSide(ChmCntrl& chmobj) +{ + string strKey; + string strVal; + string strVal32(STREAM_VAL32_STR); + string strVal8192; + for(int cnt = 0; cnt < 256; ++cnt){ + strVal8192 += STREAM_VAL32_STR; + } + + PRN("-------------------------------------------------------"); + PRN("TEST Slave side : ochmstream"); + PRN("-------------------------------------------------------"); + { + { // normal + ochmstream strm(&chmobj); + strKey = OSTREAM_KEY_STR; + + strm << strKey << endl; + strm << strVal32 << endl; + + PRN("ochmstream(normal 32): \"%s\" => \"%s\"", strKey.c_str(), strVal32.c_str()); + } + sleep(1); + { // normal(8192) + ochmstream strm(&chmobj); + strKey = OSTREAM_KEY_STR2; + + strm << strKey << endl; + strm << strVal8192 << endl; + + PRN("ochmstream(normal 8192): \"%s\" => \"%s\"", strKey.c_str(), strVal8192.c_str()); + } + sleep(1); + { // direct key + ochmstream strm(&chmobj, string(OSTREAM_DIR_KEY_STR)); + + strm << strVal32 << endl; + + PRN("ochmstream(dir key 32): \"%s\" => \"%s\"", OSTREAM_DIR_KEY_STR, strVal32.c_str()); + } + sleep(1); + { // direct key(8192) + ochmstream strm(&chmobj, string(OSTREAM_DIR_KEY_STR2)); + + strm << strVal8192 << endl; + + PRN("ochmstream(dir key 8192): \"%s\" => \"%s\"", OSTREAM_DIR_KEY_STR2, strVal8192.c_str()); + } + sleep(1); + { // hash + ochmstream strm(&chmobj, OSTREAM_DIR_HASH_VAL); + + strm << strVal32 << endl; + + PRN("ochmstream(hash 32): \"%s\" => \"%s\"", OSTREAM_DIR_HASH_VAL, strVal32.c_str()); + } + sleep(1); + { // hash(8192) + ochmstream strm(&chmobj, OSTREAM_DIR_HASH_VAL); + + strm << strVal8192 << endl; + + PRN("ochmstream(hash 8192): \"%s\" => \"%s\"", OSTREAM_DIR_HASH_VAL, strVal8192.c_str()); + } + sleep(1); + } + + PRN("-------------------------------------------------------"); + PRN("TEST Slave side : ichmstream --> this case must be server side."); + PRN("-------------------------------------------------------"); + PRN("SKIP THIS\n"); + + PRN("-------------------------------------------------------"); + PRN("TEST Slave side : chmstream"); + PRN("-------------------------------------------------------"); + { + { // normal + chmstream strm(&chmobj); + strKey = STREAM_KEY_STR; + + strm << strKey << endl; + strm << strVal32 << endl; + + PRN("chmstream(normal 32): \"%s\" => \"%s\"", strKey.c_str(), strVal32.c_str()); + + while(true){ + strm.clear(); + strm >> strVal; + if(0 < strVal.length() || strm.good()){ + break; + } + } + PRN("chmstream(normal 32): \"reply\" => \"%s\"", strVal.c_str()); + } + sleep(1); + { // normal(8192) + chmstream strm(&chmobj); + strKey = STREAM_KEY_STR2; + + strm << strKey << endl; + strm << strVal8192 << endl; + + PRN("chmstream(normal 8192): \"%s\" => \"%s\"", strKey.c_str(), strVal8192.c_str()); + + while(true){ + strm.clear(); + strm >> strVal; + if(0 < strVal.length() || strm.good()){ + break; + } + } + PRN("chmstream(normal 8192): \"reply\" => \"%s\"", strVal.c_str()); + } + sleep(1); + { // direct key(this same as normal) + chmstream strm(&chmobj, string(STREAM_DIR_KEY_STR)); + + strm << strVal32 << endl; + + PRN("chmstream(dir key 32): \"%s\" => \"%s\"", STREAM_DIR_KEY_STR, strVal32.c_str()); + + while(true){ + strm.clear(); + strm >> strVal; + if(0 < strVal.length() || strm.good()){ + break; + } + } + PRN("chmstream(dir key 32): \"reply\" => \"%s\"", strVal.c_str()); + } + sleep(1); + { // direct key 8192(this same as normal) + chmstream strm(&chmobj, string(STREAM_DIR_KEY_STR2)); + + strm << strVal8192 << endl; + + PRN("chmstream(dir key 8192): \"%s\" => \"%s\"", STREAM_DIR_KEY_STR, strVal8192.c_str()); + + while(true){ + strm.clear(); + strm >> strVal; + if(0 < strVal.length() || strm.good()){ + break; + } + } + PRN("chmstream(dir key 8192): \"reply\" => \"%s\"", strVal.c_str()); + } + sleep(1); + { // hash(this same as normal) + chmstream strm(&chmobj, STREAM_DIR_HASH_VAL); + + strm << strVal32 << endl; + + PRN("chmstream(hash 32): \"HASH\" => \"%s\"", strVal32.c_str()); + + while(true){ + strm.clear(); + strm >> strVal; + if(0 < strVal.length() || strm.good()){ + break; + } + } + PRN("chmstream(hash 32): \"reply\" => \"%s\"", strVal.c_str()); + } + sleep(1); + { // hash 8192(this same as normal) + chmstream strm(&chmobj, STREAM_DIR_HASH_VAL); + + strm << strVal8192 << endl; + + PRN("chmstream(hash 8192): \"HASH\" => \"%s\"", strVal8192.c_str()); + + while(true){ + strm.clear(); + strm >> strVal; + if(0 < strVal.length() || strm.good()){ + break; + } + } + PRN("chmstream(hash 8192): \"reply\" => \"%s\"", strVal.c_str()); + } + sleep(1); + } + + PRN("-------------------------------------------------------"); + PRN("TEST Slave side : value type"); + PRN("-------------------------------------------------------"); + { + { // char + chmstream strm(&chmobj); + strKey = VALTYPE_TEST_KEY_CHR; + char buff= VALTYPE_TEST_VAL_CHR; + + strm << strKey << endl; + strm << buff << endl; + + PRN("chmstream(char): \"%s\" => \"%c\"", strKey.c_str(), buff); + + strVal.erase(); + while(true){ + strm.clear(); + strm >> strVal; + if(0 < strVal.length() || strm.good()){ + break; + } + } + PRN("chmstream(char): \"reply\" => \"%s\"", strVal.c_str()); + } + sleep(1); + { // int + chmstream strm(&chmobj); + strKey = VALTYPE_TEST_KEY_INT; + int ival= VALTYPE_TEST_VAL_INT; + + strm << strKey << endl; + strm << ival << endl; + + PRN("chmstream(int): \"%s\" => \"%d\"", strKey.c_str(), ival); + + strVal.erase(); + while(true){ + strm.clear(); + strm >> strVal; + if(0 < strVal.length() || strm.good()){ + break; + } + } + PRN("chmstream(int): \"reply\" => \"%s\"", strVal.c_str()); + } + sleep(1); + { // long + chmstream strm(&chmobj); + strKey = VALTYPE_TEST_KEY_LNG; + long lval= VALTYPE_TEST_VAL_LNG; + + strm << strKey << endl; + strm << lval << endl; + + PRN("chmstream(long): \"%s\" => \"%ld\"", strKey.c_str(), lval); + + strVal.erase(); + while(true){ + strm.clear(); + strm >> strVal; + if(0 < strVal.length() || strm.good()){ + break; + } + } + PRN("chmstream(long): \"reply\" => \"%s\"", strVal.c_str()); + } + sleep(1); + { // seekg + chmstream strm(&chmobj); + strKey = VALTYPE_TEST_KEY_SKG; + + strm << strKey << endl; + strm << strVal32 << endl; + + PRN("chmstream(seekg): \"%s\" => \"%s\"", strKey.c_str(), strVal32.c_str()); + + strVal.erase(); + while(true){ + char buff = -1; // tmp + strm.clear(); + strm >> buff; + if(buff != -1 || strm.good()){ + break; + } + } + strm.seekg(20, std::ios_base::beg); + strm >> strVal; + + PRN("chmstream(seekg): \"reply\" => \"%s\"", strVal.c_str()); + } + sleep(1); + { // seekp + chmstream strm(&chmobj); + strKey = VALTYPE_TEST_NKEY_SKP; + + strm << strKey << endl; + strm.seekp(12, std::ios_base::beg); // back to NG word + strm << endl; // set key end + strm << strVal32 << endl; + + PRN("chmstream(seekp): \"%s\" => \"%s\"", strKey.c_str(), strVal32.c_str()); + + strVal.erase(); + while(true){ + strm.clear(); + strm >> strVal; + if(0 < strVal.length() || strm.good()){ + break; + } + } + PRN("chmstream(seekp): \"reply\" => \"%s\"", strVal.c_str()); + } + } + return true; +} + +//--------------------------------------------------------- +// Functions +//--------------------------------------------------------- +// Parse parameters +// +// -f [file name] Configuration file path +// -d [debug level] "ERR" / "WAN" / "INF" / "DUMP" +// -h display help +// +static void Help(char* progname) +{ + printf("Usage: %s [options]\n", progname ? progname : "program"); + printf("Option -s|-c Specify server side or slave side process.\n"); + printf(" -conf [file name] Configuration file( .ini / .json / .yaml ) path\n"); + printf(" -json [json string] Configuration JSON string\n"); + printf(" -d [debug level] \"ERR\" / \"WAN\" / \"INF\" / \"DUMP\"\n"); + printf(" -h display help\n"); +} + +//--------------------------------------------------------- +// Main +//--------------------------------------------------------- +int main(int argc, char** argv) +{ + ChmOpts opts((argc - 1), &argv[1]); + ChmCntrl chmobj; + + // help + if(opts.Find("h") || opts.Find("help")){ + char* pprgname = basename(argv[0]); + Help(pprgname); + exit(EXIT_SUCCESS); + } + + // DBG Mode + string dbgmode; + if(opts.Get("g", dbgmode) || opts.Get("d", dbgmode)){ + if(0 == strcasecmp(dbgmode.c_str(), "ERR")){ + SetChmDbgMode(CHMDBG_ERR); + }else if(0 == strcasecmp(dbgmode.c_str(), "WAN")){ + SetChmDbgMode(CHMDBG_WARN); + }else if(0 == strcasecmp(dbgmode.c_str(), "INF")){ + SetChmDbgMode(CHMDBG_MSG); + }else if(0 == strcasecmp(dbgmode.c_str(), "DUMP")){ + SetChmDbgMode(CHMDBG_DUMP); + }else{ + ERR_CHMPRN("Wrong parameter value \"-d\"(\"-g\") %s.", dbgmode.c_str()); + exit(EXIT_FAILURE); + } + } + + // configuration file or json + string config; + if(!opts.Get("conf", config) && !opts.Get("f", config) && !opts.Get("json", config)){ + //PRN("There is no -conf and -json option, thus check environment automatically."); + if(!getenv_chm_conffile(config) && !getenv_chm_jsonconf(config)){ + ERR_CHMPRN("There is no option(-conf or -json)."); + } + } + + // Mode + bool is_server_side = false; + if(opts.Find("s")){ + is_server_side = true; + }else if(opts.Find("c")){ + is_server_side = false; + }else{ + ERR("Parameter \"-s\" or \"-c\" must be set."); + exit(EXIT_FAILURE); + } + + // Message + PRN("-------------------------------------------------------"); + PRN(" Test chmstream class"); + PRN("-------------------------------------------------------"); + PRN(" *** NOTICE ***"); + PRN(" You MUST set REPLICA as 0 in all server configration file."); + PRN(""); + PRN("-------------------------------------------------------"); + PRN("Test process run on: %s", is_server_side ? "server side" : "slave side"); + PRN("Configration file or json: %s", config.c_str()); + PRN("-------------------------------------------------------"); + + // Initialize + bool result; + if(is_server_side){ + result = chmobj.InitializeOnServer(config.c_str(), true); // auto rejoin + }else{ + result = chmobj.InitializeOnSlave(config.c_str(), true); // auto rejoin + } + if(!result){ + ERR_CHMPRN("Could not initialize internal object/data/etc."); + chmobj.Clean(); + exit(EXIT_FAILURE); + } + + // Run test + if(is_server_side){ + result = TestServerSide(chmobj); + }else{ + result = TestSlaveSide(chmobj); + } + + // Result + PRN("-------------------------------------------------------"); + PRN("Test Result: %s", result ? "Succeed" : "Failed"); + PRN("-------------------------------------------------------"); + if(!result){ + ERR_CHMPRN("Failed test."); + } + chmobj.Clean(); + + exit(result ? EXIT_SUCCESS : EXIT_FAILURE); +} + +/* + * VIM modelines + * + * vim:set ts=4 fenc=utf-8: + */ diff --git a/tests/test.sh b/tests/test.sh new file mode 100755 index 0000000..4e85a13 --- /dev/null +++ b/tests/test.sh @@ -0,0 +1,379 @@ +#!/bin/sh +# +# CHMPX +# +# Copyright 2015 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Fri May 8 2015 +# REVISION: +# + +############################################################## +## library path & programs path +## +MYSCRIPTDIR=`dirname $0` +if [ "X${SRCTOP}" = "X" ]; then + SRCTOP=`cd ${MYSCRIPTDIR}/..; pwd` +fi +cd ${MYSCRIPTDIR} +if [ "X${OBJDIR}" = "X" ]; then + LD_LIBRARY_PATH="${SRCTOP}/lib/.lib" + TESTPROGDIR=${MYSCRIPTDIR} +else + LD_LIBRARY_PATH="${SRCTOP}/lib/${OBJDIR}" + TESTPROGDIR=${MYSCRIPTDIR}/${OBJDIR} +fi +export LD_LIBRARY_PATH +export TESTPROGDIR + +########################################################### +# Initialize +########################################################### +DATE=`date` +PROCID=$$ +COUNT=10 + +# +# Base directory +# +RUNDIR=`pwd` +TESTSCRIPTDIR=`dirname $0` +if [ "X$TESTSCRIPTDIR" = "X" -o "X$TESTSCRIPTDIR" = "X." ]; then + TMP_BASENAME=`basename $0` + TMP_FIRSTWORD=`echo $0 | awk -F"/" '{print $1}'` + + if [ "X$TMP_BASENAME" = "X$TMP_FIRSTWORD" ]; then + # search path + TESTSCRIPTDIR=`which $0` + TESTSCRIPTDIR=`dirname $TESTSCRIPTDIR` + else + TESTSCRIPTDIR=. + fi +fi +BASEDIR=`cd -P ${RUNDIR}/${TESTSCRIPTDIR}; pwd` +CHMPXDIR=`cd -P ${BASEDIR}/../src; pwd` + +# +# Binary +# +if [ -f ${CHMPXDIR}/${PLATFORM_CURRENT}/chmpx ]; then + CHMPXBIN=${CHMPXDIR}/${PLATFORM_CURRENT}/chmpx +elif [ -f ${CHMPXDIR}/${PLATFORM_CURRENT}/chmmain ]; then + CHMPXBIN=${CHMPXDIR}/${PLATFORM_CURRENT}/chmmain +else + echo "ERROR: there is no chmpx binary" + echo "RESULT --> FAILED" + exit 1 +fi +if [ -f ${BASEDIR}/${PLATFORM_CURRENT}/chmpxbench ]; then + CHMPXBENCHBIN=${BASEDIR}/${PLATFORM_CURRENT}/chmpxbench +else + echo "ERROR: there is no chmpxbench binary" + echo "RESULT --> FAILED" + exit 1 +fi +if [ -f ${BASEDIR}/${PLATFORM_CURRENT}/chmconftest ]; then + CHMCONFTEST=${BASEDIR}/${PLATFORM_CURRENT}/chmconftest +else + echo "ERROR: there is no chmconftest binary" + echo "RESULT --> FAILED" + exit 1 +fi + +########################################################### +# Start configuration test +########################################################### +# +# make parameter for JSON +# +JSON_SERVER_PARAM=`grep 'SERVER=' ${BASEDIR}/test_json_string.data 2>/dev/null | sed 's/SERVER=//g' 2>/dev/null` +JSON_SLAVE_PARAM=`grep 'SLAVE=' ${BASEDIR}/test_json_string.data 2>/dev/null | sed 's/SLAVE=//g' 2>/dev/null` + +# +# Test for conf loading +# +echo "------ LOAD INI file for server ----------------------------" +${CHMCONFTEST} -conf ${BASEDIR}/test_server.ini | grep -v NAME | grep -v DATE > /tmp/conftest_svr_ini_${PROCID}.log 2>&1 +if [ $? -ne 0 ]; then + echo "ERROR: Failed test configuration for server INI." + echo "RESULT --> FAILED" + exit 1 +fi +echo "RESULT --> SUCCEED" +echo "" + +echo "------ LOAD INI file for slave -----------------------------" +${CHMCONFTEST} -conf ${BASEDIR}/test_slave.ini | grep -v NAME | grep -v DATE > /tmp/conftest_slv_ini_${PROCID}.log 2>&1 +if [ $? -ne 0 ]; then + echo "ERROR: Failed test configuration for slave INI." + echo "RESULT --> FAILED" + exit 1 +fi +echo "RESULT --> SUCCEED" +echo "" + +echo "------ LOAD YAML file for server ---------------------------" +${CHMCONFTEST} -conf ${BASEDIR}/test_server.yaml | grep -v NAME | grep -v DATE > /tmp/conftest_svr_yaml_${PROCID}.log 2>&1 +if [ $? -ne 0 ]; then + echo "ERROR: Failed test configuration for server YAML." + echo "RESULT --> FAILED" + exit 1 +fi +echo "RESULT --> SUCCEED" +echo "" + +echo "------ LOAD YAML file for slave ----------------------------" +${CHMCONFTEST} -conf ${BASEDIR}/test_slave.yaml | grep -v NAME | grep -v DATE > /tmp/conftest_slv_yaml_${PROCID}.log 2>&1 +if [ $? -ne 0 ]; then + echo "ERROR: Failed test configuration for slave YAML." + echo "RESULT --> FAILED" + exit 1 +fi +echo "RESULT --> SUCCEED" +echo "" + +echo "------ LOAD JSON file for server ---------------------------" +${CHMCONFTEST} -conf ${BASEDIR}/test_server.json | grep -v NAME | grep -v DATE > /tmp/conftest_svr_json_${PROCID}.log 2>&1 +if [ $? -ne 0 ]; then + echo "ERROR: Failed test configuration for server JSON." + echo "RESULT --> FAILED" + exit 1 +fi +echo "RESULT --> SUCCEED" +echo "" + +echo "------ LOAD JSON file for slave ----------------------------" +${CHMCONFTEST} -conf ${BASEDIR}/test_slave.json | grep -v NAME | grep -v DATE > /tmp/conftest_slv_json_${PROCID}.log 2>&1 +if [ $? -ne 0 ]; then + echo "ERROR: Failed test configuration for slave JSON." + echo "RESULT --> FAILED" + exit 1 +fi +echo "RESULT --> SUCCEED" +echo "" + +echo "------ LOAD JSON param for server --------------------------" +${CHMCONFTEST} -json "${JSON_SERVER_PARAM}" | grep -v NAME | grep -v DATE > /tmp/conftest_svr_sjson_${PROCID}.log 2>&1 +if [ $? -ne 0 ]; then + echo "ERROR: Failed test configuration for server JSON param." + echo "RESULT --> FAILED" + exit 1 +fi +echo "RESULT --> SUCCEED" +echo "" + +echo "------ LOAD JSON param for slave ---------------------------" +${CHMCONFTEST} -json "${JSON_SLAVE_PARAM}" | grep -v NAME | grep -v DATE > /tmp/conftest_slv_sjson_${PROCID}.log 2>&1 +if [ $? -ne 0 ]; then + echo "ERROR: Failed test configuration for slave JSON param." + echo "RESULT --> FAILED" + exit 1 +fi +echo "RESULT --> SUCCEED" +echo "" + +echo "------ LOAD INI conf by env for server ---------------------" +CHMCONFFILE=${BASEDIR}/test_server.ini ${CHMCONFTEST} | grep -v NAME | grep -v DATE > /tmp/conftest_svr_inienv_${PROCID}.log 2>&1 +if [ $? -ne 0 ]; then + echo "ERROR: Failed test configuration for server INI on ENV." + echo "RESULT --> FAILED" + exit 1 +fi +echo "RESULT --> SUCCEED" +echo "" + +echo "------ LOAD JSON param by env for server ------------------" +CHMJSONCONF="${JSON_SERVER_PARAM}" ${CHMCONFTEST} | grep -v NAME | grep -v DATE > /tmp/conftest_svr_jsonenv_${PROCID}.log 2>&1 +if [ $? -ne 0 ]; then + echo "ERROR: Failed test configuration for server JSON on ENV." + echo "RESULT --> FAILED" + exit 1 +fi +echo "RESULT --> SUCCEED" +echo "" + +# +# Compare result +# +echo "------ Compare LOAD result for server ----------------------" +diff /tmp/conftest_svr_ini_${PROCID}.log /tmp/conftest_svr_yaml_${PROCID}.log +if [ $? -ne 0 ]; then + echo "RESULT --> FAILED" + exit 1 +fi + +diff /tmp/conftest_svr_ini_${PROCID}.log /tmp/conftest_svr_json_${PROCID}.log +if [ $? -ne 0 ]; then + echo "RESULT --> FAILED" + exit 1 +fi + +diff /tmp/conftest_svr_ini_${PROCID}.log /tmp/conftest_svr_sjson_${PROCID}.log +if [ $? -ne 0 ]; then + echo "RESULT --> FAILED" + exit 1 +fi + +diff /tmp/conftest_svr_ini_${PROCID}.log /tmp/conftest_svr_inienv_${PROCID}.log +if [ $? -ne 0 ]; then + echo "RESULT --> FAILED" + exit 1 +fi + +diff /tmp/conftest_svr_ini_${PROCID}.log /tmp/conftest_svr_jsonenv_${PROCID}.log +if [ $? -ne 0 ]; then + echo "RESULT --> FAILED" + exit 1 +fi +echo "RESULT --> SUCCEED" +echo "" + +echo "------ Compare LOAD result for slave -----------------------" +diff /tmp/conftest_slv_ini_${PROCID}.log /tmp/conftest_slv_yaml_${PROCID}.log +if [ $? -ne 0 ]; then + echo "RESULT --> FAILED" + exit 1 +fi + +diff /tmp/conftest_slv_ini_${PROCID}.log /tmp/conftest_slv_json_${PROCID}.log +if [ $? -ne 0 ]; then + echo "RESULT --> FAILED" + exit 1 +fi + +diff /tmp/conftest_slv_ini_${PROCID}.log /tmp/conftest_slv_sjson_${PROCID}.log +if [ $? -ne 0 ]; then + echo "RESULT --> FAILED" + exit 1 +fi +echo "RESULT --> SUCCEED" +echo "" + +########################################################### +# Start test +########################################################### +# +# chmpx for server node +# +echo "------ RUN chmpx server node -------------------------------" +${CHMPXBIN} -conf ${BASEDIR}/test_server.ini -d silent & +CHMPXSVRPID=$! +echo "chmpx server node pid = ${CHMPXSVRPID}" +sleep 2 + +# +# test client process on server node +# +echo "------ RUN test server process on server side --------------" +${CHMPXBENCHBIN} -s -f ${BASEDIR}/test_server.ini -l 0 -proccnt 1 -threadcnt 1 -ta -dl 128 -pr -g err -dummykey ${PROCID} > /tmp/testsvr_${PROCID}.log 2>&1 & +TESTSVRPID=$! +echo "test client process pid(on server node) = ${TESTSVRPID}" +sleep 2 + +# +# server chmpx service in +# +echo "------ SET chmpx mode to SERVICEIN -------------------------" +(sleep 1; echo SERVICEIN) | telnet localhost 8021 +echo "chmpx server node status to SERVICEIN" +sleep 2 + +# +# chmpx for slave node +# +echo "------ RUN chmpx slave node --------------------------------" +${CHMPXBIN} -conf ${BASEDIR}/test_slave.ini -d silent & +CHMPXSLVPID=$! +echo "chmpx slave node pid = ${CHMPXSLVPID}" +sleep 2 + +# +# test client process on slave node +# +echo "------ RUN client process on slave node & START test -------" +${CHMPXBENCHBIN} -c -f ${BASEDIR}/test_slave.ini -l ${COUNT} -proccnt 1 -threadcnt 1 -ta -dl 128 -pr -g err -dummykey ${PROCID} > /tmp/testslv_${PROCID}.log 2>&1 +echo "finish test client process on slave node" +sleep 2 + +########################################################### +# Stop all process +########################################################### +kill -HUP ${TESTSVRPID} > /dev/null 2>&1 +sleep 1 +kill -9 ${TESTSVRPID} > /dev/null 2>&1 +sleep 1 + +TESTCLIENTPIDS=`ps w | grep chmpxbench | grep dummykey | grep ${PROCID} | grep -v grep | awk '{print $1}'` +if [ "X$TESTCLIENTPIDS" != "X" ]; then + sleep 1 + kill -HUP ${TESTCLIENTPIDS} > /dev/null 2>&1 + sleep 1 + kill -9 ${TESTCLIENTPIDS} > /dev/null 2>&1 +fi + +kill -HUP ${CHMPXSLVPID} > /dev/null 2>&1 +sleep 1 +kill -HUP ${CHMPXSVRPID} > /dev/null 2>&1 +sleep 1 +kill -9 ${CHMPXSLVPID} ${CHMPXSVRPID} > /dev/null 2>&1 + +############## +# Check +############## +echo "------ RESULT ----------------------------------------------" +SVRRESULT=`cat /tmp/testsvr_${PROCID}.log` +SLVRESULTERRLINE=`grep 'Total error count' /tmp/testslv_${PROCID}.log` +SLVRESULTERRCNT=`echo $SLVRESULTERRLINE | awk '{print $4}'` + +RESULT=0 +if [ "X$SVRRESULT" != "X" -o "X$SLVRESULTERRLINE" = "X" -o "X$SLVRESULTERRCNT" != "X0" ]; then + RESULT=1 +fi + +# +# Cleanup files +# +rm -f /tmp/conftest_svr_ini_${PROCID}.log +rm -f /tmp/conftest_svr_yaml_${PROCID}.log +rm -f /tmp/conftest_svr_json_${PROCID}.log +rm -f /tmp/conftest_svr_sjson_${PROCID}.log +rm -f /tmp/conftest_svr_inienv_${PROCID}.log +rm -f /tmp/conftest_svr_jsonenv_${PROCID}.log +rm -f /tmp/conftest_slv_ini_${PROCID}.log +rm -f /tmp/conftest_slv_yaml_${PROCID}.log +rm -f /tmp/conftest_slv_json_${PROCID}.log +rm -f /tmp/conftest_slv_sjson_${PROCID}.log +rm -f /tmp/testsvr_${PROCID}.log +rm -f /tmp/testslv_${PROCID}.log +rm -f /tmp/TESTSCRPT-8021.chmpx +rm -f /tmp/TESTSCRPT-8021.k2h +rm -f /tmp/TESTSCRPT-8022.chmpx +rm -f /tmp/TESTSCRPT-8022.k2h +rm -f /tmp/.k2h_* +rm -f /tmp/chmpxbench-*.dat + +if [ $RESULT -eq 1 ]; then + echo "RESULT --> FAILED" +else + echo "RESULT --> SUCCEED" +fi +exit $RESULT + +# +# VIM modelines +# +# vim:set ts=4 fenc=utf-8: +# diff --git a/tests/test_json_string.data b/tests/test_json_string.data new file mode 100644 index 0000000..a189eee --- /dev/null +++ b/tests/test_json_string.data @@ -0,0 +1,28 @@ +# +# CHMPX CONFIGRATION FILE FOR TEST +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue Nor 15 2016 +# REVISION: +# + +SERVER={ 'GLOBAL': { 'FILEVERSION': '2', 'DATE': 'Tue, 15 Nov 2016 09:03:24 +0900', 'GROUP': 'TESTSCRPT', 'MODE': 'SERVER', 'DELIVERMODE': 'random', 'MAXCHMPX': '4', 'REPLICA': '0', 'MAXMQSERVER': '2', 'MAXMQCLIENT': '2', 'MQPERATTACH': '1', 'MAXQPERSERVERMQ': '2', 'MAXQPERCLIENTMQ': '1', 'MAXMQPERCLIENT': '1', 'MAXHISTLOG': '10000', 'PORT': '8020', 'CTLPORT': '8021', 'SELFCTLPORT': '8021', 'RWTIMEOUT': '100', 'RETRYCNT': '1000', 'CONTIMEOUT': '500000', 'MQRWTIMEOUT': '50', 'MQRETRYCNT': '20000', 'DOMERGE': 'on', 'SSL': 'no', 'K2HFULLMAP': 'on', 'K2HMASKBIT': '8', 'K2HCMASKBIT': '4', 'K2HMAXELE': '8' }, 'SVRNODE': [ { 'NAME': 'localhost', 'PORT': '8020', 'CTLPORT': '8021', 'SSL': 'no' } ], 'SLVNODE': [ { 'NAME': '[.]*', 'CTLPORT': '8022' } ] } +SLAVE={ 'GLOBAL': { 'FILEVERSION': '2', 'DATE': 'Tue, 15 Nov 2016 09:03:24 +0900', 'GROUP': 'TESTSCRPT', 'MODE': 'SLAVE', 'DELIVERMODE': 'random', 'MAXCHMPX': '4', 'REPLICA': '0', 'MAXMQSERVER': '2', 'MAXMQCLIENT': '2', 'MQPERATTACH': '1', 'MAXQPERSERVERMQ': '2', 'MAXQPERCLIENTMQ': '1', 'MAXMQPERCLIENT': '1', 'MAXHISTLOG': '10000', 'PORT': '8020', 'CTLPORT': '8022', 'SELFCTLPORT': '8022', 'RWTIMEOUT': '100', 'RETRYCNT': '1000', 'CONTIMEOUT': '500000', 'MQRWTIMEOUT': '50', 'MQRETRYCNT': '20000', 'DOMERGE': 'on', 'SSL': 'no', 'K2HFULLMAP': 'on', 'K2HMASKBIT': '8', 'K2HCMASKBIT': '4', 'K2HMAXELE': '8' }, 'SVRNODE': [ { 'NAME': 'localhost', 'PORT': '8020', 'CTLPORT': '8021', 'SSL': 'no' } ], 'SLVNODE': [ { 'NAME': '[.]*', 'CTLPORT': '8022' } ] } + +# +# EOF +# diff --git a/tests/test_server.ini b/tests/test_server.ini new file mode 100644 index 0000000..2aa1945 --- /dev/null +++ b/tests/test_server.ini @@ -0,0 +1,74 @@ +# +# CHMPX CONFIGRATION FILE FOR TEST +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Fri May 8 2015 +# REVISION: +# + +# +# GLOBAL SECTION +# +[GLOBAL] +FILEVERSION = 2 +DATE = Tue, 12 May 2015 18:10:19 +0900 +GROUP = TESTSCRPT +MODE = SERVER +DELIVERMODE = random +MAXCHMPX = 4 +REPLICA = 0 +MAXMQSERVER = 2 +MAXMQCLIENT = 2 +MQPERATTACH = 1 +MAXQPERSERVERMQ = 2 +MAXQPERCLIENTMQ = 1 +MAXMQPERCLIENT = 1 +MAXHISTLOG = 10000 +PORT = 8020 +CTLPORT = 8021 +SELFCTLPORT = 8021 +RWTIMEOUT = 100 +RETRYCNT = 1000 +CONTIMEOUT = 500000 +MQRWTIMEOUT = 50 +MQRETRYCNT = 20000 +DOMERGE = on +SSL = no +K2HFULLMAP = on +K2HMASKBIT = 8 +K2HCMASKBIT = 4 +K2HMAXELE = 8 + +# +# SERVER NODES SECTION +# +[SVRNODE] +NAME = localhost +PORT = 8020 +CTLPORT = 8021 +SSL = no + +# +# SLAVE NODES SECTION +# +[SLVNODE] +NAME = [.]* +CTLPORT = 8022 + +# +# EOF +# diff --git a/tests/test_server.json b/tests/test_server.json new file mode 100644 index 0000000..d73fc6e --- /dev/null +++ b/tests/test_server.json @@ -0,0 +1,86 @@ +# +# CHMPX CONFIGRATION FILE FOR TEST +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue Nor 15 2016 +# REVISION: +# + +{ + # + # GLOBAL SECTION + # + "GLOBAL": + { + "FILEVERSION": "2", + "DATE": "Tue, 15 Nov 2016 09:03:24 +0900", + "GROUP": "TESTSCRPT", + "MODE": "SERVER", + "DELIVERMODE": "random", + "MAXCHMPX": "4", + "REPLICA": "0", + "MAXMQSERVER": "2", + "MAXMQCLIENT": "2", + "MQPERATTACH": "1", + "MAXQPERSERVERMQ": "2", + "MAXQPERCLIENTMQ": "1", + "MAXMQPERCLIENT": "1", + "MAXHISTLOG": "10000", + "PORT": "8020", + "CTLPORT": "8021", + "SELFCTLPORT": "8021", + "RWTIMEOUT": "100", + "RETRYCNT": "1000", + "CONTIMEOUT": "500000", + "MQRWTIMEOUT": "50", + "MQRETRYCNT": "20000", + "DOMERGE": "on", + "SSL": "no", + "K2HFULLMAP": "on", + "K2HMASKBIT": "8", + "K2HCMASKBIT": "4", + "K2HMAXELE": "8" + }, + + # + # SERVER NODES SECTION + # + "SVRNODE": + [ + { + "NAME": "localhost", + "PORT": "8020", + "CTLPORT": "8021", + "SSL": "no" + } + ], + + # + # SLAVE NODES SECTION + # + "SLVNODE": + [ + { + "NAME": "[.]*", + "CTLPORT": "8022" + } + ] +} + +# +# EOF +# diff --git a/tests/test_server.yaml b/tests/test_server.yaml new file mode 100644 index 0000000..7f4c2b9 --- /dev/null +++ b/tests/test_server.yaml @@ -0,0 +1,84 @@ +# +# CHMPX CONFIGRATION FILE FOR TEST +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue Nor 15 2016 +# REVISION: +# + +# +# GLOBAL SECTION +# +GLOBAL: + { + FILEVERSION: 2, + DATE: "Tue, 15 Nov 2016 09:03:24 +0900", + GROUP: TESTSCRPT, + MODE: SERVER, + DELIVERMODE: random, + MAXCHMPX: 4, + REPLICA: 0, + MAXMQSERVER: 2, + MAXMQCLIENT: 2, + MQPERATTACH: 1, + MAXQPERSERVERMQ: 2, + MAXQPERCLIENTMQ: 1, + MAXMQPERCLIENT: 1, + MAXHISTLOG: 10000, + PORT: 8020, + CTLPORT: 8021, + SELFCTLPORT: 8021, + RWTIMEOUT: 100, + RETRYCNT: 1000, + CONTIMEOUT: 500000, + MQRWTIMEOUT: 50, + MQRETRYCNT: 20000, + DOMERGE: on, + SSL: no, + K2HFULLMAP: on, + K2HMASKBIT: 8, + K2HCMASKBIT: 4, + K2HMAXELE: 8 + } + +# +# SERVER NODES SECTION +# +SVRNODE: + [ + { + NAME: localhost, + PORT: 8020, + CTLPORT: 8021, + SSL: no + } + ] + +# +# SLAVE NODES SECTION +# +SLVNODE: + [ + { + NAME: "[.]*", + CTLPORT: 8022 + } + ] + +# +# EOF +# diff --git a/tests/test_slave.ini b/tests/test_slave.ini new file mode 100644 index 0000000..152c772 --- /dev/null +++ b/tests/test_slave.ini @@ -0,0 +1,74 @@ +# +# CHMPX CONFIGRATION FILE FOR TEST +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Fri May 8 2015 +# REVISION: +# + +# +# GLOBAL SECTION +# +[GLOBAL] +FILEVERSION = 2 +DATE = Tue, 12 May 2015 18:10:19 +0900 +GROUP = TESTSCRPT +MODE = SLAVE +DELIVERMODE = random +MAXCHMPX = 4 +REPLICA = 0 +MAXMQSERVER = 2 +MAXMQCLIENT = 2 +MQPERATTACH = 1 +MAXQPERSERVERMQ = 2 +MAXQPERCLIENTMQ = 1 +MAXMQPERCLIENT = 1 +MAXHISTLOG = 10000 +PORT = 8020 +CTLPORT = 8022 +SELFCTLPORT = 8022 +RWTIMEOUT = 100 +RETRYCNT = 1000 +CONTIMEOUT = 500000 +MQRWTIMEOUT = 50 +MQRETRYCNT = 20000 +DOMERGE = on +SSL = no +K2HFULLMAP = on +K2HMASKBIT = 8 +K2HCMASKBIT = 4 +K2HMAXELE = 8 + +# +# SERVER NODES SECTION +# +[SVRNODE] +NAME = localhost +PORT = 8020 +CTLPORT = 8021 +SSL = no + +# +# SLAVE NODES SECTION +# +[SLVNODE] +NAME = [.]* +CTLPORT = 8022 + +# +# EOF +# diff --git a/tests/test_slave.json b/tests/test_slave.json new file mode 100644 index 0000000..c2eded9 --- /dev/null +++ b/tests/test_slave.json @@ -0,0 +1,86 @@ +# +# CHMPX CONFIGRATION FILE FOR TEST +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue Nor 15 2016 +# REVISION: +# + +{ + # + # GLOBAL SECTION + # + "GLOBAL": + { + "FILEVERSION": "2", + "DATE": "Tue, 15 Nov 2016 09:03:24 +0900", + "GROUP": "TESTSCRPT", + "MODE": "SLAVE", + "DELIVERMODE": "random", + "MAXCHMPX": "4", + "REPLICA": "0", + "MAXMQSERVER": "2", + "MAXMQCLIENT": "2", + "MQPERATTACH": "1", + "MAXQPERSERVERMQ": "2", + "MAXQPERCLIENTMQ": "1", + "MAXMQPERCLIENT": "1", + "MAXHISTLOG": "10000", + "PORT": "8020", + "CTLPORT": "8022", + "SELFCTLPORT": "8022", + "RWTIMEOUT": "100", + "RETRYCNT": "1000", + "CONTIMEOUT": "500000", + "MQRWTIMEOUT": "50", + "MQRETRYCNT": "20000", + "DOMERGE": "on", + "SSL": "no", + "K2HFULLMAP": "on", + "K2HMASKBIT": "8", + "K2HCMASKBIT": "4", + "K2HMAXELE": "8" + }, + + # + # SERVER NODES SECTION + # + "SVRNODE": + [ + { + "NAME": "localhost", + "PORT": "8020", + "CTLPORT": "8021", + "SSL": "no" + } + ], + + # + # SLAVE NODES SECTION + # + "SLVNODE": + [ + { + "NAME": "[.]*", + "CTLPORT": "8022" + } + ] +} + +# +# EOF +# diff --git a/tests/test_slave.yaml b/tests/test_slave.yaml new file mode 100644 index 0000000..5dd564f --- /dev/null +++ b/tests/test_slave.yaml @@ -0,0 +1,83 @@ +# +# CHMPX CONFIGRATION FILE FOR TEST +# +# Copyright 2014 Yahoo! JAPAN corporation. +# +# CHMPX is inprocess data exchange by MQ with consistent hashing. +# CHMPX is made for the purpose of the construction of +# original messaging system and the offer of the client +# library. +# CHMPX transfers messages between the client and the server/ +# slave. CHMPX based servers are dispersed by consistent +# hashing and are automatically layouted. As a result, it +# provides a high performance, a high scalability. +# +# For the full copyright and license information, please view +# the LICENSE file that was distributed with this source code. +# +# AUTHOR: Takeshi Nakatani +# CREATE: Tue Nor 15 2016 +# REVISION: +# + +# +# GLOBAL SECTION +# +GLOBAL: + { + FILEVERSION: 2, + DATE: "Tue, 15 Nov 2016 09:03:24 +0900", + GROUP: TESTSCRPT, + MODE: SLAVE, + DELIVERMODE: random, + MAXCHMPX: 4, + REPLICA: 0, + MAXMQSERVER: 2, + MAXMQCLIENT: 2, + MQPERATTACH: 1, + MAXQPERSERVERMQ: 2, + MAXQPERCLIENTMQ: 1, + MAXMQPERCLIENT: 1, + MAXHISTLOG: 10000, + PORT: 8020, + CTLPORT: 8022, + SELFCTLPORT: 8022, + RWTIMEOUT: 100, + RETRYCNT: 1000, + CONTIMEOUT: 500000, + MQRWTIMEOUT: 50, + MQRETRYCNT: 20000, + DOMERGE: on, + SSL: no, + K2HFULLMAP: on, + K2HMASKBIT: 8, + K2HCMASKBIT: 4, + K2HMAXELE: 8 + } + +# +# SERVER NODES SECTION +# +SVRNODE: + [ + { + NAME: localhost, + PORT: 8020, + CTLPORT: 8021, + SSL: no + } + ] + +# +# SLAVE NODES SECTION +# +SLVNODE: + [ + { + NAME: "[.]*", + CTLPORT: 8022 + } + ] +# +# EOF +#