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         if (QCString(getenv("DCOPAUTHORITY")).isEmpty())
01010         {
01011                 // Create a link named like the old-style (KDE 2.x) naming
01012                 QCString compatName = DCOPClient::dcopServerFileOld();
01013                 ::symlink(fName,compatName);
01014             }
01015     }
01016 
01017 #if 0
01018     if (!SetAuthentication_local(numTransports, listenObjs))
01019         qFatal("DCOPSERVER: authentication setup failed.");
01020 #endif
01021     if (!SetAuthentication(numTransports, listenObjs, &authDataEntries))
01022         qFatal("DCOPSERVER: authentication setup failed.");
01023 
01024     IceAddConnectionWatch (DCOPWatchProc, static_cast<IcePointer>(this));
01025     _IceWriteHandler = DCOPIceWriteChar;
01026 
01027     listener.setAutoDelete( true );
01028     DCOPListener* con;
01029     for ( int i = 0; i < numTransports; i++) {
01030     con = new DCOPListener( listenObjs[i] );
01031     listener.append( con );
01032     connect( con, SIGNAL( activated(int) ), this, SLOT( newClient(int) ) );
01033     }
01034     char c = 0;
01035     write(ready[1], &c, 1); // dcopserver is started
01036     close(ready[1]);
01037 
01038     m_timer =  new QTimer(this);
01039     connect( m_timer, SIGNAL(timeout()), this, SLOT(slotTerminate()) );
01040     m_deadConnectionTimer = new QTimer(this);
01041     connect( m_deadConnectionTimer, SIGNAL(timeout()), this, SLOT(slotCleanDeadConnections()) );
01042 }
01043 
01044 DCOPServer::~DCOPServer()
01045 {
01046     system(findDcopserverShutdown()+" --nokill");
01047     IceFreeListenObjs(numTransports, listenObjs);
01048     FreeAuthenticationData(numTransports, authDataEntries);
01049     delete dcopSignals;
01050 }
01051 
01052 
01053 DCOPConnection* DCOPServer::findApp( const QCString& appId )
01054 {
01055     if ( appId.isNull() )
01056     return 0;
01057     DCOPConnection* conn = appIds.find( appId );
01058     return conn;
01059 }
01060 
01064 void DCOPServer::slotCleanDeadConnections()
01065 {
01066 qWarning("DCOP Cleaning up dead connections.");
01067     while(!deadConnections.isEmpty())
01068     {
01069        IceConn iceConn = deadConnections.take(0);
01070        IceSetShutdownNegotiation (iceConn, False);
01071        (void) IceCloseConnection( iceConn );
01072     }
01073 }
01074 
01078 void DCOPServer::ioError( IceConn iceConn  )
01079 {
01080     deadConnections.removeRef(iceConn);
01081     deadConnections.prepend(iceConn);
01082     m_deadConnectionTimer->start(0, true);
01083 }
01084 
01085 
01086 void DCOPServer::processData( int /*socket*/ )
01087 {
01088     IceConn iceConn = static_cast<const DCOPConnection*>(sender())->iceConn;
01089     IceProcessMessagesStatus status = IceProcessMessages( iceConn, 0, 0 );
01090     if ( status == IceProcessMessagesIOError ) {
01091         deadConnections.removeRef(iceConn);
01092         if (deadConnections.isEmpty())
01093            m_deadConnectionTimer->stop();
01094     IceSetShutdownNegotiation (iceConn, False);
01095     (void) IceCloseConnection( iceConn );
01096     }
01097 }
01098 
01099 void DCOPServer::newClient( int /*socket*/ )
01100 {
01101     IceAcceptStatus status;
01102     IceConn iceConn = IceAcceptConnection( static_cast<const  DCOPListener*>(sender())->listenObj, &status);
01103     if (!iceConn) {
01104       if (status == IceAcceptBadMalloc)
01105      qWarning("Failed to alloc connection object!\n");
01106       else // IceAcceptFailure
01107          qWarning("Failed to accept ICE connection!\n");
01108       return;
01109     }
01110 
01111     IceSetShutdownNegotiation( iceConn, False );
01112 
01113     IceConnectStatus cstatus;
01114     while ((cstatus = IceConnectionStatus (iceConn))==IceConnectPending) {
01115     (void) IceProcessMessages( iceConn, 0, 0 );
01116     }
01117 
01118     if (cstatus != IceConnectAccepted) {
01119     if (cstatus == IceConnectIOError)
01120         qWarning ("IO error opening ICE Connection!\n");
01121     else
01122         qWarning ("ICE Connection rejected!\n");
01123         deadConnections.removeRef(iceConn);
01124     (void) IceCloseConnection (iceConn);
01125     }
01126 }
01127 
01128 void* DCOPServer::watchConnection( IceConn iceConn )
01129 {
01130     DCOPConnection* con = new DCOPConnection( iceConn );
01131     connect( con, SIGNAL( activated(int) ), this, SLOT( processData(int) ) );
01132 
01133     clients.insert(iceConn, con );
01134     fd_clients.insert( IceConnectionNumber(iceConn), con);
01135 
01136     return static_cast<void*>(con);
01137 }
01138 
01139 void DCOPServer::removeConnection( void* data )
01140 {
01141     DCOPConnection* conn = static_cast<DCOPConnection*>(data);
01142 
01143     dcopSignals->removeConnections(conn);
01144 
01145     clients.remove(conn->iceConn );
01146     fd_clients.remove( IceConnectionNumber(conn->iceConn) );
01147 
01148     // Send DCOPReplyFailed to all in conn->waitingForReply
01149     while (!conn->waitingForReply.isEmpty()) {
01150     IceConn iceConn = conn->waitingForReply.take(0);
01151     if (iceConn) {
01152         DCOPConnection* target = clients.find( iceConn );
01153         qWarning("DCOP aborting call from '%s' to '%s'", target ? target->appId.data() : "<unknown>" , conn->appId.data() );
01154         QByteArray reply;
01155         DCOPMsg *pMsg;
01156         IceGetHeader( iceConn, majorOpcode, DCOPReplyFailed,
01157               sizeof(DCOPMsg), DCOPMsg, pMsg );
01158         pMsg->key = 1;
01159         pMsg->length += reply.size();
01160             _DCOPIceSendBegin( iceConn );
01161         DCOPIceSendData(iceConn, reply);
01162             _DCOPIceSendEnd();
01163             if (!target)
01164                qWarning("DCOP Error: unknown target in waitingForReply");
01165             else if (!target->waitingOnReply.removeRef(conn->iceConn))
01166                qWarning("DCOP Error: client in waitingForReply wasn't waiting on reply");
01167     }
01168     }
01169 
01170     // Send DCOPReplyFailed to all in conn->waitingForDelayedReply
01171     while (!conn->waitingForDelayedReply.isEmpty()) {
01172     IceConn iceConn = conn->waitingForDelayedReply.take(0);
01173     if (iceConn) {
01174         DCOPConnection* target = clients.find( iceConn );
01175         qWarning("DCOP aborting (delayed) call from '%s' to '%s'", target ? target->appId.data() : "<unknown>", conn->appId.data() );
01176         QByteArray reply;
01177         DCOPMsg *pMsg;
01178         IceGetHeader( iceConn, majorOpcode, DCOPReplyFailed,
01179               sizeof(DCOPMsg), DCOPMsg, pMsg );
01180         pMsg->key = 1;
01181         pMsg->length += reply.size();
01182             _DCOPIceSendBegin( iceConn );
01183         DCOPIceSendData( iceConn, reply );
01184             _DCOPIceSendEnd();
01185             if (!target)
01186                qWarning("DCOP Error: unknown target in waitingForDelayedReply");
01187             else if (!target->waitingOnReply.removeRef(conn->iceConn))
01188                qWarning("DCOP Error: client in waitingForDelayedReply wasn't waiting on reply");
01189     }
01190     }
01191     while (!conn->waitingOnReply.isEmpty())
01192     {
01193     IceConn iceConn = conn->waitingOnReply.take(0);
01194         if (iceConn) {
01195            DCOPConnection* target = clients.find( iceConn );
01196            if (!target)
01197            {
01198                qWarning("DCOP Error: still waiting for answer from non-existing client.");
01199                continue;
01200            }
01201            qWarning("DCOP aborting while waiting for answer from '%s'", target->appId.data());
01202            if (!target->waitingForReply.removeRef(conn->iceConn) &&
01203                !target->waitingForDelayedReply.removeRef(conn->iceConn))
01204               qWarning("DCOP Error: called client has forgotten about caller");
01205         }
01206     }
01207 
01208     if ( !conn->appId.isNull() ) {
01209 #ifndef NDEBUG
01210     qDebug("DCOP: unregister '%s'", conn->appId.data() );
01211 #endif
01212         if ( !conn->daemon )
01213         {
01214             currentClientNumber--;
01215         }
01216 
01217     appIds.remove( conn->appId );
01218 
01219         broadcastApplicationRegistration( conn, "applicationRemoved(QCString)", conn->appId );
01220     }
01221 
01222     delete conn;
01223 
01224     if ( suicide && (currentClientNumber == 0) )
01225     {
01226         m_timer->start( 10000 ); // if within 10 seconds nothing happens, we'll terminate
01227     }
01228 }
01229 
01230 void DCOPServer::slotTerminate()
01231 {
01232 #ifndef NDEBUG
01233     fprintf( stderr, "DCOPServer : slotTerminate() -> sending terminateKDE signal.\n" );
01234 #endif
01235     QByteArray data;
01236     dcopSignals->emitSignal(0L /* dcopserver */, "terminateKDE()", data, false);
01237     disconnect( m_timer, SIGNAL(timeout()), this, SLOT(slotTerminate()) );
01238     connect( m_timer, SIGNAL(timeout()), this, SLOT(slotSuicide()) );
01239     system(findDcopserverShutdown()+" --nokill");
01240 }
01241 
01242 void DCOPServer::slotSuicide()
01243 {
01244 #ifndef NDEBUG
01245     fprintf( stderr, "DCOPServer : slotSuicide() -> exit.\n" );
01246 #endif
01247     exit(0);
01248 }
01249 
01250 bool DCOPServer::receive(const QCString &/*app*/, const QCString &obj,
01251              const QCString &fun, const QByteArray& data,
01252              QCString& replyType, QByteArray &replyData,
01253              IceConn iceConn)
01254 {
01255     if ( obj == "emit")
01256     {
01257         DCOPConnection* conn = clients.find( iceConn );
01258         if (conn) {
01259         //qDebug("DCOPServer: %s emits %s", conn->appId.data(), fun.data());
01260         dcopSignals->emitSignal(conn, fun, data, false);
01261         }
01262         replyType = "void";
01263         return true;
01264     }
01265     if ( fun == "setDaemonMode(bool)" ) {
01266         QDataStream args( data, IO_ReadOnly );
01267         if ( !args.atEnd() ) {
01268             Q_INT8 iDaemon;
01269             bool daemon;
01270             args >> iDaemon;
01271 
01272             daemon = static_cast<bool>( iDaemon );
01273 
01274         DCOPConnection* conn = clients.find( iceConn );
01275             if ( conn && !conn->appId.isNull() ) {
01276                 if ( daemon ) {
01277                     if ( !conn->daemon )
01278                     {
01279                         conn->daemon = true;
01280 
01281 #ifndef NDEBUG
01282                         qDebug( "DCOP: new daemon %s", conn->appId.data() );
01283 #endif
01284 
01285                         currentClientNumber--;
01286 
01287 // David says it's safer not to do this :-)
01288 //                        if ( currentClientNumber == 0 )
01289 //                            m_timer->start( 10000 );
01290                     }
01291                 } else
01292                 {
01293                     if ( conn->daemon ) {
01294                         conn->daemon = false;
01295 
01296                         currentClientNumber++;
01297 
01298                         m_timer->stop();
01299                     }
01300                 }
01301             }
01302 
01303             replyType = "void";
01304             return true;
01305         }
01306     }
01307     if ( fun == "registerAs(QCString)" ) {
01308     QDataStream args( data, IO_ReadOnly );
01309     if (!args.atEnd()) {
01310         QCString app2 = readQCString(args);
01311         QDataStream reply( replyData, IO_WriteOnly );
01312         DCOPConnection* conn = clients.find( iceConn );
01313         if ( conn && !app2.isEmpty() ) {
01314         if ( !conn->appId.isNull() &&
01315              appIds.find( conn->appId ) == conn ) {
01316             appIds.remove( conn->appId );
01317 
01318         }
01319 
01320                 QCString oldAppId;
01321         if ( conn->appId.isNull() )
01322                 {
01323                     currentClientNumber++;
01324                     m_timer->stop(); // abort termination if we were planning one
01325 #ifndef NDEBUG
01326                     qDebug("DCOP: register '%s' -> number of clients is now %d", app2.data(), currentClientNumber );
01327 #endif
01328                 }
01329 #ifndef NDEBUG
01330         else
01331                 {
01332                     oldAppId = conn->appId;
01333             qDebug("DCOP:  '%s' now known as '%s'", conn->appId.data(), app2.data() );
01334                 }
01335 #endif
01336 
01337         conn->appId = app2;
01338         if ( appIds.find( app2 ) != 0 ) {
01339             // we already have this application, unify
01340             int n = 1;
01341             QCString tmp;
01342             do {
01343             n++;
01344             tmp.setNum( n );
01345             tmp.prepend("-");
01346             tmp.prepend( app2 );
01347             } while ( appIds.find( tmp ) != 0 );
01348             conn->appId = tmp;
01349         }
01350         appIds.insert( conn->appId, conn );
01351 
01352         int c = conn->appId.find( '-' );
01353         if ( c > 0 )
01354             conn->plainAppId = conn->appId.left( c );
01355         else
01356             conn->plainAppId = conn->appId;
01357 
01358                 if( !oldAppId.isEmpty())
01359                     broadcastApplicationRegistration( conn,
01360                         "applicationRemoved(QCString)", oldAppId );
01361                 broadcastApplicationRegistration( conn, "applicationRegistered(QCString)", conn->appId );
01362         }
01363         replyType = "QCString";
01364         reply << conn->appId;
01365         return true;
01366     }
01367     }
01368     else if ( fun == "registeredApplications()" ) {
01369     QDataStream reply( replyData, IO_WriteOnly );
01370     QCStringList applications;
01371     QAsciiDictIterator<DCOPConnection> it( appIds );
01372     while ( it.current() ) {
01373         applications << it.currentKey();
01374         ++it;
01375     }
01376     replyType = "QCStringList";
01377     reply << applications;
01378     return true;
01379     } else if ( fun == "isApplicationRegistered(QCString)" ) {
01380     QDataStream args( data, IO_ReadOnly );
01381     if (!args.atEnd()) {
01382         QCString s = readQCString(args);
01383         QDataStream reply( replyData, IO_WriteOnly );
01384         int b = ( findApp( s ) != 0 );
01385         replyType = "bool";
01386         reply << b;
01387         return true;
01388     }
01389     } else if ( fun == "setNotifications(bool)" ) {
01390     QDataStream args( data, IO_ReadOnly );
01391     if (!args.atEnd()) {
01392         Q_INT8 notifyActive;
01393         args >> notifyActive;
01394         DCOPConnection* conn = clients.find( iceConn );
01395         if ( conn ) {
01396         if ( notifyActive )
01397             conn->notifyRegister++;
01398         else if ( conn->notifyRegister > 0 )
01399             conn->notifyRegister--;
01400         }
01401         replyType = "void";
01402         return true;
01403     }
01404     } else if ( fun == "connectSignal(QCString,QCString,QCString,QCString,QCString,bool)") {
01405         DCOPConnection* conn = clients.find( iceConn );
01406         if (!conn) return false;
01407         QDataStream args(data, IO_ReadOnly );
01408         if (args.atEnd()) return false;
01409         QCString sender = readQCString(args);
01410         QCString senderObj = readQCString(args);
01411         QCString signal = readQCString(args);
01412         QCString receiverObj = readQCString(args);
01413         QCString slot = readQCString(args);
01414         Q_INT8 Volatile;
01415         args >> Volatile;
01416         //qDebug("DCOPServer: connectSignal(sender = %s senderObj = %s signal = %s recvObj = %s slot = %s)", sender.data(), senderObj.data(), signal.data(), receiverObj.data(), slot.data());
01417         bool b = dcopSignals->connectSignal(sender, senderObj, signal, conn, receiverObj, slot, (Volatile != 0));
01418         replyType = "bool";
01419         QDataStream reply( replyData, IO_WriteOnly );
01420         reply << (Q_INT8) (b?1:0);
01421         return true;
01422     } else if ( fun == "disconnectSignal(QCString,QCString,QCString,QCString,QCString)") {
01423         DCOPConnection* conn = clients.find( iceConn );
01424         if (!conn) return false;
01425         QDataStream args(data, IO_ReadOnly );
01426         if (args.atEnd()) return false;
01427         QCString sender = readQCString(args);
01428         QCString senderObj = readQCString(args);
01429         QCString signal = readQCString(args);
01430         QCString receiverObj = readQCString(args);
01431         QCString slot = readQCString(args);
01432         //qDebug("DCOPServer: disconnectSignal(sender = %s senderObj = %s signal = %s recvObj = %s slot = %s)", sender.data(), senderObj.data(), signal.data(), receiverObj.data(), slot.data());
01433         bool b = dcopSignals->disconnectSignal(sender, senderObj, signal, conn, receiverObj, slot);
01434         replyType = "bool";
01435         QDataStream reply( replyData, IO_WriteOnly );
01436         reply << (Q_INT8) (b?1:0);
01437         return true;
01438     }
01439 
01440     return false;
01441 }
01442 
01443 void DCOPServer::broadcastApplicationRegistration( DCOPConnection* conn, const QCString type,
01444     const QString& /*appId*/ )
01445 {
01446     QByteArray data;
01447     QDataStream datas( data, IO_WriteOnly );
01448     datas << conn->appId;
01449     QPtrDictIterator<DCOPConnection> it( clients );
01450     QByteArray ba;
01451     QDataStream ds( ba, IO_WriteOnly );
01452     ds <<QCString("DCOPServer") <<  QCString("") << QCString("")
01453        << type << data;
01454     int datalen = ba.size();
01455     DCOPMsg *pMsg = 0;
01456     while ( it.current() ) {
01457         DCOPConnection* c = it.current();
01458         ++it;
01459         if ( c->notifyRegister && (c != conn) ) {
01460             IceGetHeader( c->iceConn, majorOpcode, DCOPSend,
01461           sizeof(DCOPMsg), DCOPMsg, pMsg );
01462             pMsg->key = 1;
01463         pMsg->length += datalen;
01464             _DCOPIceSendBegin(c->iceConn);
01465         DCOPIceSendData( c->iceConn, ba );
01466             _DCOPIceSendEnd();
01467         }
01468     }
01469 }
01470 
01471 void
01472 DCOPServer::sendMessage(DCOPConnection *conn, const QCString &sApp,
01473                         const QCString &rApp, const QCString &rObj,
01474                         const QCString &rFun,  const QByteArray &data)
01475 {
01476    QByteArray ba;
01477    QDataStream ds( ba, IO_WriteOnly );
01478    ds << sApp << rApp << rObj << rFun << data;
01479    int datalen = ba.size();
01480    DCOPMsg *pMsg = 0;
01481 
01482    IceGetHeader( conn->iceConn, majorOpcode, DCOPSend,
01483                  sizeof(DCOPMsg), DCOPMsg, pMsg );
01484    pMsg->length += datalen;
01485    pMsg->key = 1; // important!
01486    _DCOPIceSendBegin( conn->iceConn );
01487    DCOPIceSendData(conn->iceConn, ba);
01488    _DCOPIceSendEnd();
01489 }
01490 
01491 void IoErrorHandler ( IceConn iceConn)
01492 {
01493     the_server->ioError( iceConn );
01494 }
01495 
01496 static bool isRunning(const QCString &fName, bool printNetworkId = false)
01497 {
01498     if (::access(fName.data(), R_OK) == 0) {
01499     QFile f(fName);
01500     f.open(IO_ReadOnly);
01501     int size = QMIN( 1024, f.size() ); // protection against a huge file
01502     QCString contents( size+1 );
01503     bool ok = f.readBlock( contents.data(), size ) == size;
01504     contents[size] = '\0';
01505     int pos = contents.find('\n');
01506     ok = ok && ( pos != -1 );
01507     pid_t pid = ok ? contents.mid(pos+1).toUInt(&ok) : 0;
01508     f.close();
01509     if (ok && pid && (kill(pid, SIGHUP) == 0)) {
01510         if (printNetworkId)
01511             qWarning("%s", contents.left(pos).data());
01512         else
01513         qWarning( "---------------------------------\n"
01514               "It looks like dcopserver is already running. If you are sure\n"
01515               "that it is not already running, remove %s\n"
01516               "and start dcopserver again.\n"
01517               "---------------------------------\n",
01518               fName.data() );
01519 
01520         // lock file present, die silently.
01521         return true;
01522     } else {
01523         // either we couldn't read the PID or kill returned an error.
01524         // remove lockfile and continue
01525         unlink(fName.data());
01526     }
01527     } else if (errno != ENOENT) {
01528         // remove lockfile and continue
01529         unlink(fName.data());
01530     }
01531     return false;
01532 }
01533 
01534 const char* const ABOUT =
01535 "Usage: dcopserver [--nofork] [--nosid] [--help]\n"
01536 "       dcopserver --serverid\n"
01537 "\n"
01538 "DCOP is KDE's Desktop Communications Protocol. It is a lightweight IPC/RPC\n"
01539 "mechanism built on top of the X Consortium's Inter Client Exchange protocol.\n"
01540 "It enables desktop applications to communicate reliably with low overhead.\n"
01541 "\n"
01542 "Copyright (C) 1999-2001, The KDE Developers <http://www.kde.org>\n"
01543 ;
01544 
01545 extern "C" int kdemain( int argc, char* argv[] )
01546 {
01547     bool serverid = false;
01548     bool nofork = false;
01549     bool nosid = false;
01550     bool suicide = false;
01551     for(int i = 1; i < argc; i++) {
01552     if (strcmp(argv[i], "--nofork") == 0)
01553         nofork = true;
01554     else if (strcmp(argv[i], "--nosid") == 0)
01555         nosid = true;
01556     else if (strcmp(argv[i], "--nolocal") == 0)
01557         ; // Ignore
01558     else if (strcmp(argv[i], "--suicide") == 0)
01559         suicide = true;
01560     else if (strcmp(argv[i], "--serverid") == 0)
01561         serverid = true;
01562     else {
01563         fprintf(stdout, ABOUT );
01564         return 0;
01565     }
01566     }
01567 
01568     if (serverid)
01569     {
01570        if (isRunning(DCOPClient::dcopServerFile(), true))
01571           return 0;
01572        return 1;
01573     }
01574 
01575     // check if we are already running
01576     if (isRunning(DCOPClient::dcopServerFile()))
01577        return 0;
01578     if (QCString(getenv("DCOPAUTHORITY")).isEmpty() &&
01579         isRunning(DCOPClient::dcopServerFileOld()))
01580     {
01581        // Make symlink for compatibility
01582        QCString oldFile = DCOPClient::dcopServerFileOld();
01583        QCString newFile = DCOPClient::dcopServerFile();
01584        symlink(oldFile.data(), newFile.data());
01585        return 0;
01586     }
01587 
01588     struct rlimit limits; 
01589      
01590     int retcode = getrlimit(RLIMIT_NOFILE, &limits); 
01591     if (!retcode) { 
01592        if (limits.rlim_max > 512 && limits.rlim_cur < 512)
01593        {
01594           int cur_limit = limits.rlim_cur;
01595           limits.rlim_cur = 512; 
01596           retcode = setrlimit(RLIMIT_NOFILE, &limits); 
01597 
01598           if (retcode != 0)
01599           {
01600              qWarning("dcopserver: Could not raise limit on number of open files.");
01601              qWarning("dcopserver: Current limit = %d", cur_limit);
01602           }
01603        }
01604     }
01605 
01606     pipe(ready);
01607 
01608     if (!nofork) {
01609         pid_t pid = fork();
01610     if (pid > 0) {
01611         char c = 1;
01612         close(ready[1]);
01613         read(ready[0], &c, 1); // Wait till dcopserver is started
01614         close(ready[0]);
01615         // I am the parent
01616         if (c == 0)
01617             {
01618                // Test whether we are functional.
01619                DCOPClient client;
01620                if (client.attach())
01621                   return 0;
01622             }
01623             qWarning("DCOPServer self-test failed.");
01624             system(findDcopserverShutdown()+" --kill");
01625             return 1;
01626     }
01627     close(ready[0]);
01628 
01629     if (!nosid)
01630         setsid();
01631 
01632     if (fork() > 0)
01633         return 0; // get rid of controlling terminal
01634     }
01635 
01636     pipe(pipeOfDeath);
01637 
01638     signal(SIGHUP, sighandler);
01639     signal(SIGTERM, sighandler);
01640     signal(SIGPIPE, SIG_IGN);
01641 
01642     putenv(strdup("SESSION_MANAGER="));
01643 
01644     QApplication a( argc, argv, false );
01645     
01646     QSocketNotifier DEATH(pipeOfDeath[0], QSocketNotifier::Read, 0, 0);
01647     a.connect(&DEATH, SIGNAL(activated(int)), SLOT(quit()));
01648     
01649     IceSetIOErrorHandler (IoErrorHandler );
01650     DCOPServer *server = new DCOPServer(suicide); // this sets the_server
01651 
01652     int ret = a.exec();
01653     delete server;
01654     return ret;
01655 }
01656 
01657 #include "dcopserver.moc"
KDE Logo
This file is part of the documentation for dcop Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Aug 4 05:22:38 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2003