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