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