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