summaryrefslogtreecommitdiffstats
path: root/mcop/dispatcher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mcop/dispatcher.cpp')
-rw-r--r--mcop/dispatcher.cpp1090
1 files changed, 1090 insertions, 0 deletions
diff --git a/mcop/dispatcher.cpp b/mcop/dispatcher.cpp
new file mode 100644
index 0000000..580a268
--- /dev/null
+++ b/mcop/dispatcher.cpp
@@ -0,0 +1,1090 @@
+ /*
+
+ Copyright (C) 2000-2001 Stefan Westerfeld
+ stefan@space.twc.de
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ */
+
+#include <config.h>
+#include "dispatcher.h"
+#include "delayedreturn.h"
+#include "startupmanager.h"
+#include "unixconnection.h"
+#include "tcpconnection.h"
+#include "referenceclean.h"
+#include "core.h"
+#include "md5auth.h"
+#include "mcoputils.h"
+#include "loopback.h"
+#include "debug.h"
+#include "ifacerepo_impl.h"
+#include "thread.h"
+
+#include <sys/stat.h>
+#include <stdio.h>
+#include <signal.h>
+#include <cstring>
+#include <cstdlib>
+#include <errno.h>
+#include <iostream>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#elif HAVE_SYS_TIME_H
+# include <sys/time.h>
+#else
+# include <time.h>
+#endif
+
+/* Dispatcher private data class (to ensure binary compatibility) */
+
+using namespace std;
+using namespace Arts;
+
+namespace Arts {
+
+class DispatcherWakeUpHandler;
+
+class DispatcherPrivate {
+public:
+ GlobalComm globalComm;
+ InterfaceRepo interfaceRepo;
+ AuthAccept *accept;
+ LoopbackConnection *loopbackConnection;
+ DelayedReturn *delayedReturn;
+ bool allowNoAuthentication;
+ Mutex mutex;
+
+ /*
+ * Thread condition that gets signalled whenever something relevant for
+ * waitForResult happens. Note that broken connections are also relevant
+ * for waitForResult.
+ */
+ ThreadCondition requestResultCondition;
+
+ /*
+ * Thread condition that gets signalled whenever something relevant for
+ * the server connection process happens. This is either:
+ * - authentication fails
+ * - authentication succeeds
+ * - a connection breaks
+ */
+ ThreadCondition serverConnectCondition;
+
+ DispatcherWakeUpHandler *wakeUpHandler;
+};
+
+/**
+ * Class that performs dispatcher wakeup.
+ *
+ * The sending thread (requesting wakeup) writes a byte to a pipe. The
+ * main thread watches the pipe, and as soon as the byte arrives, gets
+ * woken by the IOManager. This should work, no matter what type of IOManager
+ * is used (i.e. StdIOManager/GIOManager/QIOManager).
+ */
+class DispatcherWakeUpHandler : public IONotify {
+private:
+ enum { wReceive = 0, wSend = 1 };
+ int wakeUpPipe[2];
+
+public:
+ DispatcherWakeUpHandler()
+ {
+ if(pipe(wakeUpPipe) != 0)
+ arts_fatal("can't initialize wakeUp pipe (%s)",strerror(errno));
+
+ Dispatcher::the()->ioManager()->watchFD(wakeUpPipe[wReceive],
+ IOType::read | IOType::reentrant, this);
+ }
+ virtual ~DispatcherWakeUpHandler()
+ {
+ Dispatcher::the()->ioManager()->remove(this, IOType::all);
+
+ close(wakeUpPipe[wSend]);
+ close(wakeUpPipe[wReceive]);
+ }
+ void notifyIO(int fd, int type)
+ {
+ arts_return_if_fail(fd == wakeUpPipe[wReceive]);
+ arts_return_if_fail(type == IOType::read);
+
+ mcopbyte one;
+ int result;
+ do
+ result = read(wakeUpPipe[wReceive],&one,1);
+ while(result < 0 && errno == EINTR);
+ }
+ void wakeUp()
+ {
+ mcopbyte one = 1;
+
+ int result;
+ do
+ result = write(wakeUpPipe[wSend],&one,1);
+ while(result < 0 && errno == EINTR);
+ }
+};
+
+}
+
+Dispatcher *Dispatcher::_instance = 0;
+
+Dispatcher::Dispatcher(IOManager *ioManager, StartServer startServer)
+{
+ assert(!_instance);
+ _instance = this;
+
+ /* private data pointer */
+ d = new DispatcherPrivate();
+
+ lock();
+
+ /* makes arts_debug/arts_message/arts_return_if_fail/... threadsafe */
+ Debug::initMutex();
+
+ generateServerID();
+
+ if(ioManager)
+ {
+ _ioManager = ioManager;
+ deleteIOManagerOnExit = false;
+ }
+ else
+ {
+ _ioManager = new StdIOManager;
+ deleteIOManagerOnExit = true;
+ }
+
+ d->wakeUpHandler = new DispatcherWakeUpHandler;
+
+ objectManager = new ObjectManager;
+
+ notificationManager = new NotificationManager;
+
+ if(startServer & startUnixServer)
+ {
+ unixServer = new UnixServer(this,serverID);
+ if(!unixServer->running())
+ {
+ delete unixServer;
+ arts_warning("[mcop dispatcher] Couldn't start UnixServer");
+ unixServer = 0;
+ }
+ }
+ else unixServer = 0;
+
+ if(startServer & startTCPServer)
+ {
+ tcpServer = new TCPServer(this);
+ if(!tcpServer->running())
+ {
+ delete tcpServer;
+ arts_warning("[mcop dispatcher] Couldn't start TCPServer");
+ tcpServer = 0;
+ }
+ }
+ else tcpServer = 0;
+
+ d->allowNoAuthentication = startServer & noAuthentication;
+ d->accept = 0;
+ d->loopbackConnection = new LoopbackConnection(serverID);
+ d->interfaceRepo = InterfaceRepo::_from_base(new InterfaceRepo_impl());
+ d->delayedReturn = 0;
+
+ _flowSystem = 0;
+ referenceClean = new ReferenceClean(objectPool);
+
+ /*
+ * setup signal handler for SIGPIPE
+ */
+ orig_sigpipe = signal(SIGPIPE,SIG_IGN);
+ if((orig_sigpipe != SIG_DFL) && (orig_sigpipe != SIG_IGN))
+ {
+ cerr << "[mcop dispatcher] warning: user defined signal handler found for"
+ " SIG_PIPE, overriding" << endl;
+ }
+
+ StartupManager::startup();
+
+ /*
+ * this is required for publishing global references - might be a good
+ * reason for startup priorities as since this is required for cookie&co,
+ * no communication is possible without that
+ */
+
+
+ char *env = getenv("ARTS_SERVER");
+ bool envOk = false;
+ if(env)
+ {
+ string url = "tcp:"; url += env;
+ Connection *conn = connectUrl(url);
+ arts_debug("connection to %s for globalComm", url.c_str());
+ if(conn)
+ {
+ arts_debug("hint %s", conn->findHint("GlobalComm").c_str());
+ d->globalComm = Reference(conn->findHint("GlobalComm"));
+ envOk = true;
+ arts_debug("using globalcomm from env variable");
+ }
+ }
+
+ if(!envOk)
+ {
+ string globalCommName
+ = MCOPUtils::readConfigEntry("GlobalComm","Arts::TmpGlobalComm");
+ d->globalComm = GlobalComm(SubClass(globalCommName));
+ }
+
+ // --- initialize MD5auth ---
+ /*
+ * Path for random seed: better to store it in home, because some
+ * installations wipe /tmp on reboot.
+ */
+ string seedpath = MCOPUtils::createFilePath("random-seed");
+ string mcopdir = MCOPUtils::mcopDirectory();
+ if(!mcopdir.empty()) seedpath = mcopdir + "/random-seed";
+ arts_md5_auth_init_seed(seedpath.c_str());
+
+ /*
+ * first generate a new random cookie and try to set secret-cookie to it
+ * as put will not overwrite, this has no effect if there is already a
+ * secret cookie
+ */
+ char *cookie = arts_md5_auth_mkcookie();
+ globalComm().put("secret-cookie",cookie);
+
+ /*
+ * Then get the secret cookie from globalComm. As we've just set one,
+ * and as it is never removed, this always works.
+ */
+ string secretCookie = globalComm().get("secret-cookie");
+ if(!arts_md5_auth_set_cookie(secretCookie.c_str()))
+ {
+ /*
+ * Handle the case where the cookie obtained from GlobalComm is not
+ * a valid cookie (i.e. too short) - this should practically never
+ * happen. In this case, we will remove the cookie and overwrite it
+ * with our previously generated cookie.
+ */
+ arts_warning("[mcop dispatcher] Bad md5 secret-cookie obtained from %s - replacing it",
+ globalComm()._interfaceName().c_str());
+
+ globalComm().erase("secret-cookie");
+ globalComm().put("secret-cookie",cookie);
+
+ if(!arts_md5_auth_set_cookie(cookie))
+ arts_fatal("error initializing md5 secret cookie "
+ "(generated cookie invalid)");
+ }
+ memset(cookie,0,strlen(cookie)); // try to keep memory clean
+ free(cookie);
+
+ string::iterator i; // try to keep memory clean from secret cookie
+ for(i=secretCookie.begin();i != secretCookie.end();i++) *i = 'y';
+
+ unlock();
+}
+
+Dispatcher::~Dispatcher()
+{
+ lock();
+
+ /* no interaction possible now anymore - remove our global references */
+ if(objectManager)
+ objectManager->removeGlobalReferences();
+
+ /* remove everything that might have been tagged for remote copying */
+ referenceClean->forceClean();
+ delete referenceClean;
+
+ d->globalComm = GlobalComm::null();
+
+ /* shutdown all extensions we loaded */
+ if(objectManager)
+ objectManager->shutdownExtensions();
+
+ StartupManager::shutdown();
+
+ /* drop all open connections */
+ list<Connection *>::iterator ci;
+ for(ci=connections.begin(); ci != connections.end();ci++)
+ {
+ Connection *conn = *ci;
+ conn->drop();
+ }
+ d->requestResultCondition.wakeAll();
+ d->serverConnectCondition.wakeAll();
+
+ /*
+ * remove signal handler for SIGPIPE
+ */
+ signal(SIGPIPE,orig_sigpipe);
+
+
+ d->interfaceRepo = InterfaceRepo::null();
+
+ if(d->accept)
+ {
+ delete d->accept;
+ d->accept = 0;
+ }
+
+ if(d->loopbackConnection)
+ {
+ d->loopbackConnection->_release();
+ d->loopbackConnection = 0;
+ }
+ if(unixServer)
+ {
+ delete unixServer;
+ unixServer = 0;
+ }
+
+ if(tcpServer)
+ {
+ delete tcpServer;
+ tcpServer = 0;
+ }
+
+ if(notificationManager)
+ {
+ delete notificationManager;
+ notificationManager = 0;
+ }
+
+ if(objectManager && Object_base::_objectCount() == 0)
+ {
+ objectManager->removeExtensions();
+ delete objectManager;
+ objectManager = 0;
+ }
+
+ if(d->wakeUpHandler)
+ {
+ delete d->wakeUpHandler;
+ d->wakeUpHandler = 0;
+ }
+
+ if(deleteIOManagerOnExit)
+ {
+ delete _ioManager;
+ _ioManager = 0;
+ }
+
+ if(Object_base::_objectCount())
+ {
+ cerr << "[mcop dispatcher] warning: leaving MCOP Dispatcher and still "
+ << Object_base::_objectCount() << " object references alive." << endl;
+ list<Object_skel *> which = objectPool.enumerate();
+ list<Object_skel *>::iterator i;
+ for(i = which.begin(); i != which.end();i++)
+ cerr << " - " << (*i)->_interfaceName() << endl;
+ }
+
+ if(Type::_typeCount())
+ {
+ cerr << "[mcop dispatcher] warning: leaving MCOP Dispatcher and still "
+ << Type::_typeCount() << " types alive." << endl;
+ }
+
+ if(GenericDataPacket::_dataPacketCount())
+ {
+ cerr << "[mcop dispatcher] warning: leaving MCOP Dispatcher and still "
+ << GenericDataPacket::_dataPacketCount()
+ << " data packets alive." << endl;
+ }
+
+ Debug::freeMutex();
+
+ unlock();
+
+ /* private data pointer */
+ assert(d);
+ delete d;
+ d = 0;
+
+ assert(_instance);
+ _instance = 0;
+}
+
+InterfaceRepo Dispatcher::interfaceRepo()
+{
+ return d->interfaceRepo;
+}
+
+FlowSystem_impl *Dispatcher::flowSystem()
+{
+ assert(_flowSystem);
+ return _flowSystem;
+}
+
+GlobalComm Dispatcher::globalComm()
+{
+ assert(!d->globalComm.isNull());
+ return d->globalComm;
+}
+
+void Dispatcher::setFlowSystem(FlowSystem_impl *fs)
+{
+ assert(!_flowSystem);
+ _flowSystem = fs;
+}
+
+Dispatcher *Dispatcher::the()
+{
+ return _instance;
+}
+
+Buffer *Dispatcher::waitForResult(long requestID, Connection *connection)
+{
+ bool isMainThread = SystemThreads::the()->isMainThread();
+ Buffer *b = requestResultPool[requestID];
+
+ connection->_copy(); // Keep extra ref
+
+ while(!b && !connection->broken()) {
+ if(isMainThread)
+ _ioManager->processOneEvent(true);
+ else
+ d->requestResultCondition.wait(d->mutex);
+
+ b = requestResultPool[requestID];
+ }
+
+ requestResultPool.releaseSlot(requestID);
+
+ if(connection->broken()) // connection went away before we got some result
+ b = 0;
+
+ connection->_release(); // Give up extra ref
+
+ return b;
+}
+
+Buffer *Dispatcher::createRequest(long& requestID, long objectID, long methodID)
+{
+ Buffer *buffer = new Buffer;
+
+ // write mcop header record
+ buffer->writeLong(MCOP_MAGIC);
+ buffer->writeLong(0); // message length - to be patched later
+ buffer->writeLong(mcopInvocation);
+
+ // generate a request ID
+ requestID = requestResultPool.allocSlot();
+
+ // write invocation record
+ buffer->writeLong(objectID);
+ buffer->writeLong(methodID);
+ buffer->writeLong(requestID);
+
+ return buffer;
+}
+
+Buffer *Dispatcher::createOnewayRequest(long objectID, long methodID)
+{
+ Buffer *buffer = new Buffer;
+
+ // write mcop header record
+ buffer->writeLong(MCOP_MAGIC);
+ buffer->writeLong(0); // message length - to be patched later
+ buffer->writeLong(mcopOnewayInvocation);
+
+ // write oneway invocation record
+ buffer->writeLong(objectID);
+ buffer->writeLong(methodID);
+
+ return buffer;
+}
+
+void Dispatcher::handle(Connection *conn, Buffer *buffer, long messageType)
+{
+ _activeConnection = conn;
+
+#ifdef DEBUG_IO
+ printf("got a message %ld, %ld bytes in body\n",
+ messageType,buffer->remaining());
+ if(conn->connState() == Connection::unknown)
+ cout << "connectionState = unknown" << endl;
+ if(conn->connState() == Connection::expectClientHello)
+ cout << "connectionState = expectClientHello" << endl;
+ if(conn->connState() == Connection::expectServerHello)
+ cout << "connectionState = expectServerHello" << endl;
+ if(conn->connState() == Connection::expectAuthAccept)
+ cout << "connectionState = expectAuthAccept" << endl;
+ if(conn->connState() == Connection::established)
+ cout << "connectionState = established" << endl;
+#endif
+ switch(conn->connState())
+ {
+ case Connection::established:
+ /*
+ * we're connected to a trusted server, so we can accept
+ * invocations
+ */
+ if(messageType == mcopInvocation) {
+#ifdef DEBUG_MESSAGES
+ printf("[got Invocation]\n");
+#endif
+ long objectID = buffer->readLong();
+ long methodID = buffer->readLong();
+ long requestID = buffer->readLong();
+
+ Buffer *result = new Buffer;
+ // write mcop header record
+ result->writeLong(MCOP_MAGIC);
+ result->writeLong(0); // message length - to be patched later
+ result->writeLong(mcopReturn);
+
+ // write result record (returnCode is written by dispatch)
+ result->writeLong(requestID);
+
+ // perform the request
+ Object_skel *object = objectPool[objectID];
+ object->_copy();
+ object->_dispatch(buffer,result,methodID);
+ object->_release();
+
+ assert(!buffer->readError() && !buffer->remaining());
+ delete buffer;
+
+ if(d->delayedReturn)
+ {
+ delete result;
+
+ result = new Buffer;
+ result->writeLong(MCOP_MAGIC);
+ result->writeLong(0); // to be patched later
+ result->writeLong(mcopReturn);
+ result->writeLong(requestID);
+
+ d->delayedReturn->initialize(conn,result);
+ d->delayedReturn = 0;
+ }
+ else /* return normally */
+ {
+ result->patchLength();
+ conn->qSendBuffer(result);
+ }
+ return; /* everything ok - leave here */
+ }
+
+ if(messageType == mcopReturn)
+ {
+#ifdef DEBUG_MESSAGES
+ printf("[got Return]\n");
+#endif
+ long requestID = buffer->readLong();
+ requestResultPool[requestID] = buffer;
+ d->requestResultCondition.wakeAll();
+
+ return; /* everything ok - leave here */
+ }
+
+ if(messageType == mcopOnewayInvocation) {
+#ifdef DEBUG_MESSAGES
+ printf("[got OnewayInvocation]\n");
+#endif
+ long objectID = buffer->readLong();
+ long methodID = buffer->readLong();
+
+ // perform the request
+ Object_skel *object = objectPool[objectID];
+ object->_copy();
+ object->_dispatch(buffer,methodID);
+ object->_release();
+
+ assert(!buffer->readError() && !buffer->remaining());
+ delete buffer;
+
+ return; /* everything ok - leave here */
+ }
+ break;
+
+ case Connection::expectServerHello:
+ if(messageType == mcopServerHello)
+ {
+#ifdef DEBUG_MESSAGES
+ printf("[got ServerHello]\n");
+#endif
+ /*
+ * if we get a server hello, answer with a client hello
+ */
+ ServerHello h;
+ h.readType(*buffer);
+ bool valid = (!buffer->readError() && !buffer->remaining());
+ delete buffer;
+
+ if(!valid) break; // invalid hello received -> forget it
+
+ conn->setServerID(h.serverID);
+
+ /*
+ * check if md5auth or noauth is offered by the server
+ */
+ bool md5authSupported = false;
+ bool noauthSupported = false;
+ vector<string>::iterator ai;
+ for(ai = h.authProtocols.begin(); ai != h.authProtocols.end(); ai++)
+ {
+ if(*ai == "md5auth") md5authSupported = true;
+ if(*ai == "noauth") noauthSupported = true;
+ }
+
+ if(noauthSupported) // noauth is usually easier to pass ;)
+ {
+ Buffer *helloBuffer = new Buffer;
+
+ Header header(MCOP_MAGIC,0,mcopClientHello);
+ header.writeType(*helloBuffer);
+ ClientHello clientHello(serverID,"noauth","");
+ clientHello.writeType(*helloBuffer);
+
+ helloBuffer->patchLength();
+
+ conn->qSendBuffer(helloBuffer);
+ conn->setConnState(Connection::expectAuthAccept);
+ return; /* everything ok - leave here */
+ }
+ else if(md5authSupported)
+ {
+ Buffer *helloBuffer = new Buffer;
+
+ Header header(MCOP_MAGIC,0,mcopClientHello);
+ header.writeType(*helloBuffer);
+ ClientHello clientHello(serverID,"md5auth","");
+
+ const char *random_cookie = h.authSeed.c_str();
+ if(strlen(random_cookie) == 32)
+ {
+ char *response = arts_md5_auth_mangle(random_cookie);
+ clientHello.authData = response;
+#ifdef DEBUG_AUTH
+ printf(" got random_cookie = %s\n",random_cookie);
+ printf("reply with authData = %s\n",response);
+#endif
+ free(response);
+ }
+ clientHello.writeType(*helloBuffer);
+
+ helloBuffer->patchLength();
+
+ conn->qSendBuffer(helloBuffer);
+ conn->setConnState(Connection::expectAuthAccept);
+ return; /* everything ok - leave here */
+ }
+ else
+ {
+ cerr << "[mcop dispatcher] error: don't know authentication protocol" << endl;
+ cerr << " server offered: ";
+ for(ai = h.authProtocols.begin(); ai != h.authProtocols.end(); ai++)
+ cerr << *ai << " ";
+ cerr << endl;
+ }
+ }
+ break;
+
+ case Connection::expectClientHello:
+ if(messageType == mcopClientHello)
+ {
+#ifdef DEBUG_MESSAGES
+ printf("[got ClientHello]\n");
+#endif
+ ClientHello c;
+ c.readType(*buffer);
+ bool valid = (!buffer->readError() && !buffer->remaining());
+ delete buffer;
+
+ if(valid && (
+ (c.authProtocol == "md5auth" && c.authData == conn->cookie())
+ || (c.authProtocol == "noauth" && d->allowNoAuthentication) ))
+ {
+ conn->setServerID(c.serverID);
+
+ /* build hints only for the first connection */
+ if(!d->accept)
+ {
+ d->accept = new AuthAccept();
+
+ d->accept->hints.push_back(
+ "GlobalComm="+d->globalComm.toString());
+ d->accept->hints.push_back(
+ "InterfaceRepo="+d->interfaceRepo.toString());
+ }
+
+ Buffer *helloBuffer = new Buffer;
+ Header header(MCOP_MAGIC,0,mcopAuthAccept);
+ header.writeType(*helloBuffer);
+ d->accept->writeType(*helloBuffer);
+
+ helloBuffer->patchLength();
+ conn->qSendBuffer(helloBuffer);
+ conn->setConnState(Connection::established);
+
+ return; /* everything ok - leave here */
+ }
+ }
+ break;
+
+ case Connection::expectAuthAccept:
+ if(messageType == mcopAuthAccept)
+ {
+#ifdef DEBUG_MESSAGES
+ printf("[got AuthAccept]\n");
+#endif
+ AuthAccept a;
+ a.readType(*buffer);
+ delete buffer;
+#ifdef DEBUG_MESSAGES
+
+ vector<string>::iterator hi;
+ for(hi = a.hints.begin(); hi != a.hints.end(); hi++)
+ cout << "[got ConnectionHint] " << *hi << endl;
+
+#endif
+
+ conn->setConnState(Connection::established);
+ conn->setHints(a.hints);
+ d->serverConnectCondition.wakeAll();
+ return; /* everything ok - leave here */
+ }
+ break;
+
+ case Connection::unknown:
+ assert(false);
+ break;
+ }
+
+ /*
+ * We shouldn't reach this point if everything went all right
+ */
+ cerr << "[mcop dispatcher] Fatal communication error with a client" << endl;
+ if(conn->connState() != Connection::established)
+ {
+ cerr << "[mcop dispatcher] Authentication of this client was not successful" << endl;
+ cerr << "[mcop dispatcher] Connection dropped" << endl;
+ conn->drop();
+ }
+}
+
+long Dispatcher::addObject(Object_skel *object)
+{
+ long objectID = objectPool.allocSlot();
+
+ objectPool[objectID] = object;
+ return objectID;
+}
+
+void Dispatcher::removeObject(long objectID)
+{
+ assert(objectPool[objectID]);
+ objectPool.releaseSlot(objectID);
+}
+
+void Dispatcher::generateServerID()
+{
+ char *buffer;
+ buffer = arts_strdup_printf("%s-%04x-%08lx",
+ MCOPUtils::getFullHostname().c_str(),
+ getpid(),time(0));
+ serverID = buffer;
+ free(buffer);
+}
+
+string Dispatcher::objectToString(long objectID)
+{
+ Buffer b;
+ ObjectReference oref;
+
+ oref.serverID = serverID;
+ oref.objectID = objectID;
+
+ // prefer a unix domainsocket connection over a plain tcp connection
+ if(unixServer) oref.urls.push_back(unixServer->url());
+ if(tcpServer) oref.urls.push_back(tcpServer->url());
+
+ oref.writeType(b);
+
+ return b.toString("MCOP-Object");
+}
+
+bool Dispatcher::stringToObjectReference(ObjectReference& r, const string& s)
+{
+ if(strncmp(s.c_str(),"global:",7) == 0)
+ {
+ // if the object reference starts with "global:", it refers to
+ // a global object which can be found with the objectManager
+
+ string lookup = objectManager->getGlobalReference(&s.c_str()[7]);
+ return stringToObjectReference(r,lookup);
+ }
+
+
+ Buffer b;
+ if(!b.fromString(s,"MCOP-Object")) return false;
+
+ r.readType(b);
+ if(b.readError() || b.remaining()) return false;
+
+ return true;
+}
+
+void *Dispatcher::connectObjectLocal(ObjectReference& reference,
+ const string& interface)
+{
+ if(reference.serverID == serverID)
+ {
+ void *result = objectPool[reference.objectID]->_cast(interface);
+
+ if(result)
+ {
+ objectPool[reference.objectID]->_copy();
+ return result;
+ }
+ }
+
+ return 0;
+}
+
+Connection *Dispatcher::connectObjectRemote(ObjectReference& reference)
+{
+ if(reference.serverID == "null") // null reference?
+ return 0;
+
+ if(reference.serverID == serverID)
+ return loopbackConnection();
+
+ list<Connection *>::iterator i;
+
+ for(i=connections.begin(); i != connections.end();i++)
+ {
+ Connection *conn = *i;
+
+ if(conn->isConnected(reference.serverID))
+ {
+ // fixme: we should check for the existence of the object
+ // and increment a reference count or something like that
+ return conn;
+ }
+ }
+
+ /* try to connect the server */
+ vector<string>::iterator ui;
+ for(ui = reference.urls.begin(); ui != reference.urls.end(); ui++)
+ {
+ Connection *conn = connectUrl(*ui);
+ if(conn)
+ {
+ if(conn->isConnected(reference.serverID))
+ {
+ return conn;
+ }
+ else
+ {
+ /* we connected somewhere, but not the right server ;) */
+ connections.remove(conn);
+ conn->_release();
+ }
+ }
+ }
+ return 0;
+}
+
+Connection *Dispatcher::connectUrl(const string& url)
+{
+ Connection *conn = 0;
+ bool isMainThread = SystemThreads::the()->isMainThread();
+
+ if(strncmp(url.c_str(),"tcp:",4) == 0)
+ {
+ conn = new TCPConnection(url);
+ }
+ else if(strncmp(url.c_str(),"unix:",5) == 0)
+ {
+ conn = new UnixConnection(url);
+ }
+
+ if(conn)
+ {
+ conn->_copy(); // Keep extra ref for when the connection breaks
+ conn->setConnState(Connection::expectServerHello);
+
+ while((conn->connState() != Connection::established)
+ && !conn->broken())
+ {
+ if(isMainThread)
+ _ioManager->processOneEvent(true);
+ else
+ d->serverConnectCondition.wait(d->mutex);
+ }
+
+ if(conn->connState() == Connection::established)
+ {
+ connections.push_back(conn);
+ conn->_release(); // Give up extra ref
+ return conn;
+ }
+
+ // well - bad luck (building a connection failed)
+
+ // Give up extra ref
+ conn->_release();
+ }
+ return 0;
+}
+
+void Dispatcher::run()
+{
+ assert(SystemThreads::the()->isMainThread());
+
+ _ioManager->run();
+}
+
+void Dispatcher::terminate()
+{
+ _ioManager->terminate();
+}
+
+void Dispatcher::initiateConnection(Connection *connection)
+{
+ vector<string> authProtocols;
+ authProtocols.push_back("md5auth");
+
+ if(d->allowNoAuthentication)
+ authProtocols.push_back("noauth");
+
+ char *authSeed = arts_md5_auth_mkcookie();
+ char *authResult = arts_md5_auth_mangle(authSeed);
+
+ Buffer *helloBuffer = new Buffer;
+
+ Header header(MCOP_MAGIC,0,mcopServerHello);
+ header.writeType(*helloBuffer);
+ ServerHello serverHello("aRts/MCOP-1.0.0",serverID,authProtocols,authSeed);
+ serverHello.writeType(*helloBuffer);
+
+ helloBuffer->patchLength();
+
+ connection->qSendBuffer(helloBuffer);
+ connection->setConnState(Connection::expectClientHello);
+
+ connection->setCookie(authResult);
+ free(authSeed);
+ free(authResult);
+
+ connections.push_back(connection);
+}
+
+void Dispatcher::handleCorrupt(Connection *connection)
+{
+ if(connection->connState() != Connection::established)
+ {
+ cerr << "[mcop dispatcher] Received corrupt message on unauthenticated connection" <<endl;
+ cerr << "closing connection." << endl;
+ connection->drop();
+ d->serverConnectCondition.wakeAll();
+ }
+ else
+ {
+ cerr << "[mcop dispatcher] warning: got corrupt MCOP message !??" << endl;
+ }
+}
+
+void Dispatcher::handleConnectionClose(Connection *connection)
+{
+ /*
+ * we can't use enumerate here, because the "existing objects list" might
+ * be changing due to the other _disconnectRemote calls we make, so we
+ * enumerate() the objects manually
+ */
+ unsigned long l;
+ for(l=0; l<objectPool.max(); l++)
+ {
+ Object_skel *skel = objectPool[l];
+ if(skel) skel->_disconnectRemote(connection);
+ }
+
+ d->requestResultCondition.wakeAll();
+ d->serverConnectCondition.wakeAll();
+
+ /*
+ * FIXME:
+ *
+ * there may be error handling to do (e.g., check that the _stub's that
+ * still refer to that connection don't crash now).
+ */
+ connection->_release();
+
+ list<Connection *>::iterator i;
+ for(i=connections.begin(); i != connections.end();i++)
+ {
+ if(*i == connection)
+ {
+ connections.erase(i);
+ return;
+ }
+ }
+}
+
+Connection *Dispatcher::activeConnection()
+{
+ return _activeConnection;
+}
+
+Connection *Dispatcher::loopbackConnection()
+{
+ return d->loopbackConnection;
+}
+
+DelayedReturn *Dispatcher::delayReturn()
+{
+ assert(!d->delayedReturn);
+
+ return d->delayedReturn = new DelayedReturn();
+}
+
+Object_skel *Dispatcher::getLocalObject(long objectID)
+{
+ Object_skel *result = objectPool[objectID];
+
+ if(result) result->_copy();
+ return result;
+}
+
+void Dispatcher::lock()
+{
+ _instance->d->mutex.lock();
+}
+
+void Dispatcher::unlock()
+{
+ _instance->d->mutex.unlock();
+}
+
+void Dispatcher::wakeUp()
+{
+ if(SystemThreads::the()->isMainThread()) return;
+
+ _instance->d->wakeUpHandler->wakeUp();
+}
+
+/*
+void Dispatcher::reloadTraderData() is declared in trader_impl.cpp
+*/