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