summaryrefslogtreecommitdiffstats
path: root/rasmgr
diff options
context:
space:
mode:
Diffstat (limited to 'rasmgr')
-rw-r--r--rasmgr/Makefile.am60
-rw-r--r--rasmgr/hostcmp.cc66
-rw-r--r--rasmgr/ras_crypto.cc79
-rw-r--r--rasmgr/ras_crypto.hh49
-rw-r--r--rasmgr/rasmgr.hh116
-rw-r--r--rasmgr/rasmgr_comm.cc294
-rw-r--r--rasmgr/rasmgr_comm.hh100
-rw-r--r--rasmgr/rasmgr_comm_nb.cc921
-rw-r--r--rasmgr/rasmgr_comm_nb.hh196
-rw-r--r--rasmgr/rasmgr_config.cc601
-rw-r--r--rasmgr/rasmgr_config.hh134
-rw-r--r--rasmgr/rasmgr_dbm.cc573
-rw-r--r--rasmgr/rasmgr_dbm.hh171
-rw-r--r--rasmgr/rasmgr_error.cc101
-rw-r--r--rasmgr/rasmgr_error.hh91
-rw-r--r--rasmgr/rasmgr_host.cc575
-rw-r--r--rasmgr/rasmgr_host.hh122
-rw-r--r--rasmgr/rasmgr_localsrv.cc368
-rw-r--r--rasmgr/rasmgr_localsrv.hh81
-rw-r--r--rasmgr/rasmgr_main.cc256
-rw-r--r--rasmgr/rasmgr_master.hh136
-rw-r--r--rasmgr/rasmgr_master_nb.cc1007
-rw-r--r--rasmgr/rasmgr_master_nb_hack.cc1007
-rw-r--r--rasmgr/rasmgr_protocol.hh68
-rw-r--r--rasmgr/rasmgr_random.cc176
-rw-r--r--rasmgr/rasmgr_rascontrol.cc2187
-rw-r--r--rasmgr/rasmgr_rascontrol.hh166
-rw-r--r--rasmgr/rasmgr_rascontrol_help.cc521
-rw-r--r--rasmgr/rasmgr_srv.cc858
-rw-r--r--rasmgr/rasmgr_srv.cc.ORIG840
-rw-r--r--rasmgr/rasmgr_srv.hh165
-rw-r--r--rasmgr/rasmgr_users.cc861
-rw-r--r--rasmgr/rasmgr_users.hh257
-rw-r--r--rasmgr/test/Makefile46
-rw-r--r--rasmgr/test/Makefile.dep0
-rw-r--r--rasmgr/test/check_opensockets.sh25
-rw-r--r--rasmgr/test/check_opensockets_idleresult.log92
-rw-r--r--rasmgr/test/test_hostcmpbin0 -> 35013 bytes
-rw-r--r--rasmgr/test/test_hostcmp.cc56
-rw-r--r--rasmgr/test/test_hostcmp.sh36
-rw-r--r--rasmgr/test/test_rasmgr.sh103
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 &currentJob );
+ int processRequest( NbJob &currentJob );
+
+ 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 &currentJob)
+ {
+ 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 &currentJob )
+{
+ 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 &currentJob)
+ {
+ 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 &currentJob )
+{
+ 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
new file mode 100644
index 0000000..774a6c6
--- /dev/null
+++ b/rasmgr/test/test_hostcmp
Binary files differ
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.
+