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 QByteArray data; 01322 dcopSignals->emitSignal(0L /* dcopserver */, "terminateKDE()", data, false); 01323 m_timer->start( 10000 ); // if within 10 seconds nothing happens, we'll terminate 01324 disconnect( m_timer, SIGNAL(timeout()), this, SLOT(slotTerminate()) ); 01325 connect( m_timer, SIGNAL(timeout()), this, SLOT(slotExit()) ); 01326 if (appIds.isEmpty()) 01327 slotExit(); // Exit now 01328 } 01329 } 01330 01331 void DCOPServer::slotExit() 01332 { 01333 #ifndef NDEBUG 01334 fprintf( stderr, "DCOPServer : slotExit() -> exit.\n" ); 01335 #endif 01336 #ifdef Q_OS_WIN 01337 SetEvent(m_evTerminate); 01338 if(m_dwTerminateThreadId != GetCurrentThreadId()) 01339 WaitForSingleObject(m_hTerminateThread,INFINITE); 01340 CloseHandle(m_hTerminateThread); 01341 #endif 01342 exit(0); 01343 } 01344 01345 bool DCOPServer::receive(const QCString &/*app*/, const QCString &obj, 01346 const QCString &fun, const QByteArray& data, 01347 QCString& replyType, QByteArray &replyData, 01348 IceConn iceConn) 01349 { 01350 #ifdef DCOP_LOG 01351 (*m_stream) << "Received a message: obj =\"" 01352 << obj << "\", fun =\"" 01353 << fun << "\", replyType =\"" 01354 << replyType << "\", data.size() =\"" 01355 << data.size() << "\", replyData.size() =" 01356 << replyData.size() << "\n"; 01357 m_logger->flush(); 01358 #endif 01359 01360 if ( obj == "emit") 01361 { 01362 DCOPConnection* conn = clients.find( iceConn ); 01363 if (conn) { 01364 //qDebug("DCOPServer: %s emits %s", conn->appId.data(), fun.data()); 01365 dcopSignals->emitSignal(conn, fun, data, false); 01366 } 01367 replyType = "void"; 01368 return true; 01369 } 01370 if ( fun == "setDaemonMode(bool)" ) { 01371 QDataStream args( data, IO_ReadOnly ); 01372 if ( !args.atEnd() ) { 01373 Q_INT8 iDaemon; 01374 bool daemon; 01375 args >> iDaemon; 01376 01377 daemon = static_cast<bool>( iDaemon ); 01378 01379 DCOPConnection* conn = clients.find( iceConn ); 01380 if ( conn && !conn->appId.isNull() ) { 01381 if ( daemon ) { 01382 if ( !conn->daemon ) 01383 { 01384 conn->daemon = true; 01385 01386 #ifndef NDEBUG 01387 qDebug( "DCOP: new daemon %s", conn->appId.data() ); 01388 #endif 01389 01390 currentClientNumber--; 01391 01392 // David says it's safer not to do this :-) 01393 // if ( currentClientNumber == 0 ) 01394 // m_timer->start( 10000 ); 01395 } 01396 } else 01397 { 01398 if ( conn->daemon ) { 01399 conn->daemon = false; 01400 01401 currentClientNumber++; 01402 01403 m_timer->stop(); 01404 } 01405 } 01406 } 01407 01408 replyType = "void"; 01409 return true; 01410 } 01411 } 01412 if ( fun == "registerAs(QCString)" ) { 01413 QDataStream args( data, IO_ReadOnly ); 01414 if (!args.atEnd()) { 01415 QCString app2 = readQCString(args); 01416 QDataStream reply( replyData, IO_WriteOnly ); 01417 DCOPConnection* conn = clients.find( iceConn ); 01418 if ( conn && !app2.isEmpty() ) { 01419 if ( !conn->appId.isNull() && 01420 appIds.find( conn->appId ) == conn ) { 01421 appIds.remove( conn->appId ); 01422 01423 } 01424 01425 QCString oldAppId; 01426 if ( conn->appId.isNull() ) 01427 { 01428 currentClientNumber++; 01429 m_timer->stop(); // abort termination if we were planning one 01430 #ifndef NDEBUG 01431 qDebug("DCOP: register '%s' -> number of clients is now %d", app2.data(), currentClientNumber ); 01432 #endif 01433 } 01434 #ifndef NDEBUG 01435 else 01436 { 01437 oldAppId = conn->appId; 01438 qDebug("DCOP: '%s' now known as '%s'", conn->appId.data(), app2.data() ); 01439 } 01440 #endif 01441 01442 conn->appId = app2; 01443 if ( appIds.find( app2 ) != 0 ) { 01444 // we already have this application, unify 01445 int n = 1; 01446 QCString tmp; 01447 do { 01448 n++; 01449 tmp.setNum( n ); 01450 tmp.prepend("-"); 01451 tmp.prepend( app2 ); 01452 } while ( appIds.find( tmp ) != 0 ); 01453 conn->appId = tmp; 01454 } 01455 appIds.insert( conn->appId, conn ); 01456 01457 int c = conn->appId.find( '-' ); 01458 if ( c > 0 ) 01459 conn->plainAppId = conn->appId.left( c ); 01460 else 01461 conn->plainAppId = conn->appId; 01462 01463 if( !oldAppId.isEmpty()) 01464 broadcastApplicationRegistration( conn, 01465 "applicationRemoved(QCString)", oldAppId ); 01466 broadcastApplicationRegistration( conn, "applicationRegistered(QCString)", conn->appId ); 01467 } 01468 replyType = "QCString"; 01469 reply << conn->appId; 01470 return true; 01471 } 01472 } 01473 else if ( fun == "registeredApplications()" ) { 01474 QDataStream reply( replyData, IO_WriteOnly ); 01475 QCStringList applications; 01476 QAsciiDictIterator<DCOPConnection> it( appIds ); 01477 while ( it.current() ) { 01478 applications << it.currentKey(); 01479 ++it; 01480 } 01481 replyType = "QCStringList"; 01482 reply << applications; 01483 return true; 01484 } else if ( fun == "isApplicationRegistered(QCString)" ) { 01485 QDataStream args( data, IO_ReadOnly ); 01486 if (!args.atEnd()) { 01487 QCString s = readQCString(args); 01488 QDataStream reply( replyData, IO_WriteOnly ); 01489 int b = ( findApp( s ) != 0 ); 01490 replyType = "bool"; 01491 reply << b; 01492 return true; 01493 } 01494 } else if ( fun == "setNotifications(bool)" ) { 01495 QDataStream args( data, IO_ReadOnly ); 01496 if (!args.atEnd()) { 01497 Q_INT8 notifyActive; 01498 args >> notifyActive; 01499 DCOPConnection* conn = clients.find( iceConn ); 01500 if ( conn ) { 01501 if ( notifyActive ) 01502 conn->notifyRegister++; 01503 else if ( conn->notifyRegister > 0 ) 01504 conn->notifyRegister--; 01505 } 01506 replyType = "void"; 01507 return true; 01508 } 01509 } else if ( fun == "connectSignal(QCString,QCString,QCString,QCString,QCString,bool)") { 01510 DCOPConnection* conn = clients.find( iceConn ); 01511 if (!conn) return false; 01512 QDataStream args(data, IO_ReadOnly ); 01513 if (args.atEnd()) return false; 01514 QCString sender = readQCString(args); 01515 QCString senderObj = readQCString(args); 01516 QCString signal = readQCString(args); 01517 QCString receiverObj = readQCString(args); 01518 QCString slot = readQCString(args); 01519 Q_INT8 Volatile; 01520 args >> Volatile; 01521 //qDebug("DCOPServer: connectSignal(sender = %s senderObj = %s signal = %s recvObj = %s slot = %s)", sender.data(), senderObj.data(), signal.data(), receiverObj.data(), slot.data()); 01522 bool b = dcopSignals->connectSignal(sender, senderObj, signal, conn, receiverObj, slot, (Volatile != 0)); 01523 replyType = "bool"; 01524 QDataStream reply( replyData, IO_WriteOnly ); 01525 reply << (Q_INT8) (b?1:0); 01526 return true; 01527 } else if ( fun == "disconnectSignal(QCString,QCString,QCString,QCString,QCString)") { 01528 DCOPConnection* conn = clients.find( iceConn ); 01529 if (!conn) return false; 01530 QDataStream args(data, IO_ReadOnly ); 01531 if (args.atEnd()) return false; 01532 QCString sender = readQCString(args); 01533 QCString senderObj = readQCString(args); 01534 QCString signal = readQCString(args); 01535 QCString receiverObj = readQCString(args); 01536 QCString slot = readQCString(args); 01537 //qDebug("DCOPServer: disconnectSignal(sender = %s senderObj = %s signal = %s recvObj = %s slot = %s)", sender.data(), senderObj.data(), signal.data(), receiverObj.data(), slot.data()); 01538 bool b = dcopSignals->disconnectSignal(sender, senderObj, signal, conn, receiverObj, slot); 01539 replyType = "bool"; 01540 QDataStream reply( replyData, IO_WriteOnly ); 01541 reply << (Q_INT8) (b?1:0); 01542 return true; 01543 } 01544 01545 return false; 01546 } 01547 01548 void DCOPServer::broadcastApplicationRegistration( DCOPConnection* conn, const QCString type, 01549 const QCString& appId ) 01550 { 01551 QByteArray data; 01552 QDataStream datas( data, IO_WriteOnly ); 01553 datas << appId; 01554 QPtrDictIterator<DCOPConnection> it( clients ); 01555 QByteArray ba; 01556 QDataStream ds( ba, IO_WriteOnly ); 01557 ds <<QCString("DCOPServer") << QCString("") << QCString("") 01558 << type << data; 01559 int datalen = ba.size(); 01560 DCOPMsg *pMsg = 0; 01561 while ( it.current() ) { 01562 DCOPConnection* c = it.current(); 01563 ++it; 01564 if ( c->notifyRegister && (c != conn) ) { 01565 IceGetHeader( c->iceConn, majorOpcode, DCOPSend, 01566 sizeof(DCOPMsg), DCOPMsg, pMsg ); 01567 pMsg->key = 1; 01568 pMsg->length += datalen; 01569 _DCOPIceSendBegin(c->iceConn); 01570 DCOPIceSendData( c->iceConn, ba ); 01571 _DCOPIceSendEnd(); 01572 } 01573 } 01574 } 01575 01576 void 01577 DCOPServer::sendMessage(DCOPConnection *conn, const QCString &sApp, 01578 const QCString &rApp, const QCString &rObj, 01579 const QCString &rFun, const QByteArray &data) 01580 { 01581 QByteArray ba; 01582 QDataStream ds( ba, IO_WriteOnly ); 01583 ds << sApp << rApp << rObj << rFun << data; 01584 int datalen = ba.size(); 01585 DCOPMsg *pMsg = 0; 01586 01587 IceGetHeader( conn->iceConn, majorOpcode, DCOPSend, 01588 sizeof(DCOPMsg), DCOPMsg, pMsg ); 01589 pMsg->length += datalen; 01590 pMsg->key = 1; // important! 01591 01592 #ifdef DCOP_LOG 01593 (*m_stream) << "Sending a message: sApp =\"" 01594 << sApp << "\", rApp =\"" 01595 << rApp << "\", rObj =\"" 01596 << rObj << "\", rFun =\"" 01597 << rFun << "\", datalen =" 01598 << datalen << "\n"; 01599 m_logger->flush(); 01600 #endif 01601 01602 _DCOPIceSendBegin( conn->iceConn ); 01603 DCOPIceSendData(conn->iceConn, ba); 01604 _DCOPIceSendEnd(); 01605 } 01606 01607 void IoErrorHandler ( IceConn iceConn) 01608 { 01609 the_server->ioError( iceConn ); 01610 } 01611 01612 static bool isRunning(const QCString &fName, bool printNetworkId = false) 01613 { 01614 if (::access(fName.data(), R_OK) == 0) { 01615 QFile f(fName); 01616 f.open(IO_ReadOnly); 01617 int size = QMIN( 1024, f.size() ); // protection against a huge file 01618 QCString contents( size+1 ); 01619 bool ok = f.readBlock( contents.data(), size ) == size; 01620 contents[size] = '\0'; 01621 int pos = contents.find('\n'); 01622 ok = ok && ( pos != -1 ); 01623 pid_t pid = ok ? contents.mid(pos+1).toUInt(&ok) : 0; 01624 f.close(); 01625 if (ok && pid && (kill(pid, SIGHUP) == 0)) { 01626 if (printNetworkId) 01627 qWarning("%s", contents.left(pos).data()); 01628 else 01629 qWarning( "---------------------------------\n" 01630 "It looks like dcopserver is already running. If you are sure\n" 01631 "that it is not already running, remove %s\n" 01632 "and start dcopserver again.\n" 01633 "---------------------------------\n", 01634 fName.data() ); 01635 01636 // lock file present, die silently. 01637 return true; 01638 } else { 01639 // either we couldn't read the PID or kill returned an error. 01640 // remove lockfile and continue 01641 unlink(fName.data()); 01642 } 01643 } else if (errno != ENOENT) { 01644 // remove lockfile and continue 01645 unlink(fName.data()); 01646 } 01647 return false; 01648 } 01649 01650 const char* const ABOUT = 01651 "Usage: dcopserver [--nofork] [--nosid] [--help]\n" 01652 " dcopserver --serverid\n" 01653 "\n" 01654 "DCOP is KDE's Desktop Communications Protocol. It is a lightweight IPC/RPC\n" 01655 "mechanism built on top of the X Consortium's Inter Client Exchange protocol.\n" 01656 "It enables desktop applications to communicate reliably with low overhead.\n" 01657 "\n" 01658 "Copyright (C) 1999-2001, The KDE Developers <http://www.kde.org>\n" 01659 ; 01660 01661 extern "C" DCOP_EXPORT int kdemain( int argc, char* argv[] ) 01662 { 01663 bool serverid = false; 01664 bool nofork = false; 01665 bool nosid = false; 01666 bool suicide = false; 01667 for(int i = 1; i < argc; i++) { 01668 if (strcmp(argv[i], "--nofork") == 0) 01669 nofork = true; 01670 else if (strcmp(argv[i], "--nosid") == 0) 01671 nosid = true; 01672 else if (strcmp(argv[i], "--nolocal") == 0) 01673 ; // Ignore 01674 else if (strcmp(argv[i], "--suicide") == 0) 01675 suicide = true; 01676 else if (strcmp(argv[i], "--serverid") == 0) 01677 serverid = true; 01678 else { 01679 fprintf(stdout, "%s", ABOUT ); 01680 return 0; 01681 } 01682 } 01683 01684 if (serverid) 01685 { 01686 if (isRunning(DCOPClient::dcopServerFile(), true)) 01687 return 0; 01688 return 1; 01689 } 01690 01691 // check if we are already running 01692 if (isRunning(DCOPClient::dcopServerFile())) 01693 return 0; 01694 #ifndef Q_OS_WIN32 01695 if (QCString(getenv("DCOPAUTHORITY")).isEmpty() && 01696 isRunning(DCOPClient::dcopServerFileOld())) 01697 { 01698 // Make symlink for compatibility 01699 QCString oldFile = DCOPClient::dcopServerFileOld(); 01700 QCString newFile = DCOPClient::dcopServerFile(); 01701 symlink(oldFile.data(), newFile.data()); 01702 return 0; 01703 } 01704 01705 struct rlimit limits; 01706 01707 int retcode = getrlimit(RLIMIT_NOFILE, &limits); 01708 if (!retcode) { 01709 if (limits.rlim_max > 512 && limits.rlim_cur < 512) 01710 { 01711 int cur_limit = limits.rlim_cur; 01712 limits.rlim_cur = 512; 01713 retcode = setrlimit(RLIMIT_NOFILE, &limits); 01714 01715 if (retcode != 0) 01716 { 01717 qWarning("dcopserver: Could not raise limit on number of open files."); 01718 qWarning("dcopserver: Current limit = %d", cur_limit); 01719 } 01720 } 01721 } 01722 #endif 01723 pipe(ready); 01724 01725 #ifndef Q_OS_WIN32 01726 if (!nofork) { 01727 pid_t pid = fork(); 01728 if (pid > 0) { 01729 char c = 1; 01730 close(ready[1]); 01731 read(ready[0], &c, 1); // Wait till dcopserver is started 01732 close(ready[0]); 01733 // I am the parent 01734 if (c == 0) 01735 { 01736 // Test whether we are functional. 01737 DCOPClient client; 01738 if (client.attach()) 01739 return 0; 01740 } 01741 qWarning("DCOPServer self-test failed."); 01742 system(findDcopserverShutdown()+" --kill"); 01743 return 1; 01744 } 01745 close(ready[0]); 01746 01747 if (!nosid) 01748 setsid(); 01749 01750 if (fork() > 0) 01751 return 0; // get rid of controlling terminal 01752 } 01753 01754 pipe(pipeOfDeath); 01755 01756 signal(SIGHUP, sighandler); 01757 signal(SIGTERM, sighandler); 01758 signal(SIGPIPE, SIG_IGN); 01759 #else 01760 { 01761 char c = 1; 01762 close(ready[1]); 01763 read(ready[0], &c, 1); // Wait till dcopserver is started 01764 close(ready[0]); 01765 } 01766 #endif 01767 putenv(strdup("SESSION_MANAGER=")); 01768 01769 QApplication a( argc, argv, false ); 01770 01771 IceSetIOErrorHandler (IoErrorHandler ); 01772 DCOPServer *server = new DCOPServer(suicide); // this sets the_server 01773 01774 #ifdef Q_OS_WIN 01775 SetConsoleCtrlHandler(DCOPServer::dcopServerConsoleProc,TRUE); 01776 #else 01777 QSocketNotifier DEATH(pipeOfDeath[0], QSocketNotifier::Read, 0, 0); 01778 server->connect(&DEATH, SIGNAL(activated(int)), SLOT(slotShutdown())); 01779 #endif 01780 01781 int ret = a.exec(); 01782 delete server; 01783 return ret; 01784 } 01785 01786 #ifdef Q_OS_WIN 01787 #include "dcopserver_win.cpp" 01788 #endif 01789 01790 #include "dcopserver.moc"