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 #ifndef HAVE_MKSTEMPS
00447 #include <string.h>
00448 #include <strings.h>
00449 
00450 /* this is based on code taken from the GNU libc, distributed under the LGPL license */
00451 
00452 /* Generate a unique temporary file name from TEMPLATE.
00453 
00454    TEMPLATE has the form:
00455 
00456    <path>/ccXXXXXX<suffix>
00457 
00458    SUFFIX_LEN tells us how long <suffix> is (it can be zero length).
00459 
00460    The last six characters of TEMPLATE before <suffix> must be "XXXXXX";
00461    they are replaced with a string that makes the filename unique.
00462 
00463    Returns a file descriptor open on the file for reading and writing.  */
00464 
00465 int mkstemps (char* _template, int suffix_len)
00466 {
00467   static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
00468   char *XXXXXX;
00469   int len;
00470   int count;
00471   int value;
00472 
00473   len = strlen (_template);
00474 
00475   if ((int) len < 6 + suffix_len || strncmp (&_template[len - 6 - suffix_len], "XXXXXX", 6))
00476       return -1;
00477 
00478   XXXXXX = &_template[len - 6 - suffix_len];
00479 
00480   value = rand();
00481   for (count = 0; count < 256; ++count)
00482   {
00483       int v = value;
00484       int fd;
00485 
00486       /* Fill in the random bits.  */
00487       XXXXXX[0] = letters[v % 62];
00488       v /= 62;
00489       XXXXXX[1] = letters[v % 62];
00490       v /= 62;
00491       XXXXXX[2] = letters[v % 62];
00492       v /= 62;
00493       XXXXXX[3] = letters[v % 62];
00494       v /= 62;
00495       XXXXXX[4] = letters[v % 62];
00496       v /= 62;
00497       XXXXXX[5] = letters[v % 62];
00498 
00499       fd = open (_template, O_RDWR|O_CREAT|O_EXCL, 0600);
00500       if (fd >= 0)
00501     /* The file does not exist.  */
00502     return fd;
00503 
00504       /* This is a random value.  It is only necessary that the next
00505      TMP_MAX values generated by adding 7777 to VALUE are different
00506      with (module 2^32).  */
00507       value += 7777;
00508     }
00509   /* We return the null string if we can't find a unique file name.  */
00510   _template[0] = '\0';
00511   return -1;
00512 }
00513 
00514 #endif
00515 
00516 static char *unique_filename (const char *path, const char *prefix, int *pFd)
00517 {
00518     char tempFile[PATH_MAX];
00519     char *ptr;
00520 
00521     snprintf (tempFile, PATH_MAX, "%s/%sXXXXXX", path, prefix);
00522     ptr = static_cast<char *>(malloc(strlen(tempFile) + 1));
00523     if (ptr != NULL)
00524     {
00525         strcpy(ptr, tempFile);
00526         *pFd =  mkstemps(ptr, 0);
00527     }
00528     return ptr;
00529 }
00530 
00531 #define MAGIC_COOKIE_LEN 16
00532 
00533 Status
00534 SetAuthentication (int count, IceListenObj *_listenObjs,
00535            IceAuthDataEntry **_authDataEntries)
00536 {
00537     FILE        *addfp = NULL;
00538     const char  *path;
00539     int         original_umask;
00540     int         i;
00541     QCString command;    
00542     int         fd;
00543 
00544     original_umask = umask (0077);      /* disallow non-owner access */
00545 
00546     path = getenv ("DCOP_SAVE_DIR");
00547     if (!path)
00548     path = "/tmp";
00549 
00550     if ((addAuthFile = unique_filename (path, "dcop", &fd)) == NULL)
00551     goto bad;
00552 
00553     if (!(addfp = fdopen(fd, "wb")))
00554     goto bad;
00555 
00556     if ((*_authDataEntries = static_cast<IceAuthDataEntry *>(malloc (count * 2 * sizeof (IceAuthDataEntry)))) == NULL)
00557     goto bad;
00558 
00559     for (i = 0; i < numTransports * 2; i += 2) {
00560     (*_authDataEntries)[i].network_id =
00561         IceGetListenConnectionString (_listenObjs[i/2]);
00562     (*_authDataEntries)[i].protocol_name = const_cast<char *>("ICE");
00563     (*_authDataEntries)[i].auth_name = const_cast<char *>("MIT-MAGIC-COOKIE-1");
00564 
00565     (*_authDataEntries)[i].auth_data =
00566         IceGenerateMagicCookie (MAGIC_COOKIE_LEN);
00567     (*_authDataEntries)[i].auth_data_length = MAGIC_COOKIE_LEN;
00568 
00569     (*_authDataEntries)[i+1].network_id =
00570         IceGetListenConnectionString (_listenObjs[i/2]);
00571     (*_authDataEntries)[i+1].protocol_name = const_cast<char *>("DCOP");
00572     (*_authDataEntries)[i+1].auth_name = const_cast<char *>("MIT-MAGIC-COOKIE-1");
00573 
00574     (*_authDataEntries)[i+1].auth_data =
00575         IceGenerateMagicCookie (MAGIC_COOKIE_LEN);
00576     (*_authDataEntries)[i+1].auth_data_length = MAGIC_COOKIE_LEN;
00577 
00578     write_iceauth (addfp, &(*_authDataEntries)[i]);
00579     write_iceauth (addfp, &(*_authDataEntries)[i+1]);
00580 
00581     IceSetPaAuthData (2, &(*_authDataEntries)[i]);
00582 
00583     IceSetHostBasedAuthProc (_listenObjs[i/2], HostBasedAuthProc);
00584     }
00585 
00586     fclose (addfp);
00587 
00588     umask (original_umask);
00589 
00590     command = DCOPClient::iceauthPath();
00591     
00592     if (command.isEmpty())
00593     {
00594        fprintf( stderr, "dcopserver: 'iceauth' not found in path, aborting.\n" );
00595        exit(1);
00596     }
00597     
00598     command += " source ";
00599     command += addAuthFile;
00600     system (command);
00601 
00602     unlink(addAuthFile);
00603 
00604     return (1);
00605 
00606  bad:
00607 
00608     if (addfp)
00609     fclose (addfp);
00610 
00611     if (addAuthFile) {
00612     unlink(addAuthFile);
00613     free(addAuthFile);
00614     }
00615 
00616     umask (original_umask);
00617 
00618     return (0);
00619 }
00620 
00621 /*
00622  * Free up authentication data.
00623  */
00624 void
00625 FreeAuthenticationData(int count, IceAuthDataEntry *_authDataEntries)
00626 {
00627     /* Each transport has entries for ICE and XSMP */
00628     int i;
00629 
00630     for (i = 0; i < count * 2; i++) {
00631     free (_authDataEntries[i].network_id);
00632     free (_authDataEntries[i].auth_data);
00633     }
00634 
00635     free(_authDataEntries);
00636     free(addAuthFile);
00637 }
00638 
00639 void DCOPWatchProc ( IceConn iceConn, IcePointer client_data, Bool opening, IcePointer* watch_data)
00640 {
00641     DCOPServer* ds = static_cast<DCOPServer*>(client_data);
00642 
00643     if (opening) {
00644     *watch_data = static_cast<IcePointer>(ds->watchConnection( iceConn ));
00645     }
00646     else  {
00647     ds->removeConnection( static_cast<void*>(*watch_data) );
00648     }
00649 }
00650 
00651 void DCOPProcessMessage( IceConn iceConn, IcePointer /*clientData*/,
00652              int opcode, unsigned long length, Bool swap)
00653 {
00654     the_server->processMessage( iceConn, opcode, length, swap );
00655 }
00656 
00657 void DCOPServer::processMessage( IceConn iceConn, int opcode,
00658                  unsigned long length, Bool /*swap*/)
00659 {
00660     DCOPConnection* conn = clients.find( iceConn );
00661     if ( !conn ) {
00662     qWarning("DCOPServer::processMessage message from unknown connection. [opcode = %d]", opcode);
00663     return;
00664     }
00665     switch( opcode ) {
00666     case DCOPSend:
00667     case DCOPReplyDelayed:
00668     {
00669         DCOPMsg *pMsg = 0;
00670         IceReadMessageHeader(iceConn, sizeof(DCOPMsg), DCOPMsg, pMsg);
00671         CARD32 key = pMsg->key;
00672         QByteArray ba( length );
00673         IceReadData(iceConn, length, ba.data() );
00674         QDataStream ds( ba, IO_ReadOnly );
00675         QCString fromApp = readQCString(ds);
00676             QCString toApp = readQCString(ds);
00677 
00678         DCOPConnection* target = findApp( toApp );
00679         int datalen = ba.size();
00680         if ( opcode == DCOPReplyDelayed ) {
00681         if ( !target )
00682             qWarning("DCOPServer::DCOPReplyDelayed for unknown connection.");
00683         else if ( !conn )
00684             qWarning("DCOPServer::DCOPReplyDelayed from unknown connection.");
00685         else if (!conn->waitingForDelayedReply.removeRef( target->iceConn ))
00686             qWarning("DCOPServer::DCOPReplyDelayed from/to does not match. (#2)");
00687                 else if (!target->waitingOnReply.removeRef(iceConn))
00688                        qWarning("DCOPServer::DCOPReplyDelayed for client who wasn't waiting on one!");
00689         }
00690         if ( target ) {
00691 #ifdef DCOP_DEBUG
00692 if (opcode == DCOPSend)
00693 {
00694    QCString obj = readQCString(obj);
00695    QCString fun = readQCString(fun);
00696    qWarning("Sending %d bytes from %s to %s. DCOPSend %s", length, fromApp.data(), toApp.data(), fun.data());
00697 }
00698 #endif
00699         IceGetHeader( target->iceConn, majorOpcode, opcode,
00700                   sizeof(DCOPMsg), DCOPMsg, pMsg );
00701         pMsg->key = key;
00702         pMsg->length += datalen;
00703         _DCOPIceSendBegin( target->iceConn );
00704         DCOPIceSendData(target->iceConn, ba);
00705                 _DCOPIceSendEnd();
00706         } else if ( toApp == "DCOPServer" ) {
00707         QCString obj = readQCString(ds);
00708         QCString fun = readQCString(ds);
00709         QByteArray data = readQByteArray(ds);
00710 
00711         QCString replyType;
00712         QByteArray replyData;
00713         if ( !receive( toApp, obj, fun, data, replyType, replyData, iceConn ) ) {
00714             qWarning("%s failure: object '%s' has no function '%s'", toApp.data(), obj.data(), fun.data() );
00715         }
00716         } else if ( toApp[toApp.length()-1] == '*') {
00717 #ifdef DCOP_DEBUG
00718 if (opcode == DCOPSend)
00719 {
00720    QCString obj = readQCString(obj);
00721    QCString fun = readQCString(fun);
00722    qWarning("Sending %d bytes from %s to %s. DCOPSend %s", length, fromApp.data(), toApp.data(), fun.data());
00723 }
00724 #endif
00725         // handle a multicast.
00726         QAsciiDictIterator<DCOPConnection> aIt(appIds);
00727         int l = toApp.length()-1;
00728         for ( ; aIt.current(); ++aIt) {
00729             DCOPConnection *client = aIt.current();
00730             if (!l || (strncmp(client->appId.data(), toApp.data(), l) == 0))
00731             {
00732                 IceGetHeader(client->iceConn, majorOpcode, DCOPSend,
00733                      sizeof(DCOPMsg), DCOPMsg, pMsg);
00734                 pMsg->key = key;
00735                 pMsg->length += datalen;
00736                 _DCOPIceSendBegin( client->iceConn );
00737                 DCOPIceSendData(client->iceConn, ba);
00738                             _DCOPIceSendEnd();
00739             }
00740         }
00741         }
00742     }
00743     break;
00744     case DCOPCall:
00745     case DCOPFind:
00746     {
00747         DCOPMsg *pMsg = 0;
00748         IceReadMessageHeader(iceConn, sizeof(DCOPMsg), DCOPMsg, pMsg);
00749         CARD32 key = pMsg->key;
00750         QByteArray ba( length );
00751         IceReadData(iceConn, length, ba.data() );
00752         QDataStream ds( ba, IO_ReadOnly );
00753         QCString fromApp = readQCString(ds);
00754         QCString toApp = readQCString(ds);
00755         DCOPConnection* target = findApp( toApp );
00756         int datalen = ba.size();
00757 
00758         if ( target ) {
00759 #ifdef DCOP_DEBUG
00760 if (opcode == DCOPCall)
00761 {
00762    QCString obj = readQCString(obj);
00763    QCString fun = readQCString(fun);
00764    qWarning("Sending %d bytes from %s to %s. DCOPCall %s", length, fromApp.data(), toApp.data(), fun.data());
00765 }
00766 #endif
00767         target->waitingForReply.append( iceConn );
00768                 conn->waitingOnReply.append( target->iceConn);
00769 
00770         IceGetHeader( target->iceConn, majorOpcode, opcode,
00771                   sizeof(DCOPMsg), DCOPMsg, pMsg );
00772         pMsg->key = key;
00773         pMsg->length += datalen;
00774         _DCOPIceSendBegin( target->iceConn );
00775         DCOPIceSendData(target->iceConn, ba);
00776                 _DCOPIceSendEnd();
00777         } else {
00778         QCString replyType;
00779         QByteArray replyData;
00780         bool b = false;
00781         // DCOPServer itself does not do DCOPFind.
00782         if ( (opcode == DCOPCall) && (toApp == "DCOPServer") ) {
00783             QCString obj = readQCString(ds);
00784             QCString fun = readQCString(ds);
00785             QByteArray data = readQByteArray(ds);
00786             b = receive( toApp, obj, fun, data, replyType, replyData, iceConn );
00787             if ( !b )
00788             qWarning("%s failure: object '%s' has no function '%s'", toApp.data(), obj.data(), fun.data() );
00789         }
00790 
00791         if (b) {
00792             QByteArray reply;
00793             QDataStream replyStream( reply, IO_WriteOnly );
00794             replyStream << toApp << fromApp << replyType << replyData.size();
00795             int replylen = reply.size() + replyData.size();
00796             IceGetHeader( iceConn, majorOpcode, DCOPReply,
00797                   sizeof(DCOPMsg), DCOPMsg, pMsg );
00798             if ( key != 0 )
00799             pMsg->key = key;
00800             else
00801             pMsg->key = serverKey++;
00802             pMsg->length += replylen;
00803                     _DCOPIceSendBegin( iceConn );
00804             DCOPIceSendData( iceConn, reply);
00805             DCOPIceSendData( iceConn, replyData);
00806                     _DCOPIceSendEnd();
00807         } else {
00808             QByteArray reply;
00809             QDataStream replyStream( reply, IO_WriteOnly );
00810             replyStream << toApp << fromApp;
00811             IceGetHeader( iceConn, majorOpcode, DCOPReplyFailed,
00812                   sizeof(DCOPMsg), DCOPMsg, pMsg );
00813             if ( key != 0 )
00814             pMsg->key = key;
00815             else
00816             pMsg->key = serverKey++;
00817             pMsg->length += reply.size();
00818                     _DCOPIceSendBegin( iceConn );
00819             DCOPIceSendData( iceConn, reply );
00820                     _DCOPIceSendEnd();
00821         }
00822         }
00823     }
00824     break;
00825     case DCOPReply:
00826     case DCOPReplyFailed:
00827     case DCOPReplyWait:
00828     {
00829         DCOPMsg *pMsg = 0;
00830         IceReadMessageHeader(iceConn, sizeof(DCOPMsg), DCOPMsg, pMsg);
00831         CARD32 key = pMsg->key;
00832         QByteArray ba( length );
00833         IceReadData(iceConn, length, ba.data() );
00834         QDataStream ds( ba, IO_ReadOnly );
00835             QCString fromApp = readQCString(ds);
00836             QCString toApp = readQCString(ds);
00837 
00838         DCOPConnection* connreply = findApp( toApp );
00839         int datalen = ba.size();
00840 
00841         if ( !connreply )
00842         qWarning("DCOPServer::DCOPReply for unknown connection.");
00843         else {
00844         conn->waitingForReply.removeRef( connreply->iceConn );
00845         if ( opcode == DCOPReplyWait )
00846                 {
00847             conn->waitingForDelayedReply.append( connreply->iceConn );
00848                 }
00849                 else
00850                 { // DCOPReply or DCOPReplyFailed
00851                     if (!connreply->waitingOnReply.removeRef(iceConn))
00852                        qWarning("DCOPServer::DCOPReply for client who wasn't waiting on one!");
00853                 }
00854         IceGetHeader( connreply->iceConn, majorOpcode, opcode,
00855                   sizeof(DCOPMsg), DCOPMsg, pMsg );
00856         pMsg->key = key;
00857         pMsg->length += datalen;
00858                 _DCOPIceSendBegin( connreply->iceConn );
00859         DCOPIceSendData(connreply->iceConn, ba);
00860                 _DCOPIceSendEnd();
00861         }
00862     }
00863     break;
00864     default:
00865     qWarning("DCOPServer::processMessage unknown message");
00866     }
00867 }
00868 
00869 static const IcePaVersionRec DCOPServerVersions[] = {
00870     { DCOPVersionMajor, DCOPVersionMinor,  DCOPProcessMessage }
00871 };
00872 
00873 static const IcePoVersionRec DUMMYVersions[] = {
00874     { DCOPVersionMajor, DCOPVersionMinor, 0 }
00875 };
00876 
00877 typedef struct DCOPServerConnStruct *DCOPServerConn;
00878 
00879 struct DCOPServerConnStruct
00880 {
00881     /*
00882      * We use ICE to esablish a connection with the client.
00883    */
00884 
00885     IceConn     iceConn;
00886 
00887 
00888     /*
00889    * Major and minor versions of the XSMP.
00890    */
00891 
00892     int         proto_major_version;
00893     int         proto_minor_version;
00894 
00895 
00896     QCString clientId;
00897 };
00898 
00899 
00900 static Status DCOPServerProtocolSetupProc ( IceConn iceConn,
00901                         int majorVersion, int minorVersion,
00902                         char* vendor, char* release,
00903                         IcePointer *clientDataRet,
00904                         char **/*failureReasonRet*/)
00905 {
00906     DCOPServerConn serverConn;
00907 
00908     /*
00909      * vendor/release are undefined for ProtocolSetup in DCOP
00910      */
00911 
00912     if (vendor)
00913     free (vendor);
00914     if (release)
00915     free (release);
00916 
00917 
00918     /*
00919      * Allocate new DCOPServerConn.
00920      */
00921 
00922     serverConn = new DCOPServerConnStruct;
00923 
00924     serverConn->iceConn = iceConn;
00925     serverConn->proto_major_version = majorVersion;
00926     serverConn->proto_minor_version = minorVersion;
00927     //serverConn->clientId already initialized
00928 
00929     *clientDataRet = static_cast<IcePointer>(serverConn);
00930 
00931 
00932     return 1;
00933 }
00934 
00935 static int pipeOfDeath[2];
00936 
00937 static void sighandler(int sig)
00938 {
00939     if (sig == SIGHUP) {
00940     signal(SIGHUP, sighandler);
00941     return;
00942     }
00943 
00944     write(pipeOfDeath[1], "x", 1);
00945 }
00946 
00947 DCOPServer::DCOPServer(bool _suicide)
00948     : QObject(0,0), currentClientNumber(0), appIds(263), clients(263)
00949 {
00950     serverKey = 42;
00951 
00952     suicide = _suicide;
00953 
00954     dcopSignals = new DCOPSignals;
00955 
00956     extern int _kde_IceLastMajorOpcode; // from libICE
00957     if (_kde_IceLastMajorOpcode < 1 )
00958         IceRegisterForProtocolSetup(const_cast<char *>("DUMMY"),
00959                     const_cast<char *>("DUMMY"),
00960                     const_cast<char *>("DUMMY"),
00961                     1, const_cast<IcePoVersionRec *>(DUMMYVersions),
00962                     DCOPAuthCount, const_cast<char **>(DCOPAuthNames),
00963                     DCOPClientAuthProcs, 0);
00964     if (_kde_IceLastMajorOpcode < 1 )
00965     qWarning("DCOPServer Error: incorrect major opcode!");
00966 
00967     the_server = this;
00968     if (( majorOpcode = IceRegisterForProtocolReply (const_cast<char *>("DCOP"),
00969                              const_cast<char *>(DCOPVendorString),
00970                              const_cast<char *>(DCOPReleaseString),
00971                              1, const_cast<IcePaVersionRec *>(DCOPServerVersions),
00972                              1, const_cast<char **>(DCOPAuthNames),
00973                              DCOPServerAuthProcs,
00974                              HostBasedAuthProc,
00975                              DCOPServerProtocolSetupProc,
00976                              NULL,  /* IceProtocolActivateProc - we don't care about
00977                                    when the Protocol Reply is sent, because the
00978                                    session manager can not immediately send a
00979                                    message - it must wait for RegisterClient. */
00980                              NULL   /* IceIOErrorProc */
00981                              )) < 0)
00982     {
00983         qWarning("Could not register DCOP protocol with ICE");
00984     }
00985 
00986     char errormsg[256];
00987     int orig_umask = umask(0); /*old libICE's don't reset the umask() they set */
00988     if (!IceListenForConnections (&numTransports, &listenObjs,
00989                   256, errormsg))
00990     {
00991         fprintf (stderr, "%s\n", errormsg);
00992         exit (1);
00993     } else {
00994         (void) umask(orig_umask);
00995         // publish available transports.
00996         QCString fName = DCOPClient::dcopServerFile();
00997         FILE *f;
00998         if(!(f = ::fopen(fName.data(), "w+"))) {
00999             fprintf (stderr, "Can not create file %s: %s\n",
01000              fName.data(), ::strerror(errno));
01001         exit(1);
01002         }
01003         char *idlist = IceComposeNetworkIdList(numTransports, listenObjs);
01004         if (idlist != 0) {
01005             fprintf(f, "%s", idlist);
01006         free(idlist);
01007         }
01008         fprintf(f, "\n%i\n", getpid());
01009         fclose(f);
01010             // Create a link named like the old-style (KDE 2.x) naming
01011             QCString compatName = DCOPClient::dcopServerFileOld();
01012             ::symlink(fName,compatName);
01013     }
01014 
01015 #if 0
01016     if (!SetAuthentication_local(numTransports, listenObjs))
01017         qFatal("DCOPSERVER: authentication setup failed.");
01018 #endif
01019     if (!SetAuthentication(numTransports, listenObjs, &authDataEntries))
01020         qFatal("DCOPSERVER: authentication setup failed.");
01021 
01022     IceAddConnectionWatch (DCOPWatchProc, static_cast<IcePointer>(this));
01023     _IceWriteHandler = DCOPIceWriteChar;
01024 
01025     listener.setAutoDelete( true );
01026     DCOPListener* con;
01027     for ( int i = 0; i < numTransports; i++) {
01028     con = new DCOPListener( listenObjs[i] );
01029     listener.append( con );
01030     connect( con, SIGNAL( activated(int) ), this, SLOT( newClient(int) ) );
01031     }
01032     char c = 0;
01033     write(ready[1], &c, 1); // dcopserver is started
01034     close(ready[1]);
01035 
01036     m_timer =  new QTimer(this);
01037     connect( m_timer, SIGNAL(timeout()), this, SLOT(slotTerminate()) );
01038     m_deadConnectionTimer = new QTimer(this);
01039     connect( m_deadConnectionTimer, SIGNAL(timeout()), this, SLOT(slotCleanDeadConnections()) );
01040 }
01041 
01042 DCOPServer::~DCOPServer()
01043 {
01044     system(findDcopserverShutdown()+" --nokill");
01045     IceFreeListenObjs(numTransports, listenObjs);
01046     FreeAuthenticationData(numTransports, authDataEntries);
01047     delete dcopSignals;
01048 }
01049 
01050 
01051 DCOPConnection* DCOPServer::findApp( const QCString& appId )
01052 {
01053     if ( appId.isNull() )
01054     return 0;
01055     DCOPConnection* conn = appIds.find( appId );
01056     return conn;
01057 }
01058 
01062 void DCOPServer::slotCleanDeadConnections()
01063 {
01064 qWarning("DCOP Cleaning up dead connections.");
01065     while(!deadConnections.isEmpty())
01066     {
01067        IceConn iceConn = deadConnections.take(0);
01068        IceSetShutdownNegotiation (iceConn, False);
01069        (void) IceCloseConnection( iceConn );
01070     }
01071 }
01072 
01076 void DCOPServer::ioError( IceConn iceConn  )
01077 {
01078     deadConnections.removeRef(iceConn);
01079     deadConnections.prepend(iceConn);
01080     m_deadConnectionTimer->start(0, true);
01081 }
01082 
01083 
01084 void DCOPServer::processData( int /*socket*/ )
01085 {
01086     IceConn iceConn = static_cast<const DCOPConnection*>(sender())->iceConn;
01087     IceProcessMessagesStatus status = IceProcessMessages( iceConn, 0, 0 );
01088     if ( status == IceProcessMessagesIOError ) {
01089         deadConnections.removeRef(iceConn);
01090         if (deadConnections.isEmpty())
01091            m_deadConnectionTimer->stop();
01092     IceSetShutdownNegotiation (iceConn, False);
01093     (void) IceCloseConnection( iceConn );
01094     }
01095 }
01096 
01097 void DCOPServer::newClient( int /*socket*/ )
01098 {
01099     IceAcceptStatus status;
01100     IceConn iceConn = IceAcceptConnection( static_cast<const  DCOPListener*>(sender())->listenObj, &status);
01101     if (!iceConn) {
01102       if (status == IceAcceptBadMalloc)
01103      qWarning("Failed to alloc connection object!\n");
01104       else // IceAcceptFailure
01105          qWarning("Failed to accept ICE connection!\n");
01106       return;
01107     }
01108 
01109     IceSetShutdownNegotiation( iceConn, False );
01110 
01111     IceConnectStatus cstatus;
01112     while ((cstatus = IceConnectionStatus (iceConn))==IceConnectPending) {
01113     (void) IceProcessMessages( iceConn, 0, 0 );
01114     }
01115 
01116     if (cstatus != IceConnectAccepted) {
01117     if (cstatus == IceConnectIOError)
01118         qWarning ("IO error opening ICE Connection!\n");
01119     else
01120         qWarning ("ICE Connection rejected!\n");
01121         deadConnections.removeRef(iceConn);
01122     (void) IceCloseConnection (iceConn);
01123     }
01124 }
01125 
01126 void* DCOPServer::watchConnection( IceConn iceConn )
01127 {
01128     DCOPConnection* con = new DCOPConnection( iceConn );
01129     connect( con, SIGNAL( activated(int) ), this, SLOT( processData(int) ) );
01130 
01131     clients.insert(iceConn, con );
01132     fd_clients.insert( IceConnectionNumber(iceConn), con);
01133 
01134     return static_cast<void*>(con);
01135 }
01136 
01137 void DCOPServer::removeConnection( void* data )
01138 {
01139     DCOPConnection* conn = static_cast<DCOPConnection*>(data);
01140 
01141     dcopSignals->removeConnections(conn);
01142 
01143     clients.remove(conn->iceConn );
01144     fd_clients.remove( IceConnectionNumber(conn->iceConn) );
01145 
01146     // Send DCOPReplyFailed to all in conn->waitingForReply
01147     while (!conn->waitingForReply.isEmpty()) {
01148     IceConn iceConn = conn->waitingForReply.take(0);
01149     if (iceConn) {
01150         DCOPConnection* target = clients.find( iceConn );
01151         qWarning("DCOP aborting call from '%s' to '%s'", target ? target->appId.data() : "<unknown>" , conn->appId.data() );
01152         QByteArray reply;
01153         DCOPMsg *pMsg;
01154         IceGetHeader( iceConn, majorOpcode, DCOPReplyFailed,
01155               sizeof(DCOPMsg), DCOPMsg, pMsg );
01156         pMsg->key = 1;
01157         pMsg->length += reply.size();
01158             _DCOPIceSendBegin( iceConn );
01159         DCOPIceSendData(iceConn, reply);
01160             _DCOPIceSendEnd();
01161             if (!target)
01162                qWarning("DCOP Error: unknown target in waitingForReply");
01163             else if (!target->waitingOnReply.removeRef(conn->iceConn))
01164                qWarning("DCOP Error: client in waitingForReply wasn't waiting on reply");
01165     }
01166     }
01167 
01168     // Send DCOPReplyFailed to all in conn->waitingForDelayedReply
01169     while (!conn->waitingForDelayedReply.isEmpty()) {
01170     IceConn iceConn = conn->waitingForDelayedReply.take(0);
01171     if (iceConn) {
01172         DCOPConnection* target = clients.find( iceConn );
01173         qWarning("DCOP aborting (delayed) call from '%s' to '%s'", target ? target->appId.data() : "<unknown>", conn->appId.data() );
01174         QByteArray reply;
01175         DCOPMsg *pMsg;
01176         IceGetHeader( iceConn, majorOpcode, DCOPReplyFailed,
01177               sizeof(DCOPMsg), DCOPMsg, pMsg );
01178         pMsg->key = 1;
01179         pMsg->length += reply.size();
01180             _DCOPIceSendBegin( iceConn );
01181         DCOPIceSendData( iceConn, reply );
01182             _DCOPIceSendEnd();
01183             if (!target)
01184                qWarning("DCOP Error: unknown target in waitingForDelayedReply");
01185             else if (!target->waitingOnReply.removeRef(conn->iceConn))
01186                qWarning("DCOP Error: client in waitingForDelayedReply wasn't waiting on reply");
01187     }
01188     }
01189     while (!conn->waitingOnReply.isEmpty())
01190     {
01191     IceConn iceConn = conn->waitingOnReply.take(0);
01192         if (iceConn) {
01193            DCOPConnection* target = clients.find( iceConn );
01194            if (!target)
01195            {
01196                qWarning("DCOP Error: still waiting for answer from non-existing client.");
01197                continue;
01198            }
01199            qWarning("DCOP aborting while waiting for answer from '%s'", target->appId.data());
01200            if (!target->waitingForReply.removeRef(conn->iceConn) &&
01201                !target->waitingForDelayedReply.removeRef(conn->iceConn))
01202               qWarning("DCOP Error: called client has forgotten about caller");
01203         }
01204     }
01205 
01206     if ( !conn->appId.isNull() ) {
01207 #ifndef NDEBUG
01208     qDebug("DCOP: unregister '%s'", conn->appId.data() );
01209 #endif
01210         if ( !conn->daemon )
01211         {
01212             currentClientNumber--;
01213         }
01214 
01215     appIds.remove( conn->appId );
01216 
01217         broadcastApplicationRegistration( conn, "applicationRemoved(QCString)", conn->appId );
01218     }
01219 
01220     delete conn;
01221 
01222     if ( suicide && (currentClientNumber == 0) )
01223     {
01224         m_timer->start( 10000 ); // if within 10 seconds nothing happens, we'll terminate
01225     }
01226 }
01227 
01228 void DCOPServer::slotTerminate()
01229 {
01230 #ifndef NDEBUG
01231     fprintf( stderr, "DCOPServer : slotTerminate() -> sending terminateKDE signal.\n" );
01232 #endif
01233     QByteArray data;
01234     dcopSignals->emitSignal(0L /* dcopserver */, "terminateKDE()", data, false);
01235     disconnect( m_timer, SIGNAL(timeout()), this, SLOT(slotTerminate()) );
01236     connect( m_timer, SIGNAL(timeout()), this, SLOT(slotSuicide()) );
01237     system(findDcopserverShutdown()+" --nokill");
01238 }
01239 
01240 void DCOPServer::slotSuicide()
01241 {
01242 #ifndef NDEBUG
01243     fprintf( stderr, "DCOPServer : slotSuicide() -> exit.\n" );
01244 #endif
01245     exit(0);
01246 }
01247 
01248 bool DCOPServer::receive(const QCString &/*app*/, const QCString &obj,
01249              const QCString &fun, const QByteArray& data,
01250              QCString& replyType, QByteArray &replyData,
01251              IceConn iceConn)
01252 {
01253     if ( obj == "emit")
01254     {
01255         DCOPConnection* conn = clients.find( iceConn );
01256         if (conn) {
01257         //qDebug("DCOPServer: %s emits %s", conn->appId.data(), fun.data());
01258         dcopSignals->emitSignal(conn, fun, data, false);
01259         }
01260         replyType = "void";
01261         return true;
01262     }
01263     if ( fun == "setDaemonMode(bool)" ) {
01264         QDataStream args( data, IO_ReadOnly );
01265         if ( !args.atEnd() ) {
01266             Q_INT8 iDaemon;
01267             bool daemon;
01268             args >> iDaemon;
01269 
01270             daemon = static_cast<bool>( iDaemon );
01271 
01272         DCOPConnection* conn = clients.find( iceConn );
01273             if ( conn && !conn->appId.isNull() ) {
01274                 if ( daemon ) {
01275                     if ( !conn->daemon )
01276                     {
01277                         conn->daemon = true;
01278 
01279 #ifndef NDEBUG
01280                         qDebug( "DCOP: new daemon %s", conn->appId.data() );
01281 #endif
01282 
01283                         currentClientNumber--;
01284 
01285 // David says it's safer not to do this :-)
01286 //                        if ( currentClientNumber == 0 )
01287 //                            m_timer->start( 10000 );
01288                     }
01289                 } else
01290                 {
01291                     if ( conn->daemon ) {
01292                         conn->daemon = false;
01293 
01294                         currentClientNumber++;
01295 
01296                         m_timer->stop();
01297                     }
01298                 }
01299             }
01300 
01301             replyType = "void";
01302             return true;
01303         }
01304     }
01305     if ( fun == "registerAs(QCString)" ) {
01306     QDataStream args( data, IO_ReadOnly );
01307     if (!args.atEnd()) {
01308         QCString app2 = readQCString(args);
01309         QDataStream reply( replyData, IO_WriteOnly );
01310         DCOPConnection* conn = clients.find( iceConn );
01311         if ( conn && !app2.isEmpty() ) {
01312         if ( !conn->appId.isNull() &&
01313              appIds.find( conn->appId ) == conn ) {
01314             appIds.remove( conn->appId );
01315 
01316         }
01317 
01318                 QCString oldAppId;
01319         if ( conn->appId.isNull() )
01320                 {
01321                     currentClientNumber++;
01322                     m_timer->stop(); // abort termination if we were planning one
01323 #ifndef NDEBUG
01324                     qDebug("DCOP: register '%s' -> number of clients is now %d", app2.data(), currentClientNumber );
01325 #endif
01326                 }
01327 #ifndef NDEBUG
01328         else
01329                 {
01330                     oldAppId = conn->appId;
01331             qDebug("DCOP:  '%s' now known as '%s'", conn->appId.data(), app2.data() );
01332                 }
01333 #endif
01334 
01335         conn->appId = app2;
01336         if ( appIds.find( app2 ) != 0 ) {
01337             // we already have this application, unify
01338             int n = 1;
01339             QCString tmp;
01340             do {
01341             n++;
01342             tmp.setNum( n );
01343             tmp.prepend("-");
01344             tmp.prepend( app2 );
01345             } while ( appIds.find( tmp ) != 0 );
01346             conn->appId = tmp;
01347         }
01348         appIds.insert( conn->appId, conn );
01349 
01350         int c = conn->appId.find( '-' );
01351         if ( c > 0 )
01352             conn->plainAppId = conn->appId.left( c );
01353         else
01354             conn->plainAppId = conn->appId;
01355 
01356                 if( !oldAppId.isEmpty())
01357                     broadcastApplicationRegistration( conn,
01358                         "applicationRemoved(QCString)", oldAppId );
01359                 broadcastApplicationRegistration( conn, "applicationRegistered(QCString)", conn->appId );
01360         }
01361         replyType = "QCString";
01362         reply << conn->appId;
01363         return true;
01364     }
01365     }
01366     else if ( fun == "registeredApplications()" ) {
01367     QDataStream reply( replyData, IO_WriteOnly );
01368     QCStringList applications;
01369     QAsciiDictIterator<DCOPConnection> it( appIds );
01370     while ( it.current() ) {
01371         applications << it.currentKey();
01372         ++it;
01373     }
01374     replyType = "QCStringList";
01375     reply << applications;
01376     return true;
01377     } else if ( fun == "isApplicationRegistered(QCString)" ) {
01378     QDataStream args( data, IO_ReadOnly );
01379     if (!args.atEnd()) {
01380         QCString s = readQCString(args);
01381         QDataStream reply( replyData, IO_WriteOnly );
01382         int b = ( findApp( s ) != 0 );
01383         replyType = "bool";
01384         reply << b;
01385         return true;
01386     }
01387     } else if ( fun == "setNotifications(bool)" ) {
01388     QDataStream args( data, IO_ReadOnly );
01389     if (!args.atEnd()) {
01390         Q_INT8 notifyActive;
01391         args >> notifyActive;
01392         DCOPConnection* conn = clients.find( iceConn );
01393         if ( conn ) {
01394         if ( notifyActive )
01395             conn->notifyRegister++;
01396         else if ( conn->notifyRegister > 0 )
01397             conn->notifyRegister--;
01398         }
01399         replyType = "void";
01400         return true;
01401     }
01402     } else if ( fun == "connectSignal(QCString,QCString,QCString,QCString,QCString,bool)") {
01403         DCOPConnection* conn = clients.find( iceConn );
01404         if (!conn) return false;
01405         QDataStream args(data, IO_ReadOnly );
01406         if (args.atEnd()) return false;
01407         QCString sender = readQCString(args);
01408         QCString senderObj = readQCString(args);
01409         QCString signal = readQCString(args);
01410         QCString receiverObj = readQCString(args);
01411         QCString slot = readQCString(args);
01412         Q_INT8 Volatile;
01413         args >> Volatile;
01414         //qDebug("DCOPServer: connectSignal(sender = %s senderObj = %s signal = %s recvObj = %s slot = %s)", sender.data(), senderObj.data(), signal.data(), receiverObj.data(), slot.data());
01415         bool b = dcopSignals->connectSignal(sender, senderObj, signal, conn, receiverObj, slot, (Volatile != 0));
01416         replyType = "bool";
01417         QDataStream reply( replyData, IO_WriteOnly );
01418         reply << (Q_INT8) (b?1:0);
01419         return true;
01420     } else if ( fun == "disconnectSignal(QCString,QCString,QCString,QCString,QCString)") {
01421         DCOPConnection* conn = clients.find( iceConn );
01422         if (!conn) return false;
01423         QDataStream args(data, IO_ReadOnly );
01424         if (args.atEnd()) return false;
01425         QCString sender = readQCString(args);
01426         QCString senderObj = readQCString(args);
01427         QCString signal = readQCString(args);
01428         QCString receiverObj = readQCString(args);
01429         QCString slot = readQCString(args);
01430         //qDebug("DCOPServer: disconnectSignal(sender = %s senderObj = %s signal = %s recvObj = %s slot = %s)", sender.data(), senderObj.data(), signal.data(), receiverObj.data(), slot.data());
01431         bool b = dcopSignals->disconnectSignal(sender, senderObj, signal, conn, receiverObj, slot);
01432         replyType = "bool";
01433         QDataStream reply( replyData, IO_WriteOnly );
01434         reply << (Q_INT8) (b?1:0);
01435         return true;
01436     }
01437 
01438     return false;
01439 }
01440 
01441 void DCOPServer::broadcastApplicationRegistration( DCOPConnection* conn, const QCString type,
01442     const QString& /*appId*/ )
01443 {
01444     QByteArray data;
01445     QDataStream datas( data, IO_WriteOnly );
01446     datas << conn->appId;
01447     QPtrDictIterator<DCOPConnection> it( clients );
01448     QByteArray ba;
01449     QDataStream ds( ba, IO_WriteOnly );
01450     ds <<QCString("DCOPServer") <<  QCString("") << QCString("")
01451        << type << data;
01452     int datalen = ba.size();
01453     DCOPMsg *pMsg = 0;
01454     while ( it.current() ) {
01455         DCOPConnection* c = it.current();
01456         ++it;
01457         if ( c->notifyRegister && (c != conn) ) {
01458             IceGetHeader( c->iceConn, majorOpcode, DCOPSend,
01459           sizeof(DCOPMsg), DCOPMsg, pMsg );
01460             pMsg->key = 1;
01461         pMsg->length += datalen;
01462             _DCOPIceSendBegin(c->iceConn);
01463         DCOPIceSendData( c->iceConn, ba );
01464             _DCOPIceSendEnd();
01465         }
01466     }
01467 }
01468 
01469 void
01470 DCOPServer::sendMessage(DCOPConnection *conn, const QCString &sApp,
01471                         const QCString &rApp, const QCString &rObj,
01472                         const QCString &rFun,  const QByteArray &data)
01473 {
01474    QByteArray ba;
01475    QDataStream ds( ba, IO_WriteOnly );
01476    ds << sApp << rApp << rObj << rFun << data;
01477    int datalen = ba.size();
01478    DCOPMsg *pMsg = 0;
01479 
01480    IceGetHeader( conn->iceConn, majorOpcode, DCOPSend,
01481                  sizeof(DCOPMsg), DCOPMsg, pMsg );
01482    pMsg->length += datalen;
01483    pMsg->key = 1; // important!
01484    _DCOPIceSendBegin( conn->iceConn );
01485    DCOPIceSendData(conn->iceConn, ba);
01486    _DCOPIceSendEnd();
01487 }
01488 
01489 void IoErrorHandler ( IceConn iceConn)
01490 {
01491     the_server->ioError( iceConn );
01492 }
01493 
01494 static bool isRunning(const QCString &fName, bool printNetworkId = false)
01495 {
01496     if (::access(fName.data(), R_OK) == 0) {
01497     QFile f(fName);
01498     f.open(IO_ReadOnly);
01499     int size = QMIN( 1024, f.size() ); // protection against a huge file
01500     QCString contents( size+1 );
01501     bool ok = f.readBlock( contents.data(), size ) == size;
01502     contents[size] = '\0';
01503     int pos = contents.find('\n');
01504     ok = ok && ( pos != -1 );
01505     pid_t pid = ok ? contents.mid(pos+1).toUInt(&ok) : 0;
01506     f.close();
01507     if (ok && pid && (kill(pid, SIGHUP) == 0)) {
01508         if (printNetworkId)
01509             qWarning("%s", contents.left(pos).data());
01510         else
01511         qWarning( "---------------------------------\n"
01512               "It looks like dcopserver is already running. If you are sure\n"
01513               "that it is not already running, remove %s\n"
01514               "and start dcopserver again.\n"
01515               "---------------------------------\n",
01516               fName.data() );
01517 
01518         // lock file present, die silently.
01519         return true;
01520     } else {
01521         // either we couldn't read the PID or kill returned an error.
01522         // remove lockfile and continue
01523         unlink(fName.data());
01524     }
01525     } else if (errno != ENOENT) {
01526         // remove lockfile and continue
01527         unlink(fName.data());
01528     }
01529     return false;
01530 }
01531 
01532 const char* const ABOUT =
01533 "Usage: dcopserver [--nofork] [--nosid] [--help]\n"
01534 "       dcopserver --serverid\n"
01535 "\n"
01536 "DCOP is KDE's Desktop Communications Protocol. It is a lightweight IPC/RPC\n"
01537 "mechanism built on top of the X Consortium's Inter Client Exchange protocol.\n"
01538 "It enables desktop applications to communicate reliably with low overhead.\n"
01539 "\n"
01540 "Copyright (C) 1999-2001, The KDE Developers <http://www.kde.org>\n"
01541 ;
01542 
01543 extern "C" int kdemain( int argc, char* argv[] )
01544 {
01545     bool serverid = false;
01546     bool nofork = false;
01547     bool nosid = false;
01548     bool suicide = false;
01549     for(int i = 1; i < argc; i++) {
01550     if (strcmp(argv[i], "--nofork") == 0)
01551         nofork = true;
01552     else if (strcmp(argv[i], "--nosid") == 0)
01553         nosid = true;
01554     else if (strcmp(argv[i], "--nolocal") == 0)
01555         ; // Ignore
01556     else if (strcmp(argv[i], "--suicide") == 0)
01557         suicide = true;
01558     else if (strcmp(argv[i], "--serverid") == 0)
01559         serverid = true;
01560     else {
01561         fprintf(stdout, ABOUT );
01562         return 0;
01563     }
01564     }
01565 
01566     if (serverid)
01567     {
01568        if (isRunning(DCOPClient::dcopServerFile(), true))
01569           return 0;
01570        return 1;
01571     }
01572 
01573     // check if we are already running
01574     if (isRunning(DCOPClient::dcopServerFile()))
01575        return 0;
01576     if (isRunning(DCOPClient::dcopServerFileOld()))
01577     {
01578        // Make symlink for compatibility
01579        QCString oldFile = DCOPClient::dcopServerFileOld();
01580        QCString newFile = DCOPClient::dcopServerFile();
01581        symlink(oldFile.data(), newFile.data());
01582        return 0;
01583     }
01584 
01585     struct rlimit limits; 
01586      
01587     int retcode = getrlimit(RLIMIT_NOFILE, &limits); 
01588     if (!retcode) { 
01589        if (limits.rlim_max > 512 && limits.rlim_cur < 512)
01590        {
01591           int cur_limit = limits.rlim_cur;
01592           limits.rlim_cur = 512; 
01593           retcode = setrlimit(RLIMIT_NOFILE, &limits); 
01594 
01595           if (retcode != 0)
01596           {
01597              qWarning("dcopserver: Could not raise limit on number of open files.");
01598              qWarning("dcopserver: Current limit = %d", cur_limit);
01599           }
01600        }
01601     }
01602 
01603     pipe(ready);
01604 
01605     if (!nofork) {
01606         pid_t pid = fork();
01607     if (pid > 0) {
01608         char c = 1;
01609         close(ready[1]);
01610         read(ready[0], &c, 1); // Wait till dcopserver is started
01611         close(ready[0]);
01612         // I am the parent
01613         if (c == 0)
01614             {
01615                // Test whether we are functional.
01616                DCOPClient client;
01617                if (client.attach())
01618                   return 0;
01619             }
01620             qWarning("DCOPServer self-test failed.");
01621             system(findDcopserverShutdown()+" --kill");
01622             return 1;
01623     }
01624     close(ready[0]);
01625 
01626     if (!nosid)
01627         setsid();
01628 
01629     if (fork() > 0)
01630         return 0; // get rid of controlling terminal
01631     }
01632 
01633     pipe(pipeOfDeath);
01634 
01635     signal(SIGHUP, sighandler);
01636     signal(SIGTERM, sighandler);
01637     signal(SIGPIPE, SIG_IGN);
01638 
01639     putenv(strdup("SESSION_MANAGER="));
01640 
01641     QApplication a( argc, argv, false );
01642     
01643     QSocketNotifier DEATH(pipeOfDeath[0], QSocketNotifier::Read, 0, 0);
01644     a.connect(&DEATH, SIGNAL(activated(int)), SLOT(quit()));
01645     
01646     IceSetIOErrorHandler (IoErrorHandler );
01647     DCOPServer *server = new DCOPServer(suicide); // this sets the_server
01648 
01649     int ret = a.exec();
01650     delete server;
01651     return ret;
01652 }
01653 
01654 #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 Fri Jan 21 09:56:57 2005 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003