diff options
Diffstat (limited to 'rasmgr')
41 files changed, 13561 insertions, 0 deletions
diff --git a/rasmgr/Makefile.am b/rasmgr/Makefile.am new file mode 100644 index 0000000..f4b54e0 --- /dev/null +++ b/rasmgr/Makefile.am @@ -0,0 +1,60 @@ +# +# This file is part of rasdaman community. +# +# Rasdaman community is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Rasdaman community is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +# +# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +# rasdaman GmbH. +# +# For more information please see <http://www.rasdaman.org> +# or contact Peter Baumann via <baumann@rasdaman.com>. +# * +# * MODULE: rasmgr +# * +# * COMMENTS: +########################################################################## +bin_PROGRAMS=rasmgr +rasmgr_SOURCES=ras_crypto.cc rasmgr_config.cc rasmgr_host.cc rasmgr_main.cc rasmgr_rascontrol.cc rasmgr_srv.cc \ + rasmgr_comm.cc rasmgr_dbm.cc rasmgr_master_nb.cc rasmgr_rascontrol_help.cc \ + rasmgr_users.cc rasmgr_comm_nb.cc rasmgr_error.cc rasmgr_localsrv.cc rasmgr_random.cc \ + hostcmp.cc \ + ras_crypto.hh rasmgr_config.hh rasmgr_host.hh rasmgr_rascontrol.hh rasmgr_srv.hh \ + rasmgr_comm.hh rasmgr_dbm.hh \ + rasmgr_users.hh rasmgr_comm_nb.hh rasmgr_error.hh rasmgr_localsrv.hh \ + rasmgr.hh rasmgr_protocol.hh rasmgr_master.hh +rasmgr_LDADD=../network/libnetwork.a ../commline/libcommline.a + +SUBDIRS=../network ../commline + +$(RECURSIVE_CLEAN_TARGETS): + @$(MAKE) $(AM_MAKEFLAGS) `echo $@ | sed s/-recursive/-am/` + + + + +#ifdef STATIC_LIBS +# EXTRASTATICLIBS= -Xlinker -Bstatic -nodefaultlibs -lstdc++ +# EXTRADINAMICLIBS= -Xlinker -Bdynamic -lm -lgcc -lc -lgcc +#endif +#SRCCXX = ras_crypto.cc rasmgr_config.cc rasmgr_host.cc rasmgr_main.cc rasmgr_rascontrol.cc rasmgr_srv.cc \ +# rasmgr_comm.cc rasmgr_dbm.cc rasmgr_master_nb.cc rasmgr_rascontrol_help.cc \ +# rasmgr_users.cc rasmgr_comm_nb.cc rasmgr_error.cc rasmgr_localsrv.cc rasmgr_random.cc rasmgr_slave.cc \ +# hostcmp.cc + + +#rasmgr: rasmgr_main.o rasmgr_config.o rasmgr_comm.o rasmgr_comm_nb.o rasmgr_host.o rasmgr_dbm.o rasmgr_srv.o rasmgr_random.o \ +# rasmgr_master_nb.o rasmgr_rascontrol.always rasmgr_users.o ras_crypto.o rasmgr_slave.o rasmgr_localsrv.o rasmgr_error.o hostcmp.o +# $(PURIFY) $(CXX) $(CXXFLAGS) -o rasmgr rasmgr_main.o rasmgr_config.o rasmgr_comm.o rasmgr_comm_nb.o rasmgr_host.o rasmgr_dbm.o rasmgr_master_nb.o rasmgr_rascontrol.o rasmgr_users.o ras_crypto.o rasmgr_slave.o rasmgr_localsrv.o rasmgr_error.o rasmgr_random.o rasmgr_srv.o hostcmp.o \ +# $(LDFLAGS) $(EXTRASTATICLIBS) -lcrypto $(EXTRADINAMICLIBS) $(NETWORK) $(COMMLINE) + diff --git a/rasmgr/hostcmp.cc b/rasmgr/hostcmp.cc new file mode 100644 index 0000000..1d40e82 --- /dev/null +++ b/rasmgr/hostcmp.cc @@ -0,0 +1,66 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: hostcmp.cc + * + * MODULE: rasmgr + * CLASS: none + * + * PURPOSE: + * special comparison function for host names (cf. man gethostname). + * speciality: "a.x.y"=="a" + * + * COMMENTS: + * none + * +*/ + +#include "debug.hh" +#include <cstring> + +bool hostCmp( const char *h1, const char *h2) +{ + ENTER( "hostCmp( " << h1 << ", " << h2 << " )" ); + + bool result = false; + + if ( h1 == NULL && h2 == NULL ) + result = true; + else if ( h1 == NULL ) + result = false; + else if ( h2 == NULL ) + result = false; + else + { + if (strlen(h1)==strlen(h2)) + result = ( strcmp(h1,h2) == 0 ); + else if (strlen(h1)>strlen(h2)) + result = ( strncmp(h1,h2,strlen(h2))==0 && h1[strlen(h2)]=='.' ); + else // (strlen(h1)<strlen(h2)) + result = ( strncmp(h1,h2,strlen(h1))==0 && h2[strlen(h1)]=='.' ); + } + + LEAVE( "Configuration::hostCmp() -> " << result ); + return result; +} + diff --git a/rasmgr/ras_crypto.cc b/rasmgr/ras_crypto.cc new file mode 100644 index 0000000..da2e973 --- /dev/null +++ b/rasmgr/ras_crypto.cc @@ -0,0 +1,79 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: ras_crypto.hh + * + * MODULE: rasmgr + * CLASS: + * + * PURPOSE: + * Interface to OpenSSL MD5 - functions + * + * COMMENTS: + * None +*/ + +#include "ras_crypto.hh" + +#if defined(SOLARIS) +#include <strings.h> +#else +#include <string.h> +#endif + + +bool testIsMessageDigestAvailable(const char *mdName) + { + EVP_MD_CTX mdctx; + const EVP_MD *md; + + OpenSSL_add_all_digests(); + + md = EVP_get_digestbyname(mdName);//"MD5"); + + if(!md) return false; + return true; + } + +int messageDigest(const char *input,char *output,const char *mdName) + { + EVP_MD_CTX mdctx; + const EVP_MD *md; + unsigned int md_len, i; + unsigned char md_value[100]; + + OpenSSL_add_all_digests(); + + md = EVP_get_digestbyname(mdName); + + if(!md) return 0; + + EVP_DigestInit(&mdctx, md); + EVP_DigestUpdate(&mdctx,input, strlen(input)); + EVP_DigestFinal(&mdctx, md_value, &md_len); + + for(i = 0; i < md_len; i++) sprintf(output+i+i,"%02x", md_value[i]); + + return strlen(output); + } + diff --git a/rasmgr/ras_crypto.hh b/rasmgr/ras_crypto.hh new file mode 100644 index 0000000..2ab46e6 --- /dev/null +++ b/rasmgr/ras_crypto.hh @@ -0,0 +1,49 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: ras_crypto.hh + * + * MODULE: rasmgr + * CLASS: + * + * PURPOSE: + * Interface to OpenSSL MD5 - functions + * + * COMMENTS: + * None +*/ + +#ifndef RAS_CRYPTO_HH +#define RAS_CRYPTO_HH + +#include <openssl/evp.h> + +// to use this functions you have to link libcrypto ( parameter -lcrypto in gcc command line) + + +bool testIsMessageDigestAvailable(const char *mdName); + +int messageDigest(const char *input,char *output,const char *mdName); + + +#endif diff --git a/rasmgr/rasmgr.hh b/rasmgr/rasmgr.hh new file mode 100644 index 0000000..57b77b7 --- /dev/null +++ b/rasmgr/rasmgr.hh @@ -0,0 +1,116 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr.hh + * + * MODULE: rasmgr + * CLASS: + * + * PURPOSE: + * Used for including system headers and general defines + * + * COMMENTS: + * - Beware using VLOG in: 'if (...) VLOG...;' ! + * do it like this: 'if (...) { VLOG...; }' + * +*/ + +#ifndef RASMGR_HH +#define RASMGR_HH + +#include<iostream> +#include<stdio.h> +#include<errno.h> +#include<stdlib.h> +#include<unistd.h> +#include<sys/types.h> +#include<sys/socket.h> +#include<netinet/in.h> +#include<netdb.h> +#include<signal.h> +#include<sys/wait.h> + +#include<string.h> + +#ifdef AIX +#include<strings.h> +#include<time.h> +#endif + +#include<stdlib.h> +#include<fstream> +#include <fcntl.h> +#include<sys/time.h> + +#include <vector> +#include <list> +#include <iterator> + +#include "rasmgr_protocol.hh" // protocol keyword definitions + +// clear this def for production release! +// #define TALK(a) cout<<a<<endl << flush; +//#define TALK(a) { /* TALK (a) */ } + +void exitbyerror(const char* text); + +char *strtolwr(char*); // should be somewhere in the C-library, but can't find it + +#ifdef RASMGR_IHC + #define INCLUDE_HIDDEN_COMMANDS +#endif + +// global defs -- PB 2003-jun-05 +//------------ + +/// name of the machine where we lock into portmapper to be a Highlander: +#define HOSTNAME "localhost" +/// RPC program number used by rasmgr: +#define RPCIF 0x29990000 +/// RPC version number used by rasmgr: +#define RPCVERS 1 + +// rasmgr return codes +// note: these were from -3 to +2 for errors, and none for OK from W.Schatz. I ordered them to 0 for ok, <0 for errors. +#define RASMGR_RESULT_OK 0 +#define RASMGR_RESULT_NO_MD5 1 +#define RASMGR_RESULT_ILL_ARGS 2 +#define RASMGR_RESULT_LICENSE_FAIL 3 +#define RASMGR_RESULT_NOT_ALONE 4 +#define RASMGR_RESULT_AUTH_CORRUPT 5 +#define RASMGR_RESULT_AUTH_OTHERHOST 6 +#define RASMGR_RESULT_AUTH_INCOMPAT 7 +#define RASMGR_RESULT_NO_SLAVE_IN_TEST 8 +#define RASMGR_EXIT_FAILURE 9 // was: EXIT_FAILURE from stdlib.h +#define RASMGR_RESULT_INTERNAL 10 + +// these should be still more global, they are part of the c/s protocol +// indicator for server type +#define SERVERTYPE_FLAG_RPC 'r' +#define SERVERTYPE_FLAG_HTTP 'h' +#define SERVERTYPE_FLAG_RNP 'n' + +// output depending on verbose cmd line flag +#define VLOG if (config.isVerbose()) cout + +#endif // RASMGR_HH diff --git a/rasmgr/rasmgr_comm.cc b/rasmgr/rasmgr_comm.cc new file mode 100644 index 0000000..02fffcf --- /dev/null +++ b/rasmgr/rasmgr_comm.cc @@ -0,0 +1,294 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_comm.cc + * + * MODULE: rasmgr + * CLASS: HTTPComm + * + * PURPOSE: + * Performs reliable, but blocking HTTP communication. used by the slave rasmgr + * + * COMMENTS: + * Will be removed, the plan is to have only non-blocking communication + * +*/ + +#include "rasmgr_comm.hh" + +#ifdef X86 + #define r_Socklen_t socklen_t +#endif + +#ifdef AIX + #define r_Socklen_t socklen_t +#endif + +#ifdef SOLARIS + #define r_Socklen_t socklen_t +#endif + +#ifdef DECALPHA + #define r_Socklen_t int +#endif + +#include "debug.hh" + + +HTTPComm::HTTPComm() + { //parentPID=getpid(); + listen_socket=-1; + exitRequest=false;; + } +HTTPComm::~HTTPComm() + { + ENTER("HTTPComm::~HTTPComm: enter." ); + closeListenSocket(); + LEAVE("HTTPComm::~HTTPComm: leave." ); + } + +void HTTPComm::closeListenSocket() + { + ENTER("HTTPComm::closeListenSocket: enter." ); + + if(listen_socket>=0) close(listen_socket); + + LEAVE("HTTPComm::closeListenSocket: leave." ); + } + +int HTTPComm::sendAnswer(int socket,int len) + { + int result = 0; + + ENTER("HTTPComm::sendAnswer: enter." << std::endl ); + + int write_count=writeWholeMessage(socket,outBuffer,len); + + // adapted logic to single point of return -- PB 26-may-2003 + if(write_count<0) + { TALK( "HTTPComm::sendAnswer: Error writing answer" ); + result = -1; + } + else + result = 0; + + LEAVE("HTTPComm::sendAnswer: leave. result=" << result ); + return result; + } + +int HTTPComm::getMessage() + { + ENTER("HTTPComm::getMessage: enter." << std::endl ); + + int socket=realGetMessage(); + + if(socket>0) + { + header=inBuffer; + body=strstr(inBuffer,"\r\n\r\n"); + if(body!=NULL) + { + *body=0; + body+=4; + } + else + { close(socket); + socket = -1; + } + } + + LEAVE("HTTPComm::getMessage: leave. socket=" << socket ); + return socket; + } + + +int HTTPComm::realGetMessage() + { + struct sockaddr_in clientname; + r_Socklen_t size=sizeof(clientname); + + ENTER("HTTPComm::getRealMessage: enter." ); + + int socket=accept(listen_socket,(struct sockaddr*)&clientname,&size); + if(socket<0) + { TALK( "HTTPComm::realGetMessage: Error accepting connection."); + socket = -1; // normalize error feedback + } + + if (socket >= 0) // accept() worked fine, so wa can continue + { + int read_count=readWholeMessage(socket,inBuffer,MAXMSG); + + if(read_count<0) + { TALK( "HTTPComm::realGetMessage: Error reading message."<<std::endl ); + close(socket); + socket = -1; + } + } + + LEAVE("HTTPComm::getRealMessage: leave. socket=" << socket ); + return socket; + } + +int HTTPComm::initListenSocket(int port) + { + int queuesize=SOMAXCONN; // the maximim number allowed by SO!! + + ENTER("HTTPComm::initListenSocket: enter. port=" << port ); + + FD_ZERO(&active_fd_set); + + listen_socket=makeSocket(port); + if(listen_socket<0) + { + TALK("HTTPComm::initListenSocket: makeSocket failed, errno=" << errno ); + exitbyerror("listen"); // it's OK to exit, we didn't start yet + } + + if(listen(listen_socket,queuesize)<0) + { + TALK("HTTPComm::initListenSocket: listen failed, errno=" << errno ); + exitbyerror("listen"); // it's OK to exit, we didn't start yet + } + + FD_SET(listen_socket,&active_fd_set); + + LEAVE("HTTPComm::initListenSocket: leave. socket=" << socket ); + return 0; // no error + + } + +int HTTPComm::makeSocket(int port) + { + int sock; + struct sockaddr_in name; + struct protoent *getprotoptr; + + ENTER("HTTPComm::makeSocket: enter. port=" << port ); + + getprotoptr=getprotobyname("tcp"); + sock=socket(PF_INET,SOCK_STREAM,getprotoptr->p_proto); + if(sock<0) + { + TALK("HTTPComm::makeSocket: socket failed. errno=" << errno ); + exitbyerror("make socket"); + } + + name.sin_family=AF_INET; + name.sin_port=htons(port); + name.sin_addr.s_addr=htonl(INADDR_ANY); + +#ifdef SO_REUSEADDR + int val = 1; + int len = sizeof( val ); + if(setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char*)&val, len )) + { + TALK( "HTTPComm::makeSocket: Can't set address reusable: "<< strerror(errno) ); + } +#endif + + int sockResult = bind(sock,(sockaddr*)&name,sizeof(name)); + TALK( "HTTPComm::makeSocket: bind() with socket=" << sock << ", name.sin_port="<< name.sin_port << " returned " << sockResult ); + if(sockResult < 0) + { + TALK( "HTTPComm::makeSocket: bind failed: "<< strerror(errno) ); + exitbyerror("bind"); + // This is OK to exit, program just starts and we can't have an address + } + + ENTER("HTTPComm::makeSocket: leave. socket=" << sock << std::endl ); + return sock; + } + +void HTTPComm::shouldExit() + { + exitRequest=true; + } + +bool HTTPComm::isMessage(const char *messageStart) + { + ENTER("HTTPComm::isMessage: enter. messageStarte=" << messageStart ); + + bool rasp= (strncasecmp(header,messageStart,strlen(messageStart))==0) ? true:false; + if(rasp) + { + TALK("HTTPComm::isMessage: (b) Message=" << messageStart ); + } + + LEAVE("HTTPComm::isMessage: leave. result=" << rasp ); + return rasp; + } + +// +int readWholeMessage(int socket,char *destBuffer,int buffSize) + { + ENTER("HTTPComm::readWholeMessage: enter. socket=" << socket << ", destBuffer=" << destBuffer << ", buffSize=" << buffSize ); + + // we read what is comming in until we encounter a '\0' + // this is our end-sign. + int totalLength=0; + int redNow; + while(1) + { + redNow = read(socket,destBuffer+totalLength,buffSize-totalLength); + if(redNow == -1) + { if(errno == EINTR) continue; // read was interrupted by signal + + TALK("HTTPComm::readWholeMessage: read error. errno=" << errno ); + return -1; // another error + } + totalLength+=redNow; + + if(destBuffer[totalLength-1]==0) break; // THE END + } + + LEAVE("HTTPComm::readWholeMessage: leave. totalLength=" << totalLength ); + return totalLength; + } + +int writeWholeMessage(int socket,char *destBuffer,int buffSize) + { + ENTER("HTTPComm::writeWholeMessage: enter. socket=" << socket << ", destBuffer=" << destBuffer << ", buffSize=" << buffSize ); + + // we write the whole message, including the ending '\0', which is already in + // the buffSize provided by the caller + int totalLength=0; + int writeNow; + while(1) + { + writeNow = write(socket,destBuffer+totalLength,buffSize-totalLength); + if(writeNow == -1) + { if(errno == EINTR) continue; // read was interrupted by signal + + TALK("HTTPComm::writeWholeMessage: read error. errno=" << errno ); + return -1; // another error + } + totalLength+=writeNow; + + if( totalLength==buffSize ) break; // THE END + } + + LEAVE("HTTPComm::writeWholeMessage: leave. totalLength=" << totalLength ); + return totalLength; + } + diff --git a/rasmgr/rasmgr_comm.hh b/rasmgr/rasmgr_comm.hh new file mode 100644 index 0000000..492c9c0 --- /dev/null +++ b/rasmgr/rasmgr_comm.hh @@ -0,0 +1,100 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_comm.hh + * + * MODULE: rasmgr + * CLASS: HTTPComm + * + * PURPOSE: + * Performs reliable, but blocking HTTP communication. used by the slave rasmgr + * + * COMMENTS: + * Will be removed, the plan is to have only non-blocking communication + * +*/ + +#ifndef RASMGR_COMM_HH +#define RASMGR_COMM_HH + +#include "rasmgr.hh" + +#define MAXMSG 1024 +#define MAXMSGOUTBUFF 20000 + + +class HTTPComm + { + public: + HTTPComm(); + ~HTTPComm(); + void closeListenSocket(); + void shouldExit(); + char *decodeFlag(int statusFlag); + protected: + int initListenSocket(int port); + int makeSocket(int port); + int sendAnswer(int socket,int len); + int getMessage(); + + protected: + bool isMessage(const char *messageStart); + // pid_t parentPID; + int listen_socket; + fd_set active_fd_set,read_fd_set; + + struct timeval tv; + timeval *tvptr; + char *header; + char *body; + char inBuffer[MAXMSG]; + char outBuffer[MAXMSGOUTBUFF]; + + bool exitRequest; + private: + int realGetMessage(); + }; + +// status flags that rasmgr understands +// - these come from rasserver +#define SERVER_DOWN 0 +#define SERVER_AVAILABLE 1 +#define SERVER_REGULARSIG 3 +// - this comes from slave rasmgr and LSM +#define SERVER_CRASHED 2 + +// textual representation of the above status flags -- PB 2004-jul-16 +#define SERVER_DOWN_TXT "server down" +#define SERVER_AVAILABLE_TXT "server available" +#define SERVER_REGULARSIG_TXT "server alive" +#define SERVER_CRASHED_TXT "server aborted" + + + /* This two functions where written late in the night, when we came back from BLVA + (Bayerisches Landesvermessungsamt), where we had problems on DEC+CompaqTrue64 + this &@$! operating system wasn't able to send a message in one piece which was written in one piece + */ +int readWholeMessage(int socket,char *destBuffer,int buffSize); +int writeWholeMessage(int socket,char *destBuffer,int buffSize); + +#endif diff --git a/rasmgr/rasmgr_comm_nb.cc b/rasmgr/rasmgr_comm_nb.cc new file mode 100644 index 0000000..65b913b --- /dev/null +++ b/rasmgr/rasmgr_comm_nb.cc @@ -0,0 +1,921 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_comm_nb.cc + * + * MODULE: rasmgr + * CLASS: IOSelector, NbJob, NbServerComm + * + * PURPOSE: + * Performs reliable, non-blocking HTTP communication. used by the master rasmgr + * Hierarchy: NbServerComm uses NbJob uses IOSelector + * IOSelector maintains a set of read and write file descriptors (ie, sockets) + * plus a timeout value common to all of them. + * NbJob maintains message streams allowing partial (piecewise) read/write. + * NbServerComm bundles all this, abstracting from the particular socket used. + * + * COMMENTS: + * none + * +*/ + +#include <time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "rasmgr_comm_nb.hh" + +#include "debug.hh" + +/************************************************************************* + * + * CLASS: IOSelector + * + * PURPOSE: + * IOSelector maintains a set of read and write file descriptors to be watched + * plus a timeout value common to all of them. + * ATTENTION: This class does not really care about socket status! + * Exceptions: + * - waitForRequest() performs blocking wait (w timeout) for incoming request on the sets. + * - closeForcedAllSockets() closes all open sockets in the read and write set (should go to NbJob where bind() etc is performed) + * + * CHANGE HISTORY (append further entries): [see module header] + * + * COMMENTS: + * - this class is (almost) internal to NbJob + * - when adding sockets there is no check for valid socket, duplicate insertion, etc + * - when clearing a socket from a set, there is no check whether it was really in there + * + ***********************************************************************/ + +IOSelector::IOSelector() +{ + ENTER( "IOSelector::IOSelector: enter." ); + FD_ZERO(& watchReadFdSet); + FD_ZERO(& watchWriteFdSet); + FD_ZERO(& watchExceptFdSet); + tvptr = NULL; + LEAVE( "IOSelector::IOSelector: leave." ); +} + +void IOSelector::setTimeout(int sec,int millisec) +{ + ENTER( "IOSelector::setTimeout: enter. timeout=" << sec << "." << millisec << " secs." ); + tvinit.tv_sec=sec; + tvinit.tv_usec=millisec * 1000; + tvptr=&tv; // yes, yes, &tv + LEAVE( "IOSelector::setTimeout: leave." ); +} + +void IOSelector::disableTimeout() +{ + ENTER( "IOSelector::disableTimeout: enter." ); + tvptr=NULL; + LEAVE( "IOSelector::disableTimeout: leave." ); +} + +// add socket to the socket set watched for incoming requests +void IOSelector::setReadSocket(int socket) +{ + ENTER( "IOSelector::setReadSocket: enter. add to watchReadSet: " << socket ); + FD_SET(socket,&watchReadFdSet); + LEAVE( "IOSelector::setReadSocket: leave." ); +} + +// remove socket from read socket set +void IOSelector::clearReadSocket(int socket) +{ + ENTER( "IOSelector::clearReadSocket: enter. remove from watchReadSet: " << socket ); + FD_CLR(socket,&watchReadFdSet); + LEAVE( "IOSelector::clearReadSocket: leave." ); +} + +// add socket to the socket set watched for outgoing requests +void IOSelector::setWriteSocket(int socket) +{ + ENTER( "IOSelector::setWriteSocket: enter. add to watchWriteSet: " << socket ); + FD_SET(socket,&watchWriteFdSet); + LEAVE( "IOSelector::setWriteSocket: leave." ); +} + +// remove socket from write socket set +void IOSelector::clearWriteSocket(int socket) +{ + ENTER( "IOSelector::clearWriteSocket: enter. remove from watchWriteSet: " << socket ); + FD_CLR(socket,&watchWriteFdSet); + LEAVE( "IOSelector::clearWriteSocket: leave." ); +} + +// result = outcome of select() request: +// > 0: number of active sockets +// ==0: timeout +// < 0: error +// preconditions: +// - read set, write set contains valid sockets +// - bind() on each socket in the read and write set + +int IOSelector::waitForRequest() +{ + int result; + + ENTER( "IOSelector::waitForRequest: enter." ); + + resultReadFdSet =watchReadFdSet; + resultWriteFdSet=watchWriteFdSet; + // error unused + // tv has to be reloaded every time; if tvptr is NULL it doesn't matter + tv.tv_sec = tvinit.tv_sec; + tv.tv_usec = tvinit.tv_usec; + TALK( "IOSelector::waitForRequest: timeout=" << tv.tv_sec << "sec " << tv.tv_usec << "microsec." ); + + result = select(FD_SETSIZE,&resultReadFdSet,&resultWriteFdSet,NULL,tvptr); + if (result < 0) + { + TALK( "IOSelector::waitForRequest: select error: " << strerror(errno) ); + } + else // if ( result == 0) + { + TALK( "IOSelector::waitForRequest: select() successful, result=" << result ); + } + +#if 0 // unsuccessful try + else // (result > 0) + { + TALK( "IOSelector::waitForRequest: select() successful, returned " << result ); + int isPending = 0; + for (int i=0; i<FD_SETSIZE && isPending==0; i++) + { // unfortunately, FD_ISSET() cannot be deployed within an if() + isPending = (int) FD_ISSET( i, &resultReadFdSet ); + if ( isPending ) + { + TALK( "IOSelector::waitForRequest: input pending on socket " << i ); + result = i; // report this socket + } + isPending = (int) FD_ISSET( i, &resultWriteFdSet ); + if ( isPending ) + { + TALK( "IOSelector::waitForRequest: output pending on socket " << i ); + result = i; // report this socket + } + } + } +#endif // 0 + + LEAVE( "IOSelector::waitForRequest: leave. result=" << result ); + return result; +} + +// look into sockets if there is something pending +// precondition: a select() call has been made soon before (ie, socket state hasn't changed inbetween) +// returns: +// pending socket if found +// 0 otherwise +// cycles through all FDs, read and write interleaved +int IOSelector::someWaitingSocket() +{ + static int lastVisited = 0; // last inspecrted socket from last call, allows to run round robin strategy + int waitingSocket = 0; // some socket waiting to be treated + bool found = false; // result of FD_ISSET() call + + ENTER( "IOSelector::someWaitingSocket: enter. lastVisited=" << lastVisited ); + + for ( int i=0; i<FD_SETSIZE && waitingSocket==0; i++ ) // walk through max all possible FDs + { + lastVisited = (lastVisited == FD_SETSIZE) ? 0 : lastVisited+1; + found = ( (int) FD_ISSET( lastVisited, &resultReadFdSet ) + + (int) FD_ISSET( lastVisited, &resultWriteFdSet ) ) > 0 ? true : false; + if ( found ) // cannot use FD_ISSET in if() + waitingSocket = lastVisited; + } + + LEAVE( "IOSelector::someWaitingSocket: leave. waitingSocket=" << waitingSocket ); +} + +bool IOSelector::isReadSocket(int socket) +{ + bool result = (socket < 0) ? false: true; + if (result == true) + result = FD_ISSET(socket,&resultReadFdSet); + return result; +} + +bool IOSelector::isWriteSocket(int socket) +{ + bool result = (socket < 0) ? false: true; + if (result == true) + result = FD_ISSET(socket,&resultWriteFdSet); + return result; +} + +void IOSelector::closeForcedAllSockets() +{ + ENTER( "IOSelector::closeForcedAllSockets: enter." ); + for(int i=0;i<FD_SETSIZE;i++) + { + if(FD_ISSET(i,&watchReadFdSet) || FD_ISSET(i,&watchWriteFdSet)) + { + TALK( "IOSelector::closeForcedAllSockets: closing " << i ); + int result = close(i); + if (result < 0) + { + TALK( "IOSelector::closeListenSocket: error closing socket: " << strerror(errno) ); + } + } + } + LEAVE( "IOSelector::closeForcedAllSockets: leave." ); +} + + +// end of class IOSelector ############################################### + +/************************************************************************* + * + * CLASS: NbJob + * + * PURPOSE: + * NbJob maintains a message stream (socket) allowing partial (piecewise) read/write. + * Keeps an IOSelector instance for watching. + * Outbound messages need a NULL terminator; existence can be checked with isMessageOK(). + * + * CHANGE HISTORY (append further entries): [see module header] + * + * COMMENTS: + * - sockets must be managed elsewhere: open/select/bind/listen/close + * + ***********************************************************************/ + +// now declared private in .hh +time_t NbJob::timeOutInterv = 30; +time_t NbJob::currentTime = 0; + +NbJob::NbJob() +{ + ENTER( "NbJob::NbJob: enter." ); + socket = -1; + lastActionTime=0; + outputBuffer = 0; + inputBuffer = 0; + bigError = false; + LEAVE( "NbJob::NbJob: leave." ); +} + +void NbJob::init(IOSelector *pselector,int maxInputBuffer) +{ + ENTER( "NbJob::init: enter. maxInputBuffer=" << maxInputBuffer ); + this->pselector = pselector; + this->maxInputLength = maxInputBuffer; + messageTerminator = '\0'; + LEAVE( "NbJob::init: leave." ); +} + +// dropped (being redundant) in favour of closeConnection() -- PB 2003-jun-04 +#ifdef NEVER_AGAIN +void NbJob::reset() +{ + ENTER( "NbJob::reset: enter." ); + clearConnection(); + clearInputBuffer(); + clearOutputBuffer(); + LEAVE( "NbJob::reset: leave." ); +} +#endif + +void NbJob::clearInputBuffer() +{ + ENTER( "NbJob::clearInputBuffer: enter." ); + if(inputBuffer) + delete[] inputBuffer; + inputBuffer = 0; + nextReadPos = 0; + LEAVE( "NbJob::clearInputBuffer: leave." ); +} + +void NbJob::clearOutputBuffer() +{ + ENTER( "NbJob::clearOutputBuffer: enter." ); + if(outputBuffer) + delete[] outputBuffer; + outputBuffer = 0; + nextWritePos = 0; + LEAVE( "NbJob::clearOutputBuffer: leave." ); +} + +// clear connection completely, close socket +void NbJob::clearConnection() +{ + ENTER( "NbJob::clearConnection: enter." ); + if(socket > 0) + { + pselector->clearReadSocket(socket); + pselector->clearWriteSocket(socket); + int result = close(socket); + int tempErrno = errno; + TALK( "NbJob::clearConnection: close() on socket " << socket << " returned " << result ); + if (result != 0) + { + TALK( "NbJob::clearConnection: error closing socket: " << strerror(tempErrno) ); + } + socket = -1; + bigError = false; + } + LEAVE( "NbJob::clearConnection: leave." ); +} + +// returns true if the current job is too old +bool NbJob::processJobTimeout() +{ + bool result = (messageReadyTime + timeOutInterv > currentTime) ? false:true; + TALK( "NbJob::processJobTimeout: result=" << result ); + return result; +} + +// on timeout, reset all buffers but don't close socket +bool NbJob::cleanUpIfTimeout() +{ + ENTER( "NbJob::cleanUpIfTimeout: enter." ); + + bool result = (socket < 0 ) || (lastActionTime + timeOutInterv > currentTime) ? false : true; + + if (result==true) + { + TALK("NbJob::cleanUpIfTimeout: client timeout on socket " << socket); + closeConnection(); + } + + LEAVE( "NbJob::cleanUpIfTimeout: leave. result=" << result ); + return result; +} + +// reset all that's necessary, then do an accept() to wait [timeout] for incoming calls +// upon success, set socket to the descriptor returned by accept() +// preconditions: +// - socket initialised with bind() etc. +// Note: accept() works like an "open" in that it creates a separate socket which must be closed explicitly! + +NbJob::acceptStatus NbJob::acceptConnection(int listenSocket) +{ + ENTER( "NbJob::acceptConnection: enter. listenSocket=" << listenSocket ); + + if(socket>0) + { + bool result = cleanUpIfTimeout(); + if(result == false) + { + LEAVE("NbJob::acceptConnection: leave. cleanUpIfIimeout() returned: no timeout yet."); + return acs_Iambusy; + // free again + } + } + + markAction(); + + clearInputBuffer(); + inputBuffer = new char[maxInputLength]; + if(inputBuffer == NULL) + { + LEAVE("NbJob::acceptConnection: leave. out of memory."); + return acs_outofmem; + } + + struct sockaddr_in internetAddress; + r_Socklen_t size=sizeof(sockaddr_in); + + // extract the first pending request from the socket queue + // NB: accept() clones the socket. + errno = 0; + +// replace accept() code with a simple FD_ISSET() search +#ifndef NEVER_AGAIN + // accept() here serves to clone the socket. + // there shouldn't be any wait because of the select() call just passed via waitForRequest() + socket=accept(listenSocket,(struct sockaddr*)&internetAddress,&size); + int saveerrno=errno; + TALK("NbJob::acceptConnection: accept() with socket " << listenSocket << " returned " << socket << ", sin_port=" << htons(internetAddress.sin_port) << ", requestor=" << inet_ntoa(internetAddress.sin_addr) ); + if(socket<0) + { + if(saveerrno==EAGAIN) + { + LEAVE("NbJob::acceptConnection: leave. no pending connections"); + } + else + { + LEAVE("NbJob::acceptConnection: leave. accept error " << strerror(saveerrno) ); + } + return acs_nopending; + } + + // several flags, such as non-blocking, are not inherited accross accept() - see manual + int val = fcntl(socket,F_GETFL,0); + val |= O_NONBLOCK; + fcntl(socket,F_SETFL,val); + + pselector->setReadSocket(socket); + +#else // !NEVER_AGAIN -- but currently inactive. + + // int activeSocket = pselector->someWaitingSocket(); + // basically we should always have at least one pending request because a select() came before + if (activeSocket == 0) + { + TALK( "NbJob::acceptConnection: found NO active socket." ); + } + else + { + TALK( "NbJob::acceptConnection: found active socket " << activeSocket ); + } + +# endif // NEVER_AGAIN + + LEAVE( "NbJob::acceptConnection: leave. acs_accepted, cloned socket is " << socket); + return acs_accepted; +} // acceptConnection() + +// this method got the body of reset() which (being redundant) has been dropped -- PB 2003-jun-04 +void NbJob::closeConnection() +{ + ENTER( "NbJob::closeConnection: enter." ); + clearConnection(); + clearInputBuffer(); + clearOutputBuffer(); + LEAVE( "NbJob::closeConnection: leave." ); +} + +// PB 2003-may-29: to fix bug that connections cloned by accept() remain open, so the number grows infinitely +// ...but this doesn't work yet, don't use it... +void NbJob::closeSocket() +{ + ENTER( "NbJob::closeSocket: enter. socket=" << socket ); + int result = close(socket); + if (result != 0) + { + TALK( "NbJob::closeSocket: error closing socket: " << strerror(errno) ); + } + socket = -1; + LEAVE( "NbJob::closeSocket: leave." ); +} + +void NbJob::markAction() +{ + lastActionTime=currentTime; +} + +int NbJob::getSocket() +{ + return socket; +} + +bool NbJob::isMessageOK() +{ + return (nextReadPos > 1 && inputBuffer[nextReadPos - 1] == messageTerminator) ? true:false; +} + +bool NbJob::wasError() +{ + return bigError; +} + +// read from socket into input buffer as much as fits in or until NULL terminator +// returns true iff some bytes could be written +bool NbJob::readPartialMessage() +{ + bool messOK; + + ENTER("NbJob::readPartialMessage: enter." ); + markAction(); + errno = 0; + int nbytes = read(socket,inputBuffer + nextReadPos,maxInputLength - nextReadPos); + TALK("NbJob::readPartialMessage: read() with socket=" << socket << " returned " << nbytes ); + + if(nbytes) // wrote some bytes + { + TALK( "NbJob::readPartialMessage: read socket("<<socket<<") "<<nbytes<<" bytes to pos="<<nextReadPos); + nextReadPos += nbytes; + inputBuffer[nextReadPos] = 0; + messOK = isMessageOK(); + + if(messOK) + { + TALK("NbJob::readPartialMessage: socket read completed on " << socket ); + messageReadyTime = currentTime; + } + } + else // nothing written = error + { + int saveerrno=errno; + switch(saveerrno) + { + case EINTR: + TALK("NbJob::readPartialMessage: read: EINTR, retry please"); + break; + + case EAGAIN: + TALK("NbJob::readPartialMessage: read: EAGAIN, retry please"); + break; + + case 0: + TALK("NbJob::readPartialMessage: read: Premature End-of-file"); + bigError=true; + break; + + default: + TALK("NbJob::readPartialMessage: read: error " << saveerrno ); + bigError = true; + break; + } + messOK = false; + } + + LEAVE("NbJob::readPartialMessage: leave. read completed=" << messOK ); + return messOK; +} // readPartialMessage() + +const char* NbJob::getMessage() +{ + return inputBuffer; +} + +// initialise answer sending, copy complete message in local buffer +// set socket to write mode +bool NbJob::initSendAnswer(const char *message) +{ + ENTER("NbJob::initSendAnswer: enter. message=" << message ); + bool result = true; + + markAction(); + clearInputBuffer(); + answerLength = strlen(message)+1; + outputBuffer = new char[answerLength]; + if (outputBuffer == NULL) + { + TALK( "NbJob::initSendAnswer: error: out of memory." ); + result = false; + // FIXME: close socket? + } + + if (result == true) + { + strcpy(outputBuffer,message); + nextWritePos = 0; + + pselector->setWriteSocket(socket); + pselector->clearReadSocket(socket); // sa fie + } + + LEAVE("NbJob::initSendAnswer: leave. result=" << result ); + return result; +} + +// write current contents of output buffer to socket +// result == true iff writing went fine +bool NbJob::writePartialMessage() +{ + bool result = false; + + ENTER("NbJob::writePartialMessage: enter." ); + + markAction(); + errno = 0; + int nbytes = write(socket,outputBuffer + nextWritePos,answerLength - nextWritePos); + TALK("NbJob::writePartialMessage: write() with socket=" << socket << " returned " << nbytes ); + + if(nbytes) + { + TALK("NbJob::writePartialMessage: write to socket=" << socket << ", " << nbytes << " bytes to pos=" << nextWritePos << ", answerLength=" << answerLength ); + nextWritePos += nbytes; + + if(nextWritePos == answerLength) // everything written? + { + TALK("NbJob::writePartialMessage: write completed."); + // closeConnection(); // was here, now shifted up the hierarchy -- PB 2003-jun-10 + result = true; + } + } + else + { + int saveerrno=errno; + switch(saveerrno) + { + case EINTR: + TALK("NbJob::writePartialMessage: EINTR, retry please"); + break; + + case EAGAIN: + TALK("NbJob::writePartialMessage: EAGAIN, retry please"); + break; + + case 0: + TALK("NbJob::writePartialMessage: premature client hang up."); + bigError=true; + break; + + default: + TALK("NbJob::writePartialMessage: error "<< strerror(saveerrno) ); + bigError = true; + break; + } + } + LEAVE("NbJob::writePartialMessage: leave. result=" << result ); + return result; +} + +// is socket still open? (actually; wrong name) +bool NbJob::isOperationPending() +{ + bool result = socket > 0 ? true:false; + TALK("NbJob::isOperationPending (i.e.: socket open) -> " << result ); + return result; +} + +void NbJob::printStatus() +{ + TALK( "NbJob::printStatus: socket=" << socket << ", isRead=" << (int) pselector->isReadSocket(socket) << ", isWrite=" << (int) pselector->isWriteSocket(socket) ); +} + +//################################################################################################ + +NbServerComm::NbServerComm() +{ + listenSocket = -1; + maxJobs = 0; + job = 0; + mypid = getpid(); +} + +void NbServerComm::initJobs(int maxJobs) +{ + ENTER( "NbServerComm::initJobs: enter. maxJobs=" << maxJobs ); + + this->maxJobs = maxJobs; + job = new NbJob[maxJobs]; + + for(int i=0;i<maxJobs;i++) + { + job[i].init(&selector,MAXMSG); + } + + LEAVE( "NbServerComm::initJobs: leave." ); +} + +NbServerComm::~NbServerComm() +{ + ENTER( "NbServerComm::~NbServerComm: enter." ); + closeListenSocket(); + LEAVE( "NbServerComm::~NbServerComm: leave." ); +} + +// opens the central listen socket +bool NbServerComm::initListenSocket(int listenPort) +{ + ENTER( "NbServerComm::initListenSocket: enter. listenPort=" << listenPort ); + + struct protoent *getprotoptr = getprotobyname("tcp"); + + struct sockaddr_in name; + name.sin_family=AF_INET; + name.sin_port=htons(listenPort); // translate listen port# + name.sin_addr.s_addr=htonl(INADDR_ANY); + + listenSocket=socket(PF_INET,SOCK_STREAM,getprotoptr->p_proto); + if(listenSocket < 0) + { + TALK( "NbServerComm::initListenSocket: socket error: " << strerror(errno) ); + exitbyerror("socket"); + } + + // make the socket nonblocking + int val =fcntl(listenSocket,F_GETFL,0); + val|=O_NONBLOCK; + fcntl(listenSocket,F_SETFL,val); + + val =fcntl(listenSocket,F_GETFL,0); + if(val & O_NONBLOCK) + TALK("NbServerComm::initListenSocket: socket " << listenSocket << " is nonblocking" ); + +#ifdef SO_REUSEADDR + val = 1; + int len = sizeof( val ); + if(setsockopt( listenSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&val, len )) + { + TALK( "NbServerComm::initListenSocket: cannot set address reusable using setsockopt: " << strerror(errno) ); + } +#endif + + int sockResult = bind(listenSocket,(sockaddr*)&name,sizeof(name)); + TALK( "NbServerComm::initListenSocket: bind() with socket=" << listenSocket << ", name.port=" << name.sin_port << " returned " << sockResult ); + if (sockResult < 0) + { + TALK( "NbServerComm::initListenSocket: cannot set address reusable using bind: " << strerror(errno) ); + exitbyerror("bind"); + } + + int queuesize=SOMAXCONN; // the maximum number allowed by SO!! + sockResult = listen(listenSocket,queuesize); + TALK("NbServerComm::initListenSocket: listen() with socket=" << listenSocket << ", queuesize=" << queuesize << " returned " << sockResult ); + if(sockResult < 0) + { + TALK( "NbServerComm::initListenSocket: listen error: " << strerror(errno) ); + exitbyerror("listen"); + } + + selector.setReadSocket(listenSocket); // add this socket to the read watch list + + LEAVE( "NbServerComm::initListenSocket: leave." ); + return true; +} + +void NbServerComm::closeListenSocket() +{ + ENTER( "NbServerComm::closeListenSocket: enter." ); + if(listenSocket>0) + { + selector.clearReadSocket(listenSocket); + TALK( "NbServerComm::closeListenSocket: closing socket " << listenSocket ); + int result = close(listenSocket); + if (result < 0) + { + TALK( "NbServerComm::closeListenSocket: error closing socket: " << strerror(errno) ); + } + listenSocket = -1; + } + LEAVE( "NbServerComm::closeListenSocket: leave." ); +} + +void NbServerComm::shouldExit() +{ + ENTER( "NbServerComm::shouldExit: enter." ); + exitRequest=true; + LEAVE( "NbServerComm::shouldExit: leave." ); +} + +bool NbServerComm::mayExit() +{ + ENTER( "NbServerComm::mayExit: enter." ); + bool result = true; + + if(exitRequest==false) + result = false; + else + { + closeListenSocket(); // we don't accept requests any more + + for(int i=0;i<maxJobs && (result==true);i++) + { + if(job[i].isOperationPending()) + result = false; // no, we have pending operations, don't close + } + } + + LEAVE( "NbServerComm::mayExit: leave. result=" << result ); + return result; +} + +void NbServerComm::lookForTimeout() +{ + ENTER( "NbServerComm::lookForTimeout: enter." ); + for(int i=0;i<maxJobs;i++) + { + job[i].cleanUpIfTimeout(); + } + LEAVE( "NbServerComm::lookForTimeout: leave." ); +} + +// look through all write jobs and write out pending message +void NbServerComm::dispatchWriteRequest() +{ + ENTER( "NbServerComm::dispatchWriteRequest: enter." ); + + int i; + for(i=0;i<maxJobs;i++) + { + int socket=job[i].getSocket(); + if(socket>0) // active job pending? + { + if(selector.isWriteSocket(socket)) + { + TALK( "flushing write job " << i << ", socket " << socket ); + bool result = job[i].writePartialMessage(); + if (result == true) + { + TALK( "job done, closing connection " << i << ", socket " << socket ); + job[i].closeConnection(); // was in writePartialMessage() -- PB 2003-jun-10 + } + else + { + TALK( "connection " << i << "write error, socket " << socket ); + } + } + else + { + TALK( "job " << i << ": socket not writable, nothing to do." ); + } + } + } // for + + LEAVE( "NbServerComm::dispatchWriteRequest: leave." ); +} + +// look through all read jobs and load msg buffers +// NB: as opposed to write, here is no closeSocket! why?? +void NbServerComm::dispatchReadRequest() +{ + ENTER( "NbServerComm::dispatchReadRequest: enter." ); + + int i; + for(i=0;i<maxJobs;i++) + { + int socket=job[i].getSocket(); + if(socket>0) + { + if(selector.isReadSocket(socket)) + { + TALK( "NbServerComm::dispatchReadRequest: flush reading job " << i << ", socket " << socket << " -- NO CLOSE!?!" ); + // result code was not queried, added it -- PB 2004-jul-16 + bool allOk = job[i].readPartialMessage(); + if (allOk) + { + TALK( "connection " << i << " done reading, socket " << socket ); + // no close here, connection used for writing afterwards + } + else // could not read + { + TALK( "connection " << i << " read error, socket " << socket ); + } + } + } + } // for + + LEAVE( "NbServerComm::dispatchReadRequest: leave." ); +} + +void NbServerComm::connectNewClients() +{ + ENTER( "NbServerComm::connectNewClients: enter." ); + + // why only for read sockets? because we process _incoming_ requests + if(selector.isReadSocket(listenSocket)) + { + for(int i=0;i<maxJobs;i++) + { + // we try to connect as many pending connections as possible + + TALK( "NbServerComm::connectNewClients: trying to open #" << i << " of " << maxJobs << " sockets (initially: read)." ); + // the accept() call inside this below will clone the socket, so we get many read sockets + NbJob::acceptStatus status = job[i].acceptConnection(listenSocket); + + if(status == NbJob::acs_nopending || status == NbJob::acs_outofmem) + { + TALK( "NbServerComm::connectNewClients: aborting, bad status:" << status ); + break; + // first, because there is no pending request, second, because out of mem is not solved by retry + } + } // for + } + else + { + TALK( "NbServerComm::connectNewClients: master socket is no read socket: " << listenSocket ); + } + + LEAVE( "NbServerComm::connectNewClients: leave." ); +} + +void NbServerComm::closeForcedAllSockets() +{ + ENTER( "NbServerComm::closeForcedAllSockets: enter." ); + if(mypid != getpid()) + selector.closeForcedAllSockets(); + LEAVE( "NbServerComm::closeForcedAllSockets: leave." ); +} + +void NbServerComm::printStatus() +{ + int i; + for(i=0;i<maxJobs;i++) + { + TALK( "NbServerComm::printStatus: connection #" << i << ":" ); + job[i].printStatus(); + } + return; +} + +//####################################################### diff --git a/rasmgr/rasmgr_comm_nb.hh b/rasmgr/rasmgr_comm_nb.hh new file mode 100644 index 0000000..c6d724c --- /dev/null +++ b/rasmgr/rasmgr_comm_nb.hh @@ -0,0 +1,196 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_comm_nb.hh + * + * MODULE: rasmgr + * CLASS: <many> + * + * PURPOSE: + * Performs reliable, non-blocking HTTP communication. used by the master rasmgr + * + * COMMENTS: + * none + * +*/ +#ifndef RASMGR_COMM_NB_HH +#define RASMGR_COMM_NB_HH + +#include "rasmgr_comm.hh" + +// this is the NON-BLOCKING version, which will replace the other one + +// maximum number of slaves a master mgr can handle +// #define MAXJOBSMASTER 50 +#define MAXJOBSMASTER 1 + + +class IOSelector + { + public: + IOSelector(); + void setTimeout(int sec,int milisec); + void disableTimeout(); + void setReadSocket(int socket); + void clearReadSocket(int socket); + void setWriteSocket(int socket); + void clearWriteSocket(int socket); + + int waitForRequest(); + int someWaitingSocket(); + + bool isReadSocket(int socket); + bool isWriteSocket(int socket); + + void closeForcedAllSockets(); // useful for childs which don't have to inherit this sockets + private: + fd_set watchReadFdSet; + fd_set watchWriteFdSet; + fd_set watchExceptFdSet; // unused but ... + + fd_set resultReadFdSet; + fd_set resultWriteFdSet; + fd_set resultExceptFdSet; // unused but ... + + struct timeval tvinit; + struct timeval tv; + timeval *tvptr; // set to &tv... for timeout, NULL for no timeout + + }; + +class NbJob + { + public: + NbJob(); + void init(IOSelector *pselector,int maxInputBuffer); + + enum acceptStatus + { acs_nopending = 0, + acs_Iambusy = 1, + acs_accepted = 2, + acs_outofmem = 3, + acs_invalidsocket = 4 + }; + acceptStatus acceptConnection(int listenSocket); + + bool readPartialMessage(); + bool isMessageOK(); + const char *getMessage(); + + bool initSendAnswer(const char*); + bool writePartialMessage(); + bool isOperationPending(); + + int getSocket(); + const char *getRequestor(); // added -- PB 2004-jul-16 + + bool wasError(); + void closeConnection(); + void closeSocket(); + bool cleanUpIfTimeout(); + bool processJobTimeout(); + void printStatus(); + // void reset(); replaced by closeConnection() -- PB 2003-jun-04 + void clearConnection(); + private: + void clearInputBuffer(); + void clearOutputBuffer(); + int socket; + IOSelector *pselector; + + // reading + char *inputBuffer; + int nextReadPos; + int maxInputLength; + char messageTerminator; + // writing + char *outputBuffer; + int answerLength; + int nextWritePos; + // errors + bool bigError; + + // timing + time_t lastActionTime; + time_t messageReadyTime; + void markAction(); + // public: + static time_t timeOutInterv; + static time_t currentTime; + + }; + +//################### + +class NbServerComm + { + public: + NbServerComm(); + ~NbServerComm(); + + // void work(); + void shouldExit(); + void closeForcedAllSockets(); // useful for children which don't have to inherit these sockets + void printStatus(); + protected: + void initJobs(int maxJobs); + bool initListenSocket(int port); + void closeListenSocket(); + + // void itsRinging(); doesn't exit -- PB 2003-may-04 + void dispatchReadRequest(); + void dispatchWriteRequest(); + + void connectNewClients(); + + void lookForTimeout(); + int listenSocket; + + NbJob *job; + int maxJobs; + + volatile bool exitRequest; + bool mayExit(); + + IOSelector selector; + pid_t mypid; + + }; + +#ifdef X86 + #define r_Socklen_t socklen_t +#endif + +#ifdef AIX + #define r_Socklen_t socklen_t +#endif + +#ifdef SOLARIS + #define r_Socklen_t socklen_t +#endif + +#ifdef DECALPHA + #define r_Socklen_t int +#endif + +#endif diff --git a/rasmgr/rasmgr_config.cc b/rasmgr/rasmgr_config.cc new file mode 100644 index 0000000..e0e1f4c --- /dev/null +++ b/rasmgr/rasmgr_config.cc @@ -0,0 +1,601 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_config.cc + * + * MODULE: rasmgr + * CLASS: Configuration, RasmgrLicense + * + * PURPOSE: + * Config info from commandline, environment and license + * + * COMMENTS: + * none + * +*/ + +using namespace std; + +#include "globals.hh" // DEFAULT_HOSTNAME, RMANHOME_VAR, DEFAULT_PORT, RASMGR_CONF_FILE + +#include "rasmgr_config.hh" +#include "rasmgr.hh" +#include "rasmgr_host.hh" +#include "rasmgr_dbm.hh" +#include "rasmgr_srv.hh" +#include "ras_crypto.hh" +#include "rasmgr_users.hh" +#include "rasmgr_comm.hh" +#include "rasmgr_rascontrol.hh" + +#include <stdlib.h> // mkstemp() +#include <iomanip> +#include <time.h> + +#include "debug.hh" + +extern bool hostCmp( const char *h1, const char *h2); + + +Configuration::Configuration(): + cmlInter (CommandLineParser::getInstance()), + cmlName (cmlInter.addStringParameter(CommandLineParser::noShortName, "name", "<name> symbolic name of this rasmgr (slave only, default: the host name)")), + cmlHostName (cmlInter.addStringParameter(CommandLineParser::noShortName, "hostname", "<name> the advertized host name (master only, default: same as UNIX command 'hostname')")), + cmlPort (cmlInter.addLongParameter(CommandLineParser::noShortName, "port", "<port> listen port number", DEFAULT_PORT)), + cmlPollFrequ (cmlInter.addLongParameter(CommandLineParser::noShortName, "poll", "<poll> polling timeout (in seconds) for rasmgr listen port", DEFAULT_POLLING_FREQUENCY )), + cmlMaster (cmlInter.addStringParameter(CommandLineParser::noShortName, "master", "<name> host of rasmgr master (slave only)")), + cmlMasterPort (cmlInter.addLongParameter(CommandLineParser::noShortName, "mport", "<port> listen port number of rasmgr master (slave only)", DEFAULT_PORT)), + cmlQuiet (cmlInter.addFlagParameter( 'q', CommandLineParser::noLongName, "quiet: don't log requests (default: log requests to stdout)")), +#ifdef NO_OFFICIAL_RELEASE + cmlTest (cmlInter.addFlagParameter(CommandLineParser::noShortName, "test", "test mode")), + cmlDSup (cmlInter.addFlagParameter(CommandLineParser::noShortName, "dsup", "debug mode")), + cmlRandTest (cmlInter.addFlagParameter(CommandLineParser::noShortName, "rgt", "random generator test")), + cmlRth (cmlInter.addFlagParameter(CommandLineParser::noShortName, "rth", "disable rthl test")), + cmlMultiWT (cmlInter.addFlagParameter(CommandLineParser::noShortName, "amw", "allow multiple write transactions")), +#endif + cmlHelp (cmlInter.addFlagParameter('h', RASMGRCMD_HELP, "print this help")) +{ + ENTER( "Configuration::Configuration: enter." ); + + int ghnResult = gethostname(hostName, sizeof(hostName) ); + if (ghnResult != 0) // cannot get hostname? + { + int ghnErrno = errno; + std::cout << "Error: cannot get hostname of my machine: error " << ghnErrno << "; will use '" << DEFAULT_HOSTNAME << "' as heuristic." << endl; + strcpy( hostName, DEFAULT_HOSTNAME ); + } + strcpy(slaveName,hostName); + strcpy(publicHostName,hostName); + listenPort=DEFAULT_PORT; + masterPort=DEFAULT_PORT; + masterName[0]=0; + + pollFrequency = DEFAULT_POLLING_FREQUENCY; + + configFileName[0]=0; + slave = false; + + char *rasHome= CONFDIR; + if(rasHome!=0) + sprintf(configFileName,"%s/",rasHome); + + strcat( configFileName, RASMGR_CONF_FILE ); + altConfigFileName[0] = '\0'; + + testModus = false; + debugSupport = false; + rtHlTest = true; // by default RasMgr tests at runtime if it's the only one + allowMultiWT = false; // rasmgr doesn't allow multiple write transactions for a db, but for internal use we can try it + + LEAVE( "Configuration::Configuration: leave." ); +} + +bool Configuration::readConfigFile() +{ + //insert internal host, it's not in config file + hostmanager.insertInternalHost(); + + char inBuffer[MAXMSG]; + char outBuffer[MAXMSG]; + bool result = true; + bool fileIsOpen = false; + + ENTER( "Configuration::readConfigFile: enter. Looking for config file " << configFileName ); + + VLOG << "Inspecting config file " << configFileName << "..."; + + std::ifstream ifs(configFileName); // open config file + + if(ifs) + fileIsOpen = true; + else + { + TALK( "Configuration::readConfigFile: cannot open config file." ); + std::cout << "Warning: cannot open config file " << configFileName << endl; + fileIsOpen = false; + } + result = true; // was: false, but I want to allow a missing file + + if (fileIsOpen) + { + authorization.startConfigFile(); + + while( ! ifs.eof() ) // was: while(1), I simplified this + // processRequest() will get an additional empty line at eof, but this is harmless + { + ifs.getline(inBuffer,MAXMSG); + // if(!strlen(inBuffer) && ifs.eof()) // FIXME: what happens if last line in file is empty? + // { + // TALK( "Configuration::readConfigFile: strlen(inBuffer)=" << strlen(inBuffer) << ", eof=" << ifs.eof()); + // break; + // } + + TALK( "Configuration::readConfigFile: processing line: " << inBuffer ); + rascontrol.processRequest(inBuffer,outBuffer); + } + + authorization.endConfigFile(); + + ifs.close(); // close config file handle + } + + if (result == true && fileIsOpen) + VLOG << "ok" << endl; + + LEAVE( "Configuration::readConfigFile: leave. result=" << result ); + return true; +} + +// return name of alternate config file; +// takes value from preceding saveAltConfigFile() call. +const char *Configuration::getAltConfigFileName() +{ + return altConfigFileName; +} + +// in future this is not used directly, but through saveOrigConfigFile() and saveAltConfigFile() wrappers below +bool Configuration::saveConfigFile() +{ + ENTER( "Configuration::saveConfigFile: enter." ); + + std::ofstream ofs(configFileName); + if(!ofs) + { + LEAVE( "Configuration::saveConfigFile: leave. cannot open config file " << configFileName << " for writing." ); + return false; + } + + ofs << "# rasmgr config file (v1.1)" << std::endl; + ofs << "# warning: do not edit this file, it may be overwritten by rasmgr!" << std::endl; + ofs << "#" << std::endl; + + int i; + //serverhosts + for(i=0;i<hostmanager.countHosts();i++) + { + ServerHost &xx=hostmanager[i]; + + if(i > 0) + ofs<<"define host "<<xx.getName()<<" -net "<<xx.getNetworkName()<<" -port "<<xx.getListenPort()<<std::endl; + else + { + //by default the master RasMgr is init with the hostname as name => if we have to we change it here + if( ! hostCmp(xx.getName(),config.getHostName()) ) + ofs << "change host "<<config.getHostName()<<" -name "<<xx.getName()<<std::endl; + + if(xx.useLocalHost()==false) + ofs<<"change host "<<xx.getName()<<" -uselocalhost off"<<std::endl; + } + } + //databaseHosts + for(i=0;i<dbHostManager.countHosts();i++) + { + DatabaseHost &xx=dbHostManager[i]; + ofs<<"define dbh "<<xx.getName()<<" -connect "<<xx.getConnectionString()<<std::endl; + } + //rasservers + for(i=0;i<rasManager.countServers();i++) + { + RasServer &xx=rasManager[i]; + + ofs<<"define srv "<<xx.getName()<<" -host "<<xx.getHostName()<<" -type "<<xx.getType(); + if(xx.getType()==SERVERTYPE_FLAG_HTTP) + ofs<<" -port "<<xx.getPort(); + else + ofs<<" -port 0x"<<std::hex<<xx.getPort()<<std::dec; + if(xx.isConnectedToDBHost()) + ofs<<" -dbh "<<xx.getDBHostName(); + ofs<<std::endl; + + ofs<<"change srv "<<xx.getName()<<" -countdown "<<xx.getCountDown(); + + if(strcmp(xx.getExecutableName(),RASEXECUTABLE)) + ofs<<" -exec "<<xx.getExecutableName(); + + ofs<<" -autorestart "<<(xx.isAutoRestart() ? "on":"off")<<" -xp "<<xx.getExtraParams()<<std::endl; + } + + //databases + for(i=0;i<dbManager.countDatabases();i++) + { + Database &xx=dbManager[i]; + + for(int j=0;j<xx.countConnectionsToDBHosts();j++) + { + ofs<<"define db "<<xx.getName()<<" -dbh "<<xx.getDBHostName(j)<<std::endl; + } + } + + ofs.close(); // this was missing, therefore sometimes the config file was cleared -- PB 2003-jun-06 + LEAVE( "Configuration::saveConfigFile: leave." ); + return true; +} // saveConfigFile() + +// save config file at original place, i.e., under the name of configFile +bool Configuration::saveOrigConfigFile() +{ + ENTER( "Configuration::saveOrigConfigFile: enter." ); + + bool result = saveConfigFile(); + + LEAVE( "Configuration::saveOrigAltConfigFile: leave. result=" << result ); + return result; +} + +// save configuration file in another file, same dir as config file +bool Configuration::saveAltConfigFile() +{ + ENTER( "Configuration::saveAltConfigFile()" ); + + bool result = true; + char origFileName[ sizeof(configFileName) ]; // temp copy of origFileName + + // save original file name + (void) strcpy( origFileName, configFileName ); + + // build temp file by appending a unique string + (void) strcpy( altConfigFileName, configFileName ); + (void) strcat( altConfigFileName, ".XXXXXX" ); // 6 * 'X', see man mkstemp() + + int altFile = mkstemp( altConfigFileName ); // replaces the Xs by unique string + if (altFile < 0) // error in creating file name + { + int tempError = errno; + TALK( "Configuration::saveAltConfigFile: error creating alternate file name: " << strerror(tempError) ); + result = false; + } + + if (result == true) + { + // now we have a valid + open file, but we can't use it like that, because we open down below. + // so close it again, being happy that we have a valid file name. bad hack, though. + int closeResult = close( altFile ); + if (closeResult != 0) + TALK( "Configuration::saveAltConfigFile: error in temporary closing file, ignoring that." ); + } + + if (result == true) + { + (void) strcpy( configFileName, altConfigFileName ); // set file to be written to alternate name + result = saveConfigFile(); // save file, name has been substituted successfully + (void) strcpy( configFileName, origFileName ); // restore original config file name + } + + LEAVE( "Configuration::saveAltConfigFile: leave. result=" << result ); + return result; +} + +bool Configuration::interpretArguments(int argc, char **argv, char **envp) +{ + ENTER( "Configuration::interpretArguments: enter." ); + + bool result = true; + //errorCode=0; + + //process command line + try + { + cmlInter.processCommandLine(argc, argv); + } + catch(CmlException& err) + { + std::cout << "Error parsing command line: " << err.what() << std::endl; + printHelp(); + result = false; + } + + SET_OUTPUT( true ); // by default, enable trace (debug) output + verbose = true; // by default, be verbose + if( (result==true) && cmlQuiet.isPresent() ) + { + SET_OUTPUT( false ); // disable trace (debug) output + // debugOutput = true; // done via the above macro + verbose = false; // only minimum messages + result = true; + } + + if( (result==true) && cmlHelp.isPresent() ) + { + printHelp(); + result = false; + } + + if( (result==true) && cmlHostName.isPresent() ) + { + if (sizeof(hostName) > strlen(cmlHostName.getValueAsString())) + strcpy(publicHostName,cmlHostName.getValueAsString()); + else + { + VLOG << "Error: host name exceeds length limit of " << sizeof(hostName) << " characters." << std::endl; + result = false; + } + } + + if( (result==true) && cmlPort.isPresent() ) + { + try + { + listenPort = cmlPort.getValueAsLong(); + } + catch(CmlException& err) + { + VLOG << "Error converting port parameter " << cmlPort.getLongName() << " to integer: " << err.what() << std::endl; + result = false; + } + } + + if( (result==true) && cmlMaster.isPresent() ) + { + slave = true; + strcpy(masterName,cmlMaster.getValueAsString()); + } + + if( (result==true) && cmlMasterPort.isPresent() ) + { + try + { + masterPort = cmlMasterPort.getValueAsLong(); + } + catch(CmlException& err) + { + VLOG << "Error converting " << cmlMasterPort.getLongName() << " to integer: " << err.what() << std::endl; + result = false; + } + } + + if( (result==true) && cmlPollFrequ.isPresent() ) + { + try + { + pollFrequency = cmlPollFrequ.getValueAsLong(); + } + catch(CmlException& err) + { + VLOG << "Error converting " << cmlPollFrequ.getLongName() << " to integer: " << err.what() << std::endl; + result = false; + } + if (result == true && pollFrequency <= 0) + { + VLOG << "Error: poll frequency must be a positive integer." << std::endl; + result = false; + } + } + + if( (result==true) && cmlName.isPresent() ) + { + if (sizeof(slaveName) > strlen(cmlName.getValueAsString())) + strcpy(slaveName,cmlName.getValueAsString()); + else + { + VLOG << "Error: slave name exceeds length limit of " << sizeof(slaveName) << " characters." << std::endl; + result = false; + } + } + +#ifdef NO_OFFICIAL_RELEASE + testModus = cmlTest.isPresent(); + debugSupport=cmlDSup.isPresent(); + rtHlTest=cmlRth.isPresent(); + + if( (result==true) && cmlRandTest.isPresent() ) + { + std::cout<<"Random generator test..."<<(randomGenerator.insideTest() ? "PASSED":"FAILED" )<<std::endl; + result = false; + } + + allowMultiWT = cmlMultiWT.isPresent(); + +#endif + + LEAVE( "Configuration::interpretArguments: leave. result=" << result ); + return result; +} + +bool Configuration::allowMultipleWriteTransactions() +{ + return allowMultiWT; +} + +bool Configuration::isDebugSupport() +{ + return debugSupport; +} + +bool Configuration::isVerbose() +{ + return verbose; +} + +bool Configuration::isTestModus() +{ + return testModus; +} + +const char* Configuration::getHostName() +{ + return hostName; +} + +const char* Configuration::getPublicHostName() +{ + return publicHostName; +} + +int Configuration::getListenPort() +{ + return listenPort; +} + +const char* Configuration::getMasterName() +{ + return masterName; +} +int Configuration::getMasterPort() +{ + return masterPort; +} + +int Configuration::getPollFrequency() +{ + return pollFrequency; +} + +const char* Configuration::getSlaveName() +{ + return slaveName[0] ? slaveName:hostName; +} + + +void Configuration::printHelp() +{ + std::cout << "Usage: rasmgr [options]" << std::endl; + std::cout << "Options:" << std::endl; + cmlInter.printHelp(); + std::cout << std::endl; + return; +} + +void Configuration::printStatus() +{ + std::cout << "rasmgr configuration parameter settings:" << std::endl; + std::cout << " symb name = " << publicHostName << std::endl; + std::cout << " hostname = " << publicHostName << std::endl; + std::cout << " port = " << listenPort << std::endl; + std::cout << " poll = " << pollFrequency << std::endl; + std::cout << " quiet = " << (!verbose) << std::endl; + return; +} + +//**************************************************************************************************************** + +// FIXME: this needs refinement -- PB 2003-jun-09 +BenchmarkTimer::BenchmarkTimer(const char *text) +{ + this->text = (char*)text; + time_t now = time(NULL); + char *n = ctime(&now); + n[strlen(n)-1] = '\0'; // delete trailing newline char + // cout << "--- rasmgr timer start for \'" << text << "\' at " << n << "." << endl; + gettimeofday(&start,NULL); +} + +BenchmarkTimer::~BenchmarkTimer() +{ +} + +void BenchmarkTimer::result() +{ + timeval result; + gettimeofday(&end,NULL); + int r=timeval_subtract(&result,&end,&start); + time_t now = time(NULL); + char *n = ctime(&now); + n[strlen(n)-1] = '\0'; // delete trailing newline char + VLOG << "rasmgr timer stop for \'" << text << "\' at " << n << ": " << result.tv_sec << '.' << std::setw(3) << std::setfill('0') << result.tv_usec << " seconds elapsed." << endl; +} + +int BenchmarkTimer::timeval_subtract(timeval *result,timeval *x,timeval *y) +{ +#define ONE_MILLION 1000000 + /* Perform the carry for the later subtraction by updating Y. */ + if (x->tv_usec < y->tv_usec) + { + int nsec = (y->tv_usec - x->tv_usec) / ONE_MILLION + 1; + y->tv_usec -= ONE_MILLION * nsec; + y->tv_sec += nsec; + } + if (x->tv_usec - y->tv_usec > ONE_MILLION) + { + int nsec = (x->tv_usec - y->tv_usec) / ONE_MILLION; + y->tv_usec += ONE_MILLION * nsec; + y->tv_sec -= nsec; + } + + /* Compute the time remaining to wait. + `tv_usec' is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_usec = x->tv_usec - y->tv_usec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + +//#################################### + /*/ + + //read-pacaleala + hostmanager.insertNewHost("Ras1","martini",8081); + hostmanager.insertNewHost("Ras2","coconutkiss",8082); + hostmanager.insertNewHost("Ras3","tequilla",8082); + + dbHostManager.insertNewHost("julep", "rasdec/rasdec@julep.akglocal.de"); + dbHostManager.insertNewHost("pinacolada","rasdec/rasdec@pinacolada.akglocal.de"); + + dbManager.insertNewDatabase("RasDemo"); + dbManager[0].connectToDBHost("julep"); + dbManager[0].connectToDBHost("pinacolada"); + + dbManager.insertNewDatabase("BLVA"); + dbManager[1].connectToDBHost("pinacolada"); + + rasManager.insertNewServer("RasServer11","Ras1",SERVERTYPE_FLAG_RPC,0x29999998); + rasManager[0].connectToDBHost("julep");//,"\\"); + + rasManager.insertNewServer("RasServer12","Ras1",SERVERTYPE_FLAG_RPC,0x29999998); + rasManager[1].connectToDBHost("pinacolada");//,"\\"); + + rasManager.insertNewServer("RasServer21","Ras2",SERVERTYPE_FLAG_HTTP,8085); + rasManager[2].connectToDBHost("julep");//,"\\"); + + rasManager.insertNewServer("RasServer31","Ras3",SERVERTYPE_FLAG_HTTP,8084); + rasManager[3].connectToDBHost("julep");//,"\\"); + + rasManager.insertNewServer("RasServer00","RasMaster",SERVERTYPE_FLAG_HTTP,8083); + rasManager[4].connectToDBHost("julep");//,"\\"); + + dbManager.insertNewDatabase("RasNew"); + dbManager[2].connectToDBHost("julep"); + + return true; + // */ + diff --git a/rasmgr/rasmgr_config.hh b/rasmgr/rasmgr_config.hh new file mode 100644 index 0000000..e8e7288 --- /dev/null +++ b/rasmgr/rasmgr_config.hh @@ -0,0 +1,134 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_config.hh + * + * MODULE: rasmgr + * CLASS: Configuration, RasmgrLicense + * + * PURPOSE: + * Config info from commandline, environment and license + * + * COMMENTS: + * none + * +*/ +#ifndef RASMGR_CONFIG_HH +#define RASMGR_CONFIG_HH + +#include <iostream> +#include <string> +#include <sys/time.h> + +#include "commline/cmlparser.hh" + +// default rasmgr listen port polling frequency [secs] +// must be longer than the OS specific time to release TIM_WAIT sockets +const int DEFAULT_POLLING_FREQUENCY = 200; +// #define helps to avoid multiple defs in different .cc files: +#define DEFAULT_POLLING_FREQUENCY_STR "200" + +/// host/domain name size (See man gethostname) +#define HOSTNAME_SIZE 255 + +class Configuration + { + public: + Configuration(); + + bool interpretArguments(int argc, char **argv,char **envp); + bool readConfigFile(); + bool saveOrigConfigFile(); + bool saveAltConfigFile(); + const char *getAltConfigFileName(); + const struct tm *getExpirationDate(); + const char * getHostName(); + const char * getPublicHostName(); + int getListenPort(); + + const char * getMasterName(); + int getMasterPort(); + int getPollFrequency(); + const char * getSlaveName(); + bool isTestModus(); + bool isDebugSupport(); + bool isVerbose(); + + bool allowMultipleWriteTransactions(); + + void printStatus(); + + private: + void printHelp(); + + char hostName[HOSTNAME_SIZE]; + char publicHostName[HOSTNAME_SIZE]; // usually ==hostName, but you might want to publish IP address or hostname.domainname instead + int listenPort; + // name of configuration file + char configFileName[HOSTNAME_SIZE]; + // name of alternate configuration file for rescue save, generated by saveAltConfigFile() + char altConfigFileName[HOSTNAME_SIZE]; + + // if slave + char masterName[HOSTNAME_SIZE]; + int masterPort; + char slaveName[HOSTNAME_SIZE]; //my name, when I'm slave and no HIGHLANDER + + int pollFrequency; // listen port polling frequency in seconds + bool testModus; + bool debugSupport; + bool verbose; + bool slave; + + bool saveConfigFile(); + + bool rtHlTest; + bool allowMultiWT; + + //interface program + CommandLineParser &cmlInter; + CommandLineParameter &cmlHelp, &cmlHostName, &cmlPort, &cmlPollFrequ; + CommandLineParameter &cmlMaster, &cmlMasterPort, &cmlName, &cmlQuiet; +#ifdef NO_OFFICIAL_RELEASE + CommandLineParameter &cmlTest, &cmlDSup, &cmlRandTest, &cmlRth, &cmlMultiWT; +#endif + }; + +extern Configuration config; + +class BenchmarkTimer + { + public: + BenchmarkTimer(const char *text); + ~BenchmarkTimer(); + void result(); + private: + int timeval_subtract(timeval *result,timeval *x,timeval *y); + + struct timeval start; + struct timeval end; + + char* text; + + }; +#endif diff --git a/rasmgr/rasmgr_dbm.cc b/rasmgr/rasmgr_dbm.cc new file mode 100644 index 0000000..2890a4d --- /dev/null +++ b/rasmgr/rasmgr_dbm.cc @@ -0,0 +1,573 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_dbm.cc + * + * MODULE: rasmgr + * CLASS: DatabaseHost, DatabaseHostManager, Database, DatabaseManager + * + * COMMENTS: + * none + * +\*/ + +#include "rasmgr_dbm.hh" +#include "rasmgr_srv.hh" +#include "rasmgr_users.hh" + +#include "debug.hh" + +extern bool hostCmp( const char *h1, const char *h2); + + +DatabaseHost::DatabaseHost() + { hostName[0]=0; + connectString[0]=0; + valid=false; + activServers=0; + } +DatabaseHost::~DatabaseHost() + { + } + +const char* DatabaseHost::getName() + { return hostName; + } + +const char* DatabaseHost::getConnectionString() + { return connectString; + } + +void DatabaseHost::changeConnectionString(const char *connectString) + { strcpy(this->connectString,connectString); + } +void DatabaseHost::changeName(const char *newName) + { strcpy(hostName,newName); + } + +void DatabaseHost::init(const char* hostName,const char* connectString) + { + strcpy(this->hostName,hostName); + strcpy(this->connectString,connectString); + valid=true; + } + +void DatabaseHost::regStartServer() { activServers++;} + +void DatabaseHost::regDownServer() { activServers--;} + +//void DatabaseHost::incrConnServers() { connServers++;} + +//void DatabaseHost::decrConnServers() { connServers--;} + +//void DatabaseHost::incrConnDatabases() { connDatabases++;} + +//void DatabaseHost::decrConnDatabases() { connDatabases--;} + +bool DatabaseHost::isBusy() + { + //std::cout<<"DBH="<<hostName<<"s="<<connServers<<" d="<<connDatabases<<std::endl; + return activServers ? true:false; //(connServers + connDatabases) ? true:false; + } + + +bool DatabaseHost::isValid() + { + return valid; + } + +bool DatabaseHost::prepareToBeRemoved() + { + if(isBusy()) return false; + + //disconnect all servers from me + rasManager.disconnectAllServersFromDBH(hostName); + + //disconnect all databases from me + dbManager.disconnectAllDatabasesFromDBH(hostName); + + return true; + } + +//********************************************************************** +DatabaseHostManager::DatabaseHostManager() + { + } +DatabaseHostManager::~DatabaseHostManager() + { + + } + +bool DatabaseHostManager::insertNewHost(const char* hostName,const char* connectString) + { + char tempHostName[200]; + strcpy(tempHostName,hostName); + // why?? strtolwr(tempHostName); + + if(testUniqueness(tempHostName)==false) return false; + + DatabaseHost tempDatabaseHost; + hostList.push_back(tempDatabaseHost); + DatabaseHost &refDatabaseHost=hostList.back(); + + refDatabaseHost.init(tempHostName,connectString); + + return true; + } + +bool DatabaseHostManager::removeHost(const char* hostName) + { + list<DatabaseHost>::iterator iter=hostList.begin(); + for(int i=0;i<hostList.size();i++) + { + if(hostCmp(iter->getName(),hostName)) + { + if(iter->isBusy()) + return false; + iter->prepareToBeRemoved(); + hostList.erase(iter); + return true; + } + + iter++; + } + return false; + } + +bool DatabaseHostManager::testUniqueness(const char* hostName) + { + list<DatabaseHost>::iterator iter=hostList.begin(); + for(int i=0;i<hostList.size();i++) + { + if(hostCmp(iter->getName(),hostName)) + return false; + iter++; + } + return true; + } + +DatabaseHost& DatabaseHostManager::operator[](int x) + { + list<DatabaseHost>::iterator iter=hostList.begin(); + for(int i=0;i<x;i++) + iter++; + return *iter; + } +DatabaseHost& DatabaseHostManager::operator[](const char* hostName) + { + + list<DatabaseHost>::iterator iter=hostList.begin(); + for(int i=0;i<hostList.size();i++) + { + if(hostCmp(iter->getName(),hostName)) + return *iter; + iter++; + } + return protElem; + } + + +int DatabaseHostManager::countHosts() + { + return hostList.size(); + } + +bool DatabaseHostManager::reset() + { + if(config.isTestModus()==false) return false; + + list<DatabaseHost>::iterator iter=hostList.begin(); + for(int i=0;i<hostList.size();i++,iter++) + { + iter->prepareToBeRemoved(); + } + + while(hostList.size()) + { + hostList.pop_front(); + } + + return true; + } + +bool DatabaseHostManager::acceptChangeName(const char *oldName,const char *newName) + { + if(hostCmp(oldName,newName)) + return true; // if someone really wants to change a name with the same, + return testUniqueness(newName); + } + +//************************************************************************* + +Database::Database() + { databaseName[0]=0; + valid=false; + + traceWT = false; + countWriteTransactions =0; + countReadTransactions =0; + } +Database::~Database() + { + } + +const char* Database::getName() + { return databaseName; + } +void Database::changeName(const char* databaseName) + { strcpy(this->databaseName,databaseName); + } + +void Database::init(const char* databaseName) + { + strcpy(this->databaseName,databaseName); + valid=true; + } +bool Database::isValid() + { + return valid; + } + +const char* Database::getDescriptionHeader(char *destBuffer) + { + sprintf(destBuffer," Database Name Open Trans."); + return destBuffer; + } +const char* Database::getDescription(char *destBuffer) + { + sprintf(destBuffer,"%-20s (%dw + %dr)",databaseName,countWriteTransactions,countReadTransactions); + return destBuffer; + } + +bool Database::connectToDBHost(const char* hostName) + { + DatabaseHost &TempDBH=dbHostManager[hostName]; + + if(TempDBH.isValid()==false) return false; // no such hostName + + if(checkConnection(TempDBH)) return false; // is already connected + + hostPtrList.push_back(&TempDBH); + //removed TempDBH.incrConnDatabases(); + + // alse connecting to servers connected to this database host + for(int i=0;i<rasManager.countServers();i++) + { + RasServer &r = rasManager[i]; + if(hostCmp(r.getDBHostName(),hostName)) + { + connectToRasServer(r.getName()); + } + } + + return true; + } + +bool Database::disconnectFromDBHost(const char* hostName) + { + list<DatabaseHost*>::iterator iter=hostPtrList.begin(); + for(int i=0;i<hostPtrList.size();i++) + { + DatabaseHost *ptrDBH=*iter; + + if(hostCmp(ptrDBH->getName(),hostName)) + { + for(int j=0;j<rasManager.countServers();j++) + { // disconnectig from the RasServers connected to the same database host + if(hostCmp(hostName,rasManager[j].getDBHostName())) + { + disconnectFromRasServer(rasManager[j].getName()); + } + } + + //removed ptrDBH->decrConnDatabases(); + hostPtrList.erase(iter); + return true; + } + iter++; + } + return false;; + } +void Database::disconnectForRemove() + { + // this means disconnect from all database hosts + list<DatabaseHost*>::iterator iter=hostPtrList.begin(); + int listsize=hostPtrList.size(); + for(int i=0;i<listsize;i++) + { + //removed (*iter)->decrConnDatabases(); + iter++; + } + for(int i=0;i<listsize;i++) + { hostPtrList.pop_front(); + } + //and revoke all trustees for it + userManager.removeDatabaseRights(databaseName); + } + +bool Database::isConnectedToDBHost(const char* hostName) + { + DatabaseHost &r = dbHostManager[hostName]; + + if(r.isValid()==false) return false; + + return checkConnection(r); + } +bool Database::checkConnection(DatabaseHost &databaseHost) + { + list<DatabaseHost*>::iterator iter=hostPtrList.begin(); + for(int i=0;i<hostPtrList.size();i++) + { + if(*iter== &databaseHost) return true; + + iter++; + } + return false; + } + +int Database::countConnectionsToDBHosts() + { + return hostPtrList.size(); + } +const char* Database::getDBHostName(int x) + { + if( x < hostPtrList.size() ) + { + list<DatabaseHost*>::iterator iter=hostPtrList.begin(); + for(int i=0;i<x;i++,iter++); + return (*iter)->getName(); + } + return "noHost!"; + } + +bool Database::connectToRasServer(const char *serverName) + { + RasServer &rasServer=rasManager[serverName]; + + if(rasServer.isValid()==false) return false; // no such serverName + + if(checkConnection(rasServer)) return false; // is already connected + + rasPtrList.push_back(&rasServer); + + return true; + + } +bool Database::disconnectFromRasServer(const char *serverName) + { + list<RasServer*>::iterator iter=rasPtrList.begin(); + for(int i=0;i<rasPtrList.size();i++) + { + RasServer *ptrRas=*iter; + + if(hostCmp(ptrRas->getName(),serverName)) + { + rasPtrList.erase(iter); + return true; + } + iter++; + } + return false; + } + +bool Database::isConnectedToRasServer(const char *serverName) + { + RasServer &r = rasManager[serverName]; + + if(r.isValid()==false) return false; + + return checkConnection(r); + } + +int Database::countConnectionsToRasServers() + { + return rasPtrList.size(); + } + +const char* Database::getRasServerName(int x) + { + if( x < rasPtrList.size() ) + { + list<RasServer*>::iterator iter=rasPtrList.begin(); + for(int i=0;i<x;i++,iter++); + return (*iter)->getName(); + } + return "noRasServer!"; + } + +bool Database::checkConnection(RasServer &rasServer) + { + list<RasServer*>::iterator iter=rasPtrList.begin(); + for(int i=0;i<rasPtrList.size();i++) + { + if(*iter== &rasServer) return true; + + iter++; + } + return false; + } + +void Database::setTraceWriteTrans(bool how) + { + traceWT=how; + } + +void Database::startWriteTransaction() + { + if(traceWT) std::cout<<" DbName="<<databaseName<<" rwTrans-in"<<std::endl; + countWriteTransactions++; + } + +void Database::endWriteTransaction() + { + if(traceWT) std::cout<<" DbName="<<databaseName<<" rwTrans-out"<<std::endl; + countWriteTransactions--; + } +int Database::getWriteTransactionCount() + { + if(traceWT) std::cout<<" DbName="<<databaseName<<" ask rwTrans? ("<<countWriteTransactions<<")"<<std::endl; + return countWriteTransactions; + } + +void Database::startReadTransaction() + { + countReadTransactions++; + } + +void Database::endReadTransaction() + { + countReadTransactions--; + } +int Database::getReadTransactionCount() + { + return countReadTransactions; + } +bool Database::isBusy() + { return countReadTransactions+countWriteTransactions ? true:false; + } +//********************************************************************** +DatabaseManager::DatabaseManager() + { + } +DatabaseManager::~DatabaseManager() + { + + } + +bool DatabaseManager::insertNewDatabase(const char* databaseName) + { + if(testUniqueness(databaseName)==false) return false; + + Database tempDatabase; + dtbList.push_back(tempDatabase); + Database &refDatabase=dtbList.back(); + refDatabase.init(databaseName); + return true; + } + +bool DatabaseManager::removeDatabase(const char* databaseName) + { + list<Database>::iterator iter=dtbList.begin(); + for(int i=0;i<dtbList.size();i++) + { + if(hostCmp(iter->getName(),databaseName)) + { + iter->disconnectForRemove(); + dtbList.erase(iter); + return true; + } + + iter++; + } + return false; + } + +bool DatabaseManager::testUniqueness(const char* databaseName) + { + list<Database>::iterator iter=dtbList.begin(); + for(int i=0;i<dtbList.size();i++) + { + if(hostCmp(iter->getName(),databaseName)) + return false; + iter++; + } + return true; + } + +Database& DatabaseManager::operator[](int x) + { + list<Database>::iterator iter=dtbList.begin(); + for(int i=0;i<x;i++) iter++; + return *iter; + } +Database& DatabaseManager::operator[](const char* dbName) + { + list<Database>::iterator iter=dtbList.begin(); + for(int i=0;i<dtbList.size();i++) + { + if(hostCmp(iter->getName(),dbName)) + return *iter; + + iter++; + } + return protElem; + } + +void DatabaseManager::disconnectAllDatabasesFromDBH(const char* dbhName) + { + list<Database>::iterator iter=dtbList.begin(); + for(int i=0;i<dtbList.size();i++,iter++) + { + iter->disconnectFromDBHost(dbhName); + } + } +int DatabaseManager::countDatabases() + { + return dtbList.size(); + } + +bool DatabaseManager::reset() + { + if(config.isTestModus()==false) return false; + + list<Database>::iterator iter=dtbList.begin(); + for(int i=0;i<dtbList.size();i++,iter++) + { + iter->disconnectForRemove(); + } + + while(dtbList.size()) + { + dtbList.pop_front(); + } + return true; + } + +bool DatabaseManager::acceptChangeName(const char *oldName,const char *newName) + { + if(hostCmp(oldName,newName)) + return true; // if someone really wants to change a name with the same, + + return testUniqueness(newName); + } + diff --git a/rasmgr/rasmgr_dbm.hh b/rasmgr/rasmgr_dbm.hh new file mode 100644 index 0000000..78e7a8c --- /dev/null +++ b/rasmgr/rasmgr_dbm.hh @@ -0,0 +1,171 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_dbm.hh + * + * MODULE: rasmgr + * CLASS: DatabaseHost, DatabaseHostManager, Database, DatabaseManager + * + * COMMENTS: + * none + * +*/ + +#ifndef RASMGR_DBM_HH +#define RASMGR_DBM_HH + +#include "rasmgr.hh" +#include "rasmgr_config.hh" +//#include "rasmgr_srv.hh" + +class DatabaseHost + { + public: + DatabaseHost(); + ~DatabaseHost(); + + void init(const char* hostName,const char *connectString); + + const char* getName(); + const char* getConnectionString(); + void changeConnectionString(const char *connectString); + void changeName(const char *newName); + void regStartServer(); + void regDownServer(); + //void incrConnServers(); + //void decrConnServers(); + //void incrConnDatabases(); + //void decrConnDatabases(); + bool prepareToBeRemoved(); + bool isBusy(); + bool isValid(); + private: + char hostName[100]; + char connectString[100]; + int activServers; + //int connServers; + //int connDatabases; + bool valid; + }; + +class DatabaseHostManager + { + public: + DatabaseHostManager(); + ~DatabaseHostManager(); + bool insertNewHost(const char* hostName,const char* connectString); + bool removeHost(const char* hostName); + int countHosts(); + DatabaseHost& operator[](int); + DatabaseHost& operator[](const char* hostName); + bool reset(); + bool acceptChangeName(const char *oldName,const char *newName); + private: + bool testUniqueness(const char* hostName); + list<DatabaseHost> hostList; + DatabaseHost protElem; + }; + +extern DatabaseHostManager dbHostManager; + + +//***************************************************** + +class RasServer; + +class Database + { + public: + Database(); + ~Database(); + + void init(const char* databaseName); + const char* getName(); + void changeName(const char* databaseName); + + static const char* getDescriptionHeader(char *destBuffer); + const char* getDescription(char *destBuffer); + + bool connectToDBHost(const char* hostName); + bool disconnectFromDBHost(const char* hostName); + bool isConnectedToDBHost(const char* hostName); + int countConnectionsToDBHosts(); + const char* getDBHostName(int); + + bool connectToRasServer(const char *serverName); + bool disconnectFromRasServer(const char *serverName); + bool isConnectedToRasServer(const char *serverName); + int countConnectionsToRasServers(); + const char* getRasServerName(int); + + void disconnectForRemove(); + + void startWriteTransaction(); + void endWriteTransaction(); + void startReadTransaction(); + void endReadTransaction(); + + int getWriteTransactionCount(); + int getReadTransactionCount(); + bool isBusy(); + + void setTraceWriteTrans(bool); + + bool isValid(); + private: + bool checkConnection(DatabaseHost &); + bool checkConnection(RasServer &); + char databaseName[100]; + list<DatabaseHost*> hostPtrList; + list<RasServer*> rasPtrList; + + bool traceWT; + int countWriteTransactions; + int countReadTransactions; + + bool valid; + }; + +class DatabaseManager + { + public: + DatabaseManager(); + ~DatabaseManager(); + bool insertNewDatabase(const char* databaseName); + bool removeDatabase(const char* databaseName); + int countDatabases(); + Database& operator[](int); + Database& operator[](const char*); + void disconnectAllDatabasesFromDBH(const char* dbhName); + bool reset(); + bool acceptChangeName(const char *oldName,const char *newName); + private: + bool testUniqueness(const char* dbName); + list<Database> dtbList; + Database protElem; + }; + +extern DatabaseManager dbManager; + + +#endif diff --git a/rasmgr/rasmgr_error.cc b/rasmgr/rasmgr_error.cc new file mode 100644 index 0000000..cdc2e0e --- /dev/null +++ b/rasmgr/rasmgr_error.cc @@ -0,0 +1,101 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_error.cc + * + * MODULE: rasmgr + * CLASS: RCError and derivates + * + * COMMENTS: + * none + * +*/ + +#include "rasmgr_error.hh" + +#include "debug.hh" + + +RCError::RCError() + { + } + + +RCErrorUnexpToken::RCErrorUnexpToken(const char *token) +:pcc(token) + { + } + +const char* RCErrorUnexpToken::getString(char *destBuffer) + { + sprintf(destBuffer,"Unexpected token %s in command.",pcc); + return destBuffer; + } + + +RCErrorNoPermission::RCErrorNoPermission() + { + } + +const char* RCErrorNoPermission::getString(char *destBuffer) + { + sprintf(destBuffer,"You don't have permission for this operation."); + return destBuffer; + } + + +RCErrorInvalidName::RCErrorInvalidName(const char *name) +:pcc(name) + { + } + +const char* RCErrorInvalidName::getString(char *destBuffer) + { + sprintf(destBuffer,"Invalid %s name.",pcc); + return destBuffer; + } + + +RCErrorMissingParam::RCErrorMissingParam(const char *what) +:pcc(what) + { + } + +const char* RCErrorMissingParam::getString(char *destBuffer) + { + sprintf(destBuffer,"Missing %s.",pcc); + return destBuffer; + } + + +RCErrorIncorNumberValue::RCErrorIncorNumberValue(const char *what) +:pcc(what) + { + } + +const char* RCErrorIncorNumberValue::getString(char *destBuffer) + { + sprintf(destBuffer,"Incorect number value for parameter %s.",pcc); + return destBuffer; + } + diff --git a/rasmgr/rasmgr_error.hh b/rasmgr/rasmgr_error.hh new file mode 100644 index 0000000..bf16bb7 --- /dev/null +++ b/rasmgr/rasmgr_error.hh @@ -0,0 +1,91 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_error.hh + * + * MODULE: rasmgr + * CLASS: RCError and derivates + * + * COMMENTS: + * none + * +*/ + +#ifndef RASMGR_ERROR_HH +#define RASMGR_ERROR_HH + +#include "rasmgr.hh" + +class RCError + { + public: + RCError(); + virtual const char* getString(char *destBuffer)=0; + + }; + +class RCErrorUnexpToken : public RCError + { + public: + RCErrorUnexpToken(const char*); + const char* getString(char *destBuffer); + private: + const char *pcc; + }; + +class RCErrorNoPermission : public RCError + { + public: + RCErrorNoPermission(); + const char* getString(char *destBuffer); + private: + }; + +class RCErrorInvalidName : public RCError + { + public: + RCErrorInvalidName(const char*); + const char* getString(char *destBuffer); + private: + const char *pcc; + }; + +class RCErrorMissingParam : public RCError + { + public: + RCErrorMissingParam(const char*); + const char* getString(char *destBuffer); + private: + const char *pcc; + }; + +class RCErrorIncorNumberValue : public RCError + { + public: + RCErrorIncorNumberValue(const char*); + const char* getString(char *destBuffer); + private: + const char *pcc; + }; + +#endif diff --git a/rasmgr/rasmgr_host.cc b/rasmgr/rasmgr_host.cc new file mode 100644 index 0000000..0da6a43 --- /dev/null +++ b/rasmgr/rasmgr_host.cc @@ -0,0 +1,575 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_host.cc + * + * MODULE: rasmgr + * CLASS: ServerHost, HostManager + * + * COMMENTS: + * - undefine TALK_SECRET fpr production release!!! + * +*/ + +#include "rasmgr_host.hh" +#include "rasmgr_srv.hh" +#include "rasmgr_master.hh" + +#include "debug.hh" + +// clear this def for production release!!! +#define TALK_SECRET(a) TALK(a) +#ifndef DEBUG + #undef TALK_SECRET + #define TALK_SECRET(a) { /* TALK(a) */ } +#endif // DEBUG + +#ifdef SOLARIS + #define PORTMAP // define to use function declarations for old interfaces + #include<rpc/rpc.h> + #include<rpc/pmap_clnt.h> +#else + #include<rpc/rpc.h> + #include<rpc/pmap_clnt.h> +#endif + +typedef unsigned long r_Ptr; + +//int _rpcpmstart = 0; + +// function prototype with C linkage +//extern "C" int gethostname(char *name, int namelen); + +extern bool hostCmp( const char *h1, const char *h2); + + +ServerHost::ServerHost() +{ + hostName[0]=0; + netwName[0]=0; + setNotUp(); + isinternal=false; + valid=false; + startedServers=0; + isuseLocalHost=true; + TALK( "ServerHost::ServerHost(): hostName=" << this->hostName << ", netwName=" << this->netwName << ", listenPort=" << this->listenPort << ", isinternal=" << this->isinternal << ", valid=" << valid ); +} + +ServerHost::~ServerHost() +{ +} + +bool ServerHost::isValid() // used for the protection element +{ + TALK( "ServerHost::isValid() -> " << valid ); + return valid; +} + +const char* ServerHost::getName() +{ + return hostName; +} + +const char* ServerHost::getNetworkName() +{ + return netwName; +} + +long ServerHost::getListenPort() +{ + return listenPort; +} + +bool ServerHost::isInternal() +{ + return isinternal; +} + +void ServerHost::init(const char* hostName,const char* netwName,int listenPort,bool isinternal) +{ + strcpy(this->hostName,hostName); + strcpy(this->netwName,netwName); + + this->listenPort=listenPort; + this->isinternal=isinternal; + + isup = isinternal; + + valid=true; + TALK( "ServerHost::init(): hostName=" << this->hostName << ", netwName=" << this->netwName << ", listenPort=" << this->listenPort << ", isinternal=" << this->isinternal << ", valid=" << valid ); +} + +char* ServerHost::getDescriptionHeader(char *destBuffer) +{ + sprintf(destBuffer," %-10s %-32s %-4s %-4s %-10s ulh","Host Name","Netw. Addr","Port","Stat","Servers"); + return destBuffer; +} +char* ServerHost::getDescription(char *destBuffer) +{ + const char* sUp= isup ? "UP ":"DOWN"; + const char* uLh= " -"; + long lPort = listenPort; + + if(isinternal) + { + uLh = useLocalHost() ? "on":"off"; + lPort = config.getListenPort(); + } + + sprintf(destBuffer,"%-10s %-32s %4d %-4s %2d %s",hostName,netwName,lPort,sUp,startedServers,uLh); + + return destBuffer; +} + +bool ServerHost::isUp() +{ + return isup; +} + +bool ServerHost::downHost() +{ + // you can't stop the master with this + if(isinternal) + return false; + + int socket=getConnectionSocket(); + + if(socket<0) + { + setNotUp(); + //close(socket); + return false; + } + + const char *text="POST downhost HTTP/1.1\r\nAccept: text/plain\r\nUserAgent: RasMGR/1.0\r\n\r\nRasMGR"; + + int nbytes=write(socket,text,strlen(text)+1); + + isup=false; + + return true; +} + +bool ServerHost::checkStatus() +{ + if(isinternal) + { + isup=true; + return true; + } + + int socket=getConnectionSocket(); + + if(socket<0) + { + setNotUp(); + //close(socket); + TALK( "ServerHost::checkStatus() -> false (no socket)" ); + return false; + } + const char *text="POST getstatus HTTP/1.1\r\nAccept: text/plain\r\nUserAgent: RasMGR/1.0\r\n\r\nRasMGR"; + + int nbytes=write(socket,text,strlen(text)+1); + + if(nbytes<0) + { + setNotUp(); + close(socket); + TALK( "ServerHost::checkStatus() -> false (cannot write socket)" ); + return false; + } + + char message[200]; + nbytes=read(socket,message,200); + close(socket); + if(nbytes<0) + { + setNotUp(); + TALK( "ServerHost::checkStatus() -> false (cannot read socket)" ); + return false; + } + + char *body=strstr(message,"\r\n\r\n"); + if(body==NULL) + { + setNotUp(); + TALK( "ServerHost::checkStatus() -> false (null body)" ); + return false; + } + + int localStartedServers; //not sure that we will provide this info + sscanf(body,"%d ",&localStartedServers); + + isup=true; + + TALK( "ServerHost::checkStatus() -> true (all done)" ); + return true; +} // checkStatus() + +int ServerHost::countDefinedServers() +{ + TALK_SECRET( "ServerHost::countDefinedServers enter." ); + + int count=0; + for(int i=0;i<rasManager.countServers();i++) + { + if(hostCmp(rasManager[i].getHostName(),hostName)) + count++; + } + + TALK_SECRET( "ServerHost::countDefinedServers leave -> " << count ); + return count; +} + +int ServerHost::getConnectionSocket() +{ + TALK_SECRET( "ServerHost::getConnectionSocket enter." ); + + // create socket + int sock; + struct protoent *getprotoptr; + getprotoptr=getprotobyname("tcp"); + sock=socket(PF_INET,SOCK_STREAM,getprotoptr->p_proto); + int tempErrno = errno; + TALK_SECRET( "ServerHost::getConnectionSocket socket(PF_INET,SOCK_STREAM," << getprotoptr->p_proto << ") -> " << sock ); + if (sock < 0) + { + TALK_SECRET( "ServerHost::getConnectionSocket cannot create socket: " << strerror( tempErrno ) ); + return -1; + } + + // init sockaddr + sockaddr_in servername; + struct hostent *hostinfo; + servername.sin_family=AF_INET; + servername.sin_port=htons(listenPort); + hostinfo=gethostbyname(netwName); + if(hostinfo==NULL) + { + close(sock); + TALK_SECRET( "ServerHost::getConnectionSocket leave (invalid hostinfo) -> -1" ); + return -1; + } + servername.sin_addr=*(struct in_addr*)hostinfo->h_addr; + + // connect to slave-manager + int connectResult = connect(sock,(struct sockaddr*)&servername,sizeof(servername)); + TALK_SECRET( "ServerHost::getConnectionSocket connect() to host=" << hostinfo->h_name << ", listen port=" << servername.sin_port << " -> " << connectResult ); + if(connectResult < 0) + { + close(sock); + if (errno) + { + tempErrno = errno; + TALK_SECRET( "ServerHost::getConnectionSocket close(" << sock << ") -> " << strerror(tempErrno) ); + } + TALK_SECRET( "ServerHost::getConnectionSocket leave (cannot connect to slave mgr " << hostinfo << ") -> -1" ); + return -1; + } + + TALK_SECRET( "ServerHost::getConnectionSocket leave -> " << sock ); + return sock; +} + +void ServerHost::setNotUp() +{ + isup=false; + startedServers=0; +} + +void ServerHost::setIsUp(bool x) +{ + isup=x; +} + +void ServerHost::regStartServer() +{ + startedServers++; +} + +void ServerHost::regDownServer() +{ + startedServers--; +} + +int ServerHost::getStartedServers() +{ + return startedServers; +} + +void ServerHost::useLocalHost(bool how) +{ + isuseLocalHost=how; +} + +bool ServerHost::useLocalHost() +{ + return isinternal ? isuseLocalHost : false; +} + +void ServerHost::changeName(const char *newName) +{ + strcpy(hostName,newName); +} + +void ServerHost::changeNetName(const char *newNetName) +{ + strcpy(netwName,newNetName); +} +void ServerHost::changeListenPort(int newListenPort) +{ + listenPort=newListenPort; +} + +//********************************************************************** +HostManager::HostManager() +{ + TALK( "HostManager::HostManager()" ); +} + +HostManager::~HostManager() +{ + TALK( "HostManager::~HostManager()" ); +} + +bool HostManager::insertInternalHost() +{ + ENTER( "HostManager::insertInternalHost()" ); + + bool result = false; // function result + + ServerHost tempServerHost; + + if(hostList.empty()==false) + result = false; + else + { + //put the internal host, which is always defined, even if it has no servers attached + hostList.push_back(tempServerHost); + ServerHost &refServerHost=hostList.back(); + const char *myhostName=config.getHostName(); + const char *myPublicHostName=config.getPublicHostName(); + refServerHost.init(myhostName,myPublicHostName,-1,true); + result = true; + } + + LEAVE( "HostManager::insertInternalHost() -> " << result ); + return result; +} + +bool HostManager::insertNewHost(const char* hostName,const char *netwName,int listenport) +{ + bool result = true; // function result + + ENTER( "HostManager::insertNewHost( hostName=" << hostName << ", netwName=" << netwName << ", listenport=" << listenport << " )" ); + + if(testUniqueness(hostName)==false) + result = false; + + if (result == true) + { + ServerHost tempServerHost; + + if(hostList.empty()) + insertInternalHost(); // just protection, but shouldn't be necessary + + hostList.push_back(tempServerHost); + ServerHost &refServerHost=hostList.back(); + refServerHost.init(hostName,netwName,listenport,false); //always external + result = true; + } + + LEAVE( "HostManager::insertNewHost() -> " << result ); + return result; +} + +bool HostManager::removeHost(const char *hostName) +{ + ENTER( "HostManager::removeHost( hostName=" << hostName << " )" ); + + list<ServerHost>::iterator iter=hostList.begin(); + for(int i=0;i<hostList.size();i++) + { + if(hostCmp(iter->getName(),hostName)) + { + if(iter->countDefinedServers()>0) + { + LEAVE( "HostManager::removeHost() -> false" ); + return false; + } + hostList.erase(iter); + break; + } + iter++; + } + + LEAVE( "HostManager::removeHost() -> true" ); + return true; +} + +// FIXME: check for end of list +ServerHost& HostManager::operator[](int x) +{ + ENTER( "HostManager::operator[] ( x=" << x << " )" ); + + list<ServerHost>::iterator iter=hostList.begin(); + for(int i=0;i<x;i++) iter++; + + LEAVE( "HostManager::operator[]" ); + return *iter; +} + +// returns: +// server host object if found +// protElem (uninitialized object, not valid) if not found +ServerHost& HostManager::operator[](const char* hostName) +{ + ENTER( "HostManager::operator[] ( hostName=" << hostName << " )" ); + + list<ServerHost>::iterator iter=hostList.begin(); + for(int i=0;i<hostList.size();i++) + { + if(hostCmp(iter->getName(),hostName)) + { + LEAVE( "HostManager::operator[] -> found" ); + return *iter; + } + + iter++; + } + + LEAVE( "HostManager::operator[] -> not found" ); + return protElem; +} + +int HostManager::countHosts() +{ + return hostList.size(); +} +int HostManager::countUpHosts() +{ + int count=0; + list<ServerHost>::iterator iter=hostList.begin(); + for(int i=0;i<hostList.size();i++) + { + if(iter->isUp()) + count++; + iter++; + } + return count; +} + +bool HostManager::testUniqueness(const char* hostName) +{ + list<ServerHost>::iterator iter=hostList.begin(); + for(int i=0;i<hostList.size();i++) + { + if(hostCmp(iter->getName(),hostName)) + return false; + iter++; + } + return true; +} + +int HostManager::postSlaveMGR(char *body,char *outBuffer) +{ + char answBuffer[100]; + + do + { // so we can break + + if(body==NULL) + { + strcpy(answBuffer,"Missing identification, this is not a valid rasmgr."); // no rasmgr or soft error + break; + } + + char name[100]; + int licServ; + sscanf(body,"%s %d",name,&licServ); + + TALK_SECRET( "HostManager::postSlaveMGR: name="<<name<<" lics="<<licServ ); + + ServerHost &sh=operator[](name); + if(sh.isValid()==false) + { + strcpy(answBuffer,"Unknown slave rasmgr."); + break; + } + + TALK_SECRET( "HostManager::postSlaveMGR: Ok, valid." ); + sh.setIsUp(true); + strcpy(answBuffer,"Welcome!"); + + } while(0); + + sprintf(outBuffer,"HTTP/1.1 200 OK\r\nContent-type: text/plain\r\nContent-length: %d\r\n\r\n%s",strlen(answBuffer)+1,answBuffer); + + return strlen(outBuffer)+1; +} + +bool HostManager::reset() +{ + ENTER( "HostManager::reset()" ); + + if(config.isTestModus()==false) + { + LEAVE( "HostManager::reset() -> false" ); + return false; + } + + list<ServerHost>::iterator iter=hostList.begin(); + for(int i=0;i<hostList.size();i++,iter++) + { + if(iter->countDefinedServers()>0) + { + LEAVE( "HostManager::reset() -> false" ); + return false; + } + } + + while(hostList.size()) + { + hostList.pop_front(); + } + + LEAVE( "HostManager::reset() -> true" ); + return true; +} + +bool HostManager::acceptChangeName(const char *oldName,const char *newName) +{ + if(hostCmp(oldName,newName)) + return true; // if someone really wants to change a name with the same, + + return testUniqueness(newName); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + +void rpcif_1(struct svc_req *rqstp, register SVCXPRT *transp) +{ // this function is registered by rpc-system. No one can use it, because it's never called + //(we do not have a svc_run() - call, thus it's not doing anything +} + diff --git a/rasmgr/rasmgr_host.hh b/rasmgr/rasmgr_host.hh new file mode 100644 index 0000000..0e6e4e4 --- /dev/null +++ b/rasmgr/rasmgr_host.hh @@ -0,0 +1,122 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_host.hh + * + * MODULE: rasmgr + * CLASS: ServerHost, HostManager + * + * COMMENTS: + * none + * +*/ + +#ifndef RASMGR_HOST_HH +#define RASMGR_HOST_HH + +#include "rasmgr.hh" +#include "rasmgr_config.hh" + +//In normal release there can be just one RasMGR on a host +//But for testing we provide the possibility to put more than one on a host +//Just for fun, we call the unique RasMGR on a host the HIGHLANDER (thanks to Peter Zoller) +//So, for normal release, just... +//#define HIGHLANDER + +class ServerHost + { + public: + ServerHost(); + ~ServerHost(); + void init(const char* hostName,const char *netwName,int listenport,bool isInternal); + const char *getName(); + const char *getNetworkName(); + long getListenPort(); + bool isInternal(); + bool checkStatus(); + + int getStartedServers(); + int getLicensedServers(); + + bool isUp(); + bool downHost(); + + static char* getDescriptionHeader(char *destBuffer); + char* getDescription(char *destBuffer); + + int countDefinedServers(); + int getConnectionSocket(); + void regStartServer(); + void regDownServer(); + void setIsUp(bool); + + void useLocalHost(bool); + bool useLocalHost(); + + void changeName(const char*); + void changeNetName(const char*); + void changeListenPort(int); + + bool isValid(); + bool reset(); + private: + void setNotUp(); + char hostName[100]; + char netwName[100]; + int listenPort; + int startedServers; + bool isup; + bool isinternal; + bool isuseLocalHost; // if internal, use localhost instead of network name, default on! + + bool valid; + }; + +class HostManager + { + public: + HostManager(); + ~HostManager(); + bool insertInternalHost(); + bool checkAcceptAnotherHost(); + bool insertNewHost(const char* hostName,const char *netwName,int listenport); + bool removeHost(const char *hostName); + int countHosts(); + int countUpHosts(); + ServerHost& operator[](int); + ServerHost& operator[](const char* hostName); + + int postSlaveMGR(char *body,char *outBuffer); + bool reset(); + bool acceptChangeName(const char *oldName,const char *newName); + private: + bool testUniqueness(const char* srvName); + list<ServerHost> hostList; + ServerHost protElem; + }; + +extern HostManager hostmanager; + + +#endif + diff --git a/rasmgr/rasmgr_localsrv.cc b/rasmgr/rasmgr_localsrv.cc new file mode 100644 index 0000000..0d16318 --- /dev/null +++ b/rasmgr/rasmgr_localsrv.cc @@ -0,0 +1,368 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_localsrv.cc + * + * MODULE: rasmgr + * CLASS: LocalServer, LocalServerManager + * + * PURPOSE: + * management of rasserver executables + * + * COMMENTS: + * None + * +*/ + +using namespace std; + +#include "rasmgr_localsrv.hh" +#include "rasmgr_master.hh" +#include "rasmgr_srv.hh" +#include <signal.h> +#include <time.h> + +#include "raslib/rminit.hh" + +#include "debug.hh" + + +// aux function for now() to avoid a compiler warning (see 'man strftime') +size_t my_strftime(char *s, size_t max, const char *fmt, const struct tm *tm) +{ + return strftime(s, max, fmt, tm); +} + +// now(): aux function returning, as a static string, the current time +// keep in sync with same function in rasserver +const char* now() +{ + size_t strfResult = 0; // return value of strftime() + static char timestring[50]; // must hold 20+1 chars + + time_t t = time(NULL); // get time + struct tm* tm = localtime(&t); // break down time + strfResult = my_strftime( timestring, sizeof(timestring), "[%F %T]", tm ); // format time + if (strfResult == 0) // bad luck? then take fallback message + (void) strncpy( timestring, "[-no time available-]", sizeof(timestring) ); + return( timestring ); +} + +LocalServer::LocalServer() + { serverName[0]=0; + valid=false; + serverPid=0; + } + +void LocalServer::init(const char *name,pid_t p) + { strcpy(serverName,name); + serverPid=p; + valid=true; + } +const char* LocalServer::getName() + { return serverName; + } + +pid_t LocalServer::getPID() + { return serverPid; + } +bool LocalServer::isValid() + { return valid; + } + +//####################################### +void catch_SIGCHLD(int) + { localServerManager.childSignalIn(); + } +//####################################### + +LocalServerManager::LocalServerManager() + { wasSignal=false; + + signal (SIGCHLD, catch_SIGCHLD); + } +LocalServerManager::~LocalServerManager() + { + } +bool LocalServerManager::startNewServer(const char* commandline) + { + ENTER( "LocalServerManager::startNewServer: enter. cmdLine=" << commandline ); + char localcomm[300]; + strcpy(localcomm,commandline); + + int i; + const int maxarg=50; + char* argv[maxarg]; // rasserver command line + char* fileName; // name of executable, e.g., "rasserver" + char* serverName; // symbolic server name, e.g., "S1" + + char *pos=localcomm; + + for(i=0;i<maxarg;i++) + { +#define WHITESPACE " \t\r\n" + argv[i]=strtok(pos, WHITESPACE ); + pos=NULL; // for subsequent calls to strtok + if(argv[i]==NULL) break; + } + argv[maxarg-1]=0; // for security reasons + + serverName=argv[0]; + fileName =argv[1]; + + LocalServer &lcs=operator[](serverName); + if(lcs.isValid()) + { VLOG <<"Server "<<serverName<<" is already up."<<std::endl; + LEAVE( "LocalServerManager::startNewServer: leave. srv already up, result=false." ); + return false; + } + + // return false; + pid_t pid=fork(); + + if(pid!=0) + { //parent + LocalServer temp; + temp.init(serverName,pid); + srvList.push_back(temp); + TALK( "LocalServerManager::startNewServer: leave. parent process. result=true." ); + VLOG << now() << " starting server "<<serverName<<", executable " << fileName << "; pid "<<pid<< "..." << flush; + + } + else + { //child + + TALK( "LocalServerManager::startNewServer: leave. child process, fileName=" << fileName ); + + masterCommunicator.closeForcedAllSockets(); + + execvp(fileName, argv+2); + int execErrno = errno; + std::cout<<"Error: cannot fork server "<<fileName<< ": " << strerror (execErrno) << std::endl; + TALK( "LocalServerManager::startNewServer: cannot fork server "<<fileName<< ": " << strerror (execErrno) ); + exit(1); // if return from exec... + } + + LEAVE( "LocalServerManager::startNewServer: leave. result=true." ); + return true; + } +int LocalServerManager::countStartedServers() + { return srvList.size(); + } + +// sendTerminateSignal: terminate server process. +// if name is in list of known servers, try to terminate; otherwise, complain & do nothing. +// returns: +// true iff server was found and killed successfully +// false on error +bool LocalServerManager::sendTerminateSignal(const char *serverName) +{ + ENTER( "LocalServerManager::sendTerminateSignal: enter. serverName=" << serverName ); + + bool found = false; // list entry pertaining to serverName found? + bool result = false; // function result + + list<LocalServer>::iterator iter=srvList.begin(); + for ( int i=0; i<srvList.size() && ! found; i++) + { + if(strcmp(iter->getName(),serverName)==0) + { + found = true; + VLOG << now() << " shutting down rasdaman server " << iter->getName() << ", pid " << iter->getPID() << "..." << flush; + int killResult = kill(iter->getPID(),SIGTERM); + if (killResult == -1) + { + cout << "Error: " << strerror(errno) << endl; + result = false; + } + else + { + iter = srvList.erase(iter); + VLOG << "ok" << endl; + result = true; + break; + } + } + + iter++; + } // for + + if (!found) + { + cout << "failed: server unknown." << endl; + result = false; + } + + LEAVE( "LocalServerManager::sendTerminateSignal: leave. result=" << result ); + return result; +} + +// killServer: terminate server process. +// if name is in list of known servers, try to kill; otherwise, complain & do nothing. +// returns: +// true iff server was found and killed successfully +// false on error +bool LocalServerManager::killServer(const char *serverName) +{ + ENTER( "LocalServerManager::killServer: enter. serverName=" << serverName ); + + bool found = false; // list entry pertaining to serverName found? + bool result = false; // function result + + list<LocalServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++) + { + if(strcmp(iter->getName(),serverName)==0) + { + found = true; + VLOG << now() << " killing rasdaman server " << iter->getName() << ", pid " << iter->getPID() << "..." << flush; + + int killResult = kill(iter->getPID(),SIGKILL); + if (killResult == -1) + { + cout << "Error: " << strerror(errno) << endl; + result = false; + } + else + { + iter = srvList.erase(iter); + VLOG << "ok" << endl; + result = true; + break; + } + } + iter++; + } + + if (!found) + { + cout << "failed: server unknown." << endl; + result = false; + } + + LEAVE( "LocalServerManager::killServer: leave. result=" << result ); + return result; +} + +LocalServer& LocalServerManager::operator[](int x) + { list<LocalServer>::iterator iter=srvList.begin(); + for(int i=0;i<x;i++) iter++; + return *iter; + + } +LocalServer& LocalServerManager::operator[](const char* srvName) + { + ENTER( "LocalServerManager::operator[]: enter. srvName=" << srvName ); + list<LocalServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++) + { + if(strcmp(iter->getName(),srvName)==0) + { + LEAVE( "LocalServerManager::operator[]: leave. valid=" << (*iter).isValid() ); + return *iter; + } + iter++; + } + LEAVE( "LocalServerManager::operator[]: leave. valid=" << protElem.isValid() ); + return protElem; + } + +void LocalServerManager::childSignalIn() //only signal calls this + { wasSignal=true; + } +void LocalServerManager::cleanChild() + { + ENTER( "LocalServerManager::cleanChild: enter." ); + + if(wasSignal==false) + { + LEAVE( "LocalServerManager::cleanChild: leave. !wasSignal." ); + return; + } + +#define WAITFORANYCHILD -1 + + while(1) + { signal (SIGCHLD, catch_SIGCHLD); // some SO requieres this, otherwise they reset to default + + int status=0; + int exitpid=waitpid(WAITFORANYCHILD,&status,WNOHANG); + + if(exitpid==0) + break; // no child died + + // I'd love to put this code into a textbook as a negative example and cite you, Walter! -- PB 2003-nov-25 + if(exitpid==-1) + { + if(errno == EINTR) + continue; + break; // another error; + } + VLOG << "rasdaman server process with pid " << exitpid << " has terminated." << std::endl; + + list<LocalServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++) + { + TALK( "LocalServerManager::cleanChild: inspecting rasdaman server " << iter->getName() << "." ); + if(iter->getPID()==exitpid) + { + TALK( "LocalServerManager::cleanChild: rasdaman server " << iter->getName() << " terminated illegally, status=" << status ); + + cout<<"Error: rasdaman server " << iter->getName() << ", pid " << exitpid << " terminated illegally, reason: "; + // see 'man waitpid': decoding of status variable + if (WIFEXITED(status) != 0) + cout << "exited with return code " << WEXITSTATUS(status); + else if (WIFSIGNALED(status)) + cout << "uncaught signal " << WTERMSIG(status); + else + cout << "(unknown reason)"; + cout << endl; + + // choices: restart silently the dead server or + // just tell the manager about it + // Not restart from here, because of sync problem for capabilities, master has to do that!!! + LocalServer temp=*iter; + srvList.erase(iter); + + reportDeadServer(temp); + break; + } + iter++; + } // for + } //while + + wasSignal=false; + LEAVE( "LocalServerManager::cleanChild: leave." ); + } + +void LocalServerManager::reportDeadServer(LocalServer &srv) + { + ENTER( "LocalServerManager::reportDeadServer: enter." ); + + int dummy = -1; + RasServer &r=rasManager[srv.getName()]; + + if(r.isValid()) r.changeStatus(SERVER_CRASHED,dummy); + LEAVE( "LocalServerManager::reportDeadServer: leave." ); + } + diff --git a/rasmgr/rasmgr_localsrv.hh b/rasmgr/rasmgr_localsrv.hh new file mode 100644 index 0000000..3e10fd2 --- /dev/null +++ b/rasmgr/rasmgr_localsrv.hh @@ -0,0 +1,81 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_localsrv.hh + * + * MODULE: rasmgr + * CLASS: LocalServer, LocalServerManager + * + * PURPOSE: + * management of rasserver executables + * + * COMMENTS: + * None + * +*/ + +#ifndef RASMGR_LOCALSRV_HH +#define RASMGR_LOCALSRV_HH + +#include "rasmgr.hh" + + +class LocalServer + { + public: + LocalServer(); + void init(const char*,pid_t); + const char* getName(); + pid_t getPID(); + bool isValid(); + private: + char serverName[30]; + pid_t serverPid; + bool valid; + }; + +class LocalServerManager + { + public: + LocalServerManager(); + ~LocalServerManager(); + bool startNewServer(const char* commandline); + int countStartedServers(); + bool sendTerminateSignal(const char *serverName); + bool killServer(const char *serverName); + + LocalServer& operator[](int); + LocalServer& operator[](const char* srvName); + + void childSignalIn(); //only signal calls this + void cleanChild(); + private: + void reportDeadServer(LocalServer &); + bool wasSignal; + std::list<LocalServer> srvList; + LocalServer protElem; + + }; + +extern LocalServerManager localServerManager; +#endif diff --git a/rasmgr/rasmgr_main.cc b/rasmgr/rasmgr_main.cc new file mode 100644 index 0000000..4399d13 --- /dev/null +++ b/rasmgr/rasmgr_main.cc @@ -0,0 +1,256 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_main.cc + * + * MODULE: rasmgr + * CLASS: + * + * PURPOSE: + * management of rasserver executables + * + * COMMENTS: + * - FIXME: looks like a rasmgr slave still uses old comm scheme -- compatible? + * +*/ + +#include <iostream> + +#include "rasmgr.hh" +#include "rasmgr_config.hh" +#include "rasmgr_host.hh" +#include "rasmgr_dbm.hh" +#include "rasmgr_srv.hh" +#include "rasmgr_master.hh" +#include "rasmgr_rascontrol.hh" +#include "rasmgr_users.hh" +#include "ras_crypto.hh" +#include "rasmgr_localsrv.hh" +#include "rasmgr_error.hh" + +#ifndef COMPDATE +#error "Please specify the COMPDATE variable!" +/* +COMPDATE=`date +"%d.%m.%Y %H:%M:%S"` + +and -DCOMPDATE="\"$(COMPDATE)\"" when compiling +*/ +#endif + +#define DEBUG_MAIN +#undef DEBUG_HH +#include "debug.hh" + + +Configuration config; + +HostManager hostmanager; +DatabaseHostManager dbHostManager; +DatabaseManager dbManager; +RasServerManager rasManager; +MasterComm masterCommunicator; +RasControl rascontrol; +UserManager userManager; +Authorization authorization; +LocalServerManager localServerManager; +RandomGenerator randomGenerator; + +void installSignalHandlers(); + +int main(int argc, char** argv, char** envp) +{ + SET_OUTPUT( true ); // enable debugging trace, if compiled so + + ENTER( "main." ); + + std::cout<< "rasmgr: rasdaman server manager tool. rasdaman v" << RMANVERSION / 1000. << " -- generated on " << COMPDATE << "." << std::endl; + +// just to see the difference between an official release and the inside development version +#ifdef NO_OFFICIAL_RELEASE + std::cout<<"This is not the official release version, it supports test modus and rasserver in debugger"<<std::endl; +#else + std::cout << " Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann rasdaman GmbH." << std::endl + << "Rasdaman community is free software: you can redistribute it and/or modify " + "it under the terms of the GNU General Public License as published by " + "the Free Software Foundation, either version 3 of the License, or " + "(at your option) any later version. \n" + "Rasdaman community is distributed in the hope that it will be useful, " + "but WITHOUT ANY WARRANTY; without even the implied warranty of " + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " + "GNU General Public License for more details. \n\n"; + + std::cout << "This software contains software which is in the public domain:" << std::endl; + std::cout << "- openssl 0.96c (C) 1998-2002 The OpenSSL Project, (C) 1995-1998 Eric A. Young, Tim J. Hudson" << std::endl; +#endif + +#ifdef INCLUDE_HIDDEN_COMMANDS + std::cout << "This version is 'inside only'" <<std::endl; +#endif + + if(testIsMessageDigestAvailable("MD5")==false) + { + std::cout<<"Error: Message Digest MD5 not available."<<std::endl; + return RASMGR_RESULT_NO_MD5; + } + + installSignalHandlers(); + + bool result = config.interpretArguments(argc,argv,envp); + if (result == false) + return RASMGR_RESULT_ILL_ARGS; + + if(config.isTestModus()) + { + std::cout<<"rasmgr running in test modus "; + VLOG <<", listening on port=" << config.getListenPort() << std::endl; + } + else + { + VLOG << " rasmgr running on " << config.getHostName() << ", listening on port " << config.getListenPort(); + VLOG << " with poll timeout " << config.getPollFrequency() << " seconds. "; + } + + if(config.isTestModus()==false) + { + TALK( "hostname=" << config.getHostName() << ", publicHostname=" << config.getPublicHostName() ); + if(strcmp(config.getHostName(),config.getPublicHostName()) != 0) + { + VLOG <<"Advertised host name is "<<config.getPublicHostName()<<std::endl; + } + + bool resultConfig = config.readConfigFile(); + TALK( "rasmgr::main: resultConfig=" << resultConfig ); + rascontrol.setConfigDirty( false ); // all changes to config up to now come from config file, do not require save + + int resultAuth = authorization.readAuthFile(); + TALK( "rasmgr::main: resultAuth=" << resultAuth ); + switch( resultAuth ) + { + case RC_OK: + TALK( "rasmgr::main: auth file ok, set state to not dirty." ); + rascontrol.setAuthDirty( false ); // auth file ok, so clean init state + break; + case ERRAUTHFNOTF: + TALK( "rasmgr::main: auth file not found, loading defaults." ); + userManager.loadDefaults(); + // disabled because otherwise tons of new auth files are generated -- PB 2005-jul-02 + // rascontrol.setAuthDirty( true ); // auth file not present, write default + break; + case ERRAUTHFCORR: + LEAVE( "rasmgr::main: auth file corrupt." ); + return RASMGR_RESULT_AUTH_CORRUPT; + break; + case ERRAUTHFWRHOST: + LEAVE( "rasmgr::main: auth file for another host." ); + return RASMGR_RESULT_AUTH_OTHERHOST; + break; + case ERRAUTHFVERS: + LEAVE( "rasmgr::main: auth file version mismatch." ); + return RASMGR_RESULT_AUTH_INCOMPAT; + break; + default: // should not occur, internal enum mismatch + LEAVE( "rasmgr::main: illegal auth file result code " << resultAuth << ", internal error." ); + return RASMGR_RESULT_INTERNAL; + break; + } + + try + { + BenchmarkTimer *totalTimePtr = new BenchmarkTimer("Total master time"); + + TALK( "launching masterCommunicator.Run()..." ); + masterCommunicator.Run(); // central request handling loop + TALK( "masterCommunicator.Run() done." ); + + totalTimePtr->result(); // print total time elapsed + } + catch(RCError& e) + { + char *errorMsg; + e.getString(errorMsg); + std::cout<<"Error: "<<errorMsg<<std::endl; + } + +// write the config file only on explicit rascontrol request "save" +// (and at that moment), or at rascontrol "exit" to a rescue file -- PB 2003-jun-06 +#ifdef NEVER_AGAIN + if(!config.saveConfigFile()) + { + std::cout<<"Error saving configuration file."<<std::endl; + } + + if(!authorization.saveAuthFile()) + { + std::cout<<"Error saving user authorization file."<<std::endl; + } +#endif + } + + else if(config.isTestModus()) + { + hostmanager.insertInternalHost(); + userManager.loadDefaults(); + masterCommunicator.Run(); + } + + cout <<"rasmgr terminated."<<std::endl; + + int retval = RASMGR_RESULT_OK; + LEAVE( "main: leave. retval=" << retval ); + return retval; +} // main() + +// danger: cout in interrupt??? +// handler for SIGINT and SIGTERM = call for exit +void SigIntHandler(int sig) +{ + std::cout<<"rasmgr received terminate signal..."; + masterCommunicator.shouldExit(); +} + +void installSignalHandlers() +{ + signal (SIGINT, SigIntHandler); + signal (SIGTERM, SigIntHandler); + signal (SIGHUP, SIG_IGN); + signal (SIGPIPE,SIG_IGN); + signal (SIGTTOU,SIG_IGN); // no console, ei si? +} + +// should be replaced by something cleaner, eventually +void exitbyerror(const char* text) +{ + perror(text); + exit( RASMGR_EXIT_FAILURE ); +} + +char *strtolwr(char *string)// should be somewhere in the C-library, but can't find it +{ + char *t=string; + for(;*t;t++) + { + if(*t>='A' && *t<='Z') *t|='a'-'A'; + } + return string; +} + diff --git a/rasmgr/rasmgr_master.hh b/rasmgr/rasmgr_master.hh new file mode 100644 index 0000000..d7b639e --- /dev/null +++ b/rasmgr/rasmgr_master.hh @@ -0,0 +1,136 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_master.hh + * + * MODULE: rasmgr + * CLASS: MasterComm + * + * PURPOSE: + * Main loop of master rasmgr + * + * COMMENTS: + * None + * +*/ + +#ifndef RASMGR_MASTER_HH +#define RASMGR_MASTER_HH + +#include "rasmgr_comm_nb.hh" +#include <deque> +#include<string> + +class ClientID + { + public: + ClientID(); + void init(const char*); + + bool operator==(const ClientID&); + bool operator!=(const ClientID&); + + std::string getID() const; + bool isValid() const; + private: + std::string idstring; + bool valid; + friend std::ostream& operator<<(std::ostream&, const ClientID&); + }; + + +class ClientQueue + { + public: + ClientQueue(); + ~ClientQueue(); + + void put(ClientID&, const char *dbName, char serverType, int errorCode); + + // the answer is 0 or the errorcode + int canBeServed(ClientID&, const char *dbName, char serverType, bool fake); + + private: + struct ClientEntry + { + bool activ; + ClientID clientID; + std::string dbName; + char serverType; + int errorCode; + time_t lastAction; + time_t timeLimit; + + bool wasfake; + + ClientEntry(); + ClientEntry(ClientID &client, const char *dbName, char serverType, int errorCode); + + bool shouldWeCleanup(bool fake); + void updateTime(); + + bool isTimeout(); + }; + + std::deque<ClientEntry> clients; + + }; + +class MasterComm:public NbServerComm + { + public: + MasterComm(); + ~MasterComm(); + void Run(); + void commitChanges(); + void commitAuthFile(); + private: + bool isMessage(const char *messageStart); + int getFreeServer(bool fake); + const char* convertAnswerCode(int code); + +// int getFakeFreeServer(); + int answerAccessDenied(); + int answerAccessDeniedCode(); + void doCommit(); + + bool commit; + bool commitAuthOnly; + + void processJob( NbJob ¤tJob ); + int processRequest( NbJob ¤tJob ); + + bool fillInBuffer(const char*); + char *header; + char *body; + char inBuffer[MAXMSG]; + char outBuffer[MAXMSGOUTBUFF]; + + bool allowMultipleWriteTransactions; + + ClientQueue clientQueue; + }; + +extern MasterComm masterCommunicator; + +#endif diff --git a/rasmgr/rasmgr_master_nb.cc b/rasmgr/rasmgr_master_nb.cc new file mode 100644 index 0000000..42e1cb4 --- /dev/null +++ b/rasmgr/rasmgr_master_nb.cc @@ -0,0 +1,1007 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_master_nb.cc + * + * MODULE: rasmgr + * CLASS: MasterComm + * + * PURPOSE: + * Main loop of master rasmgr + * + * COMMENTS: + * - MasterComm::processRequest() is the central dispatcher for rasmgr requests, recognising and executing them. + * +*/ + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "rasmgr_master.hh" +#include "rasmgr_config.hh" +#include "rasmgr_rascontrol.hh" +#include "rasmgr_users.hh" +#include "rasmgr_host.hh" +#include "rasmgr_localsrv.hh" +#include "rasmgr_srv.hh" + +using namespace std; + +#include "debug.hh" + + +// from rasmgr_localsrv.cc; should go to a central location -- PB 2003-nov-25 +extern char *now(); + +// rasserver error codes (see errtxts) +// FIXME: should go into a central include file / class -- PB 2003-nov-20 +#define MSG_OK 200 +#define MSG_OK_STR "Ok" +#define MSG_UNKNOWNSERVERTYPE 1001 +#define MSG_UNKNOWNSERVERTYPE_STR "Unknown server type" +#define MSG_UNKNOWNACCESSTYPE 1002 +#define MSG_UNKNOWNACCESSTYPE_STR "Unknown access type" +#define MSG_DATABASENOTFOUND 807 +#define MSG_DATABASENOTFOUND_STR "Database not found" +#define MSG_WRITETRANSACTION 806 +#define MSG_WRITETRANSACTION_STR "Write transaction in progress" +#define MSG_NOSUITABLESERVER 805 +#define MSG_NOSUITABLESERVER_STR "No suitable servers started" +#define MSG_SYSTEMOVERLOADED 801 +#define MSG_SYSTEMOVERLOADED_STR "System overloaded" +// the following code means: I got an error code which I don't know (FIXME: reconsider nr!) +#define MSG_ILLEGAL 999 +#define MSG_ILLEGAL_STR "Internal error: Illegal response code." + +// here I start collecting rasmgr protocol tokens to eventually gather them all in one include file +#define RASMGRPROT_EOL "\r\n" +#define RASMGRPROT_DOUBLE_EOL "\r\n\r\n" + +// time [secs] until a client can be freed from the pending request list after a fake request +#define WAITTIME_AFTER_FAKE 2 +// time increment [secs] of iterated timeout (see updateTime()) +#define WAITTIME_REPEATED 3 + + +MasterComm::MasterComm() + { commit=false; + allowMultipleWriteTransactions = false; + } + +MasterComm::~MasterComm() + { + } + +void MasterComm::Run() + { + ENTER("MasterComm::Run: enter." ); + + initListenSocket(config.getListenPort()); // connect/bind the central listen socket + // using IOSelector level here! + + initJobs(MAXJOBSMASTER); // init jobs structure (nothing with sockets here) + + allowMultipleWriteTransactions = config.allowMultipleWriteTransactions(); + + selector.setTimeout( config.getPollFrequency() , 0 ); + + VLOG << "Entering server mode, prepared to receive requests." << endl << endl; + + while(mayExit()==false) + { + TALK("MasterComm::Run: new request cycle, status before processing is:" ); + printStatus(); + + if(commit) + doCommit(); + + int answerLen=0; + + TALK("MasterComm::Run: (c) Waiting..."); + + // wait for incoming requests, using select() + int r=selector.waitForRequest(); // 0 is timeout, <0 error, >0 success + // again, IOSelector level here! + + TALK("MasterComm::Run: (d) It's ringing..." << r); + + localServerManager.cleanChild(); // sa fie + + if(r<0) // nothing to read + { + TALK("MasterComm::Run: (f1) it's a signal (or a socket failure)..."); + continue; + } + if(r==0) // timeout, nothing to read + { + TALK("MasterComm::Run: (f2) nothing, look for timeouts..."); + lookForTimeout(); + continue; + } + if(r>0) // something is pending + { + + TALK("MasterComm::Run: (e) Got request, r=" << r << "..."); + + // iterate over all jobs to see what we can read / reconnect (?) / write + dispatchWriteRequest(); // first this, to increase the chance to free a client + connectNewClients(); // wait for requests, using accept() + dispatchReadRequest(); // now read in new requests + + for(int i=0;i<maxJobs;i++) + { + TALK( "- request processing: " << i ); // fake similar entry to benchmark logger + // creates too large log files, so omit in production: + // BenchmarkTimer *bPtr = new BenchmarkTimer("request processing");// print job start time to log + + processJob(job[i]); // this can involve closing the connection! + + // creates too large log files, so omit in production: + // bPtr->result(); // print stop time to log + } + } + } + LEAVE("MasterComm::Run: leave." ); + } // Run() + +// keep connection open after processing request? +// ...according to request type and comm success, shall we keep socket open? +// Note: this is a bad hack, but I don't want to change the "answer" ret code of processRequest() unless I fully understand it -- PB 2003-jun-10 +static bool keepConnection; + +void MasterComm::processJob(NbJob ¤tJob) + { + ENTER( "MasterComm::processJob: enter." ); + + if(currentJob.isOperationPending()==false ) + { + LEAVE( "MasterComm::processJob: leave. isOperationPending=false" ); + return; + } + + if(currentJob.wasError()) // low-level comm error + { + TALK( "MasterComm::processJob: closing connection." ); + currentJob.closeConnection(); + LEAVE( "MasterComm::processJob: leave." ); + return; + } + + if(currentJob.isMessageOK() == false) + { + LEAVE( "MasterComm::processJob: leave. isMessageOK=false" ); + return; + } + + if(fillInBuffer(currentJob.getMessage())==false) // fill msg into answer buffer header + body + { + TALK( "MasterComm::processJob: closing connection." ); + currentJob.closeConnection(); + LEAVE( "MasterComm::processJob: leave. fillInBuffer=false" ); + return; + } + + // now we have the message in inBuffer, with header and body set correctly + + int answer = processRequest( currentJob ); + + int outLen = strlen(outBuffer); + + if(outLen && answer != 2) // sending the answer + // FIXME: what is answer==2 ? never happens! -- PB 2003-jun-10 + { + TALK( "MasterComm::processJob: init sending answer for outBuffer, set socket to write mode." ); + currentJob.initSendAnswer(outBuffer); + } + + if(outLen == 0) // no answer to send + { + TALK( "MasterComm::processJob: no answer to send, closing connection." ); + currentJob.closeConnection(); + } + + if( answer == 1 ) // means "delayedOperation" + // FIXME: according to processRequest, delOp is 0 !!! -- PB 2003-jun-10 + // ...and 1 comes back for POST rasservernewstatus + { // the only known until now + TALK( "MasterComm::processJob: delayedOp, therefore changeServerStatus()." ); + rasManager.changeServerStatus(body); + } + /* Two words about this delayedOperation. If a remote server crashes, the remote rasmgr + sends a message and does not wait for answer. But if the master rasmgr restarts + immediately the crashed server, it could come to a deadlock. Maybe not, but we + wish to avoid any possibility, so we close first the connection and then attempt + to restart the server. + */ + + // EXPERIMENTAL: close sockets as soon and as always as possible + // if (! keepConnection) // singleton request or comm error + // { + // TALK( "MasterComm::processJob: singleton request, closing connection." ); + // currentJob.closeConnection(); + // } + + LEAVE( "MasterComm::processJob: leave." ); + } // processJob() + + +// printClientAddr(): aux fct to print client address to designated stream +const char *getClientAddr( int mySocket ) +{ + const char *result = NULL; + struct sockaddr_in s; + socklen_t sockaddrSize = (socklen_t) sizeof(s); + if ( getpeername( mySocket, (struct sockaddr*)&s, &sockaddrSize ) != 0) + result = strerror(errno); + else + result = inet_ntoa(s.sin_addr); + return result; +} + +// process request which has been prepared in inBuffer +// if 'verbose' is enabled in configuration then requests will be logged. +// returns +// 0 normally (?) +// 1 for POST rasservernewstatus +// NB: keepConnection is static above -- bad hack, see there +int MasterComm::processRequest( NbJob ¤tJob ) +{ + ENTER( "MasterComm::processRequest: enter. inBuffer=" << inBuffer ); + + // inBuffer: header + body Ok, output in outBuffer, which is not initialized here + outBuffer[0]=0; // mark outBuffer as empty + int answer = 0; + // delayedOperation = 0; + + bool fake = false; // getfreeserver request really wants to allocate a new server? + + // --- this is the central dispatcher for rasmgr requests, recognising and executing them. + + if(isMessage("POST rasmgrslave")) + { + VLOG << now() << " slave rasmgr request from " + << getClientAddr( currentJob.getSocket() ) + << ": '" << body << "'..." << flush; + + hostmanager.postSlaveMGR(body,outBuffer); // prepare outBuffer from body for send to slave + keepConnection = false; // master mgr passes thru, it's singleton commo, so don't keep conn + // FIXME: is this really correct?? -- PB 2003-jun-10 + VLOG << "ok" << endl; + } + else if(isMessage("POST rasservernewstatus")) + { + // extract server status from msg body + char serverName[50]; + int newstatus = 0; + long dummy = 0; + serverName[0] = '\0'; // initialize in case sscanf() fails + + int result = sscanf( body, "%s %d %ld", serverName, &newstatus, &dummy); + if (result == 3) // we simply ignore malformed requests, reason see below + { + const char *statusText = NULL; + switch (newstatus) + { + case SERVER_DOWN: + statusText = SERVER_DOWN_TXT; + break; + case SERVER_AVAILABLE: + statusText = SERVER_AVAILABLE_TXT; + break; + case SERVER_REGULARSIG: + statusText = SERVER_REGULARSIG_TXT; + break; + case SERVER_CRASHED: + statusText = SERVER_CRASHED_TXT; + break; + default: + statusText = "(unknown message flag)"; + break; + } + if (newstatus != SERVER_REGULARSIG) // don't blow up the log file with "still alive" signals + { + VLOG << now() << " status info from server " << serverName + << " @ " << getClientAddr( currentJob.getSocket() ) + << ": '" << statusText << "'...ok" << endl; + } + + keepConnection = false; // singleton msg slave -> master + answer = 1; + } + else // malformed request + { + VLOG << now() << " Error: malformed request (ignoring it) from " + << getClientAddr( currentJob.getSocket() ) + << ": '" << body << "'" << endl; + } + } + else if( (fake = isMessage("POST getfreeserver2")) || isMessage("POST getfreeserver")) + { + VLOG << now() << " client request from " + << getClientAddr( currentJob.getSocket() ) + << ": " << "'get server'..." << flush; + + int rc = getFreeServer(fake); // returns std rasdaman errors -- FIXME: error ignored! + keepConnection = (rc == MSG_OK) ? true : false; // 200 is "ok" + VLOG << "ok" << endl; + + + } + else if(isMessage("POST rascontrol")) + { + VLOG << now() << " rascontrol request from " + << getClientAddr( currentJob.getSocket() ) + << ": '" << body << "'..." << flush; + + if(authorization.acceptEntry(header)) + { + rascontrol.processRequest(body,outBuffer); + keepConnection = true; // rascontrol connection accepted, so keep it + VLOG << "ok" << endl; + } + else + { + answerAccessDenied(); + keepConnection = false; // this is a final answer, don't keep conn open afterwards + VLOG << "denied." << endl; + } + } + + LEAVE( "MasterComm::processRequest: leave. answer=" << answer << ", keepConnection=" << keepConnection ); + return answer; +} // processRequest() + +// fillInBuffer: fill parameter string passed into message header and body (both global) +// separator is a double newline +// FIXME: unstable and weird programming, improve! -- PB 2003-jun-10 +// input: +// s message input string +// returns: +// true filled buffer properly +// false NULL body string +// inBuffer (global buffer) set to s; header string part properly NULL terminated in inBuffer +// body (global ptr) set to beginning of message body in inBuffer +// header (global ptr) set to beginning of inBuffer +bool MasterComm::fillInBuffer(const char *s) +{ + strcpy(inBuffer,s); + header=inBuffer; // set header to begining of msg buffer + body=strstr(inBuffer, RASMGRPROT_DOUBLE_EOL ); // find double EOL, this is where body starts + if(body == NULL) // not found? this means a protocol syntax error + { + TALK( "MasterComm::fillInBuffer: Error in rasmgr protocol encountered (2xEOL missing). msg=" << inBuffer ); + return false; // only if client is stupid + } + + *body=0; // terminate header (!) string + body+= strlen( RASMGRPROT_DOUBLE_EOL ); // let body start after this double newline + + return true; +} + +// save config and auth file; deprecated +void MasterComm::doCommit() +{ + if(config.isTestModus()==false) + { + TALK( "MasterComm::doCommit: deprecated, should not be called any longer." ); +#if 0 // now done by saveCommand() directly + if(commitAuthOnly==false) + { VLOG << "Save configuration file..."; + if(config.saveConfigFile()) VLOG << "OK" << std::endl; + else VLOG << "Failed" << std::endl; + } + + VLOG << "Save authorization file..."; + if(authorization.saveAuthFile()) VLOG << "OK" << std::endl; + else VLOG << "Failed" << std::endl; +#endif + } + else + { std::cout<<"Save requested, but not permitted during test modus!"<<std::endl; + } + commit=false; + } + +void MasterComm::commitChanges() + { commit=true; + commitAuthOnly=false; + } +void MasterComm::commitAuthFile() + { commit=true; + commitAuthOnly=true; + } + +int MasterComm::answerAccessDenied() + { // send to rascontrol when wrong login + sprintf(outBuffer,"HTTP/1.1 400 Error\r\nContent-type: text/plain\r\nContent-length: %d\r\n\r\nAccess denied",strlen("Access denied")+1); + return strlen(outBuffer)+1; + } + +int MasterComm::answerAccessDeniedCode() + { // send to clients requesting free server when wrong login + sprintf(outBuffer,"HTTP/1.1 400 Error\r\nContent-type: text/plain\r\nContent-length: %d\r\n\r\n802 Access denied",strlen("802 Access denied")+1); + return strlen(outBuffer)+1; + } + +// input: +// fake true if only testing, false if server is to be allocated +// body (global var) holding string encoding of request parameters +// syntax: <dbname> [RPC|HTTP|RNP] [rw|ro] <previousID> +// where the flags are NOT case sensitive +// returns: standard rasdasman error codes +// 200 (ok), 801, 805, 999, ... +int MasterComm::getFreeServer(bool fake) +{ + // creates too large log files, so omit in production: + // BenchmarkTimer *freeServerTimePtr = new BenchmarkTimer("Get free server"); + + char databaseName[100]; + char serverType[10]; + char serverName[100]; // name of rasserver found, if any + char accessType[5]; + char prevID[200]="(none)"; + + ENTER("MasterComm::getFreeServer: enter. fake=" << fake << ", body="<<body<<'*'); + + // initialize server name + strcpy( serverName, "(none)" ); + + // extract components from body string + int count = sscanf(body,"%s %s %s %s",databaseName,serverType,accessType, prevID); + if (count != 4 && count != 3) + { + cout << "Error (internal): Cannot parse msg body received from client." << endl; + LEAVE("MasterComm::getFreeServer: leave. Fatal error: cannot parse msg body string '" << body << "'" ); + return MSG_ILLEGAL; + } + + ClientID clientID; + // if we got a previous ID then take this one + if(count >3) + clientID.init(prevID); + + TALK("GetFreeServer: db = " << databaseName << ", requested server type = " << serverType << ", access type = " << accessType << ", clientID="<<clientID<<" prevID="<< prevID ); + + char sType=0; // type of server requested, values SERVERTYPE_* + bool writeTransaction; // true <=> write transaction requested + + int answCode=MSG_OK; // request answer code + const char *answText=MSG_OK_STR; // string representation of above answer code + + char answerString[200]=""; // response string sent back to caller + + // this loop is executed at most once, it servers only to have + // a well-defined point of continuation upon evaluation errors + do + { + // --- evaluate message body ------------------------ + + // determine server type requested + if(strcasecmp(serverType,"HTTP")==0) + sType=SERVERTYPE_FLAG_HTTP; + if(strcasecmp(serverType,"RPC")==0) + sType=SERVERTYPE_FLAG_RPC; + if(strcasecmp(serverType,"RNP")==0) + sType=SERVERTYPE_FLAG_RNP; + if(sType==0) + { + cout << "Error: unknown server type: " << serverType << endl; + answCode=MSG_UNKNOWNSERVERTYPE; + break; + } + + // determine transaction mode + if (strcasecmp(accessType,"ro")==0) + writeTransaction=false; + else if (strcasecmp(accessType,"rw")==0) + writeTransaction=true; + else + { + cout << "Error: unknown transaction type: " << accessType << endl; + answCode=MSG_UNKNOWNACCESSTYPE; + break; + } + + TALK("accessType="<<accessType<<" writeTransaction="<<writeTransaction); + + // --- check against database state ------------------------ + + // does requested database exist? (i.e., is it known?) + Database &db=dbManager[databaseName]; + if(db.isValid()==false) + { + cout << "Error: database not found: " << databaseName << endl; + answCode=MSG_DATABASENOTFOUND; + break; + } + + // if r/w TA requested: is this compatible with the database's transaction state? + if(writeTransaction==true && db.getWriteTransactionCount() && allowMultipleWriteTransactions == false) + { + cout << "Error: write transaction in progress, conflicts with request." << endl; + answCode=MSG_WRITETRANSACTION; + break; + } + + // --- all fine, try to find a free server ------------------------ + + // iterate over registered servers, try to find a free one + // FIXME: should be "round robin" strategy wrt server hosts; + // take last used per server host is fine to reduce swapping + int countSuitableServers=0; // number of servers we can choose from + TALK( "starting to search for server of type " << sType << "..." ); + for(int i=0; i<db.countConnectionsToRasServers(); i++) + { + // inspect next server + RasServer &r=rasManager[db.getRasServerName(i)]; + TALK( " srv #" << i << ": name=" << r.getName() << ", type=" << r.getType() << ", isUp=" << r.isUp() << ", isAvailable=" << r.isAvailable() ); + if(sType == r.getType()) // type matches request? + { + if(r.isUp()) // server is up? + countSuitableServers++; + + if(r.isAvailable()) // server is free? + { // part A: we have what you want + int cbs = clientQueue.canBeServed(clientID, (const char*)databaseName, sType, fake); + // returns: 0=OK, otherwise rasdaman errors 801, 805 -- PB 2003-nov-20 + if(cbs != 0) + { + TALK("MasterComm::getFreeServer: clientQueue.canBeServed(" << clientID << "," << databaseName << "," << sType << "," << fake << ") -> " << cbs ); + cout << "Error: no server available, error code: " << cbs << endl; + answCode = cbs; + break; + } + if( fake == false) // server to be allocated? + { + // mark server found as unavailable to others + r.setNotAvailable(); + // set transaction mode requested + if(writeTransaction==true) + r.startWriteTransaction(db); // nothing real happens, no error can occur + else + r.startReadTransaction(db); // nothing real happens, no error can occur + TALK("MasterComm::getFreeServer: You have the server."); + // answCode is same as initialised if we come here + } + sprintf(answerString,"%s %ld %s ",r.getHostNetwName(),r.getPort(),authorization.getCapability(r.getName(),databaseName,!writeTransaction) ); + // remember server name + strncpy( serverName, r.getName(), sizeof(serverName) ); +; + TALK( "answerString=" << answerString ); + break; + } + } + } // for + + // any free server found? + if(countSuitableServers == 0) + { + cout << "Error: no suitable free server available." << endl; + answCode = MSG_NOSUITABLESERVER; + break; + } + + // no answer string provided -> no server available + // oops?? why not uniformly check against answCode? -- PB 2003-nov-20 + if(answerString[0]==0) + { + cout << "Error: cannot find any free server; answer code: " << answCode << " -> "; + answCode = MSG_SYSTEMOVERLOADED; + cout << answCode << endl; + break; + } + + } while(0); // see comment at start of "loop" + + answText = convertAnswerCode(answCode); + + if(answCode == MSG_OK) + sprintf(outBuffer,"HTTP/1.1 %d %s\r\nContent-type: text/plain\r\nContent-length: %d\r\n\r\n%s",answCode,answText,strlen(answerString)+1,answerString); + else + { + sprintf(outBuffer,"HTTP/1.1 %d %s\r\nContent-type: text/plain\r\nContent-length: %d\r\n\r\n%d %s",400,"Error",strlen(answText)+1,answCode,answText); + clientQueue.put(clientID, (const char*)databaseName, sType, answCode); + } + + // creates too large log files, so omit in production: + // freeServerTimePtr->result(); // print time elapsed + + LEAVE("MasterComm::getFreeServer: leave. answCode=" << answCode << ", server=" << serverName << ", outBuffer=" << outBuffer ); + return answCode; //strlen(outBuffer)+1; +} // getFreeServer() + +// convertAnswerCode: convert numeric answer code to error text for selected errors + OK +// input: +// code numeric error code, cf. errtxts +// returns: +// answer ptr to static error text +const char* MasterComm::convertAnswerCode(int code) +{ + const char *answer = MSG_ILLEGAL_STR; // return value, initialized to "illegal" + switch(code) + { + case MSG_OK: + answer = MSG_OK_STR; + break; + case MSG_UNKNOWNSERVERTYPE: + answer = MSG_UNKNOWNSERVERTYPE_STR; + break; + case MSG_UNKNOWNACCESSTYPE: + answer = MSG_UNKNOWNACCESSTYPE_STR; + break; + case MSG_DATABASENOTFOUND: + answer = MSG_DATABASENOTFOUND_STR; + break; + case MSG_WRITETRANSACTION: + answer = MSG_WRITETRANSACTION_STR; + break; + case MSG_NOSUITABLESERVER: + answer = MSG_NOSUITABLESERVER_STR; + break; + case MSG_SYSTEMOVERLOADED: + answer = MSG_SYSTEMOVERLOADED_STR; + break; + default: + // cout<<"Default value not allowed ="<<code<<endl; assert( 0 != 0); break; + // no program aborts deeply inside!!! -- PB 2003-jun-25 + answer = MSG_ILLEGAL_STR; + break; + } + + TALK("MasterComm::convertAnswerCode: code=" << code << ", answer=" << answer ); + return answer; +} + +// isMessage: determine whether header conforms with a given prefix; case insensitive +// input: +// messageStart prefix to compare with +// header (global) message header to be inspected +// returns: +// true on match +// false otherwise +bool MasterComm::isMessage(const char *messageStart) +{ + ENTER( "MasterComm::isMessage, messageStart=" << messageStart ); + + bool rasp= (strncasecmp(header,messageStart,strlen(messageStart))==0) ? true:false; + if(rasp) + TALK("(b) Message="<<messageStart); + + LEAVE( "MasterComm::isMessage, result=" << rasp ); + return rasp; +} + + +//******************************************************************** + +ClientID::ClientID() +{ + valid = false; +} + + +void ClientID::init(const char *stringrep) +{ + idstring = stringrep; + valid = true; +} + +string ClientID::getID() const +{ + return idstring; +} + +bool ClientID::isValid() const +{ + return valid; +} + +bool ClientID::operator==(const ClientID& cl) +{ + return (idstring == cl.idstring && valid) ? true : false; +} + +bool ClientID::operator!=(const ClientID& cl) +{ + return (idstring == cl.idstring && valid) ? false : true; +} + +std::ostream& operator<<(std::ostream &os, const ClientID &cl) +{ + os<<cl.getID(); + return os; +} + +// ---------------------------------------- + +/* +list of pending client requests. +requests are entered if for some reason server allocation failed, or if a "fake" request was sent. +*/ +// member attributes are defined in rasmgr_master.hh + +ClientQueue::ClientQueue() +{ + // do nothing +} + +ClientQueue::~ClientQueue() +{ + // do nothing +} + +// put: put client request into (static) queue; do nothing if request is malformed +// well formed if: +// - valid client ID in clientID +// - server assignment error in errorCode +void ClientQueue::put(ClientID &clientID, const char *dbName, char serverType, int errorCode) +{ + ENTER("ClientQueue::put: start, clientID=" << clientID << ", db=" << dbName << ", serverType=" << serverType << ", errorCode=" << errorCode ); + + // --- input parameter check --------------- + + if(clientID.isValid() == false) // invalid clientID's are not put in queue + return; + + if (errorCode != MSG_SYSTEMOVERLOADED + && errorCode != MSG_NOSUITABLESERVER + && errorCode != MSG_WRITETRANSACTION) // only these codes are put in queue + return; + + // --- walk through client list to find a matching entry --------------- + + ClientEntry *client = 0; // ptr to a list entry + + // iterate thru list of client requests + TALK( "iterating through list, client table size=" << clients.size() ); + for(int i=0;i<clients.size(); i++) + { + ClientEntry& curClient = clients[i]; // list entry to be inspected + + // on the fly, remove first list entry if outdated or inactive + // FIXME: what an ugly code -- PB 2003-nov-20 + if(curClient.activ == false || curClient.isTimeout()) + { + if(i==0) // do only for 1st element + { + TALK("ClientQueue::put: cleaned up client "<<curClient.clientID ); + clients.pop_front(); // remove this first element + i--; // set back loop ctr + } + continue; + } + + // have an entry with matching client ID? + if(curClient.clientID == clientID) + { + client = &clients[i]; // remember this entry + break; + } + } // for + + if(client == 0) // no matching entry found + { + ClientEntry newClient(clientID, dbName, serverType, errorCode); + newClient.activ = true; + newClient.updateTime(); + clients.push_back(newClient); + TALK("ClientQueue::put, new client first time, id="<<clientID ); + } + else // matching entry found + { + // Attention: we compare ptrs, not string contents!! -- PB 2003-nov-20 + if(client->dbName == dbName && client->serverType == serverType) + { // wants the same thing + client->errorCode = errorCode; + client->updateTime(); + TALK("ClientQueue::put, id=" << clientID << ", db=" << dbName << ", serverType=" << serverType << ": updated" ); + } + else + { // same client, wants something different, is a new client + client->activ = false; + ClientEntry newClient(clientID, dbName, serverType, errorCode); + newClient.activ = true; + newClient.updateTime(); + clients.push_back(newClient); + TALK("ClientQueue::put, known client db=" << dbName << ", serverType=" << serverType << ", but different request: id="<<clientID ); + } + } + + LEAVE("ClientQueue::put: done." ); +} // ClientQueue::put() + +// canBeServed: determine whether given request can be served +// by looking into client list; return code indicates yes/no/why not +// returns: +// 0 ok, can be served +// else rasdaman error code +int ClientQueue::canBeServed(ClientID &clientID, const char *dbName, char serverType, bool fake) +{ // the answer is the errorcode, why it can't be served + + if(clients.size() == 0) + return 0; + + for(int i=0;i<clients.size();i++) + { + ClientEntry& client = clients[i]; + + // on the fly, clean first element if necessary + // FIXME: this is not just as ugly as above, it also duplicates code! -- PB 2003-nov-20 + if(client.activ == false || client.isTimeout()) + { + if(i==0) + { + TALK("ClientQueue::canBeServed id="<<client.clientID<<" cleaned up"); + clients.pop_front(); + i--; + } + continue; + } + + if(client.dbName == dbName && client.serverType == serverType) + { // wants the same thing + if(client.clientID == clientID) + { // it's the same client + // first fake request is not cleaned up. + // Chances are 99.999% that the same client comes back very quickly with a true request + if(client.shouldWeCleanup(fake)) + { + client.activ = false; + if(i==0) + { + TALK("ClientQueue::canBeServed id="<<client.clientID<<" cleaned up, you get a server"); + clients.pop_front(); + i--; + } + } + LEAVE("ClientQueue::canBeServed id="<<clientID<<" yes (1)"); + return 0; // OK, it can be served + } + else // it's another client + { + if(client.errorCode == MSG_SYSTEMOVERLOADED || client.errorCode == MSG_NOSUITABLESERVER) + { // yes, only these two, 806 (Write trans in progr) is not inherited! + // If there would be a client waiting because of 806 then: + // - either we want to write and have also 806, and wouldn't be here at all, + // - or we want to read and we don't care for that 806 + LEAVE("ClientQueue::canBeServed id="<<clientID<<" no:"<<client.errorCode); + return client.errorCode; + } + LEAVE("ClientQueue::canBeServed id="<<clientID<<" yes (r/w)"); + return 0; // OK, can be served + } + } + } + + // if we are here, it can be served. There are clients, but not for the same reason + LEAVE("ClientQueue::canBeServed id="<<clientID<<" yes (2)"); + return 0; +} + +// ----------------------------------------------- + +/* + client entry in client request list. +Requests are put into the list only if a server allocation error +has happened before or if a fake request has been made, so that +a retry is needed. +Member attributes: + activ this entry active? (manipulated by several list functions external to this class!!) + serverType type of rasdaman server requested + errorCode error code of last call + timeLimit time when request is timed out + lastAction used by updateTime to determine timeout increment + wasfake last request was fake +*/ + +ClientQueue::ClientEntry::ClientEntry() + { + activ = false; + serverType = 'x'; + errorCode = 0; + lastAction = 0; + timeLimit = 0; + wasfake = false; + } + +ClientQueue::ClientEntry::ClientEntry(ClientID &_clientID, const char *_dbName, char _serverType, int _errorCode) +{ + activ = false; + clientID = _clientID; + dbName = _dbName; + serverType = _serverType; + errorCode = _errorCode; + lastAction = 0; + timeLimit = 0; + wasfake = false; +} + +// shouldWeCleanup: does current client need to be removed from pending request list? +// true iff fake || wasfake +// "we admit a first fake request without cleaning up, so the client gots a chance to come back +// with a true request. It's important to do this only for the first time, since a client which +// loops with openDB could lock the system!!!" +// side effects: +// if (fake && !wasfake): sets wasfake flag, increments update time +// input: +// fake true iff fake request +// returns: +// true cleanup recommended +// false not recommended +// major changes: +// single exit logic -- PB 2003-nov-20 +bool ClientQueue::ClientEntry::shouldWeCleanup(bool fake) +{ + bool result = false; // in dubio don't remove + + if(fake == false) + result = true; // yes, clean up + else + { + if(wasfake == true) + result = true; // clean up, there was a fake request already + else + { + wasfake = true; // why?? -- PB 2003-nov-20 + + // update time, but short time limit, client should come quickly + time_t now = time(NULL); + timeLimit = now + WAITTIME_AFTER_FAKE; + + result = false; + } + } + + return result; +} + +// updateTime: set new timeout interval +// "we will use an adaptative method. first the client will have 3 sec before timimg out +// than we compute deltaT and give him deltaT + 3. So clients can start by being unpatience and +// than reduce their frequency. This will reduce CPU usage and remain flexible" +void ClientQueue::ClientEntry::updateTime() +{ + + time_t now = time(NULL); + + if(lastAction == 0) + { // first use + lastAction = now; + timeLimit = now + WAITTIME_REPEATED; // client has WAITTIME_REPEATED seconds time to ask again + } + else + { + time_t deltaT = now - lastAction; + lastAction = now; + timeLimit = now + deltaT + WAITTIME_REPEATED; + } +} + +// isTimeout: has client request reached timeout limit? +bool ClientQueue::ClientEntry::isTimeout() +{ + return timeLimit < time(NULL); +} + diff --git a/rasmgr/rasmgr_master_nb_hack.cc b/rasmgr/rasmgr_master_nb_hack.cc new file mode 100644 index 0000000..42e1cb4 --- /dev/null +++ b/rasmgr/rasmgr_master_nb_hack.cc @@ -0,0 +1,1007 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_master_nb.cc + * + * MODULE: rasmgr + * CLASS: MasterComm + * + * PURPOSE: + * Main loop of master rasmgr + * + * COMMENTS: + * - MasterComm::processRequest() is the central dispatcher for rasmgr requests, recognising and executing them. + * +*/ + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "rasmgr_master.hh" +#include "rasmgr_config.hh" +#include "rasmgr_rascontrol.hh" +#include "rasmgr_users.hh" +#include "rasmgr_host.hh" +#include "rasmgr_localsrv.hh" +#include "rasmgr_srv.hh" + +using namespace std; + +#include "debug.hh" + + +// from rasmgr_localsrv.cc; should go to a central location -- PB 2003-nov-25 +extern char *now(); + +// rasserver error codes (see errtxts) +// FIXME: should go into a central include file / class -- PB 2003-nov-20 +#define MSG_OK 200 +#define MSG_OK_STR "Ok" +#define MSG_UNKNOWNSERVERTYPE 1001 +#define MSG_UNKNOWNSERVERTYPE_STR "Unknown server type" +#define MSG_UNKNOWNACCESSTYPE 1002 +#define MSG_UNKNOWNACCESSTYPE_STR "Unknown access type" +#define MSG_DATABASENOTFOUND 807 +#define MSG_DATABASENOTFOUND_STR "Database not found" +#define MSG_WRITETRANSACTION 806 +#define MSG_WRITETRANSACTION_STR "Write transaction in progress" +#define MSG_NOSUITABLESERVER 805 +#define MSG_NOSUITABLESERVER_STR "No suitable servers started" +#define MSG_SYSTEMOVERLOADED 801 +#define MSG_SYSTEMOVERLOADED_STR "System overloaded" +// the following code means: I got an error code which I don't know (FIXME: reconsider nr!) +#define MSG_ILLEGAL 999 +#define MSG_ILLEGAL_STR "Internal error: Illegal response code." + +// here I start collecting rasmgr protocol tokens to eventually gather them all in one include file +#define RASMGRPROT_EOL "\r\n" +#define RASMGRPROT_DOUBLE_EOL "\r\n\r\n" + +// time [secs] until a client can be freed from the pending request list after a fake request +#define WAITTIME_AFTER_FAKE 2 +// time increment [secs] of iterated timeout (see updateTime()) +#define WAITTIME_REPEATED 3 + + +MasterComm::MasterComm() + { commit=false; + allowMultipleWriteTransactions = false; + } + +MasterComm::~MasterComm() + { + } + +void MasterComm::Run() + { + ENTER("MasterComm::Run: enter." ); + + initListenSocket(config.getListenPort()); // connect/bind the central listen socket + // using IOSelector level here! + + initJobs(MAXJOBSMASTER); // init jobs structure (nothing with sockets here) + + allowMultipleWriteTransactions = config.allowMultipleWriteTransactions(); + + selector.setTimeout( config.getPollFrequency() , 0 ); + + VLOG << "Entering server mode, prepared to receive requests." << endl << endl; + + while(mayExit()==false) + { + TALK("MasterComm::Run: new request cycle, status before processing is:" ); + printStatus(); + + if(commit) + doCommit(); + + int answerLen=0; + + TALK("MasterComm::Run: (c) Waiting..."); + + // wait for incoming requests, using select() + int r=selector.waitForRequest(); // 0 is timeout, <0 error, >0 success + // again, IOSelector level here! + + TALK("MasterComm::Run: (d) It's ringing..." << r); + + localServerManager.cleanChild(); // sa fie + + if(r<0) // nothing to read + { + TALK("MasterComm::Run: (f1) it's a signal (or a socket failure)..."); + continue; + } + if(r==0) // timeout, nothing to read + { + TALK("MasterComm::Run: (f2) nothing, look for timeouts..."); + lookForTimeout(); + continue; + } + if(r>0) // something is pending + { + + TALK("MasterComm::Run: (e) Got request, r=" << r << "..."); + + // iterate over all jobs to see what we can read / reconnect (?) / write + dispatchWriteRequest(); // first this, to increase the chance to free a client + connectNewClients(); // wait for requests, using accept() + dispatchReadRequest(); // now read in new requests + + for(int i=0;i<maxJobs;i++) + { + TALK( "- request processing: " << i ); // fake similar entry to benchmark logger + // creates too large log files, so omit in production: + // BenchmarkTimer *bPtr = new BenchmarkTimer("request processing");// print job start time to log + + processJob(job[i]); // this can involve closing the connection! + + // creates too large log files, so omit in production: + // bPtr->result(); // print stop time to log + } + } + } + LEAVE("MasterComm::Run: leave." ); + } // Run() + +// keep connection open after processing request? +// ...according to request type and comm success, shall we keep socket open? +// Note: this is a bad hack, but I don't want to change the "answer" ret code of processRequest() unless I fully understand it -- PB 2003-jun-10 +static bool keepConnection; + +void MasterComm::processJob(NbJob ¤tJob) + { + ENTER( "MasterComm::processJob: enter." ); + + if(currentJob.isOperationPending()==false ) + { + LEAVE( "MasterComm::processJob: leave. isOperationPending=false" ); + return; + } + + if(currentJob.wasError()) // low-level comm error + { + TALK( "MasterComm::processJob: closing connection." ); + currentJob.closeConnection(); + LEAVE( "MasterComm::processJob: leave." ); + return; + } + + if(currentJob.isMessageOK() == false) + { + LEAVE( "MasterComm::processJob: leave. isMessageOK=false" ); + return; + } + + if(fillInBuffer(currentJob.getMessage())==false) // fill msg into answer buffer header + body + { + TALK( "MasterComm::processJob: closing connection." ); + currentJob.closeConnection(); + LEAVE( "MasterComm::processJob: leave. fillInBuffer=false" ); + return; + } + + // now we have the message in inBuffer, with header and body set correctly + + int answer = processRequest( currentJob ); + + int outLen = strlen(outBuffer); + + if(outLen && answer != 2) // sending the answer + // FIXME: what is answer==2 ? never happens! -- PB 2003-jun-10 + { + TALK( "MasterComm::processJob: init sending answer for outBuffer, set socket to write mode." ); + currentJob.initSendAnswer(outBuffer); + } + + if(outLen == 0) // no answer to send + { + TALK( "MasterComm::processJob: no answer to send, closing connection." ); + currentJob.closeConnection(); + } + + if( answer == 1 ) // means "delayedOperation" + // FIXME: according to processRequest, delOp is 0 !!! -- PB 2003-jun-10 + // ...and 1 comes back for POST rasservernewstatus + { // the only known until now + TALK( "MasterComm::processJob: delayedOp, therefore changeServerStatus()." ); + rasManager.changeServerStatus(body); + } + /* Two words about this delayedOperation. If a remote server crashes, the remote rasmgr + sends a message and does not wait for answer. But if the master rasmgr restarts + immediately the crashed server, it could come to a deadlock. Maybe not, but we + wish to avoid any possibility, so we close first the connection and then attempt + to restart the server. + */ + + // EXPERIMENTAL: close sockets as soon and as always as possible + // if (! keepConnection) // singleton request or comm error + // { + // TALK( "MasterComm::processJob: singleton request, closing connection." ); + // currentJob.closeConnection(); + // } + + LEAVE( "MasterComm::processJob: leave." ); + } // processJob() + + +// printClientAddr(): aux fct to print client address to designated stream +const char *getClientAddr( int mySocket ) +{ + const char *result = NULL; + struct sockaddr_in s; + socklen_t sockaddrSize = (socklen_t) sizeof(s); + if ( getpeername( mySocket, (struct sockaddr*)&s, &sockaddrSize ) != 0) + result = strerror(errno); + else + result = inet_ntoa(s.sin_addr); + return result; +} + +// process request which has been prepared in inBuffer +// if 'verbose' is enabled in configuration then requests will be logged. +// returns +// 0 normally (?) +// 1 for POST rasservernewstatus +// NB: keepConnection is static above -- bad hack, see there +int MasterComm::processRequest( NbJob ¤tJob ) +{ + ENTER( "MasterComm::processRequest: enter. inBuffer=" << inBuffer ); + + // inBuffer: header + body Ok, output in outBuffer, which is not initialized here + outBuffer[0]=0; // mark outBuffer as empty + int answer = 0; + // delayedOperation = 0; + + bool fake = false; // getfreeserver request really wants to allocate a new server? + + // --- this is the central dispatcher for rasmgr requests, recognising and executing them. + + if(isMessage("POST rasmgrslave")) + { + VLOG << now() << " slave rasmgr request from " + << getClientAddr( currentJob.getSocket() ) + << ": '" << body << "'..." << flush; + + hostmanager.postSlaveMGR(body,outBuffer); // prepare outBuffer from body for send to slave + keepConnection = false; // master mgr passes thru, it's singleton commo, so don't keep conn + // FIXME: is this really correct?? -- PB 2003-jun-10 + VLOG << "ok" << endl; + } + else if(isMessage("POST rasservernewstatus")) + { + // extract server status from msg body + char serverName[50]; + int newstatus = 0; + long dummy = 0; + serverName[0] = '\0'; // initialize in case sscanf() fails + + int result = sscanf( body, "%s %d %ld", serverName, &newstatus, &dummy); + if (result == 3) // we simply ignore malformed requests, reason see below + { + const char *statusText = NULL; + switch (newstatus) + { + case SERVER_DOWN: + statusText = SERVER_DOWN_TXT; + break; + case SERVER_AVAILABLE: + statusText = SERVER_AVAILABLE_TXT; + break; + case SERVER_REGULARSIG: + statusText = SERVER_REGULARSIG_TXT; + break; + case SERVER_CRASHED: + statusText = SERVER_CRASHED_TXT; + break; + default: + statusText = "(unknown message flag)"; + break; + } + if (newstatus != SERVER_REGULARSIG) // don't blow up the log file with "still alive" signals + { + VLOG << now() << " status info from server " << serverName + << " @ " << getClientAddr( currentJob.getSocket() ) + << ": '" << statusText << "'...ok" << endl; + } + + keepConnection = false; // singleton msg slave -> master + answer = 1; + } + else // malformed request + { + VLOG << now() << " Error: malformed request (ignoring it) from " + << getClientAddr( currentJob.getSocket() ) + << ": '" << body << "'" << endl; + } + } + else if( (fake = isMessage("POST getfreeserver2")) || isMessage("POST getfreeserver")) + { + VLOG << now() << " client request from " + << getClientAddr( currentJob.getSocket() ) + << ": " << "'get server'..." << flush; + + int rc = getFreeServer(fake); // returns std rasdaman errors -- FIXME: error ignored! + keepConnection = (rc == MSG_OK) ? true : false; // 200 is "ok" + VLOG << "ok" << endl; + + + } + else if(isMessage("POST rascontrol")) + { + VLOG << now() << " rascontrol request from " + << getClientAddr( currentJob.getSocket() ) + << ": '" << body << "'..." << flush; + + if(authorization.acceptEntry(header)) + { + rascontrol.processRequest(body,outBuffer); + keepConnection = true; // rascontrol connection accepted, so keep it + VLOG << "ok" << endl; + } + else + { + answerAccessDenied(); + keepConnection = false; // this is a final answer, don't keep conn open afterwards + VLOG << "denied." << endl; + } + } + + LEAVE( "MasterComm::processRequest: leave. answer=" << answer << ", keepConnection=" << keepConnection ); + return answer; +} // processRequest() + +// fillInBuffer: fill parameter string passed into message header and body (both global) +// separator is a double newline +// FIXME: unstable and weird programming, improve! -- PB 2003-jun-10 +// input: +// s message input string +// returns: +// true filled buffer properly +// false NULL body string +// inBuffer (global buffer) set to s; header string part properly NULL terminated in inBuffer +// body (global ptr) set to beginning of message body in inBuffer +// header (global ptr) set to beginning of inBuffer +bool MasterComm::fillInBuffer(const char *s) +{ + strcpy(inBuffer,s); + header=inBuffer; // set header to begining of msg buffer + body=strstr(inBuffer, RASMGRPROT_DOUBLE_EOL ); // find double EOL, this is where body starts + if(body == NULL) // not found? this means a protocol syntax error + { + TALK( "MasterComm::fillInBuffer: Error in rasmgr protocol encountered (2xEOL missing). msg=" << inBuffer ); + return false; // only if client is stupid + } + + *body=0; // terminate header (!) string + body+= strlen( RASMGRPROT_DOUBLE_EOL ); // let body start after this double newline + + return true; +} + +// save config and auth file; deprecated +void MasterComm::doCommit() +{ + if(config.isTestModus()==false) + { + TALK( "MasterComm::doCommit: deprecated, should not be called any longer." ); +#if 0 // now done by saveCommand() directly + if(commitAuthOnly==false) + { VLOG << "Save configuration file..."; + if(config.saveConfigFile()) VLOG << "OK" << std::endl; + else VLOG << "Failed" << std::endl; + } + + VLOG << "Save authorization file..."; + if(authorization.saveAuthFile()) VLOG << "OK" << std::endl; + else VLOG << "Failed" << std::endl; +#endif + } + else + { std::cout<<"Save requested, but not permitted during test modus!"<<std::endl; + } + commit=false; + } + +void MasterComm::commitChanges() + { commit=true; + commitAuthOnly=false; + } +void MasterComm::commitAuthFile() + { commit=true; + commitAuthOnly=true; + } + +int MasterComm::answerAccessDenied() + { // send to rascontrol when wrong login + sprintf(outBuffer,"HTTP/1.1 400 Error\r\nContent-type: text/plain\r\nContent-length: %d\r\n\r\nAccess denied",strlen("Access denied")+1); + return strlen(outBuffer)+1; + } + +int MasterComm::answerAccessDeniedCode() + { // send to clients requesting free server when wrong login + sprintf(outBuffer,"HTTP/1.1 400 Error\r\nContent-type: text/plain\r\nContent-length: %d\r\n\r\n802 Access denied",strlen("802 Access denied")+1); + return strlen(outBuffer)+1; + } + +// input: +// fake true if only testing, false if server is to be allocated +// body (global var) holding string encoding of request parameters +// syntax: <dbname> [RPC|HTTP|RNP] [rw|ro] <previousID> +// where the flags are NOT case sensitive +// returns: standard rasdasman error codes +// 200 (ok), 801, 805, 999, ... +int MasterComm::getFreeServer(bool fake) +{ + // creates too large log files, so omit in production: + // BenchmarkTimer *freeServerTimePtr = new BenchmarkTimer("Get free server"); + + char databaseName[100]; + char serverType[10]; + char serverName[100]; // name of rasserver found, if any + char accessType[5]; + char prevID[200]="(none)"; + + ENTER("MasterComm::getFreeServer: enter. fake=" << fake << ", body="<<body<<'*'); + + // initialize server name + strcpy( serverName, "(none)" ); + + // extract components from body string + int count = sscanf(body,"%s %s %s %s",databaseName,serverType,accessType, prevID); + if (count != 4 && count != 3) + { + cout << "Error (internal): Cannot parse msg body received from client." << endl; + LEAVE("MasterComm::getFreeServer: leave. Fatal error: cannot parse msg body string '" << body << "'" ); + return MSG_ILLEGAL; + } + + ClientID clientID; + // if we got a previous ID then take this one + if(count >3) + clientID.init(prevID); + + TALK("GetFreeServer: db = " << databaseName << ", requested server type = " << serverType << ", access type = " << accessType << ", clientID="<<clientID<<" prevID="<< prevID ); + + char sType=0; // type of server requested, values SERVERTYPE_* + bool writeTransaction; // true <=> write transaction requested + + int answCode=MSG_OK; // request answer code + const char *answText=MSG_OK_STR; // string representation of above answer code + + char answerString[200]=""; // response string sent back to caller + + // this loop is executed at most once, it servers only to have + // a well-defined point of continuation upon evaluation errors + do + { + // --- evaluate message body ------------------------ + + // determine server type requested + if(strcasecmp(serverType,"HTTP")==0) + sType=SERVERTYPE_FLAG_HTTP; + if(strcasecmp(serverType,"RPC")==0) + sType=SERVERTYPE_FLAG_RPC; + if(strcasecmp(serverType,"RNP")==0) + sType=SERVERTYPE_FLAG_RNP; + if(sType==0) + { + cout << "Error: unknown server type: " << serverType << endl; + answCode=MSG_UNKNOWNSERVERTYPE; + break; + } + + // determine transaction mode + if (strcasecmp(accessType,"ro")==0) + writeTransaction=false; + else if (strcasecmp(accessType,"rw")==0) + writeTransaction=true; + else + { + cout << "Error: unknown transaction type: " << accessType << endl; + answCode=MSG_UNKNOWNACCESSTYPE; + break; + } + + TALK("accessType="<<accessType<<" writeTransaction="<<writeTransaction); + + // --- check against database state ------------------------ + + // does requested database exist? (i.e., is it known?) + Database &db=dbManager[databaseName]; + if(db.isValid()==false) + { + cout << "Error: database not found: " << databaseName << endl; + answCode=MSG_DATABASENOTFOUND; + break; + } + + // if r/w TA requested: is this compatible with the database's transaction state? + if(writeTransaction==true && db.getWriteTransactionCount() && allowMultipleWriteTransactions == false) + { + cout << "Error: write transaction in progress, conflicts with request." << endl; + answCode=MSG_WRITETRANSACTION; + break; + } + + // --- all fine, try to find a free server ------------------------ + + // iterate over registered servers, try to find a free one + // FIXME: should be "round robin" strategy wrt server hosts; + // take last used per server host is fine to reduce swapping + int countSuitableServers=0; // number of servers we can choose from + TALK( "starting to search for server of type " << sType << "..." ); + for(int i=0; i<db.countConnectionsToRasServers(); i++) + { + // inspect next server + RasServer &r=rasManager[db.getRasServerName(i)]; + TALK( " srv #" << i << ": name=" << r.getName() << ", type=" << r.getType() << ", isUp=" << r.isUp() << ", isAvailable=" << r.isAvailable() ); + if(sType == r.getType()) // type matches request? + { + if(r.isUp()) // server is up? + countSuitableServers++; + + if(r.isAvailable()) // server is free? + { // part A: we have what you want + int cbs = clientQueue.canBeServed(clientID, (const char*)databaseName, sType, fake); + // returns: 0=OK, otherwise rasdaman errors 801, 805 -- PB 2003-nov-20 + if(cbs != 0) + { + TALK("MasterComm::getFreeServer: clientQueue.canBeServed(" << clientID << "," << databaseName << "," << sType << "," << fake << ") -> " << cbs ); + cout << "Error: no server available, error code: " << cbs << endl; + answCode = cbs; + break; + } + if( fake == false) // server to be allocated? + { + // mark server found as unavailable to others + r.setNotAvailable(); + // set transaction mode requested + if(writeTransaction==true) + r.startWriteTransaction(db); // nothing real happens, no error can occur + else + r.startReadTransaction(db); // nothing real happens, no error can occur + TALK("MasterComm::getFreeServer: You have the server."); + // answCode is same as initialised if we come here + } + sprintf(answerString,"%s %ld %s ",r.getHostNetwName(),r.getPort(),authorization.getCapability(r.getName(),databaseName,!writeTransaction) ); + // remember server name + strncpy( serverName, r.getName(), sizeof(serverName) ); +; + TALK( "answerString=" << answerString ); + break; + } + } + } // for + + // any free server found? + if(countSuitableServers == 0) + { + cout << "Error: no suitable free server available." << endl; + answCode = MSG_NOSUITABLESERVER; + break; + } + + // no answer string provided -> no server available + // oops?? why not uniformly check against answCode? -- PB 2003-nov-20 + if(answerString[0]==0) + { + cout << "Error: cannot find any free server; answer code: " << answCode << " -> "; + answCode = MSG_SYSTEMOVERLOADED; + cout << answCode << endl; + break; + } + + } while(0); // see comment at start of "loop" + + answText = convertAnswerCode(answCode); + + if(answCode == MSG_OK) + sprintf(outBuffer,"HTTP/1.1 %d %s\r\nContent-type: text/plain\r\nContent-length: %d\r\n\r\n%s",answCode,answText,strlen(answerString)+1,answerString); + else + { + sprintf(outBuffer,"HTTP/1.1 %d %s\r\nContent-type: text/plain\r\nContent-length: %d\r\n\r\n%d %s",400,"Error",strlen(answText)+1,answCode,answText); + clientQueue.put(clientID, (const char*)databaseName, sType, answCode); + } + + // creates too large log files, so omit in production: + // freeServerTimePtr->result(); // print time elapsed + + LEAVE("MasterComm::getFreeServer: leave. answCode=" << answCode << ", server=" << serverName << ", outBuffer=" << outBuffer ); + return answCode; //strlen(outBuffer)+1; +} // getFreeServer() + +// convertAnswerCode: convert numeric answer code to error text for selected errors + OK +// input: +// code numeric error code, cf. errtxts +// returns: +// answer ptr to static error text +const char* MasterComm::convertAnswerCode(int code) +{ + const char *answer = MSG_ILLEGAL_STR; // return value, initialized to "illegal" + switch(code) + { + case MSG_OK: + answer = MSG_OK_STR; + break; + case MSG_UNKNOWNSERVERTYPE: + answer = MSG_UNKNOWNSERVERTYPE_STR; + break; + case MSG_UNKNOWNACCESSTYPE: + answer = MSG_UNKNOWNACCESSTYPE_STR; + break; + case MSG_DATABASENOTFOUND: + answer = MSG_DATABASENOTFOUND_STR; + break; + case MSG_WRITETRANSACTION: + answer = MSG_WRITETRANSACTION_STR; + break; + case MSG_NOSUITABLESERVER: + answer = MSG_NOSUITABLESERVER_STR; + break; + case MSG_SYSTEMOVERLOADED: + answer = MSG_SYSTEMOVERLOADED_STR; + break; + default: + // cout<<"Default value not allowed ="<<code<<endl; assert( 0 != 0); break; + // no program aborts deeply inside!!! -- PB 2003-jun-25 + answer = MSG_ILLEGAL_STR; + break; + } + + TALK("MasterComm::convertAnswerCode: code=" << code << ", answer=" << answer ); + return answer; +} + +// isMessage: determine whether header conforms with a given prefix; case insensitive +// input: +// messageStart prefix to compare with +// header (global) message header to be inspected +// returns: +// true on match +// false otherwise +bool MasterComm::isMessage(const char *messageStart) +{ + ENTER( "MasterComm::isMessage, messageStart=" << messageStart ); + + bool rasp= (strncasecmp(header,messageStart,strlen(messageStart))==0) ? true:false; + if(rasp) + TALK("(b) Message="<<messageStart); + + LEAVE( "MasterComm::isMessage, result=" << rasp ); + return rasp; +} + + +//******************************************************************** + +ClientID::ClientID() +{ + valid = false; +} + + +void ClientID::init(const char *stringrep) +{ + idstring = stringrep; + valid = true; +} + +string ClientID::getID() const +{ + return idstring; +} + +bool ClientID::isValid() const +{ + return valid; +} + +bool ClientID::operator==(const ClientID& cl) +{ + return (idstring == cl.idstring && valid) ? true : false; +} + +bool ClientID::operator!=(const ClientID& cl) +{ + return (idstring == cl.idstring && valid) ? false : true; +} + +std::ostream& operator<<(std::ostream &os, const ClientID &cl) +{ + os<<cl.getID(); + return os; +} + +// ---------------------------------------- + +/* +list of pending client requests. +requests are entered if for some reason server allocation failed, or if a "fake" request was sent. +*/ +// member attributes are defined in rasmgr_master.hh + +ClientQueue::ClientQueue() +{ + // do nothing +} + +ClientQueue::~ClientQueue() +{ + // do nothing +} + +// put: put client request into (static) queue; do nothing if request is malformed +// well formed if: +// - valid client ID in clientID +// - server assignment error in errorCode +void ClientQueue::put(ClientID &clientID, const char *dbName, char serverType, int errorCode) +{ + ENTER("ClientQueue::put: start, clientID=" << clientID << ", db=" << dbName << ", serverType=" << serverType << ", errorCode=" << errorCode ); + + // --- input parameter check --------------- + + if(clientID.isValid() == false) // invalid clientID's are not put in queue + return; + + if (errorCode != MSG_SYSTEMOVERLOADED + && errorCode != MSG_NOSUITABLESERVER + && errorCode != MSG_WRITETRANSACTION) // only these codes are put in queue + return; + + // --- walk through client list to find a matching entry --------------- + + ClientEntry *client = 0; // ptr to a list entry + + // iterate thru list of client requests + TALK( "iterating through list, client table size=" << clients.size() ); + for(int i=0;i<clients.size(); i++) + { + ClientEntry& curClient = clients[i]; // list entry to be inspected + + // on the fly, remove first list entry if outdated or inactive + // FIXME: what an ugly code -- PB 2003-nov-20 + if(curClient.activ == false || curClient.isTimeout()) + { + if(i==0) // do only for 1st element + { + TALK("ClientQueue::put: cleaned up client "<<curClient.clientID ); + clients.pop_front(); // remove this first element + i--; // set back loop ctr + } + continue; + } + + // have an entry with matching client ID? + if(curClient.clientID == clientID) + { + client = &clients[i]; // remember this entry + break; + } + } // for + + if(client == 0) // no matching entry found + { + ClientEntry newClient(clientID, dbName, serverType, errorCode); + newClient.activ = true; + newClient.updateTime(); + clients.push_back(newClient); + TALK("ClientQueue::put, new client first time, id="<<clientID ); + } + else // matching entry found + { + // Attention: we compare ptrs, not string contents!! -- PB 2003-nov-20 + if(client->dbName == dbName && client->serverType == serverType) + { // wants the same thing + client->errorCode = errorCode; + client->updateTime(); + TALK("ClientQueue::put, id=" << clientID << ", db=" << dbName << ", serverType=" << serverType << ": updated" ); + } + else + { // same client, wants something different, is a new client + client->activ = false; + ClientEntry newClient(clientID, dbName, serverType, errorCode); + newClient.activ = true; + newClient.updateTime(); + clients.push_back(newClient); + TALK("ClientQueue::put, known client db=" << dbName << ", serverType=" << serverType << ", but different request: id="<<clientID ); + } + } + + LEAVE("ClientQueue::put: done." ); +} // ClientQueue::put() + +// canBeServed: determine whether given request can be served +// by looking into client list; return code indicates yes/no/why not +// returns: +// 0 ok, can be served +// else rasdaman error code +int ClientQueue::canBeServed(ClientID &clientID, const char *dbName, char serverType, bool fake) +{ // the answer is the errorcode, why it can't be served + + if(clients.size() == 0) + return 0; + + for(int i=0;i<clients.size();i++) + { + ClientEntry& client = clients[i]; + + // on the fly, clean first element if necessary + // FIXME: this is not just as ugly as above, it also duplicates code! -- PB 2003-nov-20 + if(client.activ == false || client.isTimeout()) + { + if(i==0) + { + TALK("ClientQueue::canBeServed id="<<client.clientID<<" cleaned up"); + clients.pop_front(); + i--; + } + continue; + } + + if(client.dbName == dbName && client.serverType == serverType) + { // wants the same thing + if(client.clientID == clientID) + { // it's the same client + // first fake request is not cleaned up. + // Chances are 99.999% that the same client comes back very quickly with a true request + if(client.shouldWeCleanup(fake)) + { + client.activ = false; + if(i==0) + { + TALK("ClientQueue::canBeServed id="<<client.clientID<<" cleaned up, you get a server"); + clients.pop_front(); + i--; + } + } + LEAVE("ClientQueue::canBeServed id="<<clientID<<" yes (1)"); + return 0; // OK, it can be served + } + else // it's another client + { + if(client.errorCode == MSG_SYSTEMOVERLOADED || client.errorCode == MSG_NOSUITABLESERVER) + { // yes, only these two, 806 (Write trans in progr) is not inherited! + // If there would be a client waiting because of 806 then: + // - either we want to write and have also 806, and wouldn't be here at all, + // - or we want to read and we don't care for that 806 + LEAVE("ClientQueue::canBeServed id="<<clientID<<" no:"<<client.errorCode); + return client.errorCode; + } + LEAVE("ClientQueue::canBeServed id="<<clientID<<" yes (r/w)"); + return 0; // OK, can be served + } + } + } + + // if we are here, it can be served. There are clients, but not for the same reason + LEAVE("ClientQueue::canBeServed id="<<clientID<<" yes (2)"); + return 0; +} + +// ----------------------------------------------- + +/* + client entry in client request list. +Requests are put into the list only if a server allocation error +has happened before or if a fake request has been made, so that +a retry is needed. +Member attributes: + activ this entry active? (manipulated by several list functions external to this class!!) + serverType type of rasdaman server requested + errorCode error code of last call + timeLimit time when request is timed out + lastAction used by updateTime to determine timeout increment + wasfake last request was fake +*/ + +ClientQueue::ClientEntry::ClientEntry() + { + activ = false; + serverType = 'x'; + errorCode = 0; + lastAction = 0; + timeLimit = 0; + wasfake = false; + } + +ClientQueue::ClientEntry::ClientEntry(ClientID &_clientID, const char *_dbName, char _serverType, int _errorCode) +{ + activ = false; + clientID = _clientID; + dbName = _dbName; + serverType = _serverType; + errorCode = _errorCode; + lastAction = 0; + timeLimit = 0; + wasfake = false; +} + +// shouldWeCleanup: does current client need to be removed from pending request list? +// true iff fake || wasfake +// "we admit a first fake request without cleaning up, so the client gots a chance to come back +// with a true request. It's important to do this only for the first time, since a client which +// loops with openDB could lock the system!!!" +// side effects: +// if (fake && !wasfake): sets wasfake flag, increments update time +// input: +// fake true iff fake request +// returns: +// true cleanup recommended +// false not recommended +// major changes: +// single exit logic -- PB 2003-nov-20 +bool ClientQueue::ClientEntry::shouldWeCleanup(bool fake) +{ + bool result = false; // in dubio don't remove + + if(fake == false) + result = true; // yes, clean up + else + { + if(wasfake == true) + result = true; // clean up, there was a fake request already + else + { + wasfake = true; // why?? -- PB 2003-nov-20 + + // update time, but short time limit, client should come quickly + time_t now = time(NULL); + timeLimit = now + WAITTIME_AFTER_FAKE; + + result = false; + } + } + + return result; +} + +// updateTime: set new timeout interval +// "we will use an adaptative method. first the client will have 3 sec before timimg out +// than we compute deltaT and give him deltaT + 3. So clients can start by being unpatience and +// than reduce their frequency. This will reduce CPU usage and remain flexible" +void ClientQueue::ClientEntry::updateTime() +{ + + time_t now = time(NULL); + + if(lastAction == 0) + { // first use + lastAction = now; + timeLimit = now + WAITTIME_REPEATED; // client has WAITTIME_REPEATED seconds time to ask again + } + else + { + time_t deltaT = now - lastAction; + lastAction = now; + timeLimit = now + deltaT + WAITTIME_REPEATED; + } +} + +// isTimeout: has client request reached timeout limit? +bool ClientQueue::ClientEntry::isTimeout() +{ + return timeLimit < time(NULL); +} + diff --git a/rasmgr/rasmgr_protocol.hh b/rasmgr/rasmgr_protocol.hh new file mode 100644 index 0000000..726f45f --- /dev/null +++ b/rasmgr/rasmgr_protocol.hh @@ -0,0 +1,68 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_protocol.hh + * + * MODULE: rasmgr + * CLASS: none + * + * PURPOSE: + * Centralize all keywords of the rasmgr c/s protocol + * + * COMMENTS: + * - to be completed + * +*/ + +#ifndef RASMGR_PROTOCOL_HH +#define RASMGR_PROTOCOL_HH + +#define RASMGRCMD_HELLO "Hello" + +#define RASMGRCMD_LICENCE "licence" +#define RASMGRCMD_LICENSE "license" + +#define RASMGRCMD_EXIT "exit" +#define RASMGRCMD_QUIT "quit" +#define RASMGRCMD_BYE "bye" + +#define RASMGRCMD_HELP "help" +#define RASMGRCMD_USER "user" +#define RASMGRCMD_HOST "host" +#define RASMGRCMD_SRV "srv" +#define RASMGRCMD_DATABASE "database" +#define RASMGRCMD_LIST "list" +#define RASMGRCMD_DEFINE "define" +#define RASMGRCMD_REMOVE "remove" +#define RASMGRCMD_CHECK "check" +#define RASMGRCMD_CHANGE "change" +#define RASMGRCMD_SAVE "save" +#define RASMGRCMD_RESET "reset" +#define RASMGRCMD_GRANT "grant" +#define RASMGRCMD_REVOKE "revoke" + +#define RASMGRCMD_UP "up" +#define RASMGRCMD_DOWN "down" + + +#endif // RASMGR_PROTOCOL_HH diff --git a/rasmgr/rasmgr_random.cc b/rasmgr/rasmgr_random.cc new file mode 100644 index 0000000..1fc501c --- /dev/null +++ b/rasmgr/rasmgr_random.cc @@ -0,0 +1,176 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_master.hh + * + * MODULE: rasmgr + * CLASS: RandomGenerator + * + * PURPOSE: + * Own "random number generator", used to scramble the authorization file + * + * COMMENTS: + * None + * +*/ +#include "rasmgr_users.hh" + +RandomGenerator::RandomGenerator() + { + // if somebody want's seed=1 inside it's seed =0, due to table generation way + seed=0; + fileVersion=-1; + } + +bool RandomGenerator::setFileVersion(long version) + { + static bool firstCall=true; + fileVersion = version; + if(version == 2 ) return true; // the actual version + + if(version == 1 ) { if(firstCall) std::cout<<"Authorization file will be migrated to the new version"<<std::endl; + firstCall=false; + return true; + } + return false; + } + +void +RandomGenerator::init(unsigned int newSeed) + { + switch(fileVersion) + { + case 1: srand(newSeed); + break; + + case 2: seed = (newSeed-1)%1000; + break; + } + } + +unsigned char +RandomGenerator::operator()() + { + unsigned char rasp = 0; + switch(fileVersion) + { + case 1: rasp=rand(); + break; + + case 2: rasp= randomTable[seed]; + seed=(seed+1)%1000; + break; + } + return rasp; + } + +bool RandomGenerator::insideTest() + { + //performs a test to see if rand() functions like the original one. + // usefull to see if we were paranoic for nothing + + unsigned int oldSeed=seed; + init(1); srand(1); // with other than 1 it breaks + + bool rasp=true; + for(int i=0;i<40;i++) + { + for(int j=0;j<25;j++) + { + unsigned char b=rand(); + if( b!= this->operator()() ) + { rasp=false; + + } + } + } + + seed=oldSeed; + + return rasp; + } + + +/* do not change this table by any means and any reason!! +We used for scrambling the authorization file the standard rand() function, but we are paranoic +that someone could upgrade some system and change the rand function, breaking the decoding of the file +To avoid such trouble, we put a table here generated with: + + srand(1); + + for(int i=0;i<40;i++) + { + for(int j=0;j<25;j++) + { + unsigned char b=rand(); + std::cout<<(unsigned int)b<<','; + } + std::cout<<std::endl; + } + return 0; + +*/ + + +unsigned char RandomGenerator::randomTable[1000]={ +103,198,105,115,81,255,74,236,41,205,186,171,242,251,227,70,124,194,84,248,27,232,231,141,118, +90,46,99,51,159,201,154,102,50,13,183,49,88,163,90,37,93,5,23,88,233,94,212,171,178, +205,198,155,180,84,17,14,130,116,65,33,61,220,135,112,233,62,161,65,225,252,103,62,1,126, +151,234,220,107,150,143,56,92,42,236,176,59,251,50,175,60,84,236,24,219,92,2,26,254,67, +251,250,170,58,251,41,209,230,5,60,124,148,117,216,190,97,137,249,92,187,168,153,15,149,177, +235,241,179,5,239,247,0,233,161,58,229,202,11,203,208,72,71,100,189,31,35,30,168,28,123, +100,197,20,115,90,197,94,75,121,99,59,112,100,36,17,158,9,220,170,212,172,242,27,16,175, +59,51,205,227,80,72,71,21,92,187,111,34,25,186,155,125,245,11,225,26,28,127,35,248,41, +248,164,27,19,181,202,78,232,152,50,56,224,121,77,61,52,188,95,78,119,250,203,108,5,172, +134,33,43,170,26,85,162,190,112,181,115,59,4,92,211,54,148,179,175,226,240,228,158,79,50, +21,73,253,130,78,169,8,112,212,178,138,41,84,72,154,10,188,213,14,24,168,68,172,91,243, +142,76,215,45,155,9,66,229,6,196,51,175,205,163,132,127,45,173,212,118,71,222,50,28,236, +74,196,48,246,32,35,133,108,251,178,7,4,244,236,11,185,32,186,134,195,62,5,241,236,217, +103,51,183,153,80,163,227,20,211,217,52,247,94,160,242,16,168,246,5,148,1,190,180,188,68, +120,250,73,105,230,35,208,26,218,105,106,126,76,126,81,37,179,72,132,83,58,148,251,49,153, +144,50,87,68,238,155,188,233,229,37,207,8,245,233,226,94,83,96,170,210,178,208,133,250,84, +216,53,232,212,102,130,100,152,217,168,135,117,101,112,90,138,63,98,128,41,68,222,124,165,137, +78,87,89,211,81,173,172,134,149,128,236,23,228,133,241,140,12,102,241,124,192,124,187,34,252, +228,102,218,97,11,99,175,98,188,131,180,105,47,58,255,175,39,22,147,172,7,31,184,109,17, +52,45,141,239,79,137,212,182,99,53,193,199,228,36,131,103,216,237,150,18,236,69,57,2,216, +229,10,248,157,119,9,209,165,150,193,244,31,149,170,130,202,108,73,174,144,205,22,104,186,172, +122,166,242,180,168,202,153,178,194,55,42,203,8,207,97,201,195,128,94,110,3,40,218,76,215, +106,25,237,210,211,153,76,121,139,0,34,86,154,212,24,209,254,228,217,205,69,163,145,198,1, +255,201,42,217,21,1,67,47,238,21,2,135,97,124,19,98,158,105,252,114,129,205,113,101,166, +62,171,73,207,113,75,206,58,117,167,79,118,234,126,100,255,129,235,97,253,254,195,155,103,191, +13,233,140,126,78,50,189,249,124,140,106,199,91,164,60,2,244,178,237,114,22,236,243,1,77, +240,0,16,139,103,207,153,80,91,23,159,142,212,152,10,97,3,209,188,167,13,190,155,191,171, +14,213,152,1,214,229,242,214,246,125,62,197,22,142,33,46,45,175,2,198,185,99,201,138,31, +112,151,222,12,86,137,26,43,33,27,1,7,13,216,253,139,22,194,161,164,227,207,210,146,210, +152,75,53,97,213,85,209,108,51,221,194,188,247,237,222,19,239,229,32,199,226,171,221,164,77, +129,136,28,83,26,238,235,102,36,76,59,121,30,168,172,251,106,104,243,88,70,6,71,43,38, +14,13,210,235,178,31,108,58,59,192,84,42,171,186,78,248,246,199,22,158,115,17,8,219,4, +96,34,10,167,77,49,181,91,3,160,13,34,13,71,93,205,155,135,120,86,213,112,76,156,134, +234,15,152,242,235,156,83,13,167,250,90,216,176,181,219,80,194,253,93,9,90,42,165,226,163, +251,183,19,71,84,154,49,99,50,35,78,206,118,91,117,113,182,77,33,107,40,113,46,37,207, +55,128,249,220,98,156,215,25,176,30,109,74,79,209,124,115,31,74,233,123,192,90,49,13,123, +156,54,237,202,91,188,2,219,181,222,61,82,182,87,2,212,196,76,36,149,200,151,181,18,128, +48,210,219,97,224,86,253,22,67,200,113,255,202,77,181,168,138,7,94,225,9,51,166,85,87, +59,29,238,240,47,110,32,2,73,129,226,160,127,248,227,71,105,227,17,182,152,185,65,159,24, +34,168,75,200,253,162,4,26,144,244,73,254,21,75,72,150,45,232,21,37,203,92,143,174,109 +}; diff --git a/rasmgr/rasmgr_rascontrol.cc b/rasmgr/rasmgr_rascontrol.cc new file mode 100644 index 0000000..3ec3822 --- /dev/null +++ b/rasmgr/rasmgr_rascontrol.cc @@ -0,0 +1,2187 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_rascontrol.cc + * + * MODULE: rasmgr + * CLASS: RasControl + * + * PURPOSE: + * Decodes, verifies and executes the commands + * + * COMMENTS: + * None + * +*/ + +using namespace std; + +#include "globals.hh" // DEFAULT_PORT +#include "rasmgr_rascontrol.hh" +#include "rasmgr_config.hh" +#include "rasmgr_master.hh" +#include "rasmgr_srv.hh" +#include "rasmgr_users.hh" +#include "rasmgr_error.hh" + +#ifndef RMANVERSION +#error "Please specify RAMNVERSION variable!" +#endif + +#ifndef COMPDATE +#error "Please specify the COMPDATE variable!" +/* +COMPDATE=`date +"%d.%m.%Y %H:%M:%S"` +and -DCOMPDATE="\"$(COMPDATE)\"" when compiling +*/ +#endif + +//#include "rasmgr_rascontrol_help.cc" + +#include "debug.hh" + +extern bool hostCmp( const char *h1, const char *h2); + +// function to migrate -xp parameters, only for v5.1, remove after +void migrateExtraParams(const char *orig, char *migrated); + +int RasControl::processRequest(char* reqMessage, char *answMessage) + { + ENTER( "RasControl::processRequest: enter. rascontrol msg: " << reqMessage ); + splitRequest(reqMessage); + + const char *command=argc ? token[0].take() : "#"; + + try + { + if(command) + { + TALK( "RasControl::processRequest: command=" << command ); + + if (isCommand(RASMGRCMD_HELLO)) helloCommand(); + else if(isCommand(RASMGRCMD_HELP)) helpCommand(); + else if(isCommand(RASMGRCMD_LIST)) listCommand(); + else if(isCommand(RASMGRCMD_DEFINE)) defineCommand(); + else if(isCommand(RASMGRCMD_REMOVE)) removeCommand(); + else if(isCommand(RASMGRCMD_CHECK)) checkCommand(); + else if(isCommand(RASMGRCMD_UP)) upCommand(); + else if(isCommand(RASMGRCMD_DOWN)) downCommand(); + else if(isCommand(RASMGRCMD_CHANGE)) changeCommand(); + else if(isCommand(RASMGRCMD_SAVE)) saveCommand(); + else if(isCommand(RASMGRCMD_EXIT)) exitCommand(); + else if(isCommand(RASMGRCMD_RESET)) resetCommand(); + +#ifdef INCLUDE_HIDDEN_COMMANDS + // both are unofficial, PB doesn't like them, but I do + else if(isCommand(RASMGRCMD_GRANT)) grantCommand(); + else if(isCommand(RASMGRCMD_REVOKE)) revokeCommand(); + //################ +#endif + + else if(isCommand("#")) sprintf(answBuffer," "); // comment + else + { + errorInCommand("Invalid command; try HELP." ); + cout << "Invalid command word: " << command << endl; + } + } + else + { + cout << "Error in request: " << reqMessage << endl; + errorInCommand("Error in request." ); + } + } + catch(RCError& e) + { + strcpy(answBuffer,"Error: "); + e.getString(answBuffer + strlen(answBuffer)); + cout << answBuffer << endl; + } + + LEAVE( "RasControl::processRequest: leave. answerBuffer: " << answBuffer ); + return prepareAnswer(answMessage); + } + +// set dirty flags +// used to differentiate between config file read and real changes thru rascontrol +void RasControl::setConfigDirty( bool isDirty ) + { + configDirty = isDirty; + } + +void RasControl::setAuthDirty( bool isDirty ) + { + authDirty = isDirty; + } + +int RasControl::prepareAnswer(char *answMessage) + { + sprintf(answMessage,"HTTP/1.1 200 OK\r\nContent-type: text/plain\r\nContent-length: %d\r\n\r\n%s",strlen(answBuffer)+1,answBuffer); + return strlen(answMessage)+1; + } + +//************************************************* +void RasControl::helloCommand() + { + sprintf(answBuffer,"Hello %s, you are connected to %s",authorization.getUserName(),config.getHostName()); + } +//************************************************* + +void RasControl::exitCommand() + { + bool configResult = false; + bool authResult = false; + + ENTER( "RasControl::exitCommand: enter" ); + + (void) strcpy( answBuffer, "Exiting rascontrol session." ); + + if (configDirty) + configResult = config.saveAltConfigFile(); + + if (authDirty) + authResult = authorization.saveAltAuthFile(); + + sprintf(answBuffer, "Exiting rascontrol session.%s\n%s%s%s%s%s%s%s%s%s%s", + ((argc <= 1) ? "" : " Ignoring extra parameters."), + + (!configDirty ? "" : "Configuration file was changed but not saved, storing rescue copy to " ), + (!configDirty ? "" : config.getAltConfigFileName() ), + (!configDirty ? "" : "..." ), + (!configDirty ? "" : (configResult ? "ok" : "failed") ), + (!configDirty ? "" : "\n" ), + + (!authDirty ? "" : "Authorisation file was changed but not saved, storing rescue copy to " ), + (!authDirty ? "" : authorization.getAltAuthFileName() ), + (!authDirty ? "" : "..." ), + (!authDirty ? "" : (authResult ? "ok" : "failed") ), + (!authDirty ? "" : "\n" ) ); + + // (!configDirty ? "" : "Configuration was changed but not saved, storing rescue copy to " << config.getAltConfigFileName() << "..." << (configResult ? "ok" : "failed") << "." << endl), + // (!authDirty ? "" : "Authorisation was changed but not saved, storing rescue copy to " << authorization.getAltAuthFileName() << "..." << (authResult ? "ok" : "failed") << "." << endl) ); + + LEAVE( "RasControl::exitCommand: leave. answBuffer=" << answBuffer ); + } + +void RasControl::listCommand() + { + const char *listwhat = argc==1 ? "xxx":token[1].take(); + + if (strcasecmp(listwhat,RASMGRCMD_SRV)==0) listRasServers(); + else if(strcasecmp(listwhat,"version")==0) listVersion(); + else if(strcasecmp(listwhat,"modus" )==0) listModus(); + else if(strcasecmp(listwhat,RASMGRCMD_USER )==0) listUsers(); + else if(strcasecmp(listwhat,RASMGRCMD_HOST)==0) listRasHosts(); + else if(strcasecmp(listwhat,"dbh")==0) listDBHosts(); + else if(strcasecmp(listwhat,"db")==0) listDatabases(); + +#ifdef INCLUDE_HIDDEN_COMMANDS + else if(strcasecmp(listwhat,"rights")==0) listRights();// unofficial +#endif + + else if(strcasecmp(listwhat,RASMGRCMD_HELP)==0) listHelp(); + + + else errorInCommand("Illegal LIST modifier. Try HELP LIST." ); + } + +void RasControl::listModus() + { + checkPermission(admR_info); + + checkUnexpectedTokens(); + + const char *modus = config.isTestModus() ? "test" :"normal"; + + sprintf(answBuffer,"rasmgr running in %s modus",modus); + } +void RasControl::listVersion() + { + checkPermission(admR_info); + + checkUnexpectedTokens(); + + // Version 1.1 is 1.0 with changes P.B. wanted, 06.03.2001 + // Version 1.2 is 1.1 with changes P.B. wanted, 17.04.2001 + // Version 1.3 with "list srv -x" and "-hostname" parameter + // Version 1.4 with migration of command line options from v5.0 to v5.1 + // Version 1.5 with new cmds, bug fixes in socket communication + sprintf(answBuffer,"rasdaman v%1f (rasmgr v1.5, compiled on %s)", RMANVERSION/1000, COMPDATE); + + +#ifdef INCLUDE_HIDDEN_COMMANDS + strcat(answBuffer," ('inside only'-version)"); +#endif + } + +void RasControl::listUsers() + { + checkPermission(admR_info); + + bool isRights = isFlag("-rights"); + + checkUnexpectedTokens(); + + sprintf(answBuffer,"List of defined users:"); + + for(int i=0;i<userManager.countUsers();i++) + { + User &user=userManager[i]; + //sprintf(answBuffer+strlen(answBuffer),"\r\n%d. %s (%ld)",i,u.getName(),u.getUserID()); + sprintf(answBuffer+strlen(answBuffer),"\r\n%2d. %-20s ",i+1,user.getName()); + + if(isRights) + { sprintf(answBuffer+strlen(answBuffer)," [%s]",authorization.convertAdminRights(user.getAdminRights())); + sprintf(answBuffer+strlen(answBuffer)," -[%s]",authorization.convertDatabRights(user.getDefaultDBRights())); + } + } + } + +void RasControl::listRasHosts() + { + checkPermission(admR_info); + checkUnexpectedTokens(); + + sprintf(answBuffer,"List of server hosts:\r\n"); + ServerHost::getDescriptionHeader(answBuffer+strlen(answBuffer)); + for(int i=0;i<hostmanager.countHosts();i++) + { + sprintf(answBuffer+strlen(answBuffer),"\r\n%2d. ",i+1); + hostmanager[i].getDescription(answBuffer+strlen(answBuffer)); + + } + } + +void RasControl::listDBHosts() + { + checkPermission(admR_info); + checkUnexpectedTokens(); + + sprintf(answBuffer,"List of database hosts:\r\n"); + sprintf(answBuffer+strlen(answBuffer)," Database Host Connection String Databases"); + + for(int i=0;i<dbHostManager.countHosts();i++) + { + DatabaseHost &dbh = dbHostManager[i]; + sprintf(answBuffer+strlen(answBuffer),"\r\n%2d. %-15s %-30s",(i+1),dbh.getName(),dbh.getConnectionString()); + + for(int j=0;j<dbManager.countDatabases();j++) + { + if(dbManager[j].isConnectedToDBHost(dbh.getName())) + sprintf(answBuffer+strlen(answBuffer)," %s",dbManager[j].getName()); + } + } + } + + +void RasControl::listRasServers() + { + checkPermission(admR_info); + int answLen=0; + int maxAnswLen=MAXMSGOUTBUFF-100; // if there are many servers, we could get an overflow + + bool fports= isFlag("-p"); + bool fexec = isFlag("-x"); + + if(fports && fexec) + { + // error, not both of them together! + } + + + const char *srvName = getValueOf(RASMGRCMD_SRV); + if(srvName) + { + checkUnexpectedTokens(); + RasServer &serv=getServer(srvName); + + sprintf(answBuffer,"Status of server %s\r\n",srvName); + if(fports) RasServer::getDescriptionPortHeader(answBuffer+strlen(answBuffer)); + else if(fexec) RasServer::getDescriptionExecHeader(answBuffer+strlen(answBuffer)); + else RasServer::getDescriptionHeader(answBuffer+strlen(answBuffer)); + strcat(answBuffer,"\r\n "); + if(fports) serv.getDescriptionPort(answBuffer+strlen(answBuffer)); + else if(fexec) serv.getDescriptionExec(answBuffer+strlen(answBuffer)); + else serv.getDescription(answBuffer+strlen(answBuffer)); + + return; + } + + bool isHost = isFlag("-host"); + const char *hostName = getValueOf("-host"); + bool listAll = false; + if (isHost) + { + checkUnexpectedTokens(); + checkNotNull(hostName,"host name"); + getServerHost(hostName); + + } + else + { + + // no -host hostName, so check for -all + listAll = isFlag("-all"); + checkUnexpectedTokens(); + // normally we should generate an error if -all is not, but for compatibility... + // put the error here + } + + if(hostName == NULL) sprintf(answBuffer,"List of servers:\r\n"); + else sprintf(answBuffer,"List of servers on host %s:\r\n",hostName); + + if(fports) RasServer::getDescriptionPortHeader(answBuffer+strlen(answBuffer)); + else if(fexec) RasServer::getDescriptionExecHeader(answBuffer+strlen(answBuffer)); + else RasServer::getDescriptionHeader(answBuffer+strlen(answBuffer)); + int crnt=1; + for(int i=0;i<rasManager.countServers();i++) + { + answLen=strlen(answBuffer); + + if(answLen >=maxAnswLen) + { sprintf(answBuffer+answLen,"\r\n(Answer too long, overflow!)"); + break; + } + + if(hostName) + if(!hostCmp(hostName,rasManager[i].getHostName())) + continue; + + sprintf(answBuffer+answLen,"\r\n%2d. ",crnt++); + + if(fports) rasManager[i].getDescriptionPort(answBuffer+strlen(answBuffer)); + else if(fexec) rasManager[i].getDescriptionExec(answBuffer+strlen(answBuffer)); + else rasManager[i].getDescription(answBuffer+strlen(answBuffer)); + } + + } + +void RasControl::listDatabases() + { + checkPermission(admR_info); + + const char* dbName = getValueOf("db"); + + if(dbName) + { + checkUnexpectedTokens(); + Database &db = getDatabase(dbName); + + sprintf(answBuffer,"List database: %s\r\n",dbName); + Database::getDescriptionHeader(answBuffer+strlen(answBuffer)); + strcat(answBuffer,"\r\n "); + db.getDescription(answBuffer+strlen(answBuffer)); + return; + } + + bool flagDBH = isFlag("-dbh"); + const char* dbhName = getValueOf("-dbh"); + + if(flagDBH) + { + checkUnexpectedTokens(); + checkNotNull(dbhName,"database host name"); + getDatabaseHost(dbhName); + } + + bool flagAll = isFlag("-all"); + // if flagALL is not, we should generate an error message, but we list all silently + checkUnexpectedTokens(); + + if(dbhName) + sprintf(answBuffer,"List of databases on host: %s\r\n",dbhName); + else sprintf(answBuffer,"List of databases:\r\n");// blanks necesary for nice output + Database::getDescriptionHeader(answBuffer+strlen(answBuffer)); + + int crnt=1; + for(int i=0;i<dbManager.countDatabases();i++) + { + Database &db=dbManager[i]; + + if(dbhName) + { bool found=false; + for(int j=0; j < db.countConnectionsToDBHosts();j++) + { if(hostCmp(db.getDBHostName(j),dbhName)) + { found=true;break;} + } + if(found == false) + continue; + } + sprintf(answBuffer+strlen(answBuffer),"\r\n%2d. ",crnt++); + db.getDescription(answBuffer+strlen(answBuffer)); + } + + } +//****************************************************** +void RasControl::defineCommand() + { + const char *what = argc==1 ? "xxx":token[1].take(); + + if (strcasecmp(what,RASMGRCMD_SRV)==0) + { + defineRasServers(); + configDirty = true; + } + else if(strcasecmp(what,RASMGRCMD_HOST)==0) + { + defineRasHosts(); + configDirty = true; + } + else if(strcasecmp(what,"dbh")==0) + { + defineDBHosts(); + configDirty = true; + } + else if(strcasecmp(what,"db")==0) + { + defineDatabases(); + configDirty = true; + } + else if(strcasecmp(what,RASMGRCMD_USER)==0) + { + defineUsers(); + authDirty = true; + } + else if(strcasecmp(what,RASMGRCMD_HELP)==0) + defineHelp(); + else + errorInCommand("Wrong DEFINE command"); + } + +void RasControl::defineUsers() + { + checkPermission(admR_acctrl); + + const char *userName = getValueOf(RASMGRCMD_USER); + const char *plainPass = getValueIfFlag("-passwd"); + const char *rString = getValueIfFlag("-rights",true); + int admRights =0; + int dtbRights =0; + checkUnexpectedTokens(); + checkNotNull(userName,"user name"); + + for(int i=0;userName[i];i++) + if(userName[i]==':') + { errorInCommand("Invalid character (':') in user name."); + return; + } + + if(rString) + { + admRights=authorization.convertAdminRights(rString); + dtbRights=authorization.convertDatabRights(rString); + if(admRights==-1 || dtbRights==-1) + { errorInCommand("Unknown right in command"); + return; + } + } + + if(userManager.insertNewUser(userName)==false) + { errorInCommand("User name should be unique."); + return; + } + + User &user = userManager[userName]; + + if(plainPass) user.changePTPassword(plainPass); + if(rString) { user.setAdminRights(admRights); + user.setDefaultDBRights(dtbRights); + } + + sprintf(answBuffer,"Defining user %s",userName); + } + +void RasControl::defineDBHosts() + { + checkPermission(admR_config); + + const char *dbhName=getValueOf("dbh"); + const char *connStr=getValueOf("-connect"); + + checkUnexpectedTokens(); + checkNotNull(dbhName,"database host name"); + checkNotNull(connStr,"connection string"); + + if(dbHostManager.insertNewHost(dbhName,connStr)) + { + sprintf(answBuffer,"Defining database host %s with connection string: %s",dbhName,connStr); + } + else + { + sprintf(answBuffer,"Database host %s already defined.",dbhName); + } + } + +void RasControl::defineDatabases() + { + checkPermission(admR_config); + + const char *dbName = getValueOf("db"); + const char *dbhName = getValueOf("-dbh"); + checkUnexpectedTokens(); + checkNotNull(dbName,"database name"); + // we don't accept hostless databases any more, define goes together with connect from now on + checkNotNull(dbhName,"database host name"); + getDatabaseHost(dbhName);// just check if valid + + bool defDB = dbManager.insertNewDatabase(dbName); + Database &db = dbManager[dbName]; + + bool connDB = db.connectToDBHost(dbhName); + + if(defDB) + { + sprintf(answBuffer,"Defining database %s on database host %s",dbName,dbhName); + } + else + { //database was already defined + if(connDB) sprintf(answBuffer,"Defining a mirrored instance of database %s on database host %s.",dbName,dbhName); + else sprintf(answBuffer,"Database %s already defined on database host %s.",dbName,dbhName); + } + + } + +void RasControl::defineRasHosts() + { + checkPermission(admR_config); + + const char *hostName=getValueOf(RASMGRCMD_HOST); + const char *netName =getValueOf("-net"); + const char *portStr =getValueOf("-port"); + checkUnexpectedTokens(); + + checkNotNull(hostName,"host name"); + + checkNotNull(netName,"-net parameter"); + + long listenPort; + if(portStr) listenPort=convertToULong(portStr,"port"); + else listenPort=DEFAULT_PORT; + + if(hostmanager.insertNewHost(hostName,netName,listenPort)==false) + { sprintf(answBuffer,"Error: Host %s already defined.",hostName); + return; + } + else + { sprintf(answBuffer,"Defining server host %s port=%d",hostName,listenPort); + } + } + +void RasControl::defineRasServers() + { + checkPermission(admR_config); + bool inConfigFile = authorization.isInConfigFile(); + + const char *serverName=getValueOf(RASMGRCMD_SRV); + const char *hostName =getValueOf("-host"); + const char *portStr =getValueOf("-port"); + const char *sTypeStr =getValueOf("-type"); + const char *dbhName =getValueOf("-dbh"); + + const char *autoRestart = getValueIfFlag("-autorestart"); + const char *countString = getValueIfFlag("-countdown"); + const char *execString = getValueIfFlag("-exec"); + bool isExtra = isFlag("-xp"); + // make the -xp string first + char extraString[300]; extraString[0]=0; + + if(isExtra) + { + bool found=false; + for(int i=0;i<argc;i++) + { + if(!found) + { if(strcasecmp(token[i].argv,"-xp")==0) found=true; + } + else + { strcat(extraString,token[i].take()); + strcat(extraString," "); + } + } + } + + checkUnexpectedTokens(); + + checkNotNull(serverName,"server name"); + + if(!sTypeStr) + { errorInCommand("Missing server type, specify one of r (RPC), h (HTTP) or n (RNP)."); + return; + } + + char serverType=0; + if(strcasecmp(sTypeStr,"r")==0) { serverType=SERVERTYPE_FLAG_RPC;} + if(strcasecmp(sTypeStr,"h")==0) { serverType=SERVERTYPE_FLAG_HTTP;} + if(strcasecmp(sTypeStr,"n")==0) { serverType=SERVERTYPE_FLAG_RNP;} + if(!serverType) + { errorInCommand("Illegal server type, use one of [r|h|n]."); + return; + } + + checkNotNull(hostName,"host name"); + checkNotNull(portStr,"port number"); + + if(inConfigFile == false) + { // we accept connectionless servers in config file, because we do not remove + // the connected servers when we remove a database host + checkNotNull(dbhName,"database host name"); + getDatabaseHost(dbhName);// just check if valid + + } + + long listenPort=convertToULong(portStr,"port"); + + if(autoRestart) + { + if(strcasecmp(autoRestart,"on")==0) + ; + else if(strcasecmp(autoRestart,"off")==0) + ; + else + { + errorInCommand("Incorrect autorestart option, use one of [on|off]."); + return; + } + } + + getServerHost(hostName);// just check + if(rasManager.insertNewServer(serverName,hostName,serverType,listenPort)==false) + { sprintf(answBuffer,"Error: server name already existing."); + return; + } + + if(inConfigFile && !dbhName) return; + + RasServer &srv = rasManager[serverName]; + srv.connectToDBHost(dbhName); + // if ok, write what you defined + + // we put this options in the config file with the change srv command, so it's ok that it could return before + if(countString) srv.changeCountDown(convertToULong(countString,"countdown")); + + if(extraString) srv.changeExtraParam(extraString); + + if(autoRestart) srv.changeAutoRestart( strcasecmp(autoRestart,"on")==0 ? true:false); + + if(execString) srv.changeExecutableName(execString); + + if(serverType==SERVERTYPE_FLAG_RPC) + sprintf(answBuffer,"Defining deprecated server %s of type RPC on host %s port=%#x",serverName,hostName,listenPort); + + if(serverType==SERVERTYPE_FLAG_HTTP) + sprintf(answBuffer,"Defining deprecated server %s of type HTTP on host %s port=%d",serverName,hostName,listenPort); + + if(serverType==SERVERTYPE_FLAG_RNP) + sprintf(answBuffer,"Defining server %s of type RNP on host %s port=%d",serverName,hostName,listenPort); + + } + +//---------------------------------------- +void RasControl::removeCommand() + { + const char *what = argc==1 ? "xxx":token[1].take(); + + if (strcasecmp(what,RASMGRCMD_SRV)==0) removeRasServers(); + + else if(strcasecmp(what,RASMGRCMD_HOST)==0) removeRasHosts(); + + else if(strcasecmp(what,"dbh")==0) removeDBHosts(); + + else if(strcasecmp(what,"db")==0) removeDatabases(); + + else if(strcasecmp(what,RASMGRCMD_USER)==0) removeUsers(); + + else if(strcasecmp(what,RASMGRCMD_HELP)==0) removeHelp(); + + else errorInCommand("Error in REMOVE command"); + } + +void RasControl::removeUsers() + { + checkPermission(admR_acctrl); + + const char *userName=getValueOf(RASMGRCMD_USER); + checkUnexpectedTokens(); + + checkNotNull(userName,"user name"); + + User &user=getUser(userName); + + if(user.getUserID()==0) + { errorInCommand("You cannot remove the rasadmin."); + return; + } + + if(userManager.removeUser(userName)==false) + { errorInCommand("Cannot remove user, don't know why"); + return; + } + sprintf(answBuffer,"User %s removed",userName); + } + +void RasControl::removeRasHosts() + { + checkPermission(admR_config); + + const char *hostName=getValueOf(RASMGRCMD_HOST); + checkUnexpectedTokens(); + + checkNotNull(hostName,"host name"); + + ServerHost &host = getServerHost(hostName); + + if(host.isInternal()==true) + { errorInCommand("You cannot remove the master rasmgr host."); + return; + } + + if(hostmanager.removeHost(hostName)==false) + { errorInCommand("Cannot remove host, it still has defined servers."); + return; + } + sprintf(answBuffer,"Host %s removed",hostName); + } + +void RasControl::removeRasServers() + { + checkPermission(admR_config); + + const char *srvName=getValueOf(RASMGRCMD_SRV); + checkUnexpectedTokens(); + + checkNotNull(srvName,"server name"); + + RasServer &srv = getServer(srvName); + + if(srv.isUp()) + { errorInCommand("Cannot remove the server, it's still up."); + return; + } + + if(rasManager.removeServer(srvName)==false) + { errorInCommand("Cannot remove the server, is probably still up."); + return; + } + sprintf(answBuffer,"Server %s removed",srvName); + } + +void RasControl::removeDBHosts() + { + checkPermission(admR_config); + + const char *dbhName=getValueOf("dbh"); + checkUnexpectedTokens(); + + checkNotNull(dbhName,"database host name"); + + getDatabaseHost(dbhName); + + if(dbHostManager.removeHost(dbhName)==false) + { errorInCommand("Cannot remove the database host, is probably still busy."); + return; + } + sprintf(answBuffer,"Database host %s removed",dbhName); + + // now remove all databases not connected to any database host + for(int i=0;i<dbManager.countDatabases();i++) + { + if(dbManager[i].countConnectionsToDBHosts()==0) + { dbManager.removeDatabase(dbManager[i].getName()); + i--; // you understand why + } + } + } +void RasControl::removeDatabases() + { + checkPermission(admR_config); + + const char *dbName = getValueOf("db"); + const char *dbhName = getValueOf("-dbh"); + checkUnexpectedTokens(); + + checkNotNull(dbName,"database name"); + + Database &db= getDatabase(dbName); + + checkNotNull(dbhName,"database host name"); + + getDatabaseHost(dbhName); + + if(db.isBusy()) + { errorInCommand("Database is busy."); + return; + } + + if(db.disconnectFromDBHost(dbhName) == false) + { sprintf(answBuffer,"No database %s on database host %s.",dbName,dbhName); + return; + } + + if(db.countConnectionsToDBHosts() !=0 ) + { + sprintf(answBuffer,"Database %s removed from database host %s",dbName,dbhName); + // this means disconnected + } + else + if(dbManager.removeDatabase(dbName)) + { sprintf(answBuffer,"Database %s removed from database host %s",dbName,dbhName); + // this time removed completely + } + else + { errorInCommand("Cannot remove the database, but why? (There shouldn't be any reason for that)"); + } + } + +//-------------------------------------------- +void RasControl::changeCommand() + { + const char *what = argc==1 ? "xxx":token[1].take(); + + if (strcasecmp(what,RASMGRCMD_USER)==0) + { + changeUser(); + authDirty = true; + } + else if(strcasecmp(what,RASMGRCMD_SRV)==0) + { + changeRasServer(); + configDirty = true; + } + else if(strcasecmp(what,"dbh")==0) + { + changeDBHost(); + configDirty = true; + } + else if(strcasecmp(what,"db")==0) + { + changeDB(); + configDirty = true; + } + else if(strcasecmp(what,RASMGRCMD_HOST)==0) + { + changeHost(); + configDirty = true; + } + else if(strcasecmp(what,RASMGRCMD_HELP)==0) + changeHelp(); + else + errorInCommand("Error in CHANGE command"); + + } +void RasControl::changeHost() + { + checkPermission(admR_config); + // we accept all this parameters, but we make all changes or none + const char *hostName=getValueOf(RASMGRCMD_HOST); + + const char *uselocal = getValueIfFlag("-uselocalhost"); + const char *newName = getValueIfFlag("-name"); + const char *newNet = getValueIfFlag("-net"); + const char *newPort = getValueIfFlag("-port"); + + checkUnexpectedTokens(); + + // This means changes were requested + bool chUseLoc = false; + bool chName = false; + bool chNet = false; + bool chPort = false; + + checkNotNull(hostName,"server host name"); + + ServerHost &sh= getServerHost(hostName); + + answBuffer[0]=0; + int changes = 0; + int port = 0; + + if(uselocal) + { + chUseLoc = true; + if(sh.isInternal()==false) + { errorInCommand("Option '-uselocalhost' is meaningfull only on master rasmgr host"); + return; + } + else + { + if (strcasecmp(uselocal,"on" )==0); // ok + else if(strcasecmp(uselocal,"off")==0); // ok + else { errorInCommand("Option '-uselocalhost' - wrong parameter"); + return; + } + } + } + + if(newName) + { + if(hostmanager.acceptChangeName(hostName,newName)==false) + { errorInCommand("The new name is not unique."); + return; + } + + chName = true; + } + + if(newNet ) { chNet = true; + if(sh.isUp()){ errorInCommand("You cannot change the network address of a RasMgr while it is up."); + return; + } + } + + if(newPort) { chPort = true; + if(sh.isUp()){ errorInCommand("You cannot change the listen port of a RasMgr while it is up."); + return; + } + + port = convertToULong(newPort,"port"); + } + + + + if(chUseLoc) { changes++; sh.useLocalHost( strcasecmp(uselocal,"on") ==0 ? true : false); } + + if(chName) { changes++; sh.changeName(newName); } + + if(chNet) { changes++; sh.changeNetName(newNet);} + + if(chPort) { changes++; sh.changeListenPort(port);} + + if(changes) sprintf(answBuffer,"Ready"); + else errorInCommand("Change what?"); + + } + +void RasControl::changeDBHost() + { + checkPermission(admR_config); + const char *dbhName = getValueOf("dbh"); + const char *newName = getValueIfFlag("-name"); + const char *connString = getValueIfFlag("-connect"); + checkUnexpectedTokens(); + + checkNotNull(dbhName,"database host name"); + + DatabaseHost &dbh= getDatabaseHost(dbhName); + + if(newName) + { + if(dbHostManager.acceptChangeName(dbhName,newName)==false) + { errorInCommand("The new name is not unique."); + return; + } + + dbh.changeName(newName); + } + else if(connString) dbh.changeConnectionString(connString); + + else { errorInCommand("Change what?"); + return; + } + + sprintf(answBuffer,"Ready"); + + } + +void RasControl::changeDB() + { + checkPermission(admR_config); + const char *dbName = getValueOf("db"); + const char *dbNewName = getValueIfFlag("-name"); + + checkUnexpectedTokens(); + + checkNotNull(dbName,"database name"); + + Database &db= getDatabase(dbName); + + if(dbNewName) + { + if(dbManager.acceptChangeName(dbName,dbNewName)==false) + { errorInCommand("The new name is not unique."); + return; + } + db.changeName(dbNewName); + } + else + { errorInCommand("Change what?"); + return; + } + + sprintf(answBuffer,"Ready"); + + } + +/** the version supporting only one server - the documented one +void RasControl::changeRasServer() + { + checkPermission(admR_config); + + const char *serverName = getValueOf(RASMGRCMD_SRV); + const char *newServerName = getValueIfFlag("-name"); + const char *portString = getValueIfFlag("-port"); + const char *autoRestart = getValueIfFlag("-autorestart"); + const char *countString = getValueIfFlag("-countdown"); + const char *dbhName = getValueIfFlag("-dbh"); + bool isExtra = isFlag("-xp"); + bool autorestartValue; + // make the -xp string first + char extraString[300]; extraString[0]=0; + if(isExtra) + { + bool found=false; + for(int i=0;i<argc;i++) + { + if(!found) + { if(strcasecmp(token[i].argv,"-xp")==0) found=true; + } + else + { strcat(extraString,token[i].take()); + strcat(extraString," "); + } + } + } + // so we touch all tokens + checkUnexpectedTokens(); + + checkNotNull(serverName,"server name"); + + RasServer &r= getServer(serverName); + + if(dbhName) getDatabaseHost(dbhName); + + if(autoRestart) + { if(strcasecmp(autoRestart,"on")==0) autorestartValue=true; + else if(strcasecmp(autoRestart,"off")==0) autorestartValue=false; + else { errorInCommand("Incorect autorestart option"); + return; + } + } + + if(newServerName && r.isUp()==false) ) + { errorInCommand("Cannot change the name of a running server."); + return; + } + + // from here, it cannot fail any more + if(autoRestart) r.changeAutoRestart(autorestartValue); + + if(dbhName) + { r.disconnectFromDBHost(); + r.connectToDBHost(dbhName); + } + if(newServerName) r.changeName(newServerName); + + if(portString) r.changePort(strtoul(portString,(char**)NULL,0)); + + if(countString) r.changeCountDown(strtoul(countString,(char**)NULL,0)); + + if(isExtra) r.changeExtraParam(extraString); + + sprintf(answBuffer,"Ready"); + + } +*/ + +// The version supporting change srv [s| -hos h | -all ] { rest of params } +void RasControl::changeRasServer() + { + checkPermission(admR_config); + +// first, see if params are ok + + // only if "srv name": const char *newServerName = getValueIfFlag("-name"); + // only if "srv name": const char *portString = getValueIfFlag("-port"); + const char *autoRestart = getValueIfFlag("-autorestart"); + const char *countString = getValueIfFlag("-countdown"); + const char *execString = getValueIfFlag("-exec"); + const char *dbhName = getValueIfFlag("-dbh"); + bool isExtra = isFlag("-xp"); + int countChanges = 0; + // make the -xp string first + char extraString[300]; extraString[0]=0; + + if(isExtra) + { + bool found=false; + for(int i=0;i<argc;i++) + { + if(!found) + { if(strcasecmp(token[i].argv,"-xp")==0) found=true; + } + else + { strcat(extraString,token[i].take()); + strcat(extraString," "); + } + } + countChanges++; + } + // so we touch all tokens + + + if(dbhName) { getDatabaseHost(dbhName); countChanges++; } + + if(autoRestart) + { if(strcasecmp(autoRestart,"on")==0) ; + else if(strcasecmp(autoRestart,"off")==0) ; + else { errorInCommand("Incorect autorestart option"); + return; + } + countChanges++; + } + if(countString) countChanges++; + if(execString) countChanges++; + + // ok, the existing params are ok + + const char *serverName = getValueOf(RASMGRCMD_SRV); + if(serverName) + { + // permitted only here + const char *newType = getValueIfFlag("-type"); + const char *newServerName = getValueIfFlag("-name"); + const char *portString = getValueIfFlag("-port"); + + checkUnexpectedTokens(); + RasServer &r = getServer(serverName); + char serverType=0; + if(newType) + { + if(strcasecmp(newType,"r")==0) { serverType=SERVERTYPE_FLAG_RPC;} + if(strcasecmp(newType,"h")==0) { serverType=SERVERTYPE_FLAG_HTTP;} + if(strcasecmp(newType,"n")==0) { serverType=SERVERTYPE_FLAG_RNP;} + if(!serverType) { errorInCommand("Unknown server type."); + return; + } + else countChanges++; + } + if(newServerName) + { + if(r.isUp()) { errorInCommand("Cannot change the name of a running server."); + return; + } + if(rasManager.acceptChangeName(serverName,newServerName)==false) + { errorInCommand("The new name is not unique."); + return; + } + countChanges++; + } + if(portString) countChanges++; + //------------ + if(countChanges==0) { errorInCommand("Change what?"); + return; + } + + if(newType) r.changeType(serverType); + if(portString) r.changePort(convertToULong(portString,"port")); + + changeRasServer(serverName,dbhName,countString,(isExtra ? extraString : NULL), autoRestart, execString); + + if(newServerName) r.changeName(newServerName); + + sprintf(answBuffer,"Ready"); + return; + } + + const char *hostName = getValueOf("-host"); + if(hostName) + { + ServerHost &host=getServerHost(hostName); + checkUnexpectedTokens(); + } + + bool flagAll = isFlag("-all"); + checkUnexpectedTokens(); + + if(hostName == NULL && flagAll == false) + { errorInCommand("No server specified"); + return; + } + + if(countChanges==0) { errorInCommand("Change what?"); + return; + } + + for(int i=0;i<rasManager.countServers();i++) + { + serverName = rasManager[i].getName(); + if(hostName) + if(!hostCmp(hostName,rasManager[i].getHostName())) + continue; + changeRasServer(serverName,dbhName,countString,(isExtra ? extraString : NULL), autoRestart, execString); + } + + sprintf(answBuffer,"Ready"); + + } + +void RasControl::changeRasServer(const char *serverName, const char *dbhName, const char *countString, const char *extraString, const char *autoRestart, const char* execName) + { + // called only by by changedServer, after verification of parameters + RasServer &r = getServer(serverName); + + if(dbhName) + { r.disconnectFromDBHost(); + r.connectToDBHost(dbhName); + } + + if(countString) r.changeCountDown(convertToULong(countString,"countdown")); + + if(extraString) r.changeExtraParam(extraString); + + if(autoRestart) r.changeAutoRestart( strcasecmp(autoRestart,"on")==0 ? true:false); + + if(execName) r.changeExecutableName(execName); + } + +void RasControl::changeUser() + { + const char *userName = getValueOf(RASMGRCMD_USER); + const char *newName = getValueIfFlag("-name"); + const char *plainPass = getValueIfFlag("-passwd"); + const char *encrPass = getValueIfFlag("-encrPasswd"); + const char *rString = getValueIfFlag("-rights",true); + checkUnexpectedTokens(); + + // encr passwd has priority + const char *newPass = encrPass ? encrPass:plainPass; + bool takeEncrPass = encrPass ? true:false; + + bool okChName =false; + bool okChPasswd=false; + bool okChRights=false; + int admRights = 0; + int dtbRights = 0; + + //for name change you need acces Control (you cannot change rasadmin) + //for passwd change you need also acces Control, or, without, only yourself.(only rasadmin can change his passwd) + if(userName==0) + { errorInCommand("You should provide a valid user name."); + return; + } + User &u=getUser(userName); + + if(newName!=0) + { checkPermission(admR_acctrl); + if(strcmp(userName,"rasadmin")==0) + { errorInCommand("You cannot change rasadmin's name."); + return; + } + if(userManager.acceptChangeName(userName,newName)==false) + { errorInCommand("The new name is not unique."); + return; + } + + okChName=true; + } + + if(newPass!=0) + { if (strcmp(userName ,authorization.getUserName())==0) okChPasswd=true; // may change your own passwd + + else if(strcmp("rasadmin",authorization.getUserName())==0) okChPasswd=true; // rasadmin may change all passwd + + else if(authorization.hasAdminRights(admR_acctrl)) + { if (strcmp("rasadmin",userName)!=0) okChPasswd=true; // may change all passwd, except rasadmin's one + else + { errorInCommand("You don't have permission to change rasadmin's password."); + return; + } + } + else { errorInCommand("You don't have permission for this operation."); + return; + } + + } + if(rString!=0) + { checkPermission(admR_acctrl); + admRights=authorization.convertAdminRights(rString); + dtbRights=authorization.convertDatabRights(rString); + + if(admRights==-1 || dtbRights==-1) + { errorInCommand("Unknown right in command"); + return; + } + + okChRights = true; + } + + // we don't talk so much, just say 'Ready' answBuffer[0]=0; + int changes =0; + + if(okChName) + { u.changeName(newName); + //sprintf(answBuffer,"User name changed: %s is now %s\r\n",userName,newName); + changes++; + } + if(okChPasswd) + { if(answBuffer[0]!=0) strcat(answBuffer,"\r\n"); + + if(takeEncrPass) u.changePassword(newPass); + else u.changePTPassword(newPass); + //sprintf(answBuffer+strlen(answBuffer),"Password for user %s was changed\r\n",userName); + changes++; + } + if(okChRights) + { User &user = u; // this is v1.1, is will not live long... + const char *warning = user.getUserID()!=0 ? "": (admRights=admR_full,"You cannot change rasadmin's system rights\r\n"); + //^sorry, trick + user.setAdminRights(admRights); + user.setDefaultDBRights(dtbRights); + //sprintf(answBuffer+strlen(answBuffer),"%sRights of user %s set to [%s]-[%s]\r\n", warning,userName,authorization.convertAdminRights(admRights),authorization.convertDatabRights(dtbRights)); + changes++; + } + + if(okChRights || okChPasswd) masterCommunicator.commitAuthFile(); + + if(changes==0) errorInCommand("Change what?"); + else strcpy(answBuffer,"Ready"); + + } +//--------------------------------------------- +void RasControl::upCommand() + { + const char *what = argc==1 ? "xxx":token[1].take(); + + if(strcasecmp(what,RASMGRCMD_SRV)==0) upRasServers(); + + else if(strcasecmp(what,RASMGRCMD_HELP)==0) upHelp(); + + else errorInCommand("Error in UP command"); + + } + +void RasControl::upRasServers() + { + ENTER( "RasControl::upRasServers()" ); + + checkPermission(admR_sysup); + + const char *srvName =getValueOf(RASMGRCMD_SRV); + + if(srvName) // up a specified server + { + bool flagForce = config.isDebugSupport() ? isFlag("-force") : false; + bool flagDebug = config.isDebugSupport() ? isFlag("-debug") : false; + + checkUnexpectedTokens(); + + TALK( "server name: " << srvName ); + RasServer &r=getServer(srvName); + + // just debug + if(flagForce && r.isUp()) + { r.forceAvailable(); + sprintf(answBuffer,"Server forced to be available again"); + LEAVE( "RasControl::upRasServers() -- " << answBuffer ); + return; + } + + if(r.isUp()) + { errorInCommand("Server is already up."); + LEAVE( "RasControl::upRasServers() -- " << answBuffer ); + return; + } + int rasp = flagDebug ? r.startServerInDebugger(answBuffer):r.startServer(); + + switch(rasp) + { case 0: if(flagDebug==false) sprintf(answBuffer,"Server started"); + break; + case -1: errorInCommand("Server is not connected to a database host."); + break; + case -2: errorInCommand("License violation."); + break; + case -3: + case -4: errorInCommand("Cannot contact the slave rasmgr."); + break; + case -5: errorInCommand("Server host is down."); + break; + } + LEAVE( "RasControl::upRasServers()" ); + return; + } + + const char *hostName=getValueOf("-host"); + if(hostName) + { + checkUnexpectedTokens(); + + int rasp = upAllServersOnHost(hostName); + switch(rasp) + { + //case -1: errorInCommand("Wrong server host name."); break; + case -2: errorInCommand("Server host is down."); break; + + default: sprintf(answBuffer,"Started %d servers on host %s",rasp,hostName); + break; + + } + LEAVE( "RasControl::upRasServers()" ); + return; + } + + bool flagAll = isFlag("-all"); + + if(flagAll) + { + checkUnexpectedTokens(); + + int countUpSrv = 0; + int countUpHosts = 0; + for(int i=0;i<hostmanager.countHosts();i++) + { + int rasp = upAllServersOnHost(hostmanager[i].getName()); + + if(rasp>=0) { countUpSrv+=rasp;countUpHosts++;} + + } + sprintf(answBuffer,"Started %d servers on %d hosts",countUpSrv,countUpHosts); + LEAVE( "RasControl::upRasServers() -- " << answBuffer ); + return; + } + + errorInCommand("Up what?"); + + LEAVE( "RasControl::upRasServers()" ); + } + +int RasControl::upAllServersOnHost(const char*hostName) + { + //return value is negativ =>error + //return value is positiv =>nr of started servers + + ServerHost &sh= getServerHost(hostName); + + if(sh.checkStatus() == false) + { //errorInCommand("Server host is down."); + return -2; + } + + int countStart=0; + int alreadyUp = sh.getStartedServers(); + + for(int i=0;i<rasManager.countServers();i++) + { + RasServer &r=rasManager[i]; + if(r.isUp()) continue; + if(!hostCmp(hostName,r.getHostName())) continue; + + if(r.startServer()==0) countStart++; + } + // sprintf(answBuffer,"Started %d servers on host %s",countStart,hostName); + return countStart; + } +//-------------------------------------------------------- +void RasControl::downCommand() + { + const char *what = argc==1 ? "xxx":token[1].take(); + + if (strcasecmp(what,RASMGRCMD_HOST)==0) downRasHosts(); + + else if(strcasecmp(what,RASMGRCMD_SRV)==0) downRasServers(); + + else if(strcasecmp(what,RASMGRCMD_HELP)==0) downHelp(); + + else errorInCommand("Error in DOWN command"); + + } + +void RasControl::downRasServers() + { + checkPermission(admR_sysup); + + const char *srvName=getValueOf(RASMGRCMD_SRV); + + if(srvName) + { + bool killFlag=isFlag("-kill"); + bool forceFlag=isFlag("-force"); + checkUnexpectedTokens(); + + RasServer &r= getServer(srvName); + if(r.isUp()==false && r.isStarting()==false) + { errorInCommand("Server is already down."); + return; + } + + int res=killFlag ? r.killServer():r.downServer(forceFlag); + + if(res<0) + { errorInCommand("Cannot contact the slave rasmgr."); + return; + } + + if(killFlag) sprintf(answBuffer,"Server %s was killed",srvName); + else sprintf(answBuffer,"Server %s is going down",srvName); + + return; + } + + const char *hostName=getValueOf("-host"); + + if(hostName) + { + checkUnexpectedTokens(); + + int rasp=downAllServersOnHost(hostName); + switch(rasp) + { +// case -1 : errorInCommand("Error in server host name."); +// break; + case -2 : errorInCommand("Server host is down."); + break; + case 0 : sprintf(answBuffer,"On host %s all servers are already down",hostName); + break; + default : // means >0 + sprintf(answBuffer,"%d servers on host %s are going down",rasp,hostName); + break; + } + return; + } + + bool flagAll = isFlag("-all"); + if(flagAll) + { + checkUnexpectedTokens(); + + int countDownSrv = 0; + int countDownHosts = 0; + for(int i=0;i<hostmanager.countHosts();i++) + { + int rasp = downAllServersOnHost(hostmanager[i].getName()); + + if(rasp>=0) { countDownSrv+=rasp;countDownHosts++;} + + } + sprintf(answBuffer,"%d servers on %d hosts are going down",countDownSrv,countDownHosts); + return; + } + + errorInCommand("Down what?"); + } + +int RasControl::downAllServersOnHost(const char *hostName) + { + ServerHost &sh= getServerHost(hostName); + + if(sh.checkStatus()==false) + { //errorInCommand("Server host is down."); + return -2; + } + + int countDownServers=0; + for(int i=0;i<rasManager.countServers();i++) + { + RasServer &r=rasManager[i]; + if(r.isUp()==false && r.isStarting()==false) continue; + + if(!hostCmp(hostName,r.getHostName())) + continue; + r.downServer(false); + countDownServers++; + } + return countDownServers; + } + +void RasControl::downRasHosts() + { // this is the new stop command, but this version is incipient + checkPermission(admR_sysup); + + const char *hostName=getValueOf(RASMGRCMD_HOST); + + if(hostName) + { + checkUnexpectedTokens(); + int rasp = downRasHost(hostName); + switch(rasp) + { + case 0: sprintf(answBuffer,"Server host %s is going down", hostName); + break; +// case -1: errorInCommand("Wrong server host name."); +// break; + case -2: errorInCommand("Sorry, you should down all servers on this host first."); + break; + case -3: errorInCommand("Sorry, you should down all slave hosts first, the master should be the last one."); + break; + } + return; + } + + bool flagAll = isFlag("-all"); + + if(flagAll) + { + checkUnexpectedTokens(); + if(rasManager.countUpServers()!=0) + { errorInCommand("Sorry, you should down all servers first."); + return; + } + + for(int i=1;i<hostmanager.countHosts();i++) + { //yes, from 1, master the last! + downRasHost(hostmanager[i].getName()); + } + //now the master + masterCommunicator.shouldExit(); + sprintf(answBuffer,"All hosts are down, bye"); + return; + } + + errorInCommand("Down what?"); + return; + } + +int RasControl::downRasHost(const char *hostName) + { + ServerHost &sh= getServerHost(hostName); + + if(sh.getStartedServers()) + { //errorInCommand("Sorry, you should down all servers on this host first."); + return -2; + } + + if(sh.isInternal() && hostmanager.countUpHosts()>1) + {// errorInCommand("Sorry, you should down all slave hosts first, the master should be the last one."); + return -3; + } + + if(sh.isInternal()) masterCommunicator.shouldExit(); + else { // later, sorry + sh.downHost(); + } + return 0; + } + +//---------------------------------------------------- +void RasControl::saveCommand() + { + checkPermission(admR_config); + checkUnexpectedTokens(); + + bool resultConf = config.saveOrigConfigFile(); + bool resultAuth = authorization.saveOrigAuthFile(); + + // this has been done by the lines above: -- PB 2003-jun-08 + // masterCommunicator.commitChanges(); + + sprintf(answBuffer,"Saving configuration file...%s. Saving authorization file...%s.", + (resultConf==true ? "ok" : "failed"), + (resultAuth==true ? "ok" : "failed") ); + } + +//---------------------------------------------------- +void RasControl::resetCommand() + { + checkPermission(admR_config); + checkUnexpectedTokens(); + + if(config.isTestModus()==false) + { errorInCommand("This operation is possible only in test modus."); + return; + } + + if(rasManager.reset()==false) + { errorInCommand("Resetting not possible, there are active servers."); + return; + } + + dbHostManager.reset(); + dbManager.reset(); + + hostmanager.reset(); + hostmanager.insertInternalHost(); + + userManager.reset(); + userManager.loadDefaults(); + + VLOG <<"rasmgr was succesfully reset."<<std::endl; + sprintf(answBuffer,"rasmgr was succesfully reset."); + } + +//----------------------------------------- + +void RasControl::checkCommand() + { + const char *what = argc==1 ? "xxx":token[1].take(); + + if (strcasecmp(what,RASMGRCMD_HOST)==0) checkRasHosts(); + + else if(strcasecmp(what,RASMGRCMD_HELP)==0) checkHelp(); + + else errorInCommand("Error in CHECK command"); + + } + +void RasControl::checkRasHosts() + { + checkPermission(admR_sysup); + + const char *hostName=getValueOf(RASMGRCMD_HOST); + + if(hostName) + { + checkUnexpectedTokens(); + + ServerHost &sh=getServerHost(hostName); + + sh.checkStatus(); + sprintf(answBuffer,"The host %s is %s",sh.getName(),(sh.isUp() ? "up":"down")); + return; + } + + bool flagAll = isFlag("-all"); + + if(flagAll) + { + checkUnexpectedTokens(); + for(int i=0;i<hostmanager.countHosts();i++) + { hostmanager[i].checkStatus(); + } + sprintf(answBuffer,"Checking status of all hosts ... done"); + return; + } + errorInCommand("Check what?"); + } + + + + +//###---###---###---###---###---###---###---###---###---###---### +void RasControl::errorInCommand(const char *errText) + { + sprintf(answBuffer,"Error: %s",errText); + } + +bool RasControl::isCommand( const char *key ) + { + if(argc) + return (strcasecmp(token[0].argv,key)==0) ? true:false; + else + return (strcmp("#",key)==0) ? true:false; + } + +void RasControl::splitRequest(const char* reqMessage) + { + argc=0; + commandBuffer[0]=' '; + strncpy(commandBuffer+1,reqMessage,MAXMSG-1); + commandBuffer[MAXMSG]=0; + + TALK("RasControl::splitRequest: (a) Com="<<commandBuffer); + + char *temp = commandBuffer; + + for(argc=0;argc<30;argc++) + { + token[argc].set(strtok(temp," \r\n\t\0")); + temp=NULL; + + if(token[argc].argv == NULL) break; + if(token[argc].argv[0] == '#') + { token[argc].argv=NULL; // from here, comment + break; + } + } + + } + +bool RasControl::isFlag(const char *flag, int pos) + { + if(pos<0) // doesn't matter + { for(int i=1;i<argc;i++) // flags are from 1->, 0 is the command itself + { + if(strcasecmp(flag,token[i].argv)==0) + { + token[i].used=true; + return true; + } + } + return false; + } + if(pos>1 && pos<argc) + { + if(strcasecmp(flag,token[pos].argv)==0) + { + token[pos].used=true; + return true; + } + } + return false; + } + +const char * RasControl::getValueOf(const char *flag, bool acceptMinus) + { + //no,not always if(flag[0]!='-') return NULL; // all flags start with '-' + + for(int i=1;i<argc-1;i++) + { + if(strcasecmp(flag,token[i].argv)==0) + { + token[i].used=true; + + if(acceptMinus) + { if(token[i+1].argv[0]=='-' && token[i+1].argv[1]!=0) return NULL; // values don't start with '-' (we don't have minus-signs) + } // except in void right string + else + { if(token[i+1].argv[0]=='-') return NULL; + } + token[i+1].used=true; + return token[i+1].argv; + } + } + + if(strcasecmp(flag,token[argc-1].argv)==0) token[argc-1].used=true; + + return NULL; // not found; + } +const char * RasControl::getValueIfFlag(const char *flag,bool acceptMinus) + { + if(isFlag(flag)==false) return NULL; + + const char *r = getValueOf(flag,acceptMinus); + + if(r==NULL) + { + static char temp[30]; + sprintf(temp,"'%s' value",flag); + throw RCErrorMissingParam(temp); + } + return r; + } + +void RasControl::checkUnexpectedTokens() + { + for(int i=2;i<argc;i++) + { + if(token[i].used == false) throw RCErrorUnexpToken(token[i].argv); + } + } +void RasControl::checkPermission(int reqRights) + { + if(authorization.hasAdminRights(reqRights)==false) + throw RCErrorNoPermission(); + } + +void RasControl::checkNotNull(const char *ptr, const char *what) + { + if(ptr==NULL) throw RCErrorMissingParam(what); + } + +RasServer& RasControl::getServer(const char *name) + { + RasServer &srv = rasManager[name]; + if(srv.isValid()==false) throw RCErrorInvalidName("server"); + return srv; + } +Database& RasControl::getDatabase(const char *name) + { + Database &db = dbManager[name]; + if(db.isValid()==false) throw RCErrorInvalidName(RASMGRCMD_DATABASE); + return db; + } +DatabaseHost& RasControl::getDatabaseHost(const char *name) + { + DatabaseHost &dbh = dbHostManager[name]; + if(dbh.isValid()==false) throw RCErrorInvalidName("database host"); + return dbh; + } +ServerHost& RasControl::getServerHost(const char *name) + { + TALK( "RasControl::getServerHost( " << name << " )" ); + ServerHost &host = hostmanager[name]; + if(host.isValid()==false) throw RCErrorInvalidName("server host"); + return host; + } +User& RasControl::getUser(const char *name) + { + User &user = userManager[name]; + if(user.isValid()==false) throw RCErrorInvalidName(RASMGRCMD_USER); + return user; + } + +unsigned long RasControl::convertToULong(const char *stringValue,const char *what) + { + char *end; + unsigned long rasp = strtoul(stringValue,&end,0); + + if(strlen(end)!=0) throw RCErrorIncorNumberValue(what); + + return rasp; + } + +void RasControl::Token::set(char *p) + { + argv = p; + used = false; + } +const char* RasControl::Token::take() + { + used=true; + return argv; + } + +//######### OBSOLETE in v1.1, but keep them, because could be usefull one day ################################ + +#ifdef INCLUDE_HIDDEN_COMMANDS +// removed in version 1.1, reimplemented in v1.3, but kept unofficial + +void RasControl::listRights() + { + const char *userName = getValueOf("-user"); + + if(userName==NULL) + { + errorInCommand("You have to provide an user name"); + //checkPermission(admR_info); + // list global Initial Rights + // gi rights hidden in version 1.1 sprintf(answBuffer,"Initial global rights are set to: [%s]-[%s]:",authorization.convertGlobalInitAdminRights(), + // authorization.convertGlobalInitDatabRights()); + return; + } + User &user=userManager[userName]; + + if(strcmp(userName,authorization.getUserName())!=0) + { checkPermission(admR_info); + } + if(user.isValid()==false) + { errorInCommand("Unknown user"); + //sprintf(answBuffer,"Unknown user %s",userName); + return; + } + //std::cout<<"list rights of user "<<userName<<" "<<user.getAdminRights()<<" "<<user.getDefaultDBRights()<<std::endl; + sprintf(answBuffer,"List of rights defined for user %s",userName); + sprintf(answBuffer+strlen(answBuffer),"\r\n administrative rights: [%s]",authorization.convertAdminRights(user.getAdminRights())); + sprintf(answBuffer+strlen(answBuffer),"\r\n default database rights: [%s]",authorization.convertDatabRights(user.getDefaultDBRights())); + + //sprintf(answBuffer+strlen(answBuffer),"\r\n List of defined rights on databases:"); + + + for(int i=0;i<dbManager.countDatabases();i++) + { + const char *dbName=dbManager[i].getName(); + + if(user.isTrusteeOn(dbName)) + { + int rights=user.getEffectiveDatabaseRights(dbName); + sprintf(answBuffer+strlen(answBuffer),"\r\n database %-20s [%s]",dbName,authorization.convertDatabRights(rights)); + } + } + + } + + + +void RasControl::grantCommand() + { + if(argc==1 || strcasecmp(token[1].argv,RASMGRCMD_HELP)==0) + { //grantHelp(); + return; + } + checkPermission(admR_acctrl); + + const char *rString = token[1].take(); + + const char *userName= getValueOf("-user"); + + const char *dbName = getValueOf("-db"); + + bool gi=isFlag("-gi"); + + int admRights=authorization.convertAdminRights(rString); + int dtbRights=authorization.convertDatabRights(rString); + + if(admRights==-1 || dtbRights==-1) + { errorInCommand("Unknown right in command"); + return; + } + + if(userName!=NULL) + { User &user=userManager[userName]; + if(user.isValid()==false) + { errorInCommand("Unknown user"); + return; + } + + if(dbName!=NULL) + { if(user.setDatabaseRights(dbName,dtbRights)==false) + errorInCommand("Unknown database name"); + + else sprintf(answBuffer,"Granted %s to user %s for database %s",rString,userName,dbName); + } + else + { const char *warning = user.getUserID()!=0 ? "": (admRights=admR_full,"You cannot change rasadmin's system rights\r\n"); + //^sorry, trick + user.setAdminRights(admRights); + user.setDefaultDBRights(dtbRights); + sprintf(answBuffer,"%sGranted [%s]-[%s] to user %s",warning,authorization.convertAdminRights(admRights),authorization.convertDatabRights(dtbRights),userName); + } + } + else + { if(gi) + { authorization.setGlobalInitAdminRights(admRights); + authorization.setGlobalInitDatabRights(dtbRights); + sprintf(answBuffer,"Set global initial rights to [%s]-[%s]",authorization.convertAdminRights(admRights),authorization.convertDatabRights(dtbRights)); + } + else + { errorInCommand("Error in GRANT command"); + } + } + } + +// removed in version 1.1 +void RasControl::revokeCommand() + { + if(argc==1 || strcasecmp(token[1].argv,RASMGRCMD_HELP)==0) + { //revokeHelp(); + return; + } + checkPermission(admR_acctrl); + + const char *userName= token[1].take(); + + const char *dbName = getValueOf("-db"); + + User &user=userManager[userName]; + if(user.isValid()==false) + { errorInCommand("Unknown user"); + return; + } + + if(dbName!=NULL) + { + if(user.removeDatabaseRights(dbName)==false) + errorInCommand("Unknown database name"); + + else sprintf(answBuffer,"Rights of user %s for database %s revoked",userName,dbName); + } + else + { errorInCommand("Error in REVOKE command"); + } + } + +#endif + +/* +-------- +from list servers + + bool fdbh = isFlag("-dbh",2); + const char *dbhName = getValueOf("-dbh"); + if(fdbh) listRasServersOnDBH(dbhName); + + else if(fdb) listRasServersDatabase(dbName); + + else if(hostName) + { + sprintf(answBuffer,"List of servers on host %s:",hostName); + + int crnt=0; + for(int i=0;i<rasManager.countServers();i++) + { + answLen=strlen(answBuffer); + + if(answLen >=maxAnswLen) + { sprintf(answBuffer+answLen,"\r\n(Answer too long, overflow danger!)"); + break; + } + if(strcmp(hostName,rasManager[i].getHostName())!=0) continue; + + sprintf(answBuffer+answLen,"\r\n%2d. ",crnt++); + + if(fports) rasManager[i].getDescriptionPort(answBuffer+strlen(answBuffer)); + else rasManager[i].getDescription(answBuffer+strlen(answBuffer)); + } + } + else + + +--------- +from list db + if(isFlag("-dbh",2)) + { + const char *dbhName=getValueOf("-dbh"); + + if(dbhName) + sprintf(answBuffer,"List of databases on host: %s",dbhName); + else sprintf(answBuffer,"List of databases:"); + + int crnt=0; + for(int i=0;i<dbManager.countDatabases();i++) + { + Database &refTemp=dbManager[i]; + + if( dbhName && refTemp.isConnectedToDBHost(dbhName) ) + { sprintf(answBuffer+strlen(answBuffer),"\r\n%2d. %s ",crnt++,refTemp.getName()); + } + + if( !dbhName ) + { sprintf(answBuffer+strlen(answBuffer),"\r\n%2d. %s",crnt++,refTemp.getName()); + + for(int j=0;j<refTemp.countConnectionsToDBHosts();j++) + sprintf(answBuffer+strlen(answBuffer),"\r\n %s ",refTemp.getDBHostName(j)); + } + } + } + else if(isFlag("-srv",2)) + { + const char *srvName=getValueOf("-srv"); + + if(srvName) + sprintf(answBuffer,"List of databases accesible by server: %s",srvName); + else sprintf(answBuffer,"List of databases:"); + + int crnt=0; + for(int i=0;i<dbManager.countDatabases();i++) + { + Database &refTemp=dbManager[i]; + + if( srvName && refTemp.isConnectedToRasServer(srvName) ) + { sprintf(answBuffer+strlen(answBuffer),"\r\n%2d. %s ",crnt++,refTemp.getName()); + } + + if( !srvName ) + { sprintf(answBuffer+strlen(answBuffer),"\r\n%2d. %s",crnt++,refTemp.getName()); + + for(int j=0;j<refTemp.countConnectionsToRasServers();j++) + sprintf(answBuffer+strlen(answBuffer),"\r\n %s ",refTemp.getRasServerName(j)); + + } + } + } + + else + { sprintf(answBuffer,"List of databases:"); + int crnt=0; + for(int i=0;i<dbManager.countDatabases();i++) + { + Database &refTemp=dbManager[i]; + + sprintf(answBuffer+strlen(answBuffer),"\r\n%2d. %s ",crnt++,refTemp.getName());//,refTemp.isValid()); + + sprintf(answBuffer+strlen(answBuffer),"(%dw + %dr) ",refTemp.getWriteTransactionCount(),refTemp.getReadTransactionCount()); + + } + } + +--------- + +*/ + diff --git a/rasmgr/rasmgr_rascontrol.hh b/rasmgr/rasmgr_rascontrol.hh new file mode 100644 index 0000000..2f5412d --- /dev/null +++ b/rasmgr/rasmgr_rascontrol.hh @@ -0,0 +1,166 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_rascontrol.hh + * + * MODULE: rasmgr + * CLASS: RasControl + * + * PURPOSE: + * Decodes, verifies and xecutes the commands + * + * COMMENTS: + * None + * +*/ +#ifndef RASMGR_RASCONTROL_HH +#define RASMGR_RASCONTROL_HH + +#include "rasmgr.hh" +#include "rasmgr_comm.hh" +#include "rasmgr_users.hh" +#include "rasmgr_host.hh" + + +class RasControl + { + public: + int processRequest(char* reqMessage, char *answMessage); + void setConfigDirty( bool isDirty ); + void setAuthDirty( bool isDirty ); + + private: + void helloCommand(); + void exitCommand(); + void helpCommand(); + void helpHelp(); + void exitHelp(); + void listCommand(); + void listRasServers(); + void listRasHosts(); + void listDBHosts(); + void listDatabases(); + void listUsers(); + void listModus(); + void listVersion(); + void listConnections(); + void listRights(); + void listHelp(); + void defineCommand(); + void defineRasServers(); + void defineRasHosts(); + void defineDBHosts(); + void defineDatabases(); + void defineUsers(); + void defineHelp(); + void removeCommand(); + void removeRasServers(); + void removeRasHosts(); + void removeDBHosts(); + void removeDatabases(); + void removeUsers(); + void removeHelp(); + void checkCommand(); + void checkRasHosts(); + void checkHelp(); + void upCommand(); + void upRasServers(); + int upAllServersOnHost(const char*hostName); + void upHelp(); + void downCommand(); + void downRasServers(); + int downAllServersOnHost(const char *hostName); + void downRasHosts(); + int downRasHost(const char *hostName); + void downHelp(); + void changeCommand(); + void changeHost(); + void changeUser(); + void changeRasServer(); + void changeRasServer(const char *serverName, const char *dbhName, const char *countString, const char *extraString, const char *autoRestart, const char* execName); + void changeDBHost(); + void changeDB(); + void changeHelp(); + void saveCommand(); + void saveHelp(); + // void stopCommand(); + void resetCommand(); + + void grantCommand(); + void revokeCommand(); + + // flag whether conf or auth file must be written before exit + bool configDirty; + bool authDirty; + + void errorInCommand(const char*); + int prepareAnswer(char *answMessage); + char answBuffer[MAXMSGOUTBUFF+20]; + bool isCommand(const char *key); + char commandBuffer[MAXMSG+20]; //for bug search blva + + void splitRequest(const char* reqMessage); + bool isFlag(const char*,int pos=-1); + const char * getValueOf(const char*,bool acceptMinus=false); //'-' alone, only void right string + const char * getValueIfFlag(const char*,bool acceptMinus=false); + void checkUnexpectedTokens(); + void checkPermission(int reqRights); + void checkNotNull(const char *ptr, const char *what); + unsigned long convertToULong(const char *stringValue,const char *what); + + RasServer& getServer(const char*); //later, just use rasmanager[serverName] + Database& getDatabase(const char *name); + DatabaseHost& getDatabaseHost(const char *name); + ServerHost& getServerHost(const char *name); + User& getUser(const char *name); + + struct Token + { char *argv; + bool used; + void set(char*); + const char *take(); + }; + int argc; + Token token[30]; + + }; +extern RasControl rascontrol; + +/* obsolete, but in the future... + void grantCommand(); + void revokeCommand(); + + void connectCommand(); + void connectRasServerToDBH(); + void connectDatabaseToDBH(); + void connectHelp(); + void disconnectCommand(); + void disconnectRasServerFromDBH(); + void disconnectDatabaseFromDBH(); + void disconnectHelp(); + void listRasServersOnDBH(const char*); + void listRasServersDatabase(const char*); + +*/ +#endif + diff --git a/rasmgr/rasmgr_rascontrol_help.cc b/rasmgr/rasmgr_rascontrol_help.cc new file mode 100644 index 0000000..3ed170a --- /dev/null +++ b/rasmgr/rasmgr_rascontrol_help.cc @@ -0,0 +1,521 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_rascontrol_help.hh + * + * MODULE: rasmgr + * CLASS: RasControl + * + * PURPOSE: + * Decodes, verifies and executes the help commands + * + * COMMENTS: + * Logicaly it's part of rasmgr_rascontrol.cc, but compilation unit was split + * +*/ + +// this file is a temporary file, used to keep rasmgr_rascontrol things as long as we check each one +// here are the unchecked things!! + +#include "debug.hh" +#include "rasmgr_rascontrol.hh" + + +void RasControl::helpCommand() + { + const char *what = argc==1 ? RASMGRCMD_HELP:token[1].take(); + + if (strcasecmp(what,RASMGRCMD_LIST)==0) listHelp(); + else if(strcasecmp(what,RASMGRCMD_DEFINE )==0) defineHelp(); + else if(strcasecmp(what,RASMGRCMD_REMOVE)==0) removeHelp(); + else if(strcasecmp(what,RASMGRCMD_CHANGE)==0) changeHelp(); + else if(strcasecmp(what,RASMGRCMD_UP)==0) upHelp(); + else if(strcasecmp(what,RASMGRCMD_DOWN)==0) downHelp(); + else if(strcasecmp(what,RASMGRCMD_SAVE)==0) saveHelp(); + else if(strcasecmp(what,RASMGRCMD_CHECK)==0) checkHelp(); + else if(strcasecmp(what,RASMGRCMD_EXIT)==0) exitHelp(); + else if(strcasecmp(what,RASMGRCMD_QUIT)==0) exitHelp(); + else if(strcasecmp(what,RASMGRCMD_BYE)==0) exitHelp(); + else helpHelp(); + + // no help for version and licence - they have no further params + } + + +void RasControl::helpHelp() + { + sprintf(answBuffer,"Help for rascontrol command language\r\n" + "rasdaman uses the following terms:\r\n" + " host (server host) - a computer running a RasManager (rasmgr), with or without currently active servers\r\n" + " srv (server) - the rasdaman server (rasserver)\r\n" + " dbh (data base host) - a computer running the database software\r\n" + " db (database) - the rasdaman database, hosted by the underlying database instance\r\n" + " user - a person registered by rasdaman through user name and password\r\n" + "\r\nThe rascontrol utility allows to configure and do run-time administration work for the rasdaman system\r\n" + "Commands:\r\n" + " >help ...this help\r\n" + " >exit ...exit rascontrol\r\n" + " >list ...list information about the current status of the system\r\n" + " >up ...start servers\r\n" + " >down ...stop servers and rasmanagers\r\n" + " >define ...define a new object\r\n" + " >remove ...remove an object\r\n" + " >change ...change parameters of objects\r\n" + " >save ...make changes permanent\r\n" + " >check ...checks the current status of a slave rasmgr\r\n" + "Type 'help command' to get specific information about command\r\n" + + ); + } + +void RasControl::listHelp() + { + checkPermission(admR_info); + + sprintf(answBuffer," The list command:\r\n" + "list srv [ s | -host h | -all ] [-p] \r\n" + " - list information about 'server s' or 'all servers on host h' or 'all defined servers' (default)\r\n" + " '-p' prints configuration information; default: runtime status information\r\n" + "list host\r\n" + " - list information about server hosts\r\n" + "list dbh\r\n" + " - list information about database hosts\r\n" + "list db [ d | -dbh h | -all ] \r\n" + " - list information about 'database s' or all 'databases on database host h' or 'all defined databases'\r\n" + "list user [ -rights]\r\n" + " - list the defined users\r\n" + " '-rights' additionally lists each user's rights\r\n" + "list [ license | licencce ]\r\n" + " - lists license information\r\n" + "list version\r\n" + " - list version information" + ); + + } + +void RasControl::defineHelp() + { sprintf(answBuffer," The define command:\r\n" + "define dbh 'dbhname' -connect 'connectstring'\r\n" + " - define database host with symbolic name 'dbhname'\r\n" + " 'connectstring' is the string used to connect a client to the underlying database instance\r\n" + " (example: user/passwd@hostaddress)\r\n" + "define db 'dbname' -dbh 'dbhname'\r\n" + " - define database 'dbname' on database host 'dbhname'\r\n" + " ('dbname' is not a symbolic name, it is the real name of the rasdaman database)\r\n" + "define host 'hostname' -net 'netaddress' [-port 'portnumber']\r\n" + " - define server host with symbolic name 'hostname', located at address 'netaddress:portnumber'\r\n" + " ('portnumber' defaults to 7001)\r\n" + "define srv 'srvname' -host 'hostname' -dbh 'dbhname' -type 'servertype' -port 'portnumber' \r\n" + " [-autorestart on|off] [-countdown 'number'] [-xp 'options']\r\n" + " - define server with symbolic name 'srvname' on server host 'hostname' connected to database host 'dbhname'\r\n" + " 'servertype' can be 'r' (RPC) or 'h' (HTTP) or 'n' (RNP)\r\n" + " 'portnumber' is the IP port number for HTTP servers / the 'prognum' for RPC/RNP servers\r\n" + " -autorestart (default: on): the server will autorestart after an unexpected termination\r\n" + " -countdown 'number' (default: 1000): the server will be restarted after 'number' transactions\r\n" + " -xp 'options': extra parameter string 'options' that will be passed to the server at startup \r\n" + " (default: \"\", see documentation for valid 'options')\r\n" + " this option has to be the last, because anything after it and until end of line is considered to be 'options'\r\n" + "define user 'username' [-passwd 'password'] [-rights 'rightsstring']\r\n" + " - define user account with symbolic name 'username'\r\n" + " 'password' defaults to 'username' (use the raspasswd utility to change)\r\n" + " -rights 'rightsstring': the rights granted to the user (default: none; see documentation for valid rights)\r\n" + ); + } +void RasControl::removeHelp() + { + sprintf(answBuffer," The remove command:\r\n" + "remove dbh 'dbhname'\r\n" + " - remove database host 'dbhname'\r\n" + "remove db 'dbname' -dbh 'dbhname'\r\n" + " - remove database 'dbname' from database host 'dbhname'\r\n" + " (the database itself is not deleted, only the name is removed from the config tables)\r\n" + "remove host 'hostname' \r\n" + " - remove server host 'hostname'\r\n" + "remove srv 'srvname'\r\n" + " - remove server 'srvname'\r\n" + "remove user 'username'\r\n" + " - remove the user 'username'\r\n" + ); + } + +void RasControl::changeHelp() + { sprintf(answBuffer," The change command:\r\n" + "change dbh 'dbhname' [-name 'newname'] [-connect 'newconnectstring']\r\n" + "change db 'dbname' [-name 'newname']\r\n" + "change host 'hostname' [-name 'newname'] [-net 'newnetaddress'] [-port 'newportnumber']\r\n" + "change srv 'servername' [-name 'newname'][-dbh 'newdbhname'] [-type 'newservertype'] [-port 'newportnumber'] [-autorestart on|off] [-countdown 'newnumber'] [-xp 'newoptions']\r\n" + "change user 'username' [-name 'newname'] [-passwd 'newpasswd] [-rights 'rightsstring']\r\n" + " - see the help for the define command for option description\r\n" + + ); + } + +void RasControl::upHelp() + { sprintf(answBuffer," The up command:\r\n" + "up srv [ s | -host h | -all]\r\n" + " - start 'server s' or 'all servers on host h' or 'all defined servers'\r\n" + ); + } + + +void RasControl::downHelp() + {sprintf(answBuffer," The down command:\r\n" + "down srv [ s | -host h | -all] [ -force] [-kill]\r\n" + " - stops 'server s' or 'all started servers on host h' or 'all started servers'\r\n" + " -force: stops the 'server s' without waiting to complete the current transaction (using SIGTERM)\r\n" + " -kill: instantly stops the 'server s' (using SIGKILL)\r\n" + " (without -force or -kill the server completes the current transaction and exits)\r\n" + "down host [ h | -all]\r\n" + " - stops the rasmgr on 'host h' or all started rasmgr\r\n" + ); + } + +void RasControl::checkHelp() + { + sprintf(answBuffer," The check command\r\n" + "check host 'hostname'\r\n" + " - checks the status of the slave rasmgr located on server host 'hostname'\r\n" + " (use this command if the master rasmgr started after the slave rasmgr for synchronising them)\r\n" + ); + } + +void RasControl::saveHelp() + { + sprintf(answBuffer," The save command\r\n" + "save\r\n" + " - saves the current configuration information\r\n" + " (upon changes the files will be saved automatically to rescue files next to the config files when exiting rasmgr)\r\n" + ); + } + +void RasControl::exitHelp() + { + sprintf(answBuffer," The exit command\r\n" + "exit | quit | bye\r\n" + " - finish this rascontrol session\r\n" + ); + } + + + +//### ########### ############# ################ ############## + + +/* obsolete in v1.1, but who knows, in future +void RasControl::disconnectCommand() + { + const char *what = argc==1 ? RASMGRCMD_HELP:token[1].take(); + + if (strcasecmp(what,"srv")==0) disconnectRasServerFromDBH(); + + else if(strcasecmp(what,"db")==0) disconnectDatabaseFromDBH(); + + else if(strcasecmp(what,RASMGRCMD_HELP)==0) disconnectHelp(); + + else errorInCommand("Wrong DISCONNECT command"); + + } +void RasControl::disconnectRasServerFromDBH() + { + CHECK(admR_config); + + const char *serverName=getValueOf("srv"); + + if(!serverName) + { errorInCommand("Server name missing."); + return; + } + RasServer &r=rasManager[serverName]; + + if(!r.isValid()) + { errorInCommand("Invalid server name."); + return; + } + if(r.isUp()) + { errorInCommand("Cannot disconnet a running server."); + return; + } + + const char *dbHost=r.getDBHostName(); + + if(strcasecmp(dbHost,"noDBHost!")!=0) + { sprintf(answBuffer,"Server %s isn't connected to any database host",serverName); + return; + } + if(r.disconnectFromDBHost()) + { sprintf(answBuffer,"Disconnecting server %s from database host %s",serverName,dbHost); + } + errorInCommand("Internal error during disconnect."); + } +void RasControl::disconnectDatabaseFromDBH() + { + CHECK(admR_config); + + const char *dbName=getValueOf("db"); + + if(!dbName) + { errorInCommand("Database name missing."); + return; + } + const char *dbHost=getValueOf("-dbh"); + + if(!dbHost) + { errorInCommand("Database host name missing."); + return; + } + + Database &r=dbManager[dbName]; + DatabaseHost &d=dbHostManager[dbHost]; + + if(!r.isValid()) + { errorInCommand("Invalid database name."); + return; + } + if(!d.isValid()) + { errorInCommand("Invalid database host name."); + return; + } + + if(r.disconnectFromDBHost(dbHost)) + { sprintf(answBuffer,"Disconnecting database %s from database host %s",dbName,dbHost); + } + else + { sprintf(answBuffer,"Database %s is not connected to database host %s",dbName,dbHost); + } + } +void RasControl::disconnectHelp() + { + sprintf(answBuffer,"Disconnect usage: -sorry, help not available yet"); + } + +//-------------------------------------------------------------- + +void RasControl::connectCommand() + { + const char *what = argc==1 ? RASMGRCMD_HELP:token[1].take(); + + if (strcasecmp(what,"srv")==0) connectRasServerToDBH(); + + else if(strcasecmp(what,"db")==0) connectDatabaseToDBH(); + + else if(strcasecmp(what,RASMGRCMD_HELP)==0) connectHelp(); + + else errorInCommand("Wrong CONNECT command"); + + } +void RasControl::connectRasServerToDBH() + { + CHECK(admR_config); + + const char *serverName=getValueOf("srv"); + + if(!serverName) + { errorInCommand("Server name missing."); + return; + } + const char *dbHost=getValueOf("-dbh"); + + if(!dbHost) + { errorInCommand("Database host name missing."); + return; + } + RasServer &r=rasManager[serverName]; + DatabaseHost &d=dbHostManager[dbHost]; + + if(!r.isValid()) + { errorInCommand("Invalid server name."); + return; + } + if(!d.isValid()) + { errorInCommand("Invalid database host name."); + return; + } + + // const char *connStr=getValueOf("-conn"); + // if(!connStr) connStr="\\"; + + if(r.connectToDBHost(dbHost))//,connStr)) + { sprintf(answBuffer,"Connecting server %s to database host %s",serverName,dbHost); + } + else + { sprintf(answBuffer,"Server %s already connected to database host %s",serverName,dbHost); + } + + } +void RasControl::connectDatabaseToDBH() + { + CHECK(admR_config); + + const char *dbName=getValueOf("db"); + + if(!dbName) + { errorInCommand("Database name missing."); + return; + } + const char *dbHost=getValueOf("-dbh"); + + if(!dbHost) + { errorInCommand("Database host name missing."); + return; + } + + Database &r=dbManager[dbName]; + DatabaseHost &d=dbHostManager[dbHost]; + + if(!r.isValid()) + { errorInCommand("Invalid database name."); + } + if(!d.isValid()) + { errorInCommand("Invalid database host name."); + } + + if(r.connectToDBHost(dbHost)) + { sprintf(answBuffer,"Connecting database %s to database host %s",dbName,dbHost); + } + else + { sprintf(answBuffer,"Database %s already connected to database host %s",dbName,dbHost); + } + } +void RasControl::connectHelp() + { + sprintf(answBuffer,"Connect usage: -sorry, help not available yet"); + } + +void RasControl::listRasServersDatabase(const char *dbName) + { + CHECK(admR_info); + + if(dbName) + { sprintf(answBuffer,"List of servers connected to database %s:",dbName); + + Database &db = dbManager[dbName]; + if(db.isValid()==false) + { sprintf(answBuffer+strlen(answBuffer),"\r\nNo such database"); + return; + } + int crnt=0; + for(int i=0;i<db.countConnectionsToRasServers();i++) + { + sprintf(answBuffer+strlen(answBuffer),"\r\n%2d. %s",crnt++,db.getRasServerName(i)); + } + } + else + { + errorInCommand("Database name missing."); + } + } + +void RasControl::listRasServersOnDBH(const char *dbhName) + { + CHECK(admR_info); + + if(dbhName) + { sprintf(answBuffer,"List of servers connected to database host %s:",dbhName); + + int crnt=0; + for(int i=0;i<rasManager.countServers();i++) + { + RasServer &r=rasManager[i]; + if(strcmp(dbhName,r.getDBHostName())!=0) continue; + + sprintf(answBuffer+strlen(answBuffer),"\r\n%2d. %s (%s)",crnt++,r.getName(),r.getHostName()); + } + } + else + { sprintf(answBuffer,"List of servers with connected database host:"); + + int crnt=0; + for(int i=0;i<rasManager.countServers();i++) + { + RasServer &r=rasManager[i]; + sprintf(answBuffer+strlen(answBuffer),"\r\n%2d. %-20s %s",crnt++,r.getName(),r.getDBHostName()); + } + } + } + +*/ + + + +/* no, out +void RasControl::stopCommand() + { + CHECK(admR_sysup); + + // sprintf(answBuffer,RASMGRCMD_EXIT); + // masterCommunicator.shouldExit(); + if(rasManager.countUpServers()==0) + { + sprintf(answBuffer,RASMGRCMD_EXIT); + masterCommunicator.shouldExit(); + } + else + { + sprintf(answBuffer,"Please shut down all servers first."); + } + } + + +void RasControl::listConnections() + { + CHECK(admR_info); + bool full = isFlag("-full"); + + checkUnexpectedTokens(); + + int answLen=0; + int maxAnswLen=MAXMSGOUTBUFF-100; // if there are many servers, we could get an overflow + + bool overflow=false; + sprintf(answBuffer,"List connections\r\n Database Database host Server"); + for(int i=0;i<dbManager.countDatabases();i++) + { + Database&db = dbManager[i]; + const char *dbName = db.getName(); + + for(int j=0;j<db.countConnectionsToDBHosts();j++) + { + const char *dbhName = db.getDBHostName(j); + const char* writtenDbhName = dbhName; + + for(int k=0;k < rasManager.countServers();k++) + { + RasServer &srv = rasManager[k]; + + if(strcmp(srv.getDBHostName(),dbhName) == 0) + { + int len = strlen(answBuffer); + + if(len < maxAnswLen) + { sprintf(answBuffer+len,"\r\n %-15s %-20s %-20s",dbName,writtenDbhName,srv.getName()); + if(full==false) dbName = writtenDbhName =" -\"-"; + } + else overflow=true; + } + } + } + } + if(overflow) sprintf(answBuffer+strlen(answBuffer),"\r\n(Answer too long, overflow!)"); + } + +*/ diff --git a/rasmgr/rasmgr_srv.cc b/rasmgr/rasmgr_srv.cc new file mode 100644 index 0000000..542f6dc --- /dev/null +++ b/rasmgr/rasmgr_srv.cc @@ -0,0 +1,858 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_srv.cc + * + * MODULE: rasmgr + * CLASS: RasServer, RasServerManager + * + * COMMENTS: + * None +*/ + +#include "rasmgr_srv.hh" +#include "rasmgr_localsrv.hh" +#include "rasmgr_comm.hh" +#include "rasmgr_users.hh" + +#include "debug.hh" + +// host name returned when we don't have a valid one: +#define NO_VALID_HOST "noValidHost" + +// initial countdown value +#define INITIAL_COUNTDOWN 1000 +RasServer::RasServer() +{ + serverName[0]=0; + extraParam[0]=0; + ptrServerHost=NULL; + ptrDatabaseHost=NULL; + //connStr[0]=0; + available=isup=isstarting=false; + valid=false; + downReq=false; + initialCountDown = INITIAL_COUNTDOWN; + currentCountDown = INITIAL_COUNTDOWN; // asa doreste management + + writeTransaction=false; // no clearTransaction!! + readTransaction=false; + connDatabase = NULL; + + activityExpected=false; + crashCount = 0; + activityCounter = 0; // will be changed in init() + + autorestart = true; + strcpy(executableName,RASEXECUTABLE); +} + +RasServer::~RasServer() +{ +} + +const char* RasServer::getName() +{ + return serverName; +} + +const char* RasServer::getHostName() +{ + if(ptrServerHost) + { + return ptrServerHost->getName(); + } + return NO_VALID_HOST; +} + +const char* RasServer::getHostNetwName() +{ + if(ptrServerHost) + { + return ptrServerHost->getNetworkName(); + } + return NO_VALID_HOST; +} + +long RasServer::getPort() +{ + return listenPort; +} +char RasServer::getType() +{ + return serverType; +} + +void RasServer::changeName(const char *newName) +{ +// FIXME: input parameter validity check! + strncpy( serverName, newName, sizeof(serverName) ); +} + +void RasServer::changeType(const char newType) // char not char*!! +// FIXME: input parameter validity check! +{ + serverType=newType; +} + +void RasServer::changePort(long newPort) +{ + listenPort=newPort; +} + +void RasServer::changeExtraParam(const char *xParam) +// FIXME: input parameter validity check! +{ + strncpy(extraParam,xParam,sizeof(extraParam) ); +} + +void RasServer::changeCountDown(int newCountDown) +{ + initialCountDown=newCountDown; + currentCountDown=newCountDown; +} + +void RasServer::changeAutoRestart(bool x) +{ + autorestart = x; +} + +const char* RasServer::getExtraParams() +{ + return extraParam; +} +int RasServer::getCountDown() +{ + return initialCountDown; +} + +void RasServer::init(const char *srvName,const char* hostName,char serverType,long listenPort) +{ + strcpy(this->serverName,srvName); + + ptrServerHost=&hostmanager[hostName];// should be valid! + + isinternal=ptrServerHost->isInternal(); + available=isup=false; + this->serverType=serverType; + this->listenPort=listenPort; + + // RNP servers start with first action connectToDBMS() as opposed to the others, + // therefore set counter appropriately. -- PB 2002-nov-23 + switch (serverType) + { + case SERVERTYPE_FLAG_RPC: + case SERVERTYPE_FLAG_HTTP: + TALK( "Initializing activity counter to 1 for RPC/HTTP type server." ); + activityCounter = 1; + break; + case SERVERTYPE_FLAG_RNP: + TALK( "Initializing activity counter to 0 for RNP type server." ); + activityCounter = 0; + break; + default: + TALK( "Error: illegal server type specifier " << serverType << ", assuming '" << SERVERTYPE_FLAG_RNP << "'." ); + break; + } + + valid=true; +} + +bool RasServer::isValid() +{ + return valid; +} + +bool RasServer::isUp() +{ + return isup; +} + +bool RasServer::isStarting() +{ + return isstarting; +} + +bool RasServer::isAvailable() +{ + return available; +} + +bool RasServer::forceAvailable() +{ + // this function is just for advanced system debug, not for production work + if(isup) + { + available=true; + clearPendingTransaction(); + return true; + } + return false; +} + +void RasServer::setNotAvailable() +{ + available=false; + regularSignalCounter = MAXREGULARCOUNTER; +} + +bool RasServer::isAutoRestart() +{ + return autorestart; +} + +char* RasServer::getDescriptionHeader(char *destBuffer) +{ + sprintf(destBuffer," %-20s %s %-20s %-20s %-4s %-2s %s %s","Server Name","Type","Host","Db Host","Stat","Av","Acc","Crc"); + return destBuffer; +} + +char* RasServer::getDescription(char *destBuffer) +{ + char *sType="(none)"; + if(serverType==SERVERTYPE_FLAG_RPC) + sType="(RPC) "; + if(serverType==SERVERTYPE_FLAG_HTTP) + sType="(HTTP)"; + if(serverType==SERVERTYPE_FLAG_RNP) + sType="(RNP) "; + + const char* sUp= isup ? "UP ":"DOWN"; + + const char* sAv= available ? "YES":"NO "; + + const char* host= ptrServerHost->getName();//"(internal)"; + + const char* dbHost = getDBHostName(); + //if(isinternal==false) + // { host=ptrServerHost->getName(); + // } + sprintf(destBuffer,"%-20s %s %-20s %-20s %s %s %6ld %2d",serverName,sType,host,dbHost,sUp,sAv,activityCounter,crashCount); + + return destBuffer; +} + +char* RasServer::getDescriptionPortHeader(char *destBuffer) +{ + sprintf(destBuffer," %-20s %s %-20s %-10s %-3s %-3s","Server Name","Type","Host"," Port","Ars"," Icd"); + + return destBuffer; +} + +char* RasServer::getDescriptionPort(char *destBuffer) +{ + char *sType="(none)"; + if(serverType==SERVERTYPE_FLAG_RPC) sType="(RPC) "; + if(serverType==SERVERTYPE_FLAG_HTTP) sType="(HTTP)"; + if(serverType==SERVERTYPE_FLAG_RNP) sType="(RNP) "; + +// const char* sUp= isup ? "UP ":"DOWN"; + +// const char* sAv= available ? "YES":"NO "; + + const char* host = ptrServerHost->getName(); + + const char *ars = autorestart ? "on":"off"; + + if(serverType==SERVERTYPE_FLAG_RPC) + sprintf(destBuffer,"%-20s %s %-20s %#10x %-3s %3d/%-3d",serverName,sType,host,listenPort,ars,currentCountDown,initialCountDown); + else + sprintf(destBuffer,"%-20s %s %-20s %10d %-3s %3d/%-3d",serverName,sType,host,listenPort,ars,currentCountDown,initialCountDown); + //countdown=%d InitialCountDown, + return destBuffer; +} + +char* RasServer::getDescriptionExecHeader(char *destBuffer) +{ + sprintf(destBuffer," %-20s %-10s Executable Extraparameters","Server Name","Host"); + + return destBuffer; +} + +char* RasServer::getDescriptionExec(char *destBuffer) +{ + + const char* host = ptrServerHost->getName(); + + sprintf(destBuffer,"%-20s %-10s %-15s %s",serverName,host,executableName,extraParam); + + return destBuffer; +} + +bool RasServer::connectToDBHost(const char *dbHostName)//, const char *connString) +{ + if(ptrDatabaseHost) + return false; + DatabaseHost &dbh=dbHostManager[dbHostName]; + + if(dbh.isValid()==false) + return false; + + ptrDatabaseHost = &dbh; + //removed dbh.incrConnServers(); + //strcpy(connStr,connString); + + for(int i=0;i<dbManager.countDatabases();i++) + { + if(dbManager[i].isConnectedToDBHost(dbHostName)) + { + dbManager[i].connectToRasServer(serverName); + } + } + return true; +} + +bool RasServer::disconnectFromDBHost() +{ + if(isup) + return false; + if(ptrDatabaseHost==NULL) + return false; + + const char *dbHostname=ptrDatabaseHost->getName(); + + for(int i=0;i<dbManager.countDatabases();i++) + { + dbManager[i].disconnectFromRasServer(serverName); + } + + // removed ptrDatabaseHost->decrConnServers(); + ptrDatabaseHost=NULL; + + return true; +} + +const char* RasServer::getDBHostName() +{ + if(ptrDatabaseHost) + return ptrDatabaseHost->getName(); + return "noDBHost!"; +} + +bool RasServer::isConnectedToDBHost() +{ + if(ptrDatabaseHost) + return true; + return false; +} + +void RasServer::changeExecutableName(const char *newExecutable) +{ + if(newExecutable == NULL) + strcpy(executableName,RASEXECUTABLE); + else + strcpy(executableName,newExecutable); +} + +const char* RasServer::getExecutableName() +{ + return executableName; +} + +int RasServer::startServerInDebugger(char *command) +{ + // sorry for literals, I will change this soon + if(ptrDatabaseHost==NULL) + return -1; + + if(ptrServerHost->isUp()==false) + return -5; + + downReq=false; + const char *sTypeString= serverType==SERVERTYPE_FLAG_HTTP ? "--http":""; + + const char *rasmgrHost = ptrServerHost->useLocalHost() ? "localhost" : config.getHostName(); + sprintf(command, "%s --rsn %s %s --lport %ld ",executableName,serverName,sTypeString,listenPort); + sprintf(command+strlen(command),"--mgr %s --mgrport %ld --connect %s ",rasmgrHost,config.getListenPort(),ptrDatabaseHost->getConnectionString()); + sprintf(command+strlen(command),"--sync %s %s",authorization.getSyncroString(),extraParam); + + currentCountDown=initialCountDown; + activityExpected=true; + activityCounter = 0; + + TALK( "RasServer::startServerInDebugger() -> " << command ); + return 0; +} + +int RasServer::startServer() +{ // sorry for literals, I will change this soon + if(ptrDatabaseHost==NULL) + return -1; + + if(ptrServerHost->isUp()==false) + return -5; + + downReq=false; + const char *sTypeString= serverType==SERVERTYPE_FLAG_HTTP ? "--http" : ""; + sTypeString= serverType==SERVERTYPE_FLAG_RNP ? "--rnp" : sTypeString; + + const char *rasmgrHost = ptrServerHost->useLocalHost() ? "localhost" : config.getHostName(); + char command[400]; + sprintf(command, "%s %s %s --rsn %s %s --lport %ld ",serverName,executableName,executableName,serverName,sTypeString,listenPort); + sprintf(command+strlen(command),"--mgr %s --mgrport %ld --connect %s ",rasmgrHost,config.getListenPort(),ptrDatabaseHost->getConnectionString()); + sprintf(command+strlen(command)," %s",extraParam); + + if(isinternal) + { + TALK( "launching local server, command=" << command ); + localServerManager.startNewServer(command); + } + else + { + TALK( "connecting to remote rasmgr" ); + int socket=ptrServerHost->getConnectionSocket(); + if(socket<0) + return -3; + + char message[MAXMSG]="POST exec HTTP/1.1\r\nAccept: text/plain\r\nUserAgent: RasMGR/1.0\r\nContent-length: "; + + sprintf(message+strlen(message),"%d\r\n\r\n%s",strlen(command)+1,command); + + int nbytes=write(socket,message,strlen(message)+1); + + close(socket); + if(nbytes<0) + return -4; + } + + currentCountDown=initialCountDown; + activityExpected=true; + isstarting=true; + activityCounter = 0; + return 0; +} +int RasServer::downServer(bool forced) +{ + if(available==false && (forced == false || isstarting==true) ) + { + downReq=true; + //std::cout<<"Down request, but working"<<std::endl; + return 0; + } + return downNow(); +} +int RasServer::downNow() +{ + //std::cout<<"Down server"<<std::endl; + if(isinternal) + { + localServerManager.sendTerminateSignal(serverName); + } + else + { + int socket=ptrServerHost->getConnectionSocket(); + if(socket<0) + return -2; + + char message[MAXMSG]="POST downserver HTTP/1.1\r\nAccept: text/plain\r\nUserAgent: RasMGR/1.0\r\nContent-length: "; + + sprintf(message+strlen(message),"%d\r\n\r\n%s",strlen(serverName)+1,serverName); + + int nbytes=write(socket,message,strlen(message)+1); + + close(socket); + if(nbytes<0) + return -3; + } + + //ptrServerHost->regDownServer(); + + return 0; +} +int RasServer::killServer() +{ + if(isup) + { + ptrServerHost->regDownServer(); + if(ptrDatabaseHost) + ptrDatabaseHost->regDownServer(); + isup=available=0; + activityExpected=false; + } + if(isinternal) + { + localServerManager.killServer(serverName); + } + else + { + int socket=ptrServerHost->getConnectionSocket(); + if(socket<0) + return -2; + + char message[MAXMSG]="POST killserver HTTP/1.1\r\nAccept: text/plain\r\nUserAgent: RasMGR/1.0\r\nContent-length: "; + + sprintf(message+strlen(message),"%d\r\n\r\n%s",strlen(serverName)+1,serverName); + + int nbytes=write(socket,message,strlen(message)+1); + + close(socket); + if(nbytes<0) + return -3; + } + + return 0; +} + +void RasServer::changeStatus(int newStatus,long infCount) +{ + ENTER( "RasServer::changeStatus: enter. servername="<<serverName<<", newStatus="<<newStatus<<", c="<<infCount ); + + if(activityExpected==false && newStatus==SERVER_AVAILABLE) + { + std::cout<<"Error: Server intruder detected in server '"<<serverName<< "' (trying to manually start rasserver?)"<<std::endl; + return; + } + + if(newStatus == SERVER_REGULARSIG) + { + TALK( "RasServer::changeStatus: SERVER_REGULARSIG from "<<serverName<<", newStatus=" << newStatus ); + + if(available == false) + { + TALK( "RasServer::changeStatus: "<<serverName<<" not available, SERVER_REGULARSIG ok, regularSignalCounter="<<regularSignalCounter); + regularSignalCounter--; + if(regularSignalCounter > 0) + { + LEAVE( "RasServer::changeStatus: leave. regularSignalCounter nonzero: " << regularSignalCounter ); + return; + } + newStatus = SERVER_AVAILABLE; + VLOG <<"rasmgr: Dead client detected, server "<<serverName<<" is set free again."<<std::endl; + } + else + { + LEAVE( "RasServer::changeStatus: leave. srv not available." ); + return; + } + } + bool wasup=isup; + + bool crashed= (newStatus == SERVER_CRASHED) ? true:false; + + isup=available= (newStatus == SERVER_AVAILABLE) ? true:false; + + TALK( "RasServer::changeStatus: wasup=" << wasup << ", isup=" << isup ); + + if(wasup==false && isup==true) + { + ptrServerHost->regStartServer(); + if(ptrDatabaseHost) + ptrDatabaseHost->regStartServer(); + isstarting=false; + } + if(wasup==true && isup==false) + { + //then, ok, I'm down + ptrServerHost->regDownServer(); + if(ptrDatabaseHost) + ptrDatabaseHost->regDownServer(); + activityExpected=false; + } + + clearPendingTransaction(); // when the server talks to RasMgr, is always clear, without client + + if(available) + { + activityCounter++; // just a counter + } + + if(downReq && available) + { + downReq=false; + available=false; //until it's really down it shouldn't get any clients + TALK( "RasServer::changeStatus: srv down request, available - setting to unavailable and shutting down. "); + downNow(); + } + + if(crashed) + { + crashCount++; + TALK( "server has crashed, current crash count is " << crashCount << ", activity count is " << activityCounter ); + // restart if "work has started already" (see init() comment on different counting wrt. server types) + // changed by PB 2003-nov-23 + // if(activityCounter && autorestart) + if (activityCounter>1 && autorestart) + { // a crashed server doesn't autorestart if he crashes before starting work. + TALK( "auto restart activated, restarting." ); + startServer(); + } + } + + // commented out due to some error + if(initialCountDown) + { + VLOG <<"rasmgr: initialCountDown==" << initialCountDown << std::endl; + if(available) + { + VLOG <<"rasmgr: available" << std::endl; + currentCountDown--; + if(currentCountDown==0) + { + available=false; + VLOG <<"rasmgr: Countdown reached for "<<serverName<< ", shutting down." << std::endl; + downNow(); + } + } + if(wasup==true && isup==false && currentCountDown==0) + { + VLOG <<"rasmgr: wasup==true && isup==false && currentCountDown==0" << std::endl; + VLOG <<"rasmgr: Restart after countdown for server "<<serverName<< "." << std::endl; + currentCountDown=initialCountDown; + startServer(); + } + } + + LEAVE( "RasServer::changeStatus: leave. ns="<<newStatus<<" av="<<available ); +} // changeStatus() + +void RasServer::startWriteTransaction(Database& dataBase) +{ + ENTER( "RasServer::startWriteTransaction: enter." ); + dataBase.startWriteTransaction(); + writeTransaction=true; + connDatabase=&dataBase; + LEAVE( "RasServer::startWriteTransaction: leave. servername=" << serverName << ", rwTrans-in on db " << dataBase.getName() ); +} + +void RasServer::startReadTransaction(Database& dataBase) +{ + ENTER( "RasServer::startReadTransaction: enter." ); + dataBase.startReadTransaction(); + readTransaction=true; + connDatabase=&dataBase; + LEAVE( "RasServer::startReadTransaction: leave. servername=" << serverName << " roTrans-in on db " << dataBase.getName() ); +} + +void RasServer::clearPendingTransaction() +{ + if(connDatabase) + if(writeTransaction) + connDatabase->endWriteTransaction(); + if(readTransaction ) + connDatabase->endReadTransaction(); + writeTransaction=false; + readTransaction =false; + connDatabase = NULL; +} +//********************************************************************** +RasServerManager::RasServerManager() +{ +} +RasServerManager::~RasServerManager() +{ +} + +bool RasServerManager::insertNewServer(const char *srvName,const char* hostName,char serverType,long listenPort) +{ + bool result = true; + + if(testUniqueness(srvName)==false) + result = false; + + if( result == true && hostmanager[hostName].isValid()==false) + result = false; + + if(result == true && serverType!=SERVERTYPE_FLAG_RPC && serverType!=SERVERTYPE_FLAG_HTTP && serverType!=SERVERTYPE_FLAG_RNP) + { + TALK( "RasServerManager::insertNewServer: server " << srvName << " has illegal type " << serverType ); + result = false; + } + + if (result == true) + { + RasServer tempRasServer; + srvList.push_back(tempRasServer); + RasServer &refRasServer=srvList.back(); + refRasServer.init(srvName,hostName,serverType,listenPort); + } + + return result; +} + +bool RasServerManager::removeServer(const char *srvName) +{ + list<RasServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++) + { + if(strcmp(iter->getName(),srvName)==0) + { + if(iter->isUp()) + return false; + + iter->disconnectFromDBHost(); //it's not up, so it fails only if it is not connected at all + + srvList.erase(iter); + return true; + } + iter++; + } + return false; +} + +bool RasServerManager::testUniqueness(const char* srvName) +{ + list<RasServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++) + { + if(strcmp(iter->getName(),srvName)==0) + return false; + iter++; + } + return true; +} + +RasServer& RasServerManager::operator[](int x) // FIXME: check against upper limit +{ + list<RasServer>::iterator iter=srvList.begin(); + for(int i=0;i<x;i++) + iter++; + return *iter; +} + +RasServer& RasServerManager::operator[](const char* srvName) // FIXME: check against upper limit +{ + list<RasServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++) + { + if(strcmp(iter->getName(),srvName)==0) + return *iter; + + iter++; + } + return protElem; +} + +RasServer& RasServerManager::last() +{ + return srvList.back(); +} + + +int RasServerManager::countServers() +{ + return srvList.size(); +} + +// return values of changeServerStatus: +#define SERVERSTATUS_OK 0 +#define SERVERSTATUS_ERR -1 +int RasServerManager::changeServerStatus(char *reqMessage) +{ + char serverName[50]; + int newstatus; + long infCount; + + sscanf(reqMessage,"%s %d %ld",serverName,&newstatus,&infCount); + TALK( "RasServerManager::changeServerStatus: Trying to change status of "<<serverName<<" to "<<newstatus ); + RasServer &r=operator[](serverName); + + if(r.isValid()==false) + { + std::cout<<"Error: Unexpected message from rasserver '"<<serverName<<"'; new status is "<<newstatus<<std::endl; + return SERVERSTATUS_ERR; + } + + r.changeStatus(newstatus,infCount); + return SERVERSTATUS_OK; +} + +void RasServerManager::disconnectAllServersFromDBH(const char *dbhName) +{ + list<RasServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++,iter++) + { + const char *cDbhName=iter->getDBHostName(); + + if(cDbhName==NULL) + continue; + + if(strcmp(cDbhName,dbhName)==0) iter->disconnectFromDBHost(); + } + +} + +int RasServerManager::countUpServers() +{ + int count=0; + list<RasServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++) + { + if(iter->isUp()) + count++; + iter++; + } + + TALK( "RasServerManager::countUpServers() -> " << count ); + return count; + +} + +void RasServerManager::printStatus() +{ + char buff[100]; + list<RasServer>::iterator iter=srvList.begin(); + + TALK( "RasServerManager::printStatus. current status is:" ); + for(int i=0;i<srvList.size();i++) + { + iter->getDescription(buff); + TALK( "\t" << i << ": " << buff ); + iter++; + } + +} + +bool RasServerManager::reset() +{ // test modus only + if(config.isTestModus()==false) + return false; + + list<RasServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++, iter++) + { + if(iter->isUp()) + return false; + } + + iter=srvList.begin(); + for(int i=0;i<srvList.size();i++, iter++) + { + iter->disconnectFromDBHost(); //it's not up, so it fails only if it is not connected at all + } + + while(srvList.size()) + { + srvList.pop_front(); + } + return true; + +} + +bool RasServerManager::acceptChangeName(const char *oldName,const char *newName) +{ + if(strcmp(oldName,newName)==0) + return true; // if someone really wants to change a name with the same, + return testUniqueness(newName); +} + + diff --git a/rasmgr/rasmgr_srv.cc.ORIG b/rasmgr/rasmgr_srv.cc.ORIG new file mode 100644 index 0000000..ed0dddc --- /dev/null +++ b/rasmgr/rasmgr_srv.cc.ORIG @@ -0,0 +1,840 @@ +/************************************************************************* + * + * Copyright (C) 2003 Dr. Peter Baumann + * + * SOURCE: rasmgr_srv.cc + * + * MODULE: rasmgr + * CLASS: RasServer, RasServerManager + * + * PURPOSE: + * + * + * CHANGE HISTORY (append further entries): + * when who what + * ----------------------------------------------------------------------- + * 05-Jan-01 schatz created + * 2003-jun-06 PB symbolic constants for server flags + * + * COMMENTS: + * + * + ***********************************************************************/ + +#include "rasmgr_srv.hh" +#include "rasmgr_localsrv.hh" +#include "rasmgr_comm.hh" +#include "rasmgr_users.hh" + +// the following is a quick hack until the real rminit/debug stuff links properly: +#if 0 + #include "raslib/rminit.hh" + #include "debug.hh" +#else + #ifdef DEBUG + #define ENTER(a) { cout << "ENTER: " << a << endl; } + #define LEAVE(a) { cout << "LEAVE: " << a << endl; } + #define TALK(a) { cout << a << endl; } + #define SET_OUTPUT(a) + #else + #define ENTER(a) { ; } + #define LEAVE(a) { ; } + #define TALK(a) { ; } + #define SET_OUTPUT(a) + #endif +#endif // 0 + + +RasServer::RasServer() +{ + serverName[0]=0; + extraParam[0]=0; + ptrServerHost=NULL; + ptrDatabaseHost=NULL; + //connStr[0]=0; + available=isup=isstarting=false; + valid=false; + downReq=false; + initialCountDown=1000; + currentCountDown=1000; // asa doreste management + + writeTransaction=false; // no clearTransaction!! + readTransaction=false; + connDatabase = NULL; + + activityExpected=false; + crashCount = 0; + activityCounter = 0; // will be changed in init() + + autorestart = true; + strcpy(executableName,RASEXECUTABLE); +} +RasServer::~RasServer() +{ +} + +const char* RasServer::getName() +{ + return serverName; +} + +const char* RasServer::getHostName() +{ + if(ptrServerHost) + { + return ptrServerHost->getName(); + } + return "noValidHost!"; +} + +const char* RasServer::getHostNetwName() +{ + if(ptrServerHost) + { + return ptrServerHost->getNetworkName(); + } + return "noValidHost!"; +} + +long RasServer::getPort() +{ + return listenPort; +} +char RasServer::getType() +{ + return serverType; +} + +void RasServer::changeName(const char *newName) +{ + strncpy( serverName, newName, sizeof(serverName) ); +} + +void RasServer::changeType(const char newType) // char not char*!! +{ serverType=newType; +} + +void RasServer::changePort(long newPort) +{ listenPort=newPort; +} + +void RasServer::changeExtraParam(const char *xParam) +{ + strncpy(this->extraParam,xParam,sizeof(extraParam) ); +} + +void RasServer::changeCountDown(int newCountDown) +{ + initialCountDown=newCountDown; + currentCountDown=newCountDown; +} + +void RasServer::changeAutoRestart(bool x) +{ + autorestart = x; +} + +const char* RasServer::getExtraParams() +{ + return extraParam; +} +int RasServer::getCountDown() +{ + return initialCountDown; +} + +void RasServer::init(const char *srvName,const char* hostName,char serverType,long listenPort) +{ + strcpy(this->serverName,srvName); + + ptrServerHost=&hostmanager[hostName];// should be valid! + + isinternal=ptrServerHost->isInternal(); + available=isup=false; + this->serverType=serverType; + this->listenPort=listenPort; + + // RNP servers start with first action connectToDBMS() as opposed to the others, + // therefore adapt counter. -- PB 2002-nov-23 + switch (serverType) + { + case 'r': + case 'h': + activityCounter = 1; + break: + case 'n': + activityCounter = 0; + break: + default: + TALK( "Error: illegal server type specifier " << serverType << ", assuming 'n'." ); + break; + } + + valid=true; +} + +bool RasServer::isValid() +{ + return valid; +} +bool RasServer::isUp() +{ + return isup; +} +bool RasServer::isStarting() +{ + return isstarting; +} + +bool RasServer::isAvailable() +{ + return available; +} +bool RasServer::forceAvailable() +{ + // this function is just for advanced system debug, not for production work + if(isup) + { + available=true; + clearPendingTransaction(); + return true; + } + return false; +} + +void RasServer::setNotAvailable() +{ + available=false; + regularSignalCounter = MAXREGULARCOUNTER; +} +bool RasServer::isAutoRestart() +{ + return autorestart; +} + +char* RasServer::getDescriptionHeader(char *destBuffer) +{ + sprintf(destBuffer," %-20s %s %-20s %-20s %-4s %-2s %s %s","Server Name","Type","Host","Db Host","Stat","Av","Acc","Crc"); + return destBuffer; +} + +char* RasServer::getDescription(char *destBuffer) +{ + char *sType="(none)"; + if(serverType==SERVERTYPE_FLAG_RPC) sType="(RPC) "; + if(serverType==SERVERTYPE_FLAG_HTTP) sType="(HTTP)"; + if(serverType==SERVERTYPE_FLAG_RNP) sType="(RNP) "; + + const char* sUp= isup ? "UP ":"DOWN"; + + const char* sAv= available ? "YES":"NO "; + + const char* host= ptrServerHost->getName();//"(internal)"; + + const char* dbHost = getDBHostName(); + //if(isinternal==false) + // { host=ptrServerHost->getName(); + // } + sprintf(destBuffer,"%-20s %s %-20s %-20s %s %s %6ld %2d",serverName,sType,host,dbHost,sUp,sAv,activityCounter,crashCount); + + return destBuffer; +} + +char* RasServer::getDescriptionPortHeader(char *destBuffer) +{ + sprintf(destBuffer," %-20s %s %-20s %-10s %-3s %-3s","Server Name","Type","Host"," Port","Ars"," Icd"); + + return destBuffer; +} + +char* RasServer::getDescriptionPort(char *destBuffer) +{ + char *sType="(none)"; + if(serverType==SERVERTYPE_FLAG_RPC) sType="(RPC) "; + if(serverType==SERVERTYPE_FLAG_HTTP) sType="(HTTP)"; + if(serverType==SERVERTYPE_FLAG_RNP) sType="(RNP) "; + +// const char* sUp= isup ? "UP ":"DOWN"; + +// const char* sAv= available ? "YES":"NO "; + + const char* host = ptrServerHost->getName(); + + const char *ars = autorestart ? "on":"off"; + + if(serverType==SERVERTYPE_FLAG_RPC) + sprintf(destBuffer,"%-20s %s %-20s %#10x %-3s %3d/%-3d",serverName,sType,host,listenPort,ars,currentCountDown,initialCountDown); + else + sprintf(destBuffer,"%-20s %s %-20s %10d %-3s %3d/%-3d",serverName,sType,host,listenPort,ars,currentCountDown,initialCountDown); + //countdown=%d InitialCountDown, + return destBuffer; +} + +char* RasServer::getDescriptionExecHeader(char *destBuffer) +{ + sprintf(destBuffer," %-20s %-10s Executable Extraparameters","Server Name","Host"); + + return destBuffer; +} + +char* RasServer::getDescriptionExec(char *destBuffer) +{ + + const char* host = ptrServerHost->getName(); + + sprintf(destBuffer,"%-20s %-10s %-15s %s",serverName,host,executableName,extraParam); + + return destBuffer; +} + +bool RasServer::connectToDBHost(const char *dbHostName)//, const char *connString) +{ + if(ptrDatabaseHost) + return false; + DatabaseHost &dbh=dbHostManager[dbHostName]; + + if(dbh.isValid()==false) + return false; + + ptrDatabaseHost = &dbh; + //removed dbh.incrConnServers(); + //strcpy(connStr,connString); + + for(int i=0;i<dbManager.countDatabases();i++) + { + if(dbManager[i].isConnectedToDBHost(dbHostName)) + { + dbManager[i].connectToRasServer(serverName); + } + } + return true; +} + +bool RasServer::disconnectFromDBHost() +{ + if(isup) + return false; + if(ptrDatabaseHost==NULL) + return false; + + const char *dbHostname=ptrDatabaseHost->getName(); + + for(int i=0;i<dbManager.countDatabases();i++) + { + dbManager[i].disconnectFromRasServer(serverName); + } + + // removed ptrDatabaseHost->decrConnServers(); + ptrDatabaseHost=NULL; + + return true; +} + +const char* RasServer::getDBHostName() +{ + if(ptrDatabaseHost) + return ptrDatabaseHost->getName(); + return "noDBHost!"; +} + +bool RasServer::isConnectedToDBHost() +{ + if(ptrDatabaseHost) + return true; + return false; +} + +void RasServer::changeExecutableName(const char *newExecutable) +{ + if(newExecutable == NULL) + strcpy(executableName,RASEXECUTABLE); + else + strcpy(executableName,newExecutable); +} + +const char* RasServer::getExecutableName() +{ + return executableName; +} + +int RasServer::startServerInDebugger(char *command) +{ + // sorry for literals, I will change this soon + if(ptrDatabaseHost==NULL) + return -1; + + if(ptrServerHost->isUp()==false) + return -5; + + if(ptrServerHost->mayStartServer()==false) + return -2; + + downReq=false; + const char *sTypeString= serverType==SERVERTYPE_FLAG_HTTP ? "--http":""; + + const char *rasmgrHost = ptrServerHost->useLocalHost() ? "localhost" : config.getHostName(); + sprintf(command, "%s --rsn %s %s --lport %ld ",executableName,serverName,sTypeString,listenPort); + sprintf(command+strlen(command),"--mgr %s --mgrport %ld --connect %s ",rasmgrHost,config.getListenPort(),ptrDatabaseHost->getConnectionString()); + sprintf(command+strlen(command),"--sync %s %s",authorization.getSyncroString(),extraParam); + + currentCountDown=initialCountDown; + activityExpected=true; + activityCounter = 0; + return 0; +} + +int RasServer::startServer() +{ // sorry for literals, I will change this soon + if(ptrDatabaseHost==NULL) + return -1; + + if(ptrServerHost->isUp()==false) + return -5; + + if(ptrServerHost->mayStartServer()==false) + return -2; + + downReq=false; + const char *sTypeString= serverType==SERVERTYPE_FLAG_HTTP ? "--http" : ""; + sTypeString= serverType==SERVERTYPE_FLAG_RNP ? "--rnp" : sTypeString; + + const char *rasmgrHost = ptrServerHost->useLocalHost() ? "localhost" : config.getHostName(); + char command[400]; + sprintf(command, "%s %s %s --rsn %s %s --lport %ld ",serverName,executableName,executableName,serverName,sTypeString,listenPort); + sprintf(command+strlen(command),"--mgr %s --mgrport %ld --connect %s ",rasmgrHost,config.getListenPort(),ptrDatabaseHost->getConnectionString()); + sprintf(command+strlen(command)," %s",extraParam); + + if(isinternal) + { + TALK( "launching local server, command=" << command ); + localServerManager.startNewServer(command); + } + else + { + TALK( "connecting to remote rasmgr" ); + int socket=ptrServerHost->getConnectionSocket(); + if(socket<0) + return -3; + + char message[MAXMSG]="POST exec HTTP/1.1\r\nAccept: text/plain\r\nUserAgent: RasMGR/1.0\r\nContent-length: "; + + sprintf(message+strlen(message),"%d\r\n\r\n%s",strlen(command)+1,command); + + int nbytes=write(socket,message,strlen(message)+1); + + close(socket); + if(nbytes<0) + return -4; + } + + currentCountDown=initialCountDown; + activityExpected=true; + isstarting=true; + activityCounter = 0; + return 0; +} +int RasServer::downServer(bool forced) +{ + if(available==false && (forced == false || isstarting==true) ) + { + downReq=true; + //std::cout<<"Down request, but working"<<std::endl; + return 0; + } + return downNow(); +} +int RasServer::downNow() +{ + //std::cout<<"Down server"<<std::endl; + if(isinternal) + { + localServerManager.sendTerminateSignal(serverName); + } + else + { + int socket=ptrServerHost->getConnectionSocket(); + if(socket<0) + return -2; + + char message[MAXMSG]="POST downserver HTTP/1.1\r\nAccept: text/plain\r\nUserAgent: RasMGR/1.0\r\nContent-length: "; + + sprintf(message+strlen(message),"%d\r\n\r\n%s",strlen(serverName)+1,serverName); + + int nbytes=write(socket,message,strlen(message)+1); + + close(socket); + if(nbytes<0) + return -3; + } + + //ptrServerHost->regDownServer(); + + return 0; +} +int RasServer::killServer() +{ + if(isup) + { + ptrServerHost->regDownServer(); + if(ptrDatabaseHost) + ptrDatabaseHost->regDownServer(); + isup=available=0; + activityExpected=false; + } + if(isinternal) + { + localServerManager.killServer(serverName); + } + else + { + int socket=ptrServerHost->getConnectionSocket(); + if(socket<0) + return -2; + + char message[MAXMSG]="POST killserver HTTP/1.1\r\nAccept: text/plain\r\nUserAgent: RasMGR/1.0\r\nContent-length: "; + + sprintf(message+strlen(message),"%d\r\n\r\n%s",strlen(serverName)+1,serverName); + + int nbytes=write(socket,message,strlen(message)+1); + + close(socket); + if(nbytes<0) + return -3; + } + + return 0; +} + +void RasServer::changeStatus(int newStatus,long infCount) +{ + ENTER( "RasServer::changeStatus: enter. servername="<<serverName<<", newStatus="<<newStatus<<", c="<<infCount ); + + if(activityExpected==false && newStatus==SERVER_AVAILABLE) + { + std::cout<<"Server intruder detected in server "<<serverName<< "; (not allowed to start rasserver manually)"<<std::endl; + return; + } + + if(newStatus == SERVER_REGULARSIG) + { + TALK( "RasServer::changeStatus: SERVER_REGULARSIG from "<<serverName<<", newStatus=" << newStatus ); + + if(available == false) + { + TALK( "RasServer::changeStatus: "<<serverName<<" not available, SERVER_REGULARSIG ok, regularSignalCounter="<<regularSignalCounter); + regularSignalCounter--; + if(regularSignalCounter > 0) + { + LEAVE( "RasServer::changeStatus: leave. regularSignalCounter nonzero: " << regularSignalCounter ); + return; + } + newStatus = SERVER_AVAILABLE; + std::cout<<"rasmgr: Dead client detected, server "<<serverName<<" is set free again."<<std::endl; + } + else + { + LEAVE( "RasServer::changeStatus: leave. srv not available." ); + return; + } + } + bool wasup=isup; + + bool crashed= (newStatus == SERVER_CRUSHED) ? true:false; + + isup=available= (newStatus == SERVER_AVAILABLE) ? true:false; + + if(wasup==false && isup==true) + { + ptrServerHost->regStartServer(); + if(ptrDatabaseHost) + ptrDatabaseHost->regStartServer(); + isstarting=false; + } + if(wasup==true && isup==false) + { + //then, ok, I'm down + ptrServerHost->regDownServer(); + if(ptrDatabaseHost) + ptrDatabaseHost->regDownServer(); + activityExpected=false; + } + + clearPendingTransaction(); // when the server talks to RasMgr, is always clear, without client + + if(available) + { + activityCounter++; // just a counter + } + + if(downReq && available) + { + downReq=false; + available=false; //until it's really down it shouldn't get any clients + TALK( "RasServer::changeStatus: srv down request, available - setting to unavailable and shutting down. "); + downNow(); + } + + if(crashed) + { + crashCount++; + TALK( "server has crashed, current crash count is " << crashCount << ", activity count is " << activityCounter ); + // restart if "work has started already" (see init() comment on different counting wrt. server types) + // changed by PB 2003-nov-23 + // if(activityCounter && autorestart) + if (activityCounter>1 && autorestart) + { // a crashed server doesn't autorestart if he crashes before starting work. + TALK( "auto restart activated, restarting." ); + startServer(); + } + } + + // commented out due to some error + if(initialCountDown) + { + if(available) + { + currentCountDown--; + if(currentCountDown==0) + { + available=false; + std::cout<<"rasmgr: Countdown reached for "<<serverName<< ", shutting down." << std::endl; + downNow(); + } + } + if(wasup==true && isup==false && currentCountDown==0) + { + std::cout<<"rasmgr: Restart after countdown for server "<<serverName<< "." << std::endl; + currentCountDown=initialCountDown; + startServer(); + } + } + + LEAVE( "RasServer::changeStatus: leave. ns="<<newStatus<<" av="<<available ); +} // changeStatus() + +void RasServer::startWriteTransaction(Database& dataBase) +{ + ENTER( "RasServer::startWriteTransaction: enter." ); + dataBase.startWriteTransaction(); + writeTransaction=true; + connDatabase=&dataBase; + LEAVE( "RasServer::startWriteTransaction: leave. servername=" << serverName << ", rwTrans-in on db " << dataBase.getName() ); +} + +void RasServer::startReadTransaction(Database& dataBase) +{ + ENTER( "RasServer::startReadTransaction: enter." ); + dataBase.startReadTransaction(); + readTransaction=true; + connDatabase=&dataBase; + LEAVE( "RasServer::startReadTransaction: leave. servername=" << serverName << " roTrans-in on db " << dataBase.getName() ); +} + +void RasServer::clearPendingTransaction() +{ + if(connDatabase) + if(writeTransaction) + connDatabase->endWriteTransaction(); + if(readTransaction ) + connDatabase->endReadTransaction(); + writeTransaction=false; + readTransaction =false; + connDatabase = NULL; +} +//********************************************************************** +RasServerManager::RasServerManager() +{ +} +RasServerManager::~RasServerManager() +{ +} + +bool RasServerManager::insertNewServer(const char *srvName,const char* hostName,char serverType,long listenPort) +{ + bool result = true; + + if(testUniqueness(srvName)==false) + result = false; + + if( result == true && hostmanager[hostName].isValid()==false) + result = false; + + if(result == true && serverType!=SERVERTYPE_FLAG_RPC && serverType!=SERVERTYPE_FLAG_HTTP && serverType!=SERVERTYPE_FLAG_RNP) + { + TALK( "RasServerManager::insertNewServer: server " << srvName << " has illegal type " << serverType ); + result = false; + } + + if (result == true) + { + RasServer tempRasServer; + srvList.push_back(tempRasServer); + RasServer &refRasServer=srvList.back(); + refRasServer.init(srvName,hostName,serverType,listenPort); + } + + return result; +} + +bool RasServerManager::removeServer(const char *srvName) +{ + list<RasServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++) + { + if(strcmp(iter->getName(),srvName)==0) + { + if(iter->isUp()) + return false; + + iter->disconnectFromDBHost(); //it's not up, so it fails only if it is not connected at all + + srvList.erase(iter); + return true; + } + iter++; + } + return false; +} + +bool RasServerManager::testUniqueness(const char* srvName) +{ + list<RasServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++) + { + if(strcmp(iter->getName(),srvName)==0) + return false; + iter++; + } + return true; +} + +RasServer& RasServerManager::operator[](int x) +{ + list<RasServer>::iterator iter=srvList.begin(); + for(int i=0;i<x;i++) + iter++; + return *iter; +} + +RasServer& RasServerManager::operator[](const char* srvName) +{ + list<RasServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++) + { + if(strcmp(iter->getName(),srvName)==0) + return *iter; + + iter++; + } + return protElem; +} + +RasServer& RasServerManager::last() +{ + return srvList.back(); +} + + +int RasServerManager::countServers() +{ + return srvList.size(); +} + +int RasServerManager::changeServerStatus(char *reqMessage) +{ + char serverName[50]; + int newstatus; + long infCount; + + sscanf(reqMessage,"%s %d %ld",serverName,&newstatus,&infCount); + TALK( "RasServerManager::changeServerStatus: Trying to change status of "<<serverName<<" to "<<newstatus ); + RasServer &r=operator[](serverName); + + if(r.isValid()==false) + { + std::cout<<"Incorrect message from rasserver "<<serverName<<"; new status="<<newstatus<<std::endl; + return -1; + } + + r.changeStatus(newstatus,infCount); + return 0; +} + +void RasServerManager::disconnectAllServersFromDBH(const char *dbhName) +{ + list<RasServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++,iter++) + { + const char *cDbhName=iter->getDBHostName(); + + if(cDbhName==NULL) + continue; + + if(strcmp(cDbhName,dbhName)==0) iter->disconnectFromDBHost(); + } + +} + +int RasServerManager::countUpServers() +{ + int count=0; + list<RasServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++) + { + if(iter->isUp()) + count++; + iter++; + } + return count; + +} + +void RasServerManager::printStatus() +{ + char buff[100]; + list<RasServer>::iterator iter=srvList.begin(); + + TALK( "RasServerManager::printStatus. current status is:" ); + for(int i=0;i<srvList.size();i++) + { + iter->getDescription(buff); + TALK( "\t" << i << ": " << buff ); + iter++; + } + +} + +bool RasServerManager::reset() +{ // test modus only + if(config.isTestModus()==false) + return false; + + list<RasServer>::iterator iter=srvList.begin(); + for(int i=0;i<srvList.size();i++, iter++) + { + if(iter->isUp()) + return false; + } + + iter=srvList.begin(); + for(int i=0;i<srvList.size();i++, iter++) + { + iter->disconnectFromDBHost(); //it's not up, so it fails only if it is not connected at all + } + + while(srvList.size()) + { + srvList.pop_front(); + } + return true; + +} + +bool RasServerManager::acceptChangeName(const char *oldName,const char *newName) +{ + if(strcmp(oldName,newName)==0) + return true; // if someone really wants to change a name with the same, + return testUniqueness(newName); +} + + diff --git a/rasmgr/rasmgr_srv.hh b/rasmgr/rasmgr_srv.hh new file mode 100644 index 0000000..30011e9 --- /dev/null +++ b/rasmgr/rasmgr_srv.hh @@ -0,0 +1,165 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_srv.hh + * + * MODULE: rasmgr + * CLASS: RasServer, RasServerManager + * + * COMMENTS: + * None + * +*/ + +#ifndef RASMGR_SRV_HH +#define RASMGR_SRV_HH + +#include "rasmgr.hh" +#include "rasmgr_config.hh" +#include "rasmgr_host.hh" +#include "rasmgr_dbm.hh" + +//#define RASEXECUTABLE "rst" +#define RASEXECUTABLE BINDIR"rasserver" +#define MAXREGULARCOUNTER 3 + +class RasServer + { + public: + RasServer(); + ~RasServer(); + void init(const char *srvName,const char* hostName,char serverType,long listenPort); + const char *getName(); + const char *getHostName(); + const char *getHostNetwName(); + long getPort(); + char getType(); + void changeName(const char *newName); + void changeType(const char newType); // char not char*!! + + void changePort(long newPort); + void changeExtraParam(const char *extraParam); + void changeCountDown(int); + void changeAutoRestart(bool); + const char *getExtraParams(); + int getCountDown(); + + bool connectToDBHost(const char *dbHostName);//,const char *connString); + bool disconnectFromDBHost(); + const char *getDBHostName(); + + static char* getDescriptionHeader(char *destBuffer); + char* getDescription(char *destBuffer); + static char* getDescriptionExecHeader(char *destBuffer); + char* getDescriptionExec(char *destBuffer); + static char* getDescriptionPortHeader(char *destBuffer); + char* getDescriptionPort(char *destBuffer); + + int startServer(); + int startServerInDebugger(char *command); // test modus only + + int downServer(bool forced); + int killServer(); + + void changeStatus(int,long); + bool isUp(); + bool isStarting(); + bool isValid(); + bool isAvailable(); + bool forceAvailable(); + + bool isConnectedToDBHost(); + bool isAutoRestart(); + + void setNotAvailable(); + void startWriteTransaction(Database& dataBase); + void startReadTransaction(Database& dataBase); + void changeExecutableName(const char*); + const char* getExecutableName(); + private: + int downNow(); + void clearPendingTransaction(); + + char serverName[100]; + ServerHost *ptrServerHost; + bool isinternal; + char serverType; //'r','h' + long listenPort; // 'r' ->rpc prognum; 'h' ->TCP/IP port + char extraParam[100]; + + char executableName[100]; + + DatabaseHost *ptrDatabaseHost; + //char connStr[100]; + + bool downReq; + bool available; + bool isup; + bool isstarting; + bool activityExpected; // to avoid the possibility of starting rasserver by hand + + int regularSignalCounter; // how namy times should a nonavailable server signal before we put it available again + unsigned long activityCounter; // counts "activities" per server, i.e., actions that are noticeable by the server + + int initialCountDown; + int currentCountDown; + int crashCount; + bool autorestart; + + bool writeTransaction; + bool readTransaction; + Database *connDatabase; + + bool valid; + }; + +class RasServerManager + { + public: + RasServerManager(); + ~RasServerManager(); + bool insertNewServer(const char *srvName,const char* hostName,char serverType,long listenPort); + bool removeServer(const char *srvName); + int countServers(); + RasServer& operator[](int); + RasServer& operator[](const char*srvName); + RasServer& last(); + + int changeServerStatus(char *reqMessage); + void disconnectAllServersFromDBH(const char *dbhName); + + int countUpServers(); + void printStatus(); + bool reset(); // test modus only + bool acceptChangeName(const char *oldName,const char *newName); + private: + bool testUniqueness(const char* srvName); + list<RasServer> srvList; + RasServer protElem; + }; + +extern RasServerManager rasManager; + +#endif + + diff --git a/rasmgr/rasmgr_users.cc b/rasmgr/rasmgr_users.cc new file mode 100644 index 0000000..3b72308 --- /dev/null +++ b/rasmgr/rasmgr_users.cc @@ -0,0 +1,861 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_users.cc + * + * MODULE: rasmgr + * CLASS: User, UserManager, Authorization + * + * PURPOSE: + * User management + * + * COMMENTS: + * None + * +*/ + +using namespace std; + +#include "rasmgr_users.hh" +#include "ras_crypto.hh" +#include "rasmgr_host.hh" +#include <time.h> + +#include "debug.hh" + +extern bool hostCmp( const char *h1, const char *h2); + + +User::User() + { userID=-1; + userName[0]=0; + passWord[0]=0; + valid=false; + adminRight=authorization.getGlobalInitAdminRights(); + databRight=authorization.getGlobalInitDatabRights();; + } + +void User::init(long userID, const char *name) + { strcpy(userName,name); + this->userID=userID; + changePTPassword(name); + valid=true; + } +void User::changeName(const char *name) + { strcpy(userName,name); + } + +void User::changePassword(const char *encrPass) + { strcpy(passWord,encrPass); + } +void User::changePTPassword(const char *plainTextPass) + { + messageDigest(plainTextPass,passWord,"MD5"); + //std::cout<<"passwd="<<passWord<< " strlen="<<strlen(passWord)<<std::endl; + } + +const char* User::getName() + { return userName; + } + +long User::getUserID() + { return userID; + } + +bool User::isThisMe(const char *name,const char *encrPass) + { //std::cout<<"Is this me: "<<name<<'/'<<userName<<std::endl; + //std::cout<<"My Pass="<<passWord<<std::endl; + //std::cout<<"His one="<<encrPass<<std::endl; + + return (strcmp(name,userName)==0 && strcmp(encrPass,passWord)==0) ? true:false; + } +void User::setAdminRights(int r) + { adminRight = r; + } + +bool User::hasAdminRights(int r) + { + return ((adminRight & r)==r) ? true:false; + } +int User::getAdminRights() + { return adminRight; + } + +void User::setDefaultDBRights(int r) + { + databRight=r; + } +int User::getDefaultDBRights() + { return databRight; + } + +int User::getEffectiveDatabaseRights(const char *databName) + { + list<UserDBRight>::iterator iter=dbRList.begin(); + for(int i=0;i<dbRList.size();i++) + { + if(strcmp(iter->ptrDatabase->getName(),databName)==0) + { + return iter->databRight; + } + iter++; + } + return databRight; + } + +bool User::isTrusteeOn(const char *databName) + { + list<UserDBRight>::iterator iter=dbRList.begin(); + for(int i=0;i<dbRList.size();i++) + { + if(strcmp(iter->ptrDatabase->getName(),databName)==0) + { + return true; + } + iter++; + } + return false; + } + +bool User::setDatabaseRights(const char *databName,int rights) + { + Database &db=dbManager[databName]; + if(db.isValid()==false) return false; + + UserDBRight ur; + ur.ptrDatabase=&db; + ur.databRight=rights; + + removeDatabaseRights(databName); + dbRList.push_back(ur); + return true; + } +bool User::removeDatabaseRights(const char *databName) + { + list<UserDBRight>::iterator iter=dbRList.begin(); + for(int i=0;i<dbRList.size();i++) + { + //if(iter->ptrDatabase==NULL) { std::cout<<"Huo!!"<<std::endl;break;} + + if(strcmp(iter->ptrDatabase->getName(),databName)==0) + { + dbRList.erase(iter); + return true; + } + iter++; + } + + return false; + } + +void User::loadToRec(AuthUserRec &rec) + { + rec.userID=userID; + strcpy(rec.userName,userName); + strcpy(rec.passWord,passWord); + + rec.adminRight=adminRight; + rec.databRight=databRight; + rec.countRights=dbRList.size(); + } +void User::loadFromRec(AuthUserRec &rec) + { + userID=rec.userID; + strcpy(userName,rec.userName); + strcpy(passWord,rec.passWord); + + adminRight =rec.adminRight; + databRight =rec.databRight; + valid=true; + } + +long User::countRights() + { return dbRList.size(); + } + +bool User::loadRightToRec(int x,AuthDbRRec &rec) + { + if(x>=dbRList.size()) return false; + + list<UserDBRight>::iterator iter=dbRList.begin(); + for(int i=0;i<x;i++) iter++; + + strcpy(rec.dbName,iter->ptrDatabase->getName()); + rec.right=iter->databRight; + return true; + } + +bool User::loadRightFromRec(AuthDbRRec &rec) + { return setDatabaseRights(rec.dbName,rec.right); + } + +bool User::isValid() + { return valid; + } + +//------------------------------------------------------------------ + +UserManager::UserManager() + { + lastUserID=0; + } +UserManager::~UserManager() + { + } +void UserManager::loadDefaults() + { + insertNewUser("rasadmin"); + userList.back().changePTPassword("rasadmin"); + userList.back().setAdminRights(admR_full); + userList.back().setDefaultDBRights(dbR_read+dbR_write); + + // if there is a license for one user only, we don't have a rasguest + if(userManager.insertNewUser("rasguest")) + { userList.back().changePTPassword("rasguest"); + userList.back().setAdminRights(admR_none); + userList.back().setDefaultDBRights(dbR_read); + } + + } + + +bool UserManager::insertNewUser(const char *userName) + { + if(testUniqueness(userName)==false) return false; + + User tempUser; + userList.push_back(tempUser); + User &refUser=userList.back(); + + refUser.init(lastUserID++,userName); + + return true; + } + +bool UserManager::removeUser(const char *userName) + { + list<User>::iterator iter=userList.begin(); + for(int i=0;i<userList.size();i++) + { + if(strcmp(iter->getName(),userName)==0) + { + userList.erase(iter); + return true; + } + + iter++; + } + return false; + } + +int UserManager::countUsers() + { return userList.size(); + } + +User& UserManager::operator[](int x) + { list<User>::iterator iter=userList.begin(); + for(int i=0;i<x;i++) iter++; + return *iter; + } + +User& UserManager::operator[](const char* userName) + { + list<User>::iterator iter=userList.begin(); + //std::cout<<"Size="<<userList.size()<<std::endl; + for(int i=0;i<userList.size();i++) + { + //std::cout<<i<<" "<<iter->getName()<<" "<<iter->isValid()<<std::endl; + if(strcmp(iter->getName(),userName)==0) return *iter; + iter++; + } + return protElem; + } + +void UserManager::removeDatabaseRights(const char *databName) + { + list<User>::iterator iter=userList.begin(); + for(int i=0;i<userList.size();i++) + { + iter->removeDatabaseRights(databName); + iter++; + } + } + +bool UserManager::testUniqueness(const char* userName) + { + list<User>::iterator iter=userList.begin(); + for(int i=0;i<userList.size();i++) + { + if(strcmp(iter->getName(),userName)==0) return false; + iter++; + } + return true; + } + +User& UserManager::loadUser(AuthUserRec &rec) + { removeUser(rec.userName); + + User tempUser; + userList.push_back(tempUser); + User &refUser=userList.back(); + + refUser.loadFromRec(rec); + + return refUser; + } +long UserManager::getLastUserID() + { return lastUserID; + } +void UserManager::setLastUserID(long lastUserID) + { this->lastUserID=lastUserID; + } + +User* UserManager::acceptEntry(const char *name,const char *encrPass) + { + list<User>::iterator iter=userList.begin(); + for(int i=0;i<userList.size();i++) + { + if(iter->isThisMe(name,encrPass)) return &(*iter); + iter++; + } + return NULL; + } +bool UserManager::reset() + { + if(config.isTestModus()==false) return false; + + while(userList.size()) + { + userList.pop_front(); + } + return true; + } + +bool UserManager::acceptChangeName(const char *oldName,const char *newName) + { + if(strcmp(oldName,newName)==0) return true; // if someone really wants to change a name with the same, + + return testUniqueness(newName); + } + +//################################################################## + +Authorization::Authorization() + { inConfigFile=false; + + authFileName[0]=0; + +#ifdef HIGHLANDER + char *rasHome=CONFDIR; + if(rasHome!=0) sprintf(authFileName,"%s/",rasHome); +#endif + + strcat(authFileName,"rasmgr_auth.dat"); + globalInitAdminRight=admR_none; + globalInitDatabRight=dbR_none; + } + +bool Authorization::acceptEntry(const char *header) + { + char myheader[500]; strncpy(myheader,header,299); myheader[299]=0; + char *auth=strstr(myheader,"Authorization:"); + if(!auth) return false; + + //TALK("auth="<<auth); + + char *scheme=strtok(auth+strlen("Authorization:")," "); + if(!scheme) return false; + + char *uname=strtok(NULL,":"); + char *upass=strtok(NULL," \r\n\t"); + + //TALK("Auth. scheme="<<scheme<<" user="<<uname<<" pass="<<upass); + + curUser=userManager.acceptEntry(uname,upass); + if(curUser==NULL) return false; + + return true; + } + +bool Authorization::hasFullAdmin() + { return curUser->hasAdminRights(admR_full); + } +const char* Authorization::getUserName() + { return curUser->getName(); + } + +const char* Authorization::getCapability(const char *serverName,const char *databaseName, bool readonly) + { + //Format of Capability (no brackets()) + //$I(userID)$E(effectivRights)$B(databaseName)$T(timeout)$N(serverName)$D(messageDigest)$K + int rights=curUser->getEffectiveDatabaseRights(databaseName); + + if(readonly) rights&=~dbR_write; + + const char *rString=convertDatabRights(rights); + + long userID=curUser->getUserID(); + + char capaS[300]; + sprintf(capaS,"$I%d$E%s$B%s$T%s$N%s",userID,rString,databaseName,getFormatedTime(180),serverName); + + static char capaQ[300]; + sprintf(capaQ,"$Canci%s",capaS); + + char digest[50]; // 33 is enough + messageDigest(capaQ,digest,"MD5"); + + sprintf(capaQ,"%s$D%s$K",capaS,digest); + + return capaQ; + } + +void Authorization::startConfigFile() + { inConfigFile=true; + } + +void Authorization::endConfigFile() + { inConfigFile=false; + } +bool Authorization::isInConfigFile() + { return inConfigFile; + } + +bool Authorization::saveAuthFile() + { + + std::ofstream ofs(authFileName); + if(!ofs) return false; + + EVP_MD_CTX mdctx; + const EVP_MD *md; + unsigned int md_len; + //unsigned char md_value[30]; + + OpenSSL_add_all_digests(); + md = EVP_get_digestbyname("MD5"); + if(!md) return false; + EVP_DigestInit(&mdctx, md); + + AuthFileHeader header; + header.fileID =AUTHFILEID; + header.fileVersion =AUTHFILEVERS; + header.headerLength=sizeof(header); + header.lastUserID =userManager.getLastUserID(); + strcpy(header.hostName,config.getHostName()); + header.countUsers=userManager.countUsers(); + for(int i=0;i< 50;i++) header.messageDigest[i]=0; + header.globalInitAdmR=authorization.getGlobalInitAdminRights(); + header.globalInitDbsR=authorization.getGlobalInitDatabRights(); + + randomGenerator.setFileVersion(header.fileVersion); + + for(int i=0;i<100;i++) header._unused[i]=0; + ofs.write((char*)&header,sizeof(header)); + + initcrypt(header.lastUserID); + + for(int i=0;i<header.countUsers;i++) + { + User &u=userManager[i]; + AuthUserRec uRec; + + u.loadToRec(uRec); + EVP_DigestUpdate(&mdctx,&uRec,sizeof(uRec)); + + crypt(&uRec,sizeof(uRec)); + ofs.write((char*)&uRec,sizeof(uRec)); + + for(int j=0;j<u.countRights();j++) + { + AuthDbRRec dbRec; + u.loadRightToRec(j,dbRec); + EVP_DigestUpdate(&mdctx,&dbRec,sizeof(dbRec)); + + crypt(&dbRec,sizeof(dbRec)); + ofs.write((char*)&dbRec,sizeof(dbRec)); + } + + } + + EVP_DigestFinal(&mdctx, header.messageDigest, &md_len); + ofs.seekp(0,std::ios::beg); + ofs.write((char*)&header,sizeof(header)); + ofs.close(); + + return true; + } + +int Authorization::readAuthFile() + { + int result = RC_OK; // enum values from rasmgr_users.hh + + ENTER( "Authorization::readAuthFile: enter." ); + + VLOG << "Inspecting authorization file '"<<authFileName<< "'..."; + + std::ifstream ifs(authFileName); + if(!ifs) + result = ERRAUTHFNOTF; + + if (result == RC_OK) + { + int ver=verifyAuthFile(ifs); + if(ver) + result = ver; + TALK( "Authorization::readAuthFile: verifyAuthFile returned ." << result ); + } + + if (result == RC_OK) + { + AuthFileHeader header; + ifs.read((char*)&header,sizeof(header)); + + // not necessary, done by verify if(header.fileID != AUTHFIELID) return ERRAUTHFCORR; + + // this is needed + if(!randomGenerator.setFileVersion(header.fileVersion)) return ERRAUTHFVERS; + + userManager.setLastUserID(header.lastUserID); + authorization.setGlobalInitAdminRights(header.globalInitAdmR); + authorization.setGlobalInitDatabRights(header.globalInitDbsR); + + TALK( "Authorization::readAuthFile: Auth file host="<<header.hostName<<" lastUserID="<<header.lastUserID ); + initcrypt(header.lastUserID); + + for(int i=0;i<header.countUsers;i++) + { + AuthUserRec uRec; + ifs.read((char*)&uRec,sizeof(uRec)); + + decrypt(&uRec,sizeof(uRec)); + + User &u=userManager.loadUser(uRec); + TALK( "Authorization::readAuthFile: User "<<i<<" "<<u.getName() ); + + for(int j=0;j<uRec.countRights;j++) + { + AuthDbRRec dbRec; + ifs.read((char*)&dbRec,sizeof(dbRec)); + + decrypt(&dbRec,sizeof(dbRec)); + u.loadRightFromRec(dbRec); + } + } + } + + if (result != ERRAUTHFNOTF) + ifs.close(); + + switch(result) + { + case RC_OK: + VLOG << "ok" << endl; + break; + case ERRAUTHFNOTF: + cout<<"Warning: User authorization file not found, using default user settings."<<std::endl; + break; + case ERRAUTHFCORR: + cout<<"Error: User authorization file is corrupt, aborting."<<std::endl; + break; + case ERRAUTHFWRHOST: + cout<<"Error: User authorization file is not for this host."<<std::endl; + break; + case ERRAUTHFVERS: + cout<<"Error: User authorization file is incompatible due to different encryption used - see migration documentation."<<std::endl; + break; + default: // should not occur, internal enum mismatch + cout<<"Error: Internal evaluation error."<<std::endl; + break; + } + + LEAVE( "Authorization::readAuthFile: leave. result=" << result ); + return result; + } // readAuthFile() + +int Authorization::verifyAuthFile(std::ifstream &ifs) + { + EVP_MD_CTX mdctx; + const EVP_MD *md; + unsigned int md_len; + unsigned char md_value[50]; + + OpenSSL_add_all_digests(); + md = EVP_get_digestbyname("MD5"); + if(!md) + return false; + + EVP_DigestInit(&mdctx, md); + + AuthFileHeader header; + ifs.read((char*)&header,sizeof(header)); + + if(header.fileID != AUTHFILEID) + return ERRAUTHFCORR; + + if(!randomGenerator.setFileVersion(header.fileVersion)) + return ERRAUTHFVERS; + + if(!hostCmp(header.hostName,config.getHostName())) + return ERRAUTHFWRHOST; + + initcrypt(header.lastUserID); + + /* + for(int i=0;i<header.countUsers;i++) + { + AuthUserRec uRec; + ifs.read(&uRec,sizeof(uRec)); + decrypt(&uRec,sizeof(uRec)); + + EVP_DigestUpdate(&mdctx,&uRec,sizeof(uRec)); + + for(int j=0;j<uRec.countRights;j++) + { + AuthDbRRec dbRec; + ifs.read(&dbRec,sizeof(dbRec)); + decrypt(&dbRec,sizeof(dbRec)); + + EVP_DigestUpdate(&mdctx,&dbRec,sizeof(dbRec)); + } + + } + */ + #define MAXBUFF 500 + unsigned char buff[MAXBUFF]; + long cpos = ifs.tellg(); + ifs.seekg(0,std::ios::end); + long endpos=ifs.tellg(); + ifs.seekg(cpos,std::ios::beg); + //std::cout<<"c="<<cpos<<" end="<<endpos<<std::endl; + + for(;;) + { + int r = endpos-cpos > MAXBUFF ? MAXBUFF : endpos-cpos; + if(r==0) + break; //{ std::cout<<"xx"<<std::endl; break; } + ifs.read((char*)buff,r); + if(!ifs) + break; //{ std::cout<<"yy"<<std::endl; break; } + cpos +=r; + + decrypt(buff,r); + + EVP_DigestUpdate(&mdctx,buff,r); + //std::cout<<"verify "<<r<<std::endl; + } + + EVP_DigestFinal(&mdctx, md_value, &md_len); + + ifs.seekg(0,std::ios::beg); + + for(int i=0;i<md_len;i++) + { if(md_value[i]!=header.messageDigest[i]) + return ERRAUTHFCORR; + } + return 0; + } + +void Authorization::initcrypt(int seed) + { //srand(seed); + randomGenerator.init(seed); + } +void Authorization::crypt(void *vbuffer,int length) + { + unsigned char *buff=(unsigned char*)vbuffer; + // std::cout<<" crypt length="<<length<<flush; + for(int i=0;i<length;i++) buff[i]^=randomGenerator();//rand(); + } +void Authorization::decrypt(void *vbuffer,int length) + { + crypt(vbuffer,length); + } + +const char* Authorization::getSyncroString() + { + return getFormatedTime(0); + } + +const char* Authorization::getFormatedTime(long int delta) + { + time_t tmx=time(NULL)+delta; + tm *b=localtime(&tmx); + static char buffer[30]; + sprintf(buffer,"%d:%d:%d:%d:%d:%d",b->tm_mday,b->tm_mon+1,b->tm_year+1900,b->tm_hour,b->tm_min,b->tm_sec); + return buffer; + } + + +void Authorization::setGlobalInitAdminRights(int rights) + { globalInitAdminRight=rights; + } +void Authorization::setGlobalInitDatabRights(int rights) + { globalInitDatabRight=rights; + } +int Authorization::getGlobalInitAdminRights() + { return globalInitAdminRight; + } +int Authorization::getGlobalInitDatabRights() + { return globalInitDatabRight; + } + +const char * Authorization::convertAdminRights(int r) + { static char buffer[20]; + + char C= (r & admR_config) ? 'C':'.'; + char A= (r & admR_acctrl) ? 'A':'.'; + char S= (r & admR_sysup ) ? 'S':'.'; + char I= (r & admR_info ) ? 'I':'.'; + + sprintf(buffer,"%c%c%c%c",C,A,S,I); + return buffer; + } +const char * Authorization::convertDatabRights(int r) + { static char buffer[20]; + + char R= (r & dbR_read) ? 'R':'.'; + char W= (r & dbR_write) ? 'W':'.'; + + sprintf(buffer,"%c%c",R,W); + return buffer; + + } + +const char * Authorization::convertGlobalInitAdminRights() + { return convertAdminRights(globalInitAdminRight); + } +const char * Authorization::convertGlobalInitDatabRights() + { return convertDatabRights(globalInitDatabRight); + } + +int Authorization::convertAdminRights(const char *rString) + { + int rights=admR_none; + for(int i=0;rString[i];i++) + { + switch(rString[i]) + { case 'C': rights|=admR_config;break; + case 'A': rights|=admR_acctrl;break; + case 'S': rights|=admR_sysup; break; + case 'I': rights|=admR_info; break; + case 'R': + case 'W': + case '[': + case ']': + case '-': + case '.': break; + default : return -1; // error!!! + } + } + return rights; + } + +int Authorization::convertDatabRights(const char *rString) + { + int rights=dbR_none; + for(int i=0;rString[i];i++) + { + switch(rString[i]) + { + case 'C': + case 'A': + case 'S': + case 'I': break; + case 'R': rights|=dbR_read; break; + case 'W': rights|=dbR_write;break; + case '[': + case ']': + case '-': + case '.': break; + default : return -1; // error!!! + } + } + return rights; + } + +bool Authorization::hasAdminRights(int right) + { + return inConfigFile ? true : curUser->hasAdminRights(right); + } + +// return name of alternate config file; +// takes value from preceding saveAltAuthFile() call. +const char *Authorization::getAltAuthFileName() + { + return altAuthFileName; + } + +// save auth file at original place, i.e., under the name of authFile +bool Authorization::saveOrigAuthFile() + { + ENTER( "Authorization::saveOrigAuthFile: enter." ); + + bool result = saveAuthFile(); + + LEAVE( "Authorization::saveOrigAltAuthFile: leave. result=" << result ); + return result; + } + +// save authorization file in another file, same dir as auth file +bool Authorization::saveAltAuthFile() + { + bool result = true; + char origFileName[ sizeof(authFileName) ]; // temp copy of origFileName + + ENTER( "Authorization::saveAltAuthFile: enter." ); + + // save original file name + (void) strcpy( origFileName, authFileName ); + + // build temp file by appending a unique string + (void) strcpy( altAuthFileName, authFileName ); + (void) strcat( altAuthFileName, ".XXXXXX" ); // 6 * 'X', see man mkstemp() + + int altFile = mkstemp( altAuthFileName ); // replaces the Xs by unique string + if (altFile < 0) // error in creating file name + { + int tempError = errno; + TALK( "Authorization::saveAltAuthFile: error creating alternate file name: " << strerror(tempError) ); + result = false; + } + if (result == true) + { + // now we have a valid + open file, but we can't use it like that, because we open down below. + // so close it again, being happy that we have a valid file name. bad hack, though. + int closeResult = close( altFile ); + if (closeResult != 0) + TALK( "Authorization::saveAltAuthFile: error in temporary closing file, ignoring that." ); + } + + if (result == true) + { + (void) strcpy( authFileName, altAuthFileName ); // set file to be written to alternate name + result = saveAuthFile(); // save file, name has been substituted successfully + TALK( "Authorization::saveAltAuthFile: save to " << authFileName << " done, result=" << result ); + (void) strcpy( authFileName, origFileName ); // restore original auth file name + } + + LEAVE( "Authorization::saveAltAuthFile: leave. result=" << result ); + return result; + } + diff --git a/rasmgr/rasmgr_users.hh b/rasmgr/rasmgr_users.hh new file mode 100644 index 0000000..0bffe90 --- /dev/null +++ b/rasmgr/rasmgr_users.hh @@ -0,0 +1,257 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: rasmgr_users.hh + * + * MODULE: rasmgr + * CLASS: User, UserManager, Authorization + * + * PURPOSE: + * User management + * + * COMMENTS: + * None + * +*/ + +#ifndef RASMGR_USERS_HH +#define RASMGR_USERS_HH + +#include "rasmgr.hh" +#include "rasmgr_config.hh" +#include "rasmgr_dbm.hh" + +enum AdminRight + { + admR_none = 0, + admR_config= 1, // C + admR_acctrl= 2, // A + admR_sysup = 4, // S - up-down + admR_info = 8, // I + admR_full =255 + }; + +enum DatabRight // maybe we'll put them together one day + { + dbR_none = 0<<8, + dbR_read = 1<<8, // R + dbR_write = 2<<8 // W + }; + +struct UserDBRight + { + Database *ptrDatabase; + int databRight; + }; + +// For persistency +#define AUTHFILEID 26012001 +#define AUTHFILEVERS 2; + +struct AuthFileHeader + { + long fileID; + long fileVersion; + long headerLength; + long lastUserID; + char hostName[100]; + long countUsers; + unsigned char messageDigest[35]; + int globalInitAdmR; + int globalInitDbsR; + char _unused[100]; + }; + +struct AuthUserRec + { + long userID; + char userName[100]; + char passWord[50]; + + int adminRight; + int databRight; + long countRights; + char _unused[32]; + }; + +struct AuthDbRRec + { + char dbName[100]; + int right; + }; +//++++++++++++++++++++++++++++++++++++++++++++++++ + +class User + { + public: + User(); + void init(long userID, const char *name); + void changeName(const char *name); + void changePassword(const char *encrPass); + void changePTPassword(const char *plainTextPass); + + const char* getName(); + + long getUserID(); + + bool isThisMe(const char *name,const char *encrPass); + + void setAdminRights(int rights); + bool hasAdminRights(int rights); + int getAdminRights(); + + + void setDefaultDBRights(int); + int getDefaultDBRights(); + + int getEffectiveDatabaseRights(const char *databName); + bool setDatabaseRights(const char *databName,int rights); + bool removeDatabaseRights(const char *databName); + bool isTrusteeOn(const char *databName); + + void loadToRec(AuthUserRec&); + void loadFromRec(AuthUserRec&); + + long countRights(); + bool loadRightToRec(int,AuthDbRRec&); + bool loadRightFromRec(AuthDbRRec&); + bool isValid(); + private: + long userID; + char userName[100]; + char passWord[50]; + + int adminRight; + int databRight; + + list<UserDBRight> dbRList; + bool valid; + }; + +class UserManager + { + public: + UserManager(); + ~UserManager(); + void loadDefaults(); + bool insertNewUser(const char *userName); + bool removeUser(const char *userName); + int countUsers(); + User& operator[](int); + User& operator[](const char* userName); + + User* acceptEntry(const char *name,const char *encrPass); + void removeDatabaseRights(const char *databName); + // for loading only + User& loadUser(AuthUserRec&); + long getLastUserID(); + void setLastUserID(long); + bool reset(); + bool acceptChangeName(const char *oldName,const char *newName); + private: + bool testUniqueness(const char* userName); + list<User> userList; + User protElem; + + long lastUserID; + }; + +extern UserManager userManager; + +class Authorization + { + public: + Authorization(); + bool acceptEntry(const char*message); + const char *getUserName(); + bool hasFullAdmin(); + //bool hasConfigAdmin(); + const char* getSyncroString(); + const char* getCapability(const char *serverName,const char *databaseName, bool readonly); + void startConfigFile(); + void endConfigFile(); + int readAuthFile(); + bool saveOrigAuthFile(); + bool saveAltAuthFile(); + const char* getAltAuthFileName(); + + void setGlobalInitAdminRights(int rights); + void setGlobalInitDatabRights(int rights); + int getGlobalInitAdminRights(); + int getGlobalInitDatabRights(); + const char * convertGlobalInitAdminRights(); + const char * convertGlobalInitDatabRights(); + const char * convertAdminRights(int); + const char * convertDatabRights(int); + int convertAdminRights(const char *); + int convertDatabRights(const char *); + + bool hasAdminRights(int); + bool isInConfigFile(); + private: + int verifyAuthFile(std::ifstream&); + const char* getFormatedTime(long int); + + bool saveAuthFile(); + + void initcrypt(int); + void crypt(void*,int); + void decrypt(void*,int); + + + User *curUser; + bool inConfigFile; + char authFileName[100]; + char altAuthFileName[100]; + + int globalInitAdminRight; + int globalInitDatabRight; + }; + +extern Authorization authorization; + +class RandomGenerator + { + public: + RandomGenerator(); + + bool setFileVersion(long); // false, if not supported encr. method + + void init(unsigned int); + unsigned char operator()(); + bool insideTest(); + private: + static unsigned char randomTable[1000]; + unsigned int seed; + int fileVersion; + }; +extern RandomGenerator randomGenerator; + +// return codes: +#define RC_OK 0 +#define ERRAUTHFNOTF -1 +#define ERRAUTHFCORR -2 +#define ERRAUTHFWRHOST -3 +#define ERRAUTHFVERS -4 + +#endif diff --git a/rasmgr/test/Makefile b/rasmgr/test/Makefile new file mode 100644 index 0000000..6150f87 --- /dev/null +++ b/rasmgr/test/Makefile @@ -0,0 +1,46 @@ +# +# This file is part of rasdaman community. +# +# Rasdaman community is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Rasdaman community is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +# +# Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +# rasdaman GmbH. +# +# For more information please see <http://www.rasdaman.org> +# or contact Peter Baumann via <baumann@rasdaman.com>. # Top Level makefile. This points to the various modules that have to be build +# and/or deployed +# +######################### Definitions ############################ + +# standard include with general options +include $(RMANBASE)/Makefile.inc + +SRCCXX = test_hostcmp.cc + +OBJS = ${SRCCXX:%.cc=%.o} +MISCCLEAN = test_hostcmp + +########################### Targets ############################## + +all: test_hostcmp + +test_hostcmp: test_hostcmp.o ../hostcmp.o + $(PURIFY) $(CXX) $(CXXFLAGS) -o test_hostcmp $^ + +.PHONY: clean +clean: + -rm $(OBJS) $(MISCCLEAN) + +######################## Dependencies ############################ + diff --git a/rasmgr/test/Makefile.dep b/rasmgr/test/Makefile.dep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/rasmgr/test/Makefile.dep diff --git a/rasmgr/test/check_opensockets.sh b/rasmgr/test/check_opensockets.sh new file mode 100644 index 0000000..7ec3ab2 --- /dev/null +++ b/rasmgr/test/check_opensockets.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# check_opensockets.sh: periodically check the number of waiting sockets in the local system +# parameters: +# $1 sleep time between checks + +export SLEEPTIME=$1 + +if test "$1" == "" +then + echo usage: $0 sleepseconds + exit +fi + +echo "$0: start checking for open (waiting) sockets, in intervals of $SLEEPTIME seconds." + +while echo \--- check time: `date` \---------------- +do + netstat -na | grep WAIT + echo summary: `netstat -na | grep WAIT | wc -l` TIME_WAIT state sockets. + sleep $SLEEPTIME +done + +echo $0: done. + diff --git a/rasmgr/test/check_opensockets_idleresult.log b/rasmgr/test/check_opensockets_idleresult.log new file mode 100644 index 0000000..a309b52 --- /dev/null +++ b/rasmgr/test/check_opensockets_idleresult.log @@ -0,0 +1,92 @@ +./check_opensockets.sh: start checking for open (waiting) sockets, in intervals of 1 seconds. +--- check time: Die Jun 3 23:22:34 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:35 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:36 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:37 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:38 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:39 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:40 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:41 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:42 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:43 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:44 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:45 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:46 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:47 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:48 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:50 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:51 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:52 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:53 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:54 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:55 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:56 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:57 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:58 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:22:59 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:00 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:01 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:02 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:03 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:04 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:05 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:06 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:07 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:08 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:09 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:10 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:11 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:12 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:13 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:14 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:15 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:16 CEST 2003 ---------------- +tcp 0 0 127.0.0.1:620 127.0.0.1:686 TIME_WAIT +--- check time: Die Jun 3 23:23:18 CEST 2003 ---------------- +--- check time: Die Jun 3 23:23:19 CEST 2003 ---------------- +--- check time: Die Jun 3 23:23:20 CEST 2003 ---------------- +--- check time: Die Jun 3 23:23:21 CEST 2003 ---------------- +--- check time: Die Jun 3 23:23:22 CEST 2003 ---------------- +--- check time: Die Jun 3 23:23:23 CEST 2003 ---------------- +--- check time: Die Jun 3 23:23:24 CEST 2003 ---------------- diff --git a/rasmgr/test/test_hostcmp b/rasmgr/test/test_hostcmp Binary files differnew file mode 100644 index 0000000..774a6c6 --- /dev/null +++ b/rasmgr/test/test_hostcmp diff --git a/rasmgr/test/test_hostcmp.cc b/rasmgr/test/test_hostcmp.cc new file mode 100644 index 0000000..c88e330 --- /dev/null +++ b/rasmgr/test/test_hostcmp.cc @@ -0,0 +1,56 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ +/** + * SOURCE: test_hostcmp.cc + * + * MODULE: rasmgr + * CLASS: + * + * SYNOPSIS: + * test_hostcmp hostname1 hostname2 + * + * PURPOSE: + * test hostname comparison function of rasmgr, Configuration::hostCmp(). + * + * COMMENTS: + * None + * +*/ + +using namespace std; + +#include <iostream> + +extern int hostCmp( const char *h1, const char *h2 ); + +int main(int argc, char** argv) +{ + if (argc != 3) + { + cout << "usage: " << argv[0] << " hostname1 hostname2" << endl; + return -2; + } + + cout << argv[0] << ": hostCmp( " << argv[1] << ", " << argv[2] << " ) -> " << (hostCmp( argv[1], argv[2]) ? "true" : "false") << endl; + return 0; +} diff --git a/rasmgr/test/test_hostcmp.sh b/rasmgr/test/test_hostcmp.sh new file mode 100644 index 0000000..c9a99cc --- /dev/null +++ b/rasmgr/test/test_hostcmp.sh @@ -0,0 +1,36 @@ +#!/bin/bash -x + +# test_hostcmp: test hostCmp() function of rasmgr + +# return codes +export RC_OK=0 +export RC_ERROR=1 + +INDENT="+++ " +TESTPROG=./test_hostcmp + +HOST_SHORT=abc +HOST_LONG=${HOST_SHORT}.def.ghi +HOST_OTHER=xyz + +echo $0: testing rasmgr/rascontrol. + +echo $INDENT good cases, equal: +$TESTPROG $HOST_SHORT $HOST_SHORT +$TESTPROG $HOST_LONG $HOST_LONG +$TESTPROG "" "" + +echo $INDENT general cases, equal: +$TESTPROG $HOST_SHORT $HOST_LONG +$TESTPROG $HOST_LONG $HOST_SHORT + +echo $INDENT general cases, NOT equal: +$TESTPROG $HOST_SHORT $HOST_OTHER +$TESTPROG $HOST_LONG $HOST_OTHER + +echo $INDENT special cases, NOT equal: +$TESTPROG $HOST_SHORT "" +$TESTPROG "" $HOST_SHORT + +echo $0: done. + diff --git a/rasmgr/test/test_rasmgr.sh b/rasmgr/test/test_rasmgr.sh new file mode 100644 index 0000000..5d4f522 --- /dev/null +++ b/rasmgr/test/test_rasmgr.sh @@ -0,0 +1,103 @@ +#!/bin/bash -x + +# return codes +export RC_OK=0 +export RC_ERROR=1 + +export RASMGR=rasmgr +export RASMGR_CONF=rasmgr.conf +export RASMGR_AUTH=rasmgr_auth.dat + +# settings demanded by rasmgr +export RMANHOME=. + +echo $0: testing rasmgr/rascontrol. + +# start rasmgr + +if [ -f nohup.out ] +then + rm nohup.out || (echo "Error: cannot remove old nohup log file, exiting."; exit $RC_ERROR) +fi + +export START_AND_KILL_RASMGR=" + ( nohup $RASMGR & ; \ + export RASMGR_PID=\$!; \ + sleep 4; \ + kill \$RASMGR_PID; \ + cat nohup.out; \ + rm nohup.out \ + ) \ + || echo Error: cannot start/terminate/... rasmgr. " + +echo $START_AND_KILL_RASMGR + +# -- conf file ------------------- + +# conf file not present +if [ -f $RASMGR_CONF ] +then + rm $RASMGR_CONF || (echo "Error: cannot remove old $RASMGR_CONF file, exiting."; exit $RC_ERROR) +fi +eval $START_AND_KILL_RASMGR +exit + +# empty conf file +echo >$RASMGR_CONF +$START_AND_KILL_RASMGR + +# conf file not readable +chmod a-r $RASMGR_CONF +$START_AND_KILL_RASMGR +chmod a+rw $RASMGR_CONF + +# illegal cmd in conf file +cat >$RASMGR_CONF <<EOF +iiiiiiiiiiillegal cmd +EOF +$START_AND_KILL_RASMGR + +# for further tests, we provide a sane conf file +cat >$RASMGR_CONF <<EOF +define dbh melange_host -connect / +define srv S1 -host `hostname` -type r -port 0x29999901 -dbh melange_host +define db RASBASE -dbh melange_host +EOF + +# -- auth file ------------------- +rm $RASMGR_AUTH || (echo "Error: cannot remove old $RASMGR_AUTH file, exiting."; exit $RC_ERROR) + +# auth file not present +if [ -f $RASMGR_AUTH ] +then + rm $RASMGR_AUTH || (echo "Error: cannot remove old $RASMGR_AUTH file, exiting."; exit $RC_ERROR) +fi +$START_AND_KILL_RASMGR + +# empty auth file +echo >$RASMGR_AUTH +$START_AND_KILL_RASMGR + +# auth file not readable +chmod a-r $RASMGR_AUTH +$START_AND_KILL_RASMGR +chmod a+rw $RASMGR_AUTH + +# auth file has illegal contents +echo iiiiiiiiillegal >$RAMGR_AUTH +$START_AND_KILL_RASMGR + +# auth file has good contents -- let rasmgr generate the default file, then check +rm $RASMGR_AUTH +$START_AND_KILL_RASMGR +$START_AND_KILL_RASMGR + +# -- highlander check ------------ +# start an additional rasmgr, see what it tells +$START_RASMGR +export SECOND_PID=$! +$START_AND_KILL_RASMGR +kill $SECOND_PID + +echo $0: done. + |