dcop Library API Documentation

dcopserver.cpp

00001 /*****************************************************************
00002 
00003 #include "dcopserver.h"
00004 
00005 Copyright (c) 1999,2000 Preston Brown <pbrown@kde.org>
00006 Copyright (c) 1999,2000 Matthias Ettrich <ettrich@kde.org>
00007 Copyright (c) 1999,2001 Waldo Bastian <bastian@kde.org>
00008 
00009 Permission is hereby granted, free of charge, to any person obtaining a copy
00010 of this software and associated documentation files (the "Software"), to deal
00011 in the Software without restriction, including without limitation the rights
00012 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00013 copies of the Software, and to permit persons to whom the Software is
00014 furnished to do so, subject to the following conditions:
00015 
00016 The above copyright notice and this permission notice shall be included in
00017 all copies or substantial portions of the Software.
00018 
00019 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00020 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00021 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00022 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00023 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00024 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00025 
00026 ******************************************************************/
00027 
00028 #include <config.h>
00029 
00030 #include <sys/types.h>
00031 #ifdef HAVE_SYS_STAT_H
00032 #include <sys/stat.h>
00033 #endif
00034 #ifdef HAVE_SYS_PARAM_H
00035 #include <sys/param.h>
00036 #endif
00037 #include <sys/resource.h>
00038                    
00039 #include <unistd.h>
00040 #include <stdlib.h>
00041 #include <signal.h>
00042 #include <unistd.h>
00043 #include <fcntl.h>
00044 #include <errno.h>
00045 #ifdef HAVE_LIMITS_H
00046 #include <limits.h>
00047 #endif
00048 
00049 #define QT_CLEAN_NAMESPACE 1
00050 #include <qfile.h>
00051 #include <qtextstream.h>
00052 #include <qdatastream.h>
00053 #include <qptrstack.h>
00054 #include <qtimer.h>
00055 
00056 #include <dcopserver.h>
00057 #include <dcopsignals.h>
00058 #include <dcopclient.h>
00059 #include <dcopglobal.h>
00060 #include "dcop-path.h"
00061 
00062 // #define DCOP_DEBUG
00063 
00064 DCOPServer* the_server;
00065 
00066 template class QDict<DCOPConnection>;
00067 template class QPtrDict<DCOPConnection>;
00068 template class QPtrList<DCOPListener>;
00069 
00070 #define _DCOPIceSendBegin(x)    \
00071    int fd = IceConnectionNumber( x );       \
00072    long fd_fl = fcntl(fd, F_GETFL, 0);      \
00073    fcntl(fd, F_SETFL, fd_fl | O_NDELAY);
00074 #define _DCOPIceSendEnd()   \
00075    fcntl(fd, F_SETFL, fd_fl);
00076 
00077 static QCString findDcopserverShutdown()
00078 {
00079    QCString path = getenv("PATH");
00080    char *dir = strtok(path.data(), ":");
00081    while (dir)
00082    {
00083       QCString file = dir;
00084       file += "/dcopserver_shutdown";
00085       if (access(file.data(), X_OK) == 0)
00086          return file;
00087       dir = strtok(NULL, ":");
00088    }
00089    QCString file = DCOP_PATH;
00090    file += "/dcopserver_shutdown";
00091    if (access(file.data(), X_OK) == 0)
00092       return file;
00093 
00094    return QCString("dcopserver_shutdown");
00095 }
00096 
00097 static Bool HostBasedAuthProc ( char* /*hostname*/)
00098 {
00099     return false; // no host based authentication
00100 }
00101 
00102 extern "C" {
00103 extern IceWriteHandler _kde_IceWriteHandler;
00104 extern IceIOErrorHandler _kde_IceIOErrorHandler;
00105 void DCOPIceWriteChar(register IceConn iceConn, unsigned long nbytes, char *ptr);
00106 }
00107 
00108 static QCString readQCString(QDataStream &ds)
00109 {
00110    QCString result;
00111    Q_UINT32 len;
00112    ds >> len;
00113    QIODevice *device = ds.device();
00114    int bytesLeft = device->size()-device->at();
00115    if ((bytesLeft < 0 ) || (len > (uint) bytesLeft))
00116    {
00117       qWarning("Corrupt data!\n");
00118       return result;
00119    }
00120    result.QByteArray::resize( (uint)len );
00121    if (len > 0)
00122       ds.readRawBytes( result.data(), (uint)len);
00123    return result;
00124 }
00125 
00126 static QByteArray readQByteArray(QDataStream &ds)
00127 {
00128    QByteArray result;
00129    Q_UINT32 len;
00130    ds >> len;
00131    QIODevice *device = ds.device();
00132    int bytesLeft = device->size()-device->at();
00133    if ((bytesLeft < 0 ) || (len > (uint) bytesLeft))
00134    {
00135       qWarning("Corrupt data!\n");
00136       return result;
00137    }
00138    result.resize( (uint)len );
00139    if (len > 0)
00140       ds.readRawBytes( result.data(), (uint)len);
00141    return result;
00142 }
00143 
00144 static unsigned long writeIceData(IceConn iceConn, unsigned long nbytes, char *ptr)
00145 {
00146     int fd = IceConnectionNumber(iceConn);
00147     unsigned long nleft = nbytes;
00148     while (nleft > 0)
00149     {
00150     int nwritten;
00151 
00152     if (iceConn->io_ok)
00153         nwritten = write(fd, ptr, (int) nleft);
00154     else
00155         return 0;
00156 
00157     if (nwritten <= 0)
00158     {
00159             if (errno == EINTR)
00160                continue;
00161 
00162             if (errno == EAGAIN)
00163                return nleft;
00164 
00165         /*
00166          * Fatal IO error.  First notify each protocol's IceIOErrorProc
00167          * callback, then invoke the application IO error handler.
00168          */
00169 
00170         iceConn->io_ok = False;
00171 
00172         if (iceConn->connection_status == IceConnectPending)
00173         {
00174         /*
00175          * Don't invoke IO error handler if we are in the
00176          * middle of a connection setup.
00177          */
00178 
00179         return 0;
00180         }
00181 
00182         if (iceConn->process_msg_info)
00183         {
00184         int i;
00185 
00186         for (i = iceConn->his_min_opcode;
00187              i <= iceConn->his_max_opcode; i++)
00188         {
00189             _IceProcessMsgInfo *process;
00190 
00191             process = &iceConn->process_msg_info[
00192             i - iceConn->his_min_opcode];
00193 
00194             if (process->in_use)
00195             {
00196             IceIOErrorProc IOErrProc = process->accept_flag ?
00197                 process->protocol->accept_client->io_error_proc :
00198                 process->protocol->orig_client->io_error_proc;
00199 
00200             if (IOErrProc)
00201                 (*IOErrProc) (iceConn);
00202             }
00203         }
00204         }
00205 
00206         (*_kde_IceIOErrorHandler) (iceConn);
00207         return 0;
00208     }
00209 
00210     nleft -= nwritten;
00211     ptr   += nwritten;
00212     }
00213     return 0;
00214 }
00215 
00216 void DCOPIceWriteChar(register IceConn iceConn, unsigned long nbytes, char *ptr)
00217 {
00218     DCOPConnection* conn = the_server->findConn( iceConn );
00219 #ifdef DCOP_DEBUG
00220 qWarning("DCOPServer: DCOPIceWriteChar() Writing %d bytes to %d [%s]", nbytes, fd, conn ? conn->appId.data() : "<unknown>");
00221 #endif
00222 
00223     if (conn)
00224     {
00225        if (conn->outputBlocked)
00226        {
00227           QByteArray _data(nbytes);
00228           memcpy(_data.data(), ptr, nbytes);
00229 #ifdef DCOP_DEBUG
00230 qWarning("DCOPServer: _IceWrite() outputBlocked. Queuing %d bytes.", _data.size());
00231 #endif
00232           conn->outputBuffer.append(_data);
00233           return;
00234        }
00235        // assert(conn->outputBuffer.isEmpty());
00236     }
00237 
00238     unsigned long nleft = writeIceData(iceConn, nbytes, ptr);
00239     if ((nleft > 0) && conn)
00240     {
00241         QByteArray _data(nleft);
00242         memcpy(_data.data(), ptr, nleft);
00243         conn->waitForOutputReady(_data, 0);
00244         return;
00245     }
00246 }
00247 
00248 static void DCOPIceWrite(IceConn iceConn, const QByteArray &_data)
00249 {
00250     DCOPConnection* conn = the_server->findConn( iceConn );
00251 #ifdef DCOP_DEBUG
00252 qWarning("DCOPServer: DCOPIceWrite() Writing %d bytes to %d [%s]", _data.size(), fd, conn ? conn->appId.data() : "<unknown>");
00253 #endif
00254     if (conn)
00255     {
00256        if (conn->outputBlocked)
00257        {
00258 #ifdef DCOP_DEBUG
00259 qWarning("DCOPServer: DCOPIceWrite() outputBlocked. Queuing %d bytes.", _data.size());
00260 #endif
00261           conn->outputBuffer.append(_data);
00262           return;
00263        }
00264        // assert(conn->outputBuffer.isEmpty());
00265     }
00266 
00267     unsigned long nleft = writeIceData(iceConn, _data.size(), _data.data());
00268     if ((nleft > 0) && conn)
00269     {
00270         conn->waitForOutputReady(_data, _data.size() - nleft);
00271         return;
00272     }
00273 }
00274 
00275 void DCOPConnection::waitForOutputReady(const QByteArray &_data, int start)
00276 {
00277 #ifdef DCOP_DEBUG
00278 qWarning("DCOPServer: waitForOutputReady fd = %d datasize = %d start = %d", socket(), _data.size(), start);
00279 #endif
00280    outputBlocked = true;
00281    outputBuffer.append(_data);
00282    outputBufferStart = start;
00283    if (!outputBufferNotifier)
00284    {
00285       outputBufferNotifier = new QSocketNotifier(socket(), Write);
00286       connect(outputBufferNotifier, SIGNAL(activated(int)),
00287               the_server, SLOT(slotOutputReady(int)));
00288    }
00289    outputBufferNotifier->setEnabled(true);
00290    return;
00291 }
00292 
00293 void DCOPServer::slotOutputReady(int socket)
00294 {
00295 #ifdef DCOP_DEBUG
00296 qWarning("DCOPServer: slotOutputReady fd = %d", socket);
00297 #endif
00298    // Find out connection.
00299    DCOPConnection *conn = fd_clients.find(socket);
00300    //assert(conn);
00301    //assert(conn->outputBlocked);
00302    //assert(conn->socket() == socket);
00303    // Forward
00304    conn->slotOutputReady();
00305 }
00306 
00307 
00308 void DCOPConnection::slotOutputReady()
00309 {
00310    //assert(outputBlocked);
00311    //assert(!outputBuffer.isEmpty());
00312 
00313    QByteArray data = outputBuffer.first();
00314 
00315    int fd = socket();
00316 
00317    long fd_fl = fcntl(fd, F_GETFL, 0);
00318    fcntl(fd, F_SETFL, fd_fl | O_NDELAY);
00319    int nwritten = write(fd, data.data()+outputBufferStart, data.size()-outputBufferStart);
00320    int e = errno;
00321    fcntl(fd, F_SETFL, fd_fl);
00322 
00323 #ifdef DCOP_DEBUG
00324 qWarning("DCOPServer: slotOutputReady() %d bytes written", nwritten);
00325 #endif
00326 
00327    if (nwritten < 0)
00328    {
00329       if ((e == EINTR) || (e == EAGAIN))
00330          return;
00331       (*_kde_IceIOErrorHandler) (iceConn);
00332       return;
00333    }
00334    outputBufferStart += nwritten;
00335 
00336    if (outputBufferStart == data.size())
00337    {
00338       outputBufferStart = 0;
00339       outputBuffer.remove(outputBuffer.begin());
00340       if (outputBuffer.isEmpty())
00341       {
00342 #ifdef DCOP_DEBUG
00343 qWarning("DCOPServer: slotOutputRead() all data transmitted.");
00344 #endif
00345          outputBlocked = false;
00346          outputBufferNotifier->setEnabled(false);
00347       }
00348 #ifdef DCOP_DEBUG
00349 else
00350 {
00351 qWarning("DCOPServer: slotOutputRead() more data to send.");
00352 }
00353 #endif
00354    }
00355 }
00356 
00357 static void DCOPIceSendData(register IceConn _iceConn,
00358                             const QByteArray &_data)
00359 {
00360    if (_iceConn->outbufptr > _iceConn->outbuf)
00361    {
00362 #ifdef DCOP_DEBUG
00363 qWarning("DCOPServer: Flushing data, fd = %d", IceConnectionNumber(_iceConn));
00364 #endif
00365       IceFlush( _iceConn );
00366    }
00367    DCOPIceWrite(_iceConn, _data);
00368 }
00369 
00370 class DCOPListener : public QSocketNotifier
00371 {
00372 public:
00373     DCOPListener( IceListenObj obj )
00374     : QSocketNotifier( IceGetListenConnectionNumber( obj ),
00375                QSocketNotifier::Read, 0, 0)
00376 {
00377     listenObj = obj;
00378 }
00379 
00380     IceListenObj listenObj;
00381 };
00382 
00383 DCOPConnection::DCOPConnection( IceConn conn )
00384     : QSocketNotifier( IceConnectionNumber( conn ),
00385                QSocketNotifier::Read, 0, 0 )
00386 {
00387     iceConn = conn;
00388     notifyRegister = 0;
00389     _signalConnectionList = 0;
00390     daemon = false;
00391     outputBlocked = false;
00392     outputBufferNotifier = 0;
00393     outputBufferStart = 0;
00394 }
00395 
00396 DCOPConnection::~DCOPConnection()
00397 {
00398     delete _signalConnectionList;
00399     delete outputBufferNotifier;
00400 }
00401 
00402 DCOPSignalConnectionList *
00403 DCOPConnection::signalConnectionList()
00404 {
00405     if (!_signalConnectionList)
00406        _signalConnectionList = new DCOPSignalConnectionList;
00407     return _signalConnectionList;
00408 }
00409 
00410 static IceAuthDataEntry *authDataEntries;
00411 static char *addAuthFile;
00412 
00413 static IceListenObj *listenObjs;
00414 static int numTransports;
00415 static int ready[2];
00416 
00417 
00418 /* for printing hex digits */
00419 static void fprintfhex (FILE *fp, unsigned int len, char *cp)
00420 {
00421     static char hexchars[] = "0123456789abcdef";
00422 
00423     for (; len > 0; len--, cp++) {
00424     unsigned char s = *cp;
00425     putc(hexchars[s >> 4], fp);
00426     putc(hexchars[s & 0x0f], fp);
00427     }
00428 }
00429 
00430 /*
00431  * We use temporary files which contain commands to add entries to
00432  * the .ICEauthority file.
00433  */
00434 static void
00435 write_iceauth (FILE *addfp, IceAuthDataEntry *entry)
00436 {
00437     fprintf (addfp,
00438          "add %s \"\" %s %s ",
00439          entry->protocol_name,
00440          entry->network_id,
00441          entry->auth_name);
00442     fprintfhex (addfp, entry->auth_data_length, entry->auth_data);
00443     fprintf (addfp, "\n");
00444 }
00445 
00446 
00447 #ifndef HAVE_MKSTEMP
00448 static char *unique_filename (const char *path, const char *prefix)
00449 #else
00450 static char *unique_filename (const char *path, const char *prefix, int *pFd)
00451 #endif
00452 {
00453 #ifndef HAVE_MKSTEMP
00454 #ifndef X_NOT_POSIX
00455     return ((char *) tempnam (path, prefix));
00456 #else
00457     char tempFile[PATH_MAX];
00458     char *tmp;
00459 
00460     snprintf (tempFile, PATH_MAX, "%s/%sXXXXXX", path, prefix);
00461     tmp = (char *) mktemp (tempFile);
00462     if (tmp)
00463     {
00464         char *ptr = (char *) malloc (strlen (tmp) + 1);
00465         if (ptr != NULL)
00466         {
00467             strcpy (ptr, tmp);
00468         }
00469         return (ptr);
00470     }
00471     else
00472     return (NULL);
00473 #endif
00474 #else
00475     char tempFile[PATH_MAX];
00476     char *ptr;
00477 
00478     snprintf (tempFile, PATH_MAX, "%s/%sXXXXXX", path, prefix);
00479     ptr = static_cast<char *>(malloc(strlen(tempFile) + 1));
00480     if (ptr != NULL)
00481     {
00482         strcpy(ptr, tempFile);
00483         *pFd =  mkstemp(ptr);
00484     }
00485     return ptr;
00486 #endif
00487 }
00488 
00489 #if 0
00490 Status SetAuthentication_local (int count, IceListenObj *listenObjs)
00491 {
00492     int i;
00493     for (i = 0; i < count; i ++) {
00494     char *prot = IceGetListenConnectionString(listenObjs[i]);
00495     if (!prot) continue;
00496     char *host = strchr(prot, '/');
00497     char *sock = 0;
00498     if (host) {
00499         *host=0;
00500         host++;
00501         sock = strchr(host, ':');
00502         if (sock) {
00503         *sock = 0;
00504         sock++;
00505         }
00506     }
00507 #ifndef NDEBUG
00508     qDebug("DCOPServer: SetAProc_loc: conn %d, prot=%s, file=%s",
00509         (unsigned)i, prot, sock);
00510 #endif
00511     if (sock && !strcmp(prot, "local")) {
00512         chmod(sock, 0700);
00513     }
00514     IceSetHostBasedAuthProc (listenObjs[i], HostBasedAuthProc);
00515     free(prot);
00516     }
00517     return 1;
00518 }
00519 #endif
00520 
00521 #define MAGIC_COOKIE_LEN 16
00522 
00523 Status
00524 SetAuthentication (int count, IceListenObj *_listenObjs,
00525            IceAuthDataEntry **_authDataEntries)
00526 {
00527     FILE        *addfp = NULL;
00528     const char  *path;
00529     int         original_umask;
00530     int         i;
00531     QCString command;    
00532 #ifdef HAVE_MKSTEMP
00533     int         fd;
00534 #endif
00535 
00536     original_umask = umask (0077);      /* disallow non-owner access */
00537 
00538     path = getenv ("DCOP_SAVE_DIR");
00539     if (!path)
00540     path = "/tmp";
00541 #ifndef HAVE_MKSTEMP
00542     if ((addAuthFile = unique_filename (path, "dcop")) == NULL)
00543     goto bad;
00544 
00545     if (!(addfp = fopen (addAuthFile, "w")))
00546     goto bad;
00547 #else
00548     if ((addAuthFile = unique_filename (path, "dcop", &fd)) == NULL)
00549     goto bad;
00550 
00551     if (!(addfp = fdopen(fd, "wb")))
00552     goto bad;
00553 #endif
00554 
00555     if ((*_authDataEntries = static_cast<IceAuthDataEntry *>(malloc (count * 2 * sizeof (IceAuthDataEntry)))) == NULL)
00556     goto bad;
00557 
00558     for (i = 0; i < numTransports * 2; i += 2) {
00559     (*_authDataEntries)[i].network_id =
00560         IceGetListenConnectionString (_listenObjs[i/2]);
00561     (*_authDataEntries)[i].protocol_name = const_cast<char *>("ICE");
00562     (*_authDataEntries)[i].auth_name = const_cast<char *>("MIT-MAGIC-COOKIE-1");
00563 
00564     (*_authDataEntries)[i].auth_data =
00565         IceGenerateMagicCookie (MAGIC_COOKIE_LEN);
00566     (*_authDataEntries)[i].auth_data_length = MAGIC_COOKIE_LEN;
00567 
00568     (*_authDataEntries)[i+1].network_id =
00569         IceGetListenConnectionString (_listenObjs[i/2]);
00570     (*_authDataEntries)[i+1].protocol_name = const_cast<char *>("DCOP");
00571     (*_authDataEntries)[i+1].auth_name = const_cast<char *>("MIT-MAGIC-COOKIE-1");
00572 
00573     (*_authDataEntries)[i+1].auth_data =
00574         IceGenerateMagicCookie (MAGIC_COOKIE_LEN);
00575     (*_authDataEntries)[i+1].auth_data_length = MAGIC_COOKIE_LEN;
00576 
00577     write_iceauth (addfp, &(*_authDataEntries)[i]);
00578     write_iceauth (addfp, &(*_authDataEntries)[i+1]);
00579 
00580     IceSetPaAuthData (2, &(*_authDataEntries)[i]);
00581 
00582     IceSetHostBasedAuthProc (_listenObjs[i/2], HostBasedAuthProc);
00583     }
00584 
00585     fclose (addfp);
00586 
00587     umask (original_umask);
00588 
00589     command = DCOPClient::iceauthPath();
00590     
00591     if (command.isEmpty())
00592     {
00593        fprintf( stderr, "dcopserver: 'iceauth' not found in path, aborting.\n" );
00594        exit(1);
00595     }
00596     
00597     command += " source ";
00598     command += addAuthFile;
00599     system (command);
00600 
00601     unlink(addAuthFile);
00602 
00603     return (1);
00604 
00605  bad:
00606 
00607     if (addfp)
00608     fclose (addfp);
00609 
00610     if (addAuthFile) {
00611     unlink(addAuthFile);
00612     free(addAuthFile);
00613     }
00614 
00615     umask (original_umask);
00616 
00617     return (0);
00618 }
00619 
00620 /*
00621  * Free up authentication data.
00622  */
00623 void
00624 FreeAuthenticationData(int count, IceAuthDataEntry *_authDataEntries)
00625 {
00626     /* Each transport has entries for ICE and XSMP */
00627     int i;
00628 
00629     for (i = 0; i < count * 2; i++) {
00630     free (_authDataEntries[i].network_id);
00631     free (_authDataEntries[i].auth_data);
00632     }
00633 
00634     free(_authDataEntries);
00635     free(addAuthFile);
00636 }
00637 
00638 void DCOPWatchProc ( IceConn iceConn, IcePointer client_data, Bool opening, IcePointer* watch_data)
00639 {
00640     DCOPServer* ds = static_cast<DCOPServer*>(client_data);
00641 
00642     if (opening) {
00643     *watch_data = static_cast<IcePointer>(ds->watchConnection( iceConn ));
00644     }
00645     else  {
00646     ds->removeConnection( static_cast<void*>(*watch_data) );
00647     }
00648 }
00649 
00650 void DCOPProcessMessage( IceConn iceConn, IcePointer /*clientData*/,
00651              int opcode, unsigned long length, Bool swap)
00652 {
00653     the_server->processMessage( iceConn, opcode, length, swap );
00654 }
00655 
00656 void DCOPServer::processMessage( IceConn iceConn, int opcode,
00657                  unsigned long length, Bool /*swap*/)
00658 {
00659     DCOPConnection* conn = clients.find( iceConn );
00660     if ( !conn ) {
00661     qWarning("DCOPServer::processMessage message from unknown connection. [opcode = %d]", opcode);
00662     return;
00663     }
00664     switch( opcode ) {
00665     case DCOPSend:
00666     case DCOPReplyDelayed:
00667     {
00668         DCOPMsg *pMsg = 0;
00669         IceReadMessageHeader(iceConn, sizeof(DCOPMsg), DCOPMsg, pMsg);
00670         CARD32 key = pMsg->key;
00671         QByteArray ba( length );
00672         IceReadData(iceConn, length, ba.data() );
00673         QDataStream ds( ba, IO_ReadOnly );
00674         QCString fromApp = readQCString(ds);
00675             QCString toApp = readQCString(ds);
00676 
00677         DCOPConnection* target = findApp( toApp );
00678         int datalen = ba.size();
00679         if ( opcode == DCOPReplyDelayed ) {
00680         if ( !target )
00681             qWarning("DCOPServer::DCOPReplyDelayed for unknown connection.");
00682         else if ( !conn )
00683             qWarning("DCOPServer::DCOPReplyDelayed from unknown connection.");
00684         else if (!conn->waitingForDelayedReply.removeRef( target->iceConn ))
00685             qWarning("DCOPServer::DCOPReplyDelayed from/to does not match. (#2)");
00686                 else if (!target->waitingOnReply.removeRef(iceConn))
00687                        qWarning("DCOPServer::DCOPReplyDelayed for client who wasn't waiting on one!");
00688         }
00689         if ( target ) {
00690 #ifdef DCOP_DEBUG
00691 if (opcode == DCOPSend)
00692 {
00693    QCString obj = readQCString(obj);
00694    QCString fun = readQCString(fun);
00695    qWarning("Sending %d bytes from %s to %s. DCOPSend %s", length, fromApp.data(), toApp.data(), fun.data());
00696 }
00697 #endif
00698         IceGetHeader( target->iceConn, majorOpcode, opcode,
00699                   sizeof(DCOPMsg), DCOPMsg, pMsg );
00700         pMsg->key = key;
00701         pMsg->length += datalen;
00702         _DCOPIceSendBegin( target->iceConn );
00703         DCOPIceSendData(target->iceConn, ba);
00704                 _DCOPIceSendEnd();
00705         } else if ( toApp == "DCOPServer" ) {
00706         QCString obj = readQCString(ds);
00707         QCString fun = readQCString(ds);
00708         QByteArray data = readQByteArray(ds);
00709 
00710         QCString replyType;
00711         QByteArray replyData;
00712         if ( !receive( toApp, obj, fun, data, replyType, replyData, iceConn ) ) {
00713             qWarning("%s failure: object '%s' has no function '%s'", toApp.data(), obj.data(), fun.data() );
00714         }
00715         } else if ( toApp[toApp.length()-1] == '*') {
00716 #ifdef DCOP_DEBUG
00717 if (opcode == DCOPSend)
00718 {
00719    QCString obj = readQCString(obj);
00720    QCString fun = readQCString(fun);
00721    qWarning("Sending %d bytes from %s to %s. DCOPSend %s", length, fromApp.data(), toApp.data(), fun.data());
00722 }
00723 #endif
00724         // handle a multicast.
00725         QAsciiDictIterator<DCOPConnection> aIt(appIds);
00726         int l = toApp.length()-1;
00727         for ( ; aIt.current(); ++aIt) {
00728             DCOPConnection *client = aIt.current();
00729             if (!l || (strncmp(client->appId.data(), toApp.data(), l) == 0))
00730             {
00731                 IceGetHeader(client->iceConn, majorOpcode, DCOPSend,
00732                      sizeof(DCOPMsg), DCOPMsg, pMsg);
00733                 pMsg->key = key;
00734                 pMsg->length += datalen;
00735                 _DCOPIceSendBegin( client->iceConn );
00736                 DCOPIceSendData(client->iceConn, ba);
00737                             _DCOPIceSendEnd();
00738             }
00739         }
00740         }
00741     }
00742     break;
00743     case DCOPCall:
00744     case DCOPFind:
00745     {
00746         DCOPMsg *pMsg = 0;
00747         IceReadMessageHeader(iceConn, sizeof(DCOPMsg), DCOPMsg, pMsg);
00748         CARD32 key = pMsg->key;
00749         QByteArray ba( length );
00750         IceReadData(iceConn, length, ba.data() );
00751         QDataStream ds( ba, IO_ReadOnly );
00752         QCString fromApp = readQCString(ds);
00753         QCString toApp = readQCString(ds);
00754         DCOPConnection* target = findApp( toApp );
00755         int datalen = ba.size();
00756 
00757         if ( target ) {
00758 #ifdef DCOP_DEBUG
00759 if (opcode == DCOPCall)
00760 {
00761    QCString obj = readQCString(obj);
00762    QCString fun = readQCString(fun);
00763    qWarning("Sending %d bytes from %s to %s. DCOPCall %s", length, fromApp.data(), toApp.data(), fun.data());
00764 }
00765 #endif
00766         target->waitingForReply.append( iceConn );
00767                 conn->waitingOnReply.append( target->iceConn);
00768 
00769         IceGetHeader( target->iceConn, majorOpcode, opcode,
00770                   sizeof(DCOPMsg), DCOPMsg, pMsg );
00771         pMsg->key = key;
00772         pMsg->length += datalen;
00773         _DCOPIceSendBegin( target->iceConn );
00774         DCOPIceSendData(target->iceConn, ba);
00775                 _DCOPIceSendEnd();
00776         } else {
00777         QCString replyType;
00778         QByteArray replyData;
00779         bool b = false;
00780         // DCOPServer itself does not do DCOPFind.
00781         if ( (opcode == DCOPCall) && (toApp == "DCOPServer") ) {
00782             QCString obj = readQCString(ds);
00783             QCString fun = readQCString(ds);
00784             QByteArray data = readQByteArray(ds);
00785             b = receive( toApp, obj, fun, data, replyType, replyData, iceConn );
00786             if ( !b )
00787             qWarning("%s failure: object '%s' has no function '%s'", toApp.data(), obj.data(), fun.data() );
00788         }
00789 
00790         if (b) {
00791             QByteArray reply;
00792             QDataStream replyStream( reply, IO_WriteOnly );
00793             replyStream << toApp << fromApp << replyType << replyData.size();
00794             int replylen = reply.size() + replyData.size();
00795             IceGetHeader( iceConn, majorOpcode, DCOPReply,
00796                   sizeof(DCOPMsg), DCOPMsg, pMsg );
00797             if ( key != 0 )
00798             pMsg->key = key;
00799             else
00800             pMsg->key = serverKey++;
00801             pMsg->length += replylen;
00802                     _DCOPIceSendBegin( iceConn );
00803             DCOPIceSendData( iceConn, reply);
00804             DCOPIceSendData( iceConn, replyData);
00805                     _DCOPIceSendEnd();
00806         } else {
00807             QByteArray reply;
00808             QDataStream replyStream( reply, IO_WriteOnly );
00809             replyStream << toApp << fromApp;
00810             IceGetHeader( iceConn, majorOpcode, DCOPReplyFailed,
00811                   sizeof(DCOPMsg), DCOPMsg, pMsg );
00812             if ( key != 0 )
00813             pMsg->key = key;
00814             else
00815             pMsg->key = serverKey++;
00816             pMsg->length += reply.size();
00817                     _DCOPIceSendBegin( iceConn );
00818             DCOPIceSendData( iceConn, reply );
00819                     _DCOPIceSendEnd();
00820         }
00821         }
00822     }
00823     break;
00824     case DCOPReply:
00825     case DCOPReplyFailed:
00826     case DCOPReplyWait:
00827     {
00828         DCOPMsg *pMsg = 0;
00829         IceReadMessageHeader(iceConn, sizeof(DCOPMsg), DCOPMsg, pMsg);
00830         CARD32 key = pMsg->key;
00831         QByteArray ba( length );
00832         IceReadData(iceConn, length, ba.data() );
00833         QDataStream ds( ba, IO_ReadOnly );
00834             QCString fromApp = readQCString(ds);
00835             QCString toApp = readQCString(ds);
00836 
00837         DCOPConnection* connreply = findApp( toApp );
00838         int datalen = ba.size();
00839 
00840         if ( !connreply )
00841         qWarning("DCOPServer::DCOPReply for unknown connection.");
00842         else {
00843         conn->waitingForReply.removeRef( connreply->iceConn );
00844         if ( opcode == DCOPReplyWait )
00845                 {
00846             conn->waitingForDelayedReply.append( connreply->iceConn );
00847                 }
00848                 else
00849                 { // DCOPReply or DCOPReplyFailed
00850                     if (!connreply->waitingOnReply.removeRef(iceConn))
00851                        qWarning("DCOPServer::DCOPReply for client who wasn't waiting on one!");
00852                 }
00853         IceGetHeader( connreply->iceConn, majorOpcode, opcode,
00854                   sizeof(DCOPMsg), DCOPMsg, pMsg );
00855         pMsg->key = key;
00856         pMsg->length += datalen;
00857                 _DCOPIceSendBegin( connreply->iceConn );
00858         DCOPIceSendData(connreply->iceConn, ba);
00859                 _DCOPIceSendEnd();
00860         }
00861     }
00862     break;
00863     default:
00864     qWarning("DCOPServer::processMessage unknown message");
00865     }
00866 }
00867 
00868 static const IcePaVersionRec DCOPServerVersions[] = {
00869     { DCOPVersionMajor, DCOPVersionMinor,  DCOPProcessMessage }
00870 };
00871 
00872 static const IcePoVersionRec DUMMYVersions[] = {
00873     { DCOPVersionMajor, DCOPVersionMinor, 0 }
00874 };
00875 
00876 typedef struct DCOPServerConnStruct *DCOPServerConn;
00877 
00878 struct DCOPServerConnStruct
00879 {
00880     /*
00881      * We use ICE to esablish a connection with the client.
00882    */
00883 
00884     IceConn     iceConn;
00885 
00886 
00887     /*
00888    * Major and minor versions of the XSMP.
00889    */
00890 
00891     int         proto_major_version;
00892     int         proto_minor_version;
00893 
00894 
00895     QCString clientId;
00896 };
00897 
00898 
00899 static Status DCOPServerProtocolSetupProc ( IceConn iceConn,
00900                         int majorVersion, int minorVersion,
00901                         char* vendor, char* release,
00902                         IcePointer *clientDataRet,
00903                         char **/*failureReasonRet*/)
00904 {
00905     DCOPServerConn serverConn;
00906 
00907     /*
00908      * vendor/release are undefined for ProtocolSetup in DCOP
00909      */
00910 
00911     if (vendor)
00912     free (vendor);
00913     if (release)
00914     free (release);
00915 
00916 
00917     /*
00918      * Allocate new DCOPServerConn.
00919      */
00920 
00921     serverConn = new DCOPServerConnStruct;
00922 
00923     serverConn->iceConn = iceConn;
00924     serverConn->proto_major_version = majorVersion;
00925     serverConn->proto_minor_version = minorVersion;
00926     //serverConn->clientId already initialized
00927 
00928     *clientDataRet = static_cast<IcePointer>(serverConn);
00929 
00930 
00931     return 1;
00932 }
00933 
00934 static int pipeOfDeath[2];
00935 
00936 static void sighandler(int sig)
00937 {
00938     if (sig == SIGHUP) {
00939     signal(SIGHUP, sighandler);
00940     return;
00941     }
00942 
00943     write(pipeOfDeath[1], "x", 1);
00944 }
00945 
00946 DCOPServer::DCOPServer(bool _suicide)
00947     : QObject(0,0), currentClientNumber(0), appIds(263), clients(263)
00948 {
00949     serverKey = 42;
00950 
00951     suicide = _suicide;
00952 
00953     dcopSignals = new DCOPSignals;
00954 
00955     extern int _kde_IceLastMajorOpcode; // from libICE
00956     if (_kde_IceLastMajorOpcode < 1 )
00957         IceRegisterForProtocolSetup(const_cast<char *>("DUMMY"),
00958                     const_cast<char *>("DUMMY"),
00959                     const_cast<char *>("DUMMY"),
00960                     1, const_cast<IcePoVersionRec *>(DUMMYVersions),
00961                     DCOPAuthCount, const_cast<char **>(DCOPAuthNames),
00962                     DCOPClientAuthProcs, 0);
00963     if (_kde_IceLastMajorOpcode < 1 )
00964     qWarning("DCOPServer Error: incorrect major opcode!");
00965 
00966     the_server = this;
00967     if (( majorOpcode = IceRegisterForProtocolReply (const_cast<char *>("DCOP"),
00968                              const_cast<char *>(DCOPVendorString),
00969                              const_cast<char *>(DCOPReleaseString),
00970                              1, const_cast<IcePaVersionRec *>(DCOPServerVersions),
00971                              1, const_cast<char **>(DCOPAuthNames),
00972                              DCOPServerAuthProcs,
00973                              HostBasedAuthProc,
00974                              DCOPServerProtocolSetupProc,
00975                              NULL,  /* IceProtocolActivateProc - we don't care about
00976                                    when the Protocol Reply is sent, because the
00977                                    session manager can not immediately send a
00978                                    message - it must wait for RegisterClient. */
00979                              NULL   /* IceIOErrorProc */
00980                              )) < 0)
00981     {
00982         qWarning("Could not register DCOP protocol with ICE");
00983     }
00984 
00985     char errormsg[256];
00986     int orig_umask = umask(0); /*old libICE's don't reset the umask() they set */
00987     if (!IceListenForConnections (&numTransports, &listenObjs,
00988                   256, errormsg))
00989     {
00990         fprintf (stderr, "%s\n", errormsg);
00991         exit (1);
00992     } else {
00993         (void) umask(orig_umask);
00994         // publish available transports.
00995         QCString fName = DCOPClient::dcopServerFile();
00996         FILE *f;
00997         if(!(f = ::fopen(fName.data(), "w+"))) {
00998             fprintf (stderr, "Can not create file %s: %s\n",
00999              fName.data(), ::strerror(errno));
01000         exit(1);
01001         }
01002         char *idlist = IceComposeNetworkIdList(numTransports, listenObjs);
01003         if (idlist != 0) {
01004             fprintf(f, "%s", idlist);
01005         free(idlist);
01006         }
01007         fprintf(f, "\n%i\n", getpid());
01008         fclose(f);
01009             // Create a link named like the old-style (KDE 2.x) naming
01010             QCString compatName = DCOPClient::dcopServerFileOld();
01011             ::symlink(fName,compatName);
01012     }
01013 
01014 #if 0
01015     if (!SetAuthentication_local(numTransports, listenObjs))
01016         qFatal("DCOPSERVER: authentication setup failed.");
01017 #endif
01018     if (!SetAuthentication(numTransports, listenObjs, &authDataEntries))
01019         qFatal("DCOPSERVER: authentication setup failed.");
01020 
01021     IceAddConnectionWatch (DCOPWatchProc, static_cast<IcePointer>(this));
01022     _IceWriteHandler = DCOPIceWriteChar;
01023 
01024     listener.setAutoDelete( true );
01025     DCOPListener* con;
01026     for ( int i = 0; i < numTransports; i++) {
01027     con = new DCOPListener( listenObjs[i] );
01028     listener.append( con );
01029     connect( con, SIGNAL( activated(int) ), this, SLOT( newClient(int) ) );
01030     }
01031     char c = 0;
01032     write(ready[1], &c, 1); // dcopserver is started
01033     close(ready[1]);
01034 
01035     m_timer =  new QTimer(this);
01036     connect( m_timer, SIGNAL(timeout()), this, SLOT(slotTerminate()) );
01037     m_deadConnectionTimer = new QTimer(this);
01038     connect( m_deadConnectionTimer, SIGNAL(timeout()), this, SLOT(slotCleanDeadConnections()) );
01039 }
01040 
01041 DCOPServer::~DCOPServer()
01042 {
01043     system(findDcopserverShutdown()+" --nokill");
01044     IceFreeListenObjs(numTransports, listenObjs);
01045     FreeAuthenticationData(numTransports, authDataEntries);
01046     delete dcopSignals;
01047 }
01048 
01049 
01050 DCOPConnection* DCOPServer::findApp( const QCString& appId )
01051 {
01052     if ( appId.isNull() )
01053     return 0;
01054     DCOPConnection* conn = appIds.find( appId );
01055     return conn;
01056 }
01057 
01061 void DCOPServer::slotCleanDeadConnections()
01062 {
01063 qWarning("DCOP Cleaning up dead connections.");
01064     while(!deadConnections.isEmpty())
01065     {
01066        IceConn iceConn = deadConnections.take(0);
01067        IceSetShutdownNegotiation (iceConn, False);
01068        (void) IceCloseConnection( iceConn );
01069     }
01070 }
01071 
01075 void DCOPServer::ioError( IceConn iceConn  )
01076 {
01077     deadConnections.removeRef(iceConn);
01078     deadConnections.prepend(iceConn);
01079     m_deadConnectionTimer->start(0, true);
01080 }
01081 
01082 
01083 void DCOPServer::processData( int /*socket*/ )
01084 {
01085     IceConn iceConn = static_cast<const DCOPConnection*>(sender())->iceConn;
01086     IceProcessMessagesStatus status = IceProcessMessages( iceConn, 0, 0 );
01087     if ( status == IceProcessMessagesIOError ) {
01088         deadConnections.removeRef(iceConn);
01089         if (deadConnections.isEmpty())
01090            m_deadConnectionTimer->stop();
01091     IceSetShutdownNegotiation (iceConn, False);
01092     (void) IceCloseConnection( iceConn );
01093     }
01094 }
01095 
01096 void DCOPServer::newClient( int /*socket*/ )
01097 {
01098     IceAcceptStatus status;
01099     IceConn iceConn = IceAcceptConnection( static_cast<const  DCOPListener*>(sender())->listenObj, &status);
01100     if (!iceConn) {
01101       if (status == IceAcceptBadMalloc)
01102      qWarning("Failed to alloc connection object!\n");
01103       else // IceAcceptFailure
01104          qWarning("Failed to accept ICE connection!\n");
01105       return;
01106     }
01107 
01108     IceSetShutdownNegotiation( iceConn, False );
01109 
01110     IceConnectStatus cstatus;
01111     while ((cstatus = IceConnectionStatus (iceConn))==IceConnectPending) {
01112     (void) IceProcessMessages( iceConn, 0, 0 );
01113     }
01114 
01115     if (cstatus != IceConnectAccepted) {
01116     if (cstatus == IceConnectIOError)
01117         qWarning ("IO error opening ICE Connection!\n");
01118     else
01119         qWarning ("ICE Connection rejected!\n");
01120         deadConnections.removeRef(iceConn);
01121     (void) IceCloseConnection (iceConn);
01122     }
01123 }
01124 
01125 void* DCOPServer::watchConnection( IceConn iceConn )
01126 {
01127     DCOPConnection* con = new DCOPConnection( iceConn );
01128     connect( con, SIGNAL( activated(int) ), this, SLOT( processData(int) ) );
01129 
01130     clients.insert(iceConn, con );
01131     fd_clients.insert( IceConnectionNumber(iceConn), con);
01132 
01133     return static_cast<void*>(con);
01134 }
01135 
01136 void DCOPServer::removeConnection( void* data )
01137 {
01138     DCOPConnection* conn = static_cast<DCOPConnection*>(data);
01139 
01140     dcopSignals->removeConnections(conn);
01141 
01142     clients.remove(conn->iceConn );
01143     fd_clients.remove( IceConnectionNumber(conn->iceConn) );
01144 
01145     // Send DCOPReplyFailed to all in conn->waitingForReply
01146     while (!conn->waitingForReply.isEmpty()) {
01147     IceConn iceConn = conn->waitingForReply.take(0);
01148     if (iceConn) {
01149         DCOPConnection* target = clients.find( iceConn );
01150         qWarning("DCOP aborting call from '%s' to '%s'", target ? target->appId.data() : "<unknown>" , conn->appId.data() );
01151         QByteArray reply;
01152         DCOPMsg *pMsg;
01153         IceGetHeader( iceConn, majorOpcode, DCOPReplyFailed,
01154               sizeof(DCOPMsg), DCOPMsg, pMsg );
01155         pMsg->key = 1;
01156         pMsg->length += reply.size();
01157             _DCOPIceSendBegin( iceConn );
01158         DCOPIceSendData(iceConn, reply);
01159             _DCOPIceSendEnd();
01160             if (!target)
01161                qWarning("DCOP Error: unknown target in waitingForReply");
01162             else if (!target->waitingOnReply.removeRef(conn->iceConn))
01163                qWarning("DCOP Error: client in waitingForReply wasn't waiting on reply");
01164     }
01165     }
01166 
01167     // Send DCOPReplyFailed to all in conn->waitingForDelayedReply
01168     while (!conn->waitingForDelayedReply.isEmpty()) {
01169     IceConn iceConn = conn->waitingForDelayedReply.take(0);
01170     if (iceConn) {
01171         DCOPConnection* target = clients.find( iceConn );
01172         qWarning("DCOP aborting (delayed) call from '%s' to '%s'", target ? target->appId.data() : "<unknown>", conn->appId.data() );
01173         QByteArray reply;
01174         DCOPMsg *pMsg;
01175         IceGetHeader( iceConn, majorOpcode, DCOPReplyFailed,
01176               sizeof(DCOPMsg), DCOPMsg, pMsg );
01177         pMsg->key = 1;
01178         pMsg->length += reply.size();
01179             _DCOPIceSendBegin( iceConn );
01180         DCOPIceSendData( iceConn, reply );
01181             _DCOPIceSendEnd();
01182             if (!target)
01183                qWarning("DCOP Error: unknown target in waitingForDelayedReply");
01184             else if (!target->waitingOnReply.removeRef(conn->iceConn))
01185                qWarning("DCOP Error: client in waitingForDelayedReply wasn't waiting on reply");
01186     }
01187     }
01188     while (!conn->waitingOnReply.isEmpty())
01189     {
01190     IceConn iceConn = conn->waitingOnReply.take(0);
01191         if (iceConn) {
01192            DCOPConnection* target = clients.find( iceConn );
01193            if (!target)
01194            {
01195                qWarning("DCOP Error: still waiting for answer from non-existing client.");
01196                continue;
01197            }
01198            qWarning("DCOP aborting while waiting for answer from '%s'", target->appId.data());
01199            if (!target->waitingForReply.removeRef(conn->iceConn) &&
01200                !target->waitingForDelayedReply.removeRef(conn->iceConn))
01201               qWarning("DCOP Error: called client has forgotten about caller");
01202         }
01203     }
01204 
01205     if ( !conn->appId.isNull() ) {
01206 #ifndef NDEBUG
01207     qDebug("DCOP: unregister '%s'", conn->appId.data() );
01208 #endif
01209         if ( !conn->daemon )
01210         {
01211             currentClientNumber--;
01212         }
01213 
01214     appIds.remove( conn->appId );
01215 
01216         broadcastApplicationRegistration( conn, "applicationRemoved(QCString)", conn->appId );
01217     }
01218 
01219     delete conn;
01220 
01221     if ( suicide && (currentClientNumber == 0) )
01222     {
01223         m_timer->start( 10000 ); // if within 10 seconds nothing happens, we'll terminate
01224     }
01225 }
01226 
01227 void DCOPServer::slotTerminate()
01228 {
01229 #ifndef NDEBUG
01230     fprintf( stderr, "DCOPServer : slotTerminate() -> sending terminateKDE signal.\n" );
01231 #endif
01232     QByteArray data;
01233     dcopSignals->emitSignal(0L /* dcopserver */, "terminateKDE()", data, false);
01234     disconnect( m_timer, SIGNAL(timeout()), this, SLOT(slotTerminate()) );
01235     connect( m_timer, SIGNAL(timeout()), this, SLOT(slotSuicide()) );
01236     system(findDcopserverShutdown()+" --nokill");
01237 }
01238 
01239 void DCOPServer::slotSuicide()
01240 {
01241 #ifndef NDEBUG
01242     fprintf( stderr, "DCOPServer : slotSuicide() -> exit.\n" );
01243 #endif
01244     exit(0);
01245 }
01246 
01247 bool DCOPServer::receive(const QCString &/*app*/, const QCString &obj,
01248              const QCString &fun, const QByteArray& data,
01249              QCString& replyType, QByteArray &replyData,
01250              IceConn iceConn)
01251 {
01252     if ( obj == "emit")
01253     {
01254         DCOPConnection* conn = clients.find( iceConn );
01255         if (conn) {
01256         //qDebug("DCOPServer: %s emits %s", conn->appId.data(), fun.data());
01257         dcopSignals->emitSignal(conn, fun, data, false);
01258         }
01259         replyType = "void";
01260         return true;
01261     }
01262     if ( fun == "setDaemonMode(bool)" ) {
01263         QDataStream args( data, IO_ReadOnly );
01264         if ( !args.atEnd() ) {
01265             Q_INT8 iDaemon;
01266             bool daemon;
01267             args >> iDaemon;
01268 
01269             daemon = static_cast<bool>( iDaemon );
01270 
01271         DCOPConnection* conn = clients.find( iceConn );
01272             if ( conn && !conn->appId.isNull() ) {
01273                 if ( daemon ) {
01274                     if ( !conn->daemon )
01275                     {
01276                         conn->daemon = true;
01277 
01278 #ifndef NDEBUG
01279                         qDebug( "DCOP: new daemon %s", conn->appId.data() );
01280 #endif
01281 
01282                         currentClientNumber--;
01283 
01284 // David says it's safer not to do this :-)
01285 //                        if ( currentClientNumber == 0 )
01286 //                            m_timer->start( 10000 );
01287                     }
01288                 } else
01289                 {
01290                     if ( conn->daemon ) {
01291                         conn->daemon = false;
01292 
01293                         currentClientNumber++;
01294 
01295                         m_timer->stop();
01296                     }
01297                 }
01298             }
01299 
01300             replyType = "void";
01301             return true;
01302         }
01303     }
01304     if ( fun == "registerAs(QCString)" ) {
01305     QDataStream args( data, IO_ReadOnly );
01306     if (!args.atEnd()) {
01307         QCString app2 = readQCString(args);
01308         QDataStream reply( replyData, IO_WriteOnly );
01309         DCOPConnection* conn = clients.find( iceConn );
01310         if ( conn && !app2.isEmpty() ) {
01311         if ( !conn->appId.isNull() &&
01312              appIds.find( conn->appId ) == conn ) {
01313             appIds.remove( conn->appId );
01314 
01315         }
01316 
01317                 QCString oldAppId;
01318         if ( conn->appId.isNull() )
01319                 {
01320                     currentClientNumber++;
01321                     m_timer->stop(); // abort termination if we were planning one
01322 #ifndef NDEBUG
01323                     qDebug("DCOP: register '%s' -> number of clients is now %d", app2.data(), currentClientNumber );
01324 #endif
01325                 }
01326 #ifndef NDEBUG
01327         else
01328                 {
01329                     oldAppId = conn->appId;
01330             qDebug("DCOP:  '%s' now known as '%s'", conn->appId.data(), app2.data() );
01331                 }
01332 #endif
01333 
01334         conn->appId = app2;
01335         if ( appIds.find( app2 ) != 0 ) {
01336             // we already have this application, unify
01337             int n = 1;
01338             QCString tmp;
01339             do {
01340             n++;
01341             tmp.setNum( n );
01342             tmp.prepend("-");
01343             tmp.prepend( app2 );
01344             } while ( appIds.find( tmp ) != 0 );
01345             conn->appId = tmp;
01346         }
01347         appIds.insert( conn->appId, conn );
01348 
01349         int c = conn->appId.find( '-' );
01350         if ( c > 0 )
01351             conn->plainAppId = conn->appId.left( c );
01352         else
01353             conn->plainAppId = conn->appId;
01354 
01355                 if( !oldAppId.isEmpty())
01356                     broadcastApplicationRegistration( conn,
01357                         "applicationRemoved(QCString)", oldAppId );
01358                 broadcastApplicationRegistration( conn, "applicationRegistered(QCString)", conn->appId );
01359         }
01360         replyType = "QCString";
01361         reply << conn->appId;
01362         return true;
01363     }
01364     }
01365     else if ( fun == "registeredApplications()" ) {
01366     QDataStream reply( replyData, IO_WriteOnly );
01367     QCStringList applications;
01368     QAsciiDictIterator<DCOPConnection> it( appIds );
01369     while ( it.current() ) {
01370         applications << it.currentKey();
01371         ++it;
01372     }
01373     replyType = "QCStringList";
01374     reply << applications;
01375     return true;
01376     } else if ( fun == "isApplicationRegistered(QCString)" ) {
01377     QDataStream args( data, IO_ReadOnly );
01378     if (!args.atEnd()) {
01379         QCString s = readQCString(args);
01380         QDataStream reply( replyData, IO_WriteOnly );
01381         int b = ( findApp( s ) != 0 );
01382         replyType = "bool";
01383         reply << b;
01384         return true;
01385     }
01386     } else if ( fun == "setNotifications(bool)" ) {
01387     QDataStream args( data, IO_ReadOnly );
01388     if (!args.atEnd()) {
01389         Q_INT8 notifyActive;
01390         args >> notifyActive;
01391         DCOPConnection* conn = clients.find( iceConn );
01392         if ( conn ) {
01393         if ( notifyActive )
01394             conn->notifyRegister++;
01395         else if ( conn->notifyRegister > 0 )
01396             conn->notifyRegister--;
01397         }
01398         replyType = "void";
01399         return true;
01400     }
01401     } else if ( fun == "connectSignal(QCString,QCString,QCString,QCString,QCString,bool)") {
01402         DCOPConnection* conn = clients.find( iceConn );
01403         if (!conn) return false;
01404         QDataStream args(data, IO_ReadOnly );
01405         if (args.atEnd()) return false;
01406         QCString sender = readQCString(args);
01407         QCString senderObj = readQCString(args);
01408         QCString signal = readQCString(args);
01409         QCString receiverObj = readQCString(args);
01410         QCString slot = readQCString(args);
01411         Q_INT8 Volatile;
01412         args >> Volatile;
01413         //qDebug("DCOPServer: connectSignal(sender = %s senderObj = %s signal = %s recvObj = %s slot = %s)", sender.data(), senderObj.data(), signal.data(), receiverObj.data(), slot.data());
01414         bool b = dcopSignals->connectSignal(sender, senderObj, signal, conn, receiverObj, slot, (Volatile != 0));
01415         replyType = "bool";
01416         QDataStream reply( replyData, IO_WriteOnly );
01417         reply << (Q_INT8) (b?1:0);
01418         return true;
01419     } else if ( fun == "disconnectSignal(QCString,QCString,QCString,QCString,QCString)") {
01420         DCOPConnection* conn = clients.find( iceConn );
01421         if (!conn) return false;
01422         QDataStream args(data, IO_ReadOnly );
01423         if (args.atEnd()) return false;
01424         QCString sender = readQCString(args);
01425         QCString senderObj = readQCString(args);
01426         QCString signal = readQCString(args);
01427         QCString receiverObj = readQCString(args);
01428         QCString slot = readQCString(args);
01429         //qDebug("DCOPServer: disconnectSignal(sender = %s senderObj = %s signal = %s recvObj = %s slot = %s)", sender.data(), senderObj.data(), signal.data(), receiverObj.data(), slot.data());
01430         bool b = dcopSignals->disconnectSignal(sender, senderObj, signal, conn, receiverObj, slot);
01431         replyType = "bool";
01432         QDataStream reply( replyData, IO_WriteOnly );
01433         reply << (Q_INT8) (b?1:0);
01434         return true;
01435     }
01436 
01437     return false;
01438 }
01439 
01440 void DCOPServer::broadcastApplicationRegistration( DCOPConnection* conn, const QCString type,
01441     const QString& /*appId*/ )
01442 {
01443     QByteArray data;
01444     QDataStream datas( data, IO_WriteOnly );
01445     datas << conn->appId;
01446     QPtrDictIterator<DCOPConnection> it( clients );
01447     QByteArray ba;
01448     QDataStream ds( ba, IO_WriteOnly );
01449     ds <<QCString("DCOPServer") <<  QCString("") << QCString("")
01450        << type << data;
01451     int datalen = ba.size();
01452     DCOPMsg *pMsg = 0;
01453     while ( it.current() ) {
01454         DCOPConnection* c = it.current();
01455         ++it;
01456         if ( c->notifyRegister && (c != conn) ) {
01457             IceGetHeader( c->iceConn, majorOpcode, DCOPSend,
01458           sizeof(DCOPMsg), DCOPMsg, pMsg );
01459             pMsg->key = 1;
01460         pMsg->length += datalen;
01461             _DCOPIceSendBegin(c->iceConn);
01462         DCOPIceSendData( c->iceConn, ba );
01463             _DCOPIceSendEnd();
01464         }
01465     }
01466 }
01467 
01468 void
01469 DCOPServer::sendMessage(DCOPConnection *conn, const QCString &sApp,
01470                         const QCString &rApp, const QCString &rObj,
01471                         const QCString &rFun,  const QByteArray &data)
01472 {
01473    QByteArray ba;
01474    QDataStream ds( ba, IO_WriteOnly );
01475    ds << sApp << rApp << rObj << rFun << data;
01476    int datalen = ba.size();
01477    DCOPMsg *pMsg = 0;
01478 
01479    IceGetHeader( conn->iceConn, majorOpcode, DCOPSend,
01480                  sizeof(DCOPMsg), DCOPMsg, pMsg );
01481    pMsg->length += datalen;
01482    pMsg->key = 1; // important!
01483    _DCOPIceSendBegin( conn->iceConn );
01484    DCOPIceSendData(conn->iceConn, ba);
01485    _DCOPIceSendEnd();
01486 }
01487 
01488 void IoErrorHandler ( IceConn iceConn)
01489 {
01490     the_server->ioError( iceConn );
01491 }
01492 
01493 static bool isRunning(const QCString &fName, bool printNetworkId = false)
01494 {
01495     if (::access(fName.data(), R_OK) == 0) {
01496     QFile f(fName);
01497     f.open(IO_ReadOnly);
01498     int size = QMIN( 1024, f.size() ); // protection against a huge file
01499     QCString contents( size+1 );
01500     bool ok = f.readBlock( contents.data(), size ) == size;
01501     contents[size] = '\0';
01502     int pos = contents.find('\n');
01503     ok = ok && ( pos != -1 );
01504     pid_t pid = ok ? contents.mid(pos+1).toUInt(&ok) : 0;
01505     f.close();
01506     if (ok && pid && (kill(pid, SIGHUP) == 0)) {
01507         if (printNetworkId)
01508             qWarning("%s", contents.left(pos).data());
01509         else
01510         qWarning( "---------------------------------\n"
01511               "It looks like dcopserver is already running. If you are sure\n"
01512               "that it is not already running, remove %s\n"
01513               "and start dcopserver again.\n"
01514               "---------------------------------\n",
01515               fName.data() );
01516 
01517         // lock file present, die silently.
01518         return true;
01519     } else {
01520         // either we couldn't read the PID or kill returned an error.
01521         // remove lockfile and continue
01522         unlink(fName.data());
01523     }
01524     } else if (errno != ENOENT) {
01525         // remove lockfile and continue
01526         unlink(fName.data());
01527     }
01528     return false;
01529 }
01530 
01531 const char* const ABOUT =
01532 "Usage: dcopserver [--nofork] [--nosid] [--help]\n"
01533 "       dcopserver --serverid\n"
01534 "\n"
01535 "DCOP is KDE's Desktop Communications Protocol. It is a lightweight IPC/RPC\n"
01536 "mechanism built on top of the X Consortium's Inter Client Exchange protocol.\n"
01537 "It enables desktop applications to communicate reliably with low overhead.\n"
01538 "\n"
01539 "Copyright (C) 1999-2001, The KDE Developers <http://www.kde.org>\n"
01540 ;
01541 
01542 extern "C" int kdemain( int argc, char* argv[] )
01543 {
01544     bool serverid = false;
01545     bool nofork = false;
01546     bool nosid = false;
01547     bool suicide = false;
01548     for(int i = 1; i < argc; i++) {
01549     if (strcmp(argv[i], "--nofork") == 0)
01550         nofork = true;
01551     else if (strcmp(argv[i], "--nosid") == 0)
01552         nosid = true;
01553     else if (strcmp(argv[i], "--nolocal") == 0)
01554         ; // Ignore
01555     else if (strcmp(argv[i], "--suicide") == 0)
01556         suicide = true;
01557     else if (strcmp(argv[i], "--serverid") == 0)
01558         serverid = true;
01559     else {
01560         fprintf(stdout, ABOUT );
01561         return 0;
01562     }
01563     }
01564 
01565     if (serverid)
01566     {
01567        if (isRunning(DCOPClient::dcopServerFile(), true))
01568           return 0;
01569        return 1;
01570     }
01571 
01572     // check if we are already running
01573     if (isRunning(DCOPClient::dcopServerFile()))
01574        return 0;
01575     if (isRunning(DCOPClient::dcopServerFileOld()))
01576     {
01577        // Make symlink for compatibility
01578        QCString oldFile = DCOPClient::dcopServerFileOld();
01579        QCString newFile = DCOPClient::dcopServerFile();
01580        symlink(oldFile.data(), newFile.data());
01581        return 0;
01582     }
01583 
01584     struct rlimit limits; 
01585      
01586     int retcode = getrlimit(RLIMIT_NOFILE, &limits); 
01587     if (!retcode) { 
01588        if (limits.rlim_max > 512 && limits.rlim_cur < 512)
01589        {
01590           int cur_limit = limits.rlim_cur;
01591           limits.rlim_cur = 512; 
01592           retcode = setrlimit(RLIMIT_NOFILE, &limits); 
01593 
01594           if (retcode != 0)
01595           {
01596              qWarning("dcopserver: Could not raise limit on number of open files.");
01597              qWarning("dcopserver: Current limit = %d", cur_limit);
01598           }
01599        }
01600     }
01601 
01602     pipe(ready);
01603 
01604     if (!nofork) {
01605         pid_t pid = fork();
01606     if (pid > 0) {
01607         char c = 1;
01608         close(ready[1]);
01609         read(ready[0], &c, 1); // Wait till dcopserver is started
01610         close(ready[0]);
01611         // I am the parent
01612         if (c == 0)
01613             {
01614                // Test whether we are functional.
01615                DCOPClient client;
01616                if (client.attach())
01617                   return 0;
01618             }
01619             qWarning("DCOPServer self-test failed.");
01620             system(findDcopserverShutdown()+" --kill");
01621             return 1;
01622     }
01623     close(ready[0]);
01624 
01625     if (!nosid)
01626         setsid();
01627 
01628     if (fork() > 0)
01629         return 0; // get rid of controlling terminal
01630     }
01631 
01632     pipe(pipeOfDeath);
01633 
01634     signal(SIGHUP, sighandler);
01635     signal(SIGTERM, sighandler);
01636     signal(SIGPIPE, SIG_IGN);
01637 
01638     putenv(strdup("SESSION_MANAGER="));
01639 
01640     QApplication a( argc, argv, false );
01641     
01642     QSocketNotifier DEATH(pipeOfDeath[0], QSocketNotifier::Read, 0, 0);
01643     a.connect(&DEATH, SIGNAL(activated(int)), SLOT(quit()));
01644     
01645     IceSetIOErrorHandler (IoErrorHandler );
01646     DCOPServer *server = new DCOPServer(suicide); // this sets the_server
01647 
01648     int ret = a.exec();
01649     delete server;
01650     return ret;
01651 }
01652 
01653 #include "dcopserver.moc"
KDE Logo
This file is part of the documentation for dcop Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed May 5 07:15:24 2004 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003