dcop Library API Documentation

dcopserver.cpp

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